PEAR::Net_UserAgent_Mobile用のi-mode機種情報xml

Smarty動的Webサイト構築入門の5.3 Smartyとケータイで、PEAR::Net_UserAgent_Mobileパッケージを使っています。このパッケージは外部ファイルからi-mode機種情報を読み込むことができます。本の付録CDROMに、筆者作成のdocomo_netuamobile.xmlを収録しました。

ところが、このdocomo_netuamobile.xmlには不具合があり、下から5~6行目の「SO902WP+」以降を正しく読み込みません。次の2点の修正が必要です。

1点目は、docomo_netuamobile.xmlです。「SO902WP+」ではなく、「SO902WPplus」とします。

2点目は、keitai_ini.phpを修正して、「SO902WP+」自体を認識できるようにします。SO902WP+の場合は、Net_UserAgent_Mobile内部の機種情報を使い、そうでない場合は、docomo_netuamobile.xmlを使うようにします。

修正前
7: $_SERVER[‘DOCOMO_MAP’] = dirname(__FILE__) . “/docomo_netuamobile.xml”;
8: $agent = &Net_UserAgent_Mobile::factory();
9: $display = $agent->getDisplay();

修正後
7:  if ( ! preg_match(‘/SO902iWP[+]/’, $_SERVER[‘HTTP_USER_AGENT’]) ) {
8:     $_SERVER[‘DOCOMO_MAP’] = dirname(__FILE__) . “/docomo_netuamobile.xml”;
9:   }
10: $agent = &Net_UserAgent_Mobile::factory();
11: $display = $agent->getDisplay();

Smarty動的Webサイト構築入門
Amazon.co.jpの詳細ページへ

Smarty動的Webサイト構築入門

「Smarty動的Webサイト構築入門」が技術評論社から発売されました。原、青木、川野辺、鵜飼の共著で、私も執筆者の一人として参加しました。


Amazon.co.jpの詳細ページへ

本書はPHP上で動くテンプレートエンジン「Smarty」の解説書です。

Smartyを使った動的なサイトの構築方法から、応用テクニックまで幅広く説明します。

・テンプレートとは
・PHPの基礎
・Smartyの基礎
・CMSやブログの作成
・MySQLとphpMyAdmin
・Flashとの連携
・XMLの出力
・JavaScriptでフォトギャラリー
・ケータイ向けWebページ
・APIとの連携

“Smarty動的Webサイト構築入門” の続きを読む

FFFTP:コマンドプロンプトでミラーリングダウンロード

FFFTPはコマンドプロンプトでもミラーリングダウンロードができます。GUIでホスト設定してダウンロード確認してから、コマンドプロンプトで実行するので、とても安心感があります。

例えば、バッチファイルなら次のようになります。(実際は改行せず、1行です)

"C:Program Files\ffftp\ffftp.exe" -s ホストの設定名 --mirrordown --force --quit

–mirrordown ミラーリングダウンロード
–force ミラーリング開始の確認をしない
–quit ミラーリング終了後、FFFTPを閉じる

コマンドラインオプションでiniファイルを指定することもできます。FFFTPヘルプの目次>使い方>コマンドラインに説明があります。

history.txtによると、ver1.59からこの機能を追加したようです。(現在は、ver1.96b)

共有サーバ内のデイリーバックアップを自動ダウンロードしたいが、コマンドプロンプトは苦手なお客様にお薦めしています。

FFFTP

 

Smartyの{section}

Smartyの{section}のloop属性に「数値」を渡すと、ループ回数を指定できます。

{section name=i loop=10}

または

{assign var=n value=10}
{section name=i loop=$n}

上の例は、次のようなPHPに相当します。

for ( $i = 0; $i < 10; ++$i ) {

Smartyマニュアルの例のように、loop属性に配列を渡すと、count(配列)をループ回数にします。

{section name=i loop=$arr}

上の例は、次のようなPHPに相当します。

$n = count($arr);
for ( $i = 0; $i < $n; ++$i ) {

やっと、Smartyマニュアルの次の説明の意味が解けて、すっきりしています。
「loop属性で指定されたループ変数(たいていは配列)は、セクションのループ回数を決定するためにのみ使用されます」

C言語:float型の精度

  1. long型からfloat型への変換で精度落ちするはずが
  2. float型変数の初期化
  3. 計算過程を追ってみる
  4. いろいろ試す
  5. アセンブラを見る
  6. おまけ

1. long型からfloat型への変換で精度落ちするはずが

藤原博文著「Cプログラミング専門過程」(技術評論社)のサンプルプログラムを試していたときであった。

  • C言語の式にfloat型とlong型が混在するとき、long型からfloat型へ格上げされて、計算が行われる。
  • float型の有効桁数は、6桁である。
  • long型は、-134217728~134217727が範囲である。
  • long型の範囲でかつ±106を超える範囲では、float型の精度は、long型の精度より低くなる。

したがって次のプログラムでは精度落ちが発生し、計算結果は1234567890にはならない(はず)。

/* float1.c : long型からfloat型へ格上げ時の精度落ち */
#include <stdio.h>

main()
{
    long    lbig = 1234567890L;
    float   ften = 10.0f;
    long    l;

    /* 元の値 */
    printf( "lbig: %ldn", lbig );

    /*
     *      l = (lbig / ften) * ften;
     *
     *  の格上げの様子をキャスト演算子を使って示すと
     *
     *      l = (long)(  ((float)lbig / ften) * ften );
     *
     *  (float)lbig のキャスト時に、精度落ちが発生する
     */
    l = (lbig / ften) * ften;
    printf( "l   : %ldn", l );
}
/*EOF*/

実行結果

lbig: 1234567890
l   : 1234567890

精度が落ちると1234567936になっているはずが、精度落ちしていない!

浮動小数点プロセッサが普及していないころのCコンパイラは、計算途中ではfloat型を常にdouble型で計算を行い、変数に保存するときにfloat型で保存していた。VisualCもfloat型をdouble型として計算しているのだろうか?

2. float型変数の初期化

まず、変数の初期化と表示だけを行った。float型は、1234567890.0を保持できないため、1234567936.0 となる。そのときのメモリ内容は、数値の上位桁から 4e 93 2c 06である。

printf( “%08lx”, *(long *)&f ); は、メモリ内容の16進表示を行っている。

/* float2.c : float型の 1234567890.0 の確認 */
#include <stdio.h>

main()
{
    float f1, f2;

    /* float型の定数で初期化する */
    f1 = 1234567890.0f;
    printf( "f1: %f %08lxn", f1, *(long *)&f1 );

    /* メモリ内容ズバリで初期化する */
    *(long *)&f2 = 0x4e932c06;
    printf( "f2: %f %08lxn", f2, *(long *)&f2 );
}
/*EOF*/

結果は次のとおり。数値表現では1234567936.0, メモリ内容は 0x4e932c06 であった。ここでは、float型の精度に落ちている。

f1: 1234567936.000000 4e932c06
f2: 1234567936.000000 4e932c06

3. 計算過程を追ってみる

float1.cのプログラムの計算式、l = (lbig / ften) * ften の計算過程を各段階で値を表示する。

/* float3.c : float1.cの段階を追う */
#include <stdio.h>

main()
{
    long    lbig = 1234567890L;
    float   ften = 10.0f;
    long    l;
    float   f;

    /* l = (lbig / ften) * ften; */

    /* 予想(1)
     * long型からfloat型への変換で精度落ちする。
     * 1234567936.0
     */
    f = lbig;
    printf( "(1) %fn", f );

    /* 予想(2)
     * 計算式をdouble型で計算する。
     * 123456789.0
     */
    f = lbig / ften;
    printf( "(2) %fn", f );

    /* 予想(3)
     * 計算式をdouble型で計算するが、
     * fに保存するとき度落ち。
     * 1234567936.0
     */
    f = (lbig / ften) * ften;
    printf( "(3) %fn", f );

    /* 予想(4)
     * 精度落ちしたものが復活するはずないので、
     * 1234567936
     */
    l = f;
    printf( "(4) %ldn", l );
}
/*EOF*/

結果

    実際         予想
(1) 1234567890.000000    1234567936.0 ×
(2) 123456789.000000     123456789.0  ○
(3) 1234567890.000000    1234567936.0 ×
(4) 1234567936           12345678936 ○

(3)まで精度落ちしていない。計算途中はdouble型精度で計算していると推測できる。(4)でようやく精度落ちしている。変数f は double型→float型の精度落ちした値が保存されたと推測できる。それならば、(1)(3)でも、変数 f には精度落ちした値が保存されているはずだが、精度落ちしていない。

そういえば、printf のような引数の個数が可変の関数に float型が渡されるときは、double型に変換されてから渡される。計算途中はdouble型精度の一時変数が存在し、この一時変数が printf に渡されているとしたら、精度落ちしない表示がされる。そして一時変数からではなく、float型変数から読み出してきたとき、精度落ちした表示がされるのではないか?

例えばコンパイラが次のような実装だったとすれば。

main()
{
 float  f;
 double tmp; /* 一時変数 */

  tmp = lbig;
  f = tmp;
  printf( "...", tmp );

  tmp = lbig / ften;
  f = tmp;
  printf( "...", tmp );

  l = f; /* l = tmp; なら精度落ちも小さいが、何らかの理由でそうではない */
  printf( "...", l );
}

このようなdouble型の一時変数は、一昔前のコンパイラである。いまどきのコンパイラは、float型のまま計算しているはずである。また「何らかの理由」が思いつかない。

4. いろいろ試す

もう一つ考えられるのが、浮動小数点プロセッサ内のレジスタが常にdouble型であること。一種の、グローバルな一時変数とでも言えるだろう。

double tmp; /* 一時変数 */

main()
{
 float  f;

  tmp = lbig;
  f = tmp;
  printf( "...", tmp );

  tmp = lbig / ften;
  f = tmp;
  printf( "...", tmp );

  l = f; /* 何らかの理由で、 l = tmp; ではない */
  printf( "...", l );
}

「何らかの理由」は、直前で関数(この例ではprintf)を呼んでいることだろう。関数内で浮動小数点の計算をしていたら、double tmp; の値は変化しているだろうから。つまり計算の流れが続いているときは double tmp、すなわち浮動小数点プロセッサのレジスタの値を使う。計算の流れが断ち切られたら、本来のfloat変数を読みにいっているのではないか?

/* float4.c : いろいろ試す */
#include <stdio.h>

void func(void);
void print_float( float f );
void print_double( double d );

main()
{
    long    l = 1234567890L;
    float   f, g;

    f = l;
    printf( "%fn", f );
    printf( "%fn", f );  /* 連続して printf */
    printf( "n" );

    f = l;
    g = f * 2.0;        /* 別の計算を挿入 */
    printf( "%fn", f );
    printf( "n" );

    f = l;
    func();             /* 関数を挿入 */
    printf( "%fn", f );
    printf( "n" );

    f = l;
    print_double( f );
    print_float( f );
    printf( "n" );

    f = l;
    print_float( f );
    print_double( f );
    printf( "n" );

    f = l;
    print_double( f );
    print_double( f );
    printf( "n" );
}

void func(void)
{
}

void print_float( float f )
{
    printf( "%fn", f );
}

void print_double( double d )
{
    printf( "%fn", d );
}
/*EOF*/

どうやら予想はあっていそうだ。

float4.c
1234567890.000000
1234567936.000000 精度落ち

1234567936.000000 精度落ち

1234567936.000000 精度落ち

1234567890.000000
1234567936.000000 精度落ち

1234567936.000000 精度落ち
1234567936.000000 精度落ち

1234567890.000000
1234567936.000000 精度落ち

5. アセンブラを見てみる

念のため、アセンブラコードを見る。(実はアセンブラコードを見てから予想したのです)

  1. ViuaslCの[プロジェクト]-[設定…]を開く。
  2. [プロジェクトの設定]ダイアログの右側で、[C/C++]タブを開く。
  3. 中央の[カテゴリ]一覧から、”ファイルリスティング”を選択する。
  4. [リスティングファイルタイプ]一覧から、”ソースコードを含む”を選択する。
  5. [OK]で閉じて、コンパイルする。
  6. Debugフォルダ内の、拡張子asm が、アセンブラファイルである。
; 37   :
; 38   :     f = l;

    fild  DWORD PTR _l$[ebp]  ....(1)
    fst   DWORD PTR _f$[ebp]  ....(2)

; 39   :     print_double( f );

    sub   esp, 8
    fstp  QWORD PTR [esp]     ....(3)
    call  _print_double
    add   esp, 8

; 40   :     print_double( f );

    fld   DWORD PTR _f$[ebp]  ....(4)
    sub   esp, 8
    fstp  QWORD PTR [esp]     ....(5)
    call  _print_double
    add   esp, 8

擬似コードで書き直すため、内部レジスタを reg, スタック(関数の引数を渡すための領域)を stk とすると

(1)、reg = (double)l;
(2)、f = (float)reg; 精度落ち発生!
(3)、stk = reg;
(4)、reg = (float)f;
(5)、stk = reg;

(3)でレジスタの値をスタックに保存している、値の由来は(1)。
(5)でレジスタの値をスタックに保存している、値の由来は(4)、すなわち精度落ちした変数f。

6. おまけ

long型からfloat型への格上げ(?)の問題が、VisualCでの float型の扱いの確認になってしまった。

VisualC+Pentiumでは、float型の変数は、float型のまま扱われていることがわかった。コンパイラの最適化と浮動小数点プロセッサのおかげで、float型でも精度落ちが発生しにくいことがわかった。(コンパイルオプションでプロセッサを386/486に指定しても結果は同じだった)精度落ち「しにくい」であって「しない」わけではない。

変数のサイズは、float 4バイト、double 8バイトである。floatのメリットはサイズが小さいので、巨大配列が必要な場合はメモリを節約でき、転送時間が速い。

しかしC標準の算術関数は、引数も戻り値も double型ばかりである。float型<–>double型の型変換の警告はかなりうるさい。また引数を渡すときの float型<–>double型の型変換は、処理が遅くなってしまう可能性がある。

精度としては、通常はfloat型の精度で十分である。計算精度が問題になるような処理では当然double型を使うだろう。

初心者に浮動小数点を教えるときに、どちらから教えるかは悩みどころだ。筆者自身は特に理由がなければ、double型を使っている。

float型
printf/scanf で、両方 “%f” を使うことができる。
(printfにfloat型を渡すときは、自動的にdouble型に変換されている)
sin/cosなどの算術関数の引数はdouble型が多いので「型変換をしました」警告がうっとおしい。
double型
printfでは “%f”、scanfでは “%lf” と使い分ける必要がある。

(2000年頃)

C言語:2進数表記マクロ

C言語はアセンブラに近いにもかかわらず、なぜか2進表記ができません。ハードウェアに近いところを触っているときは、特に思います。

unsigned char a = 01000101B;

のような記述がなぜできないのか!と思ったのは私だけではないでしょう。独自の拡張をしているコンパイラもあるようです。

しかし、いまや、2進数表記マクロを作ることができます。

コンパイラの条件は、
・long型が32ビットであること。
・プリプロセッサに文字列連結演算子 ## があること。

#define B8(y)  ( ((0x##y##L & 0x00000001L) >>  0) 
               | ((0x##y##L & 0x00000010L) >>  3) 
               | ((0x##y##L & 0x00000100L) >>  6) 
               | ((0x##y##L & 0x00001000L) >>  9) 
               | ((0x##y##L & 0x00010000L) >> 12) 
               | ((0x##y##L & 0x00100000L) >> 15) 
               | ((0x##y##L & 0x01000000L) >> 18) 
               | ((0x##y##L & 0x10000000L) >> 21) )

#define B16(h,l)            (B8(h)<<8 | B8(l))
#define B32(hh, hl, lh, ll) (B16(hh, hl)<<16 | B16(lh, ll))

main()
{
    unsigned char      a=B8(10101010);
    unsigned short int b=B16(10101010, 01010101);
    unsigned long      c=B32(10101010, 01010101,
                                       11111111, 00000000);

    printf( "%02lxn", a ); /* AA と表示するはず */
    printf( "%04lxn", b ); /* AA55 と表示するはず */
    printf( "%08lxn", c ); /* AA55FF00 と表示するはず */
}

(2000年頃の記事)

C言語:extern

全ファイル中のどこかに定義してある、という意味です。複数ファイル構成で分割コンパイルする場合に必要で、一般的には共通ヘッダファイルに記述します。

ファイルが一つだけのプログラムでは、あまり使い道がありません。しかし厳密には、ファイルが一つでもライブラリ内の関数や変数を参照する場合にextern宣言が必要です。ライブラリはコンパイル済みCファイルの集合体だからです。
1. 関数のプロトタイプ宣言

関数のプロトタイプ宣言にexternを付ける付けないは、あまり問題になりません。

----------- a.c -----------
       int func_A( int n );    /* OK */
extern int func_A( int n );    /* OK */

int func_A( int n )
{
}

----------- b.c -----------
       int func_A( int n );    /* OK */
extern int func_A( int n );    /* OK */

int func_C( int n )
{
    /* a.c の func_A を呼んでいる */
    func_A( n );
}

externは「全ファイル中のどれかに定義されている」ですから、 extern int func_A(int n); は、a.c でも、b.c でも同じように宣言できます。

staticではない関数は、ファイル外部から見えると解釈されるので、関数のexternは省略できます。

2. 変数

2.1 正しい使い方

まずは正しい使い方です。a.c 内で定義された int a; を b.c 内で使っています。

----------- a.c -----------
int    a;

func_A()
{
    a = 1;
}
----------- b.c -----------
extern int    a;

func_B()
{
    a = 2;
}

2.2 コンパイルエラー

この例では、a = 2; の a は、b.c内では定義されていないよ!とコンパイルエラーになります。

----------- b.c -----------
/* extern int    a; */

func_B()
{
    a = 2;
}

2.3 リンクエラー(1)

----------- b.c -----------
/*extern*/ int    a;

func_B()
{
    a = 2;
}

コンパイルはOKです。しかしリンクで、変数 a が、a.cとb.cの2箇所で定義されています?とリンクエラーになります。一つのプログラム内では、同じ名前のグローバル変数は複数使えません。(グローバル変数とは、関数の外で定義した変数)

似たような例を紹介しますが、一つの関数内では、同じ名前のローカル変数を複数使えません。(ローカル変数とは、関数の中で定義した変数)

func_A()
{
    int    x;
    int    x;    /* エラー */
}

2.4 リンクエラー(2)

----------- a.c -----------
/* int a; を定義しない */

----------- b.c -----------
extern int    a;    /* どこかにあるはず */

func_B()
{
    a = 2;
}

b.c のコンパイルはOKです。b.cの変数aの扱いは「別ファイルにあるはず」と、変数aに関する問題を先送りにします。しかしリンクのとき、全ファイルを見渡しても変数a はどこにもない、とリンクエラーになります。

(2000年頃の記事)

MS-DOS:ESCシーケンス

WindowsのMSDOS窓では、一部はサポートされていません。この資料は(おそらく)NECのPC9801用MS-DOSリファレンスからの引用です。

エスケープシーケンス 機能
ESC [pl;pcH Direct cursor
addressing
カーソルを指定位置に移動します。
pl=mのときはm行目でmが最終行の値より大きい場合は最終行に位置付けます。
pl=0あるいはplが省略された場合は1行目に位置付けます。
pc=nのときはnカラム目でnが最終カラムより大きい場合は最終カラムに位置付け、pc=0あるいはpcが省略された場合は1カラム目に位置付けます。
ESC [pl;pcf ESC[pl;pcHと同様の処理を行います。
ESC =lc ESC[pl;pcHと同様の処理を行います。
この場合パラメータ lc
は2進数で20Hのオフセットが加えられた値です。
l
は行位置の指定であり1行目に位置付けるには20Hとなります。
c はカラム位置の指定であり1カラム目に位置付けるには20Hとなります。
l
c のパラメータは省略できません。
ESC [pnA Cursor up
カーソルをおなじカラム位置で上にn行(pn=n)移動します。カーソルが先頭行にある場合、あるいは先頭行を越えた場合には先頭行に位置します。pnが省略されるかpn=0の場合はpn=1として処理します。
ESC [pnB Cursor down
カーソルをおなじカラム位置で下にn行(pn=n)移動します。カーソルが最終行にある場合、あるいは最終行を越えた場合には最終行に位置します。pnが省略されるかpn=0の場合はpn=1として処理します。
ESC [pnC Cursor
foreward
カーソルを右にn文字(pn=n)移動します。カーソルが行の右端にある場合、あるいは右端を越えた場合には右端に位置します。pnが省略されるかpn=0の場合はpn=1として処理します。
ESC [pnD Cursor
backward
カーソルを左にn文字(pn=n)移動します。カーソルが行の左端にある場合、あるいは左端を越えた場合には左端に位置します。pnが省略されるかpn=0の場合はpn= 1として処理します。
ESC [0J Clear from cursor to end of screen
カーソル位置から最終行の右端までクリアします。カーソル位置はそのままです。パラメータ0は省略できます。
ESC [1J Clear from beginning of screen to cursor
先頭行の左端から、カーソル位置までをクリアします。カーソル位置はそのままです。
ESC [2J Clear screen
CRT画面をすべてクリアします。カーソル位置はホーム位置となります。
ESC * ESC[2Jと同様の処理を行います。
ESC [0K Clear from cursor to end of line
カーソル位置から、行の右端までをクリアします。カーソル位置はそのままです。パラメータ0は省略できます。
ESC [1K Clear from beginning of line to cursor
行の左端から、カーソル位置までをクリアします。カーソル位置はそのままです。
ESC [2K Clear entire line containing cursor
カーソルが位置している行の左端から右端までをクリアします。カーソル位置はそのままです。
ESC [pnM Delete line
カーソルの位置する行から下にn行(pn=n)削除し、以降の行を上に詰めます。カーソルの位置は詰められた行の左端になります。最終行を越えての削除は行われません。pnが省略されるかpn=0の場合はpn=1として処理します。
ESC [pnL Insert line
カーソルの位置する行以降をn行(pn=n)下に移動し、空白のn行を挿入します。カーソルは先頭の挿入行の左端に位置します。挿入行が最終行を越えた場合、移動する行が最終行を越えた場合は、その越えた行は失なわれますpnが省略されるかpn=0の場合はpn=1として処理します。
ESC D Index
カーソルと同じカラム位置で1行下に移動します。カーソルが最終行にある場合は1行スクロールアップします。
ESC E Next line
カーソルを1行下の行の左端に移動します。カーソルが最終行にある場合は1行スクロールアップします。
ESC M Reverse index
カーソルを同じカラム位置で1行上に移動します。カーソルが先頭行にある場合は1行スクロールダウンします。
ESC [s Save cursor position
カーソル位置(行、カラム)とその表示文字の属性をセーブします。
ESC [u Set cursor position
ESC[sでセーブした内容を戻します。以前にESC[sが実行されていない場合は、カーソルはホーム位置に移動し、属性は規定値となります。
ESC [6n Cursor position report
カーソル位置を直後のコンソール入力呼出しにて知らせます。その形式はESC[pl;pcRです。
ESC )0 Select kanji mode
ESC )3 Select graph mode
ESC[>5l Enable cursor display
ESC[>5h Disable cursor display
ESC[>1h Enable bottom line
ESC[>1l Disable botttom line
ESC[>3h Select 20 line
ESC[>3l Select 25 line mode
ESC [ps;...;psm Character attribute

ps
0 規定の属性
1 ハイライト(モノクロのみ)
2 バーティカルライン
4 アンダーライン
5 ブリンク
7 リバース
16 8 シークレット30 黒 淡(暗)
18 34 青
17 31 赤
19 35 紫
20 32 緑
22 36 水色
21 33 黄色
23 37 白 濃(明)
40 リバース黒
41 リバース赤
42 リバース緑
43 リバース黄色
44 リバース青
45 リバース紫
46 リバース水色
47 リバース白

(2000年頃の記事)

MS-DOS:ASCII制御コード

WindowsのMSDOS窓では、一部はサポートされていません。この資料は(おそらく)NECのPC9801用MS-DOSリファレンスからの引用です。

記号 16進数 機能
BEL 07 Sound bell
ブザーを約1秒鳴らします。
BS 08 Cursor backward
カーソルを1文字左に移動します。カーソルが行の左端にある場合は1行上の右端に移動し、カーソルがホーム位置にある場合は何もしません。
HT 09 Skip to next tab stop.
カーソルを次のタブ位置に移動します。
タブ位置は次のように決められています。
08, 16, 24, 32, 40, 48, 56, 64, 72
カーソルが72カラム目より右側にある場合は1行下の左端に移動し、最終行の場合は1行スクロールアップします。
LF 0A Cursor down
カーソルをおなじカラム位置で1行下に移動します。カーソルが最終行にある場合は何もしません。
VT 0B Cursor up
カーソルをおなじカラム位置で1行上に移動します。カーソルが先頭行にある場合は何もしません。
FF 0C Cursor foreward
カーソルを1文字右に移動します。
カーソルが行の右端にある場合は1行下の左端に移動し、カーソルが最終行の右端にある場合は1行スクロールアップして左端に移動します。
CR 0D Cursor to left margin
カーソルを行の左端に移動します。
SUB 1A Clear screen. (and Cursor HOME)
CRT画面をすべてクリアします。カーソルはホーム位置となります。
ESC 1B Introduce on ESC sequence
エスケープコードです。
RS 1E Cursor HOME
カーソルをホーム位置に移動します。

(2000年頃の記事)