このページの目次

コマンド処理

1行の処理(^ParseLine)から呼び出されて1つのコマンド(命令文)をコンパイルします。

コマンドの解析

コマンドの種類を解析して、対応するルーチンに分岐します。分岐先のルーチンでは一気にコード生成まで行います。rvtlの文法では、基本的にコマンドの先頭の1文字だけでコマンドの種類が決まるため、先頭文字からIFで対応する処理に分岐させます。

18670 :----------------------------------------------------------
18680 : 1命令の処理
18690 :----------------------------------------------------------
18700 ^Command
18710   c=C(I) I=I+1
18720   d=c
18730   ;=D>1 / "<com [" $=d "]>"
18740   c=C(I)  : 1文字先読み
18750   ;=d='!' #=^Com_GOSUB    : 21  !  GOSUB
18760   ;=d='"' #=^Com_String   : 22  "  文字列出力
18770   ;=d='#' #=^Com_GO       : 23  #  GOTO 実行中の行番号を保持
18780   ;=d='$' #=^Com_OutChar  : 24  $  文字コード出力
18790   ;=d='%' #=^Com_Error    : 25  %  直前の除算の剰余または usec を保持
18800   ;=d='&' #=^Com_Error    : 26  &  NEW, VTLコードの最終使用アドレスを保持
18810   ;=d=$27 #=^Com_Error    : 27  '  文字定数
18820   ;=d='(' #=^Com_FileWrite :28  (  File 書き出し
18830   ;=d=')' #=^Com_FileRead : 29  )  File 読み込み, 読み込みサイズ保持
18840   ;=d='*' #=^Com_BRK      : 2A  *  メモリ最終(brk)を設定, 保持
18850   ;=d='+' #=^Com_VarPush  : 2B  +  ローカル変数PUSH, 加算演算子, 絶対値
18860   ;=d=',' #=^Com_Exec     : 2C  ,  fork & exec
18870   ;=d='-' #=^Com_VarPop   : 2D  -  ローカル変数POP,減算演算子,負の十進数
18880   ;=d='.' #=^Com_Space    : 2E  .  空白出力
18890   ;=d='/' #=^Com_NewLine  : 2F  /  改行出力, 除算演算子
18900   ;=d=':' #=^Com_Comment  : 3A  :  行末まで注釈
18910   ;=d=';' #=^Com_IF       : 3B  ;  IF
18920   ;=d='<' #=^Com_Error    : 3C  <  rvtlコードのファイル出力
18930   ;=d='=' #=^Com_Error    : 3D  =  コード先頭アドレス
18940   ;=d='>' #=^Com_Error    : 3E  >  rvtlコードのファイル入力
18950   ;=d='?' #=^Com_OutNum   : 3F  ?  数値出力  数値入力
18960   ;=d='@' #=^Com_DO       : 40  @  DO UNTIL NEXT
18970   ;=d='\' #=^Com_Ext      : 5C  \  拡張用  除算演算子(unsigned)
18980   ;=d=']' #=^Com_Return   : 5D  ]  RETURN
18990   ;=d='^' #=^Com_Label    : 5E  ^  ラベル宣言, 排他OR演算子, ラベル参照
19000   ;=d='_' #=^Com_USleep   : 5F  _  usleep, gettimeofday
19010   ;=d='`' #=^Com_RANDOM   : 60  `  擬似乱数を保持 (乱数シード設定)
19020   ;=d='{' #=^Com_FileTop  : 7B  {  ファイル先頭(ヒープ領域)
19030   ;=d='|' #=^Com_Function : 7C  |  組み込みコマンド, エラーコード保持
19040   ;=d='}' #=^Com_FileEnd  : 7D  }  ファイル末(ヒープ領域)
19050   ;=d='~' #=^Com_Exit     : 7E  ~  VTL終了
19060   ;=(d>='A')*(d<='z') #=^Com_Var
19070   #=^Com_Error
19080 ]
19090 ^NextCom
19100 :
19110 ]

変数代入

右辺の式の値を左辺の変数に代入します。変数には単純変数、配列変数があります。変数名は先頭の1文字のみ有効で続く英文字は読み飛ばされます。変数名の次の文字で単純変数、配列変数、文字列のコピーを判断します。

19120 :----------------------------------------------------------
19130 : 代入文
19140 :----------------------------------------------------------
19150 ^Com_Var
19160    ;=D>1 "<Com_Var>"
19170    !=^SkipAlfa
19180    v=d                            : variable
19190    ;=C(I)='=' !=^SimpleVar  #=^NextCom
19200    ;=C(I)='*' !=^CopyString #=^NextCom
19210    ;=C(I)='(' !=^SetArray1  #=^NextCom
19220    ;=C(I)='{' !=^SetArray2  #=^NextCom
19230    ;=C(I)='[' !=^SetArray4  #=^NextCom
19240    !=^Com_Error
19250  #=^NextCom
19260 :

単純変数

単純変数は A から Z と a から z までの 52 個の変数を使うことができます。変数名は 1 文字だけですが、冗長型として Integer, Command, ok などのように長い名前を使うことができます。 この場合には I と Integer は同じものとみなされることに注意してください。単純変数を格納する領域はあらかじめ用意されています。単純変数への代入文の右辺にカンマをはさんで式が続いた場合は FOR文となります。

19270 ^SimpleVar
19280    I=I+1                          : skip =
19290    !=^Expression
19300    +=$9E89 !=^PutObj2             : MOV [ESI+v*4],EBX
19310    +=v*4   !=^PutObj4
19320    ;=C(I)=',' !=^ForCmd           : FOR statement
19330 ]
19340 :

文字列を配列に代入

代入文の特殊な形式として文字列からなる配列(256KBまで)を別の領域の配列にコピーする命令があります。配列のためのメモリ領域を確保しておく必要があります。文字列定数を代入する形式もあります。実行後、% に文字数を返します。文字列を直接指定した場合は 256 バイト以内に限定されます。

    例: 200 A*=e            : 256 キロバイトまで
        210 B*="ABCDEFG"    : 256 バイト以内

コピー先とコピー元のアドレスが同じ場合はコピーは実行されず、% に文字が 0 となるまでの文字数を返します。文字列長を求めることができます。

    例: 200 A*=A ?=%
19350 ^CopyString
19360    I=I+2                          : skip *=
19370    ;=C(I)='"' #=^CopyString2
19380    !=^Expression
19390    +=$868B  !=^PutObj2            : MOV EAX, [ESI+v*4]
19400    +=v*4    !=^PutObj4
19410    !=^XchgAXBX                    : EBX <--> EAX
19420    +=$B9    !=^PutObj1            : MOV ECX, 256K
19430    +=256*1024 !=^PutObj4
19440    +=41     !=^CallLib            : call CopyString
19450    +=$DB31  !=^PutObj2            : XOR EBX,EBX
19460    +=$B3    !=^PutObj1            : MOV BL, '%'
19470    +='%'    !=^PutObj1
19480    +=$89    !=^PutObj1            : MOV [ESI+EBX*4], ECX
19490    +=$9E0C  !=^PutObj2
19500 ]
19510 ^CopyString2
19520    I=I+1                          : skip =
19530    +=$9E8B  !=^PutObj2            : MOV EBX, [ESI+v*4]
19540    +=v*4    !=^PutObj4
19550    !=^GetString                   : eax : string top address
19560    +=$B9    !=^PutObj1            : MOV ECX, 126
19570    +=126    !=^PutObj4
19580    +=41     !=^CallLib            : call CopyString
19590    +=$DB31  !=^PutObj2            : XOR EBX,EBX
19600    +=$B3    !=^PutObj1            : MOV BL, '%'
19610    +='%'    !=^PutObj1
19620    +=$89    !=^PutObj1            : MOV [ESI+EBX*4], ECX
19630    +=$9E0C  !=^PutObj2
19640 ]
19650 :

配列への代入

配列は C 言語の配列のように配列を格納する領域の先頭アドレスを配列名に設定しておく必要があります。以下の例では変数Aに空きメモリ先頭アドレスを代入し、変数Bには変数Aより100バイト大きい値を設定しています。これで変数Aは100バイトの領域を配列として使用できます。1バイト配列では A(99)まで、2バイト配列では A{49}まで、4バイト配列ではA[24]までが B(0) と重ならないでアクセスできます。配列 B は残りの空きメモリをすべて使用可能です。システム変数「*」は rvtl のプログラムの大きさ + 空きメモリ = 256Kバイトが初期値になっていますが、システム変数「*」を再設定すればメモリの許す限りの領域をアクセスできます。

    例:
    100 A=&
    110 B=A+100
    120 A(99)=123
    130 B[5]=456
    140 ?=B[5]

配列への代入は配列のインデックスの式を最初に評価した結果に設定される ebx をスタックに退避します。次に代入する式を評価して ebx を eax に転送、配列のインデックスの値を ebx に戻し、配列のサイズを乗じた後、配列変数のアドレスに加えます。結果として得られたアドレスに eax を転送します。

19660 ^SetArray1
19670    I=I+1
19680    !=^Expression                  : index
19690    +=$53    !=^PutObj1            : PUSH EBX
19700    I=I+2                          : skip ')='
19710    !=^Expression
19720    !=^XchgAXBX
19730    +=$5B    !=^PutObj1            : POP  EBX
19740    +=$8E8B  !=^PutObj2
19750    +=v*4    !=^PutObj4            : MOV ECX,[ESI+v*4]
19760    +=$88    !=^PutObj1
19770    +=$1904  !=^PutObj2            : MOV [ECX+EBX*4],AL
19780 ]
19790 :
19800 ^SetArray2
19810    I=I+1
19820    !=^Expression                  : index
19830    +=$53    !=^PutObj1            : PUSH EBX
19840    I=I+2                          : skip '}='
19850    !=^Expression
19860    !=^XchgAXBX
19870    +=$5B    !=^PutObj1            : POP  EBX
19880    +=$8E8B  !=^PutObj2
19890    +=v*4    !=^PutObj4            : MOV ECX,[ESI+v*4]
19900    +=$8966  !=^PutObj2
19910    +=$5904  !=^PutObj2            : MOV [ECX+EBX*4],AX
19920 ]
19930 :
19940 ^SetArray4
19950    I=I+1                          : skip [
19960    !=^Expression                  : index
19970    +=$53    !=^PutObj1            : PUSH EBX
19980    I=I+2                          : skip ']='
19990    !=^Expression
20000    !=^XchgAXBX
20010    +=$5B    !=^PutObj1            : POP  EBX
20020    +=$8E8B  !=^PutObj2
20030    +=v*4    !=^PutObj4            : MOV ECX,[ESI+v*4]
20040    +=$89    !=^PutObj1
20050    +=$9904  !=^PutObj2            : MOV [ECX+EBX*4],AX
20060 ]
20070 :

FOR - NEXT

FOR文は代入文にカンマと終了値を加えた形式で記述します。NEXT文は @=A+1 のようにFOR文で使った変数を @= の直後に置きます。増分は +1 以外の値も可能です。また乗算を使用することもできます。通常、繰り返し回数が決まっている場合に使います。

    100 A=1,10
    110  "do something"
    120 @=A+1

1行中にFOR-NEXTループを置くこともできます。この例では 1 から 10までの数値を順に詰めて表示して最後に改行します。

    100 A=1,10 ?=A @=A+1 /

次のように降順にすることもできます。

    100 A=10,1
    110   "do something " ?=A /
    120 @=A-1

rvtlcでは終了条件が制御変数の値と一致する必要があります。

1010 I=1,10
1020   ?=I
1030 @=I+1

上のコードをコンパイルして、逆アセンブルすると次のようになります。

1010 I=1,10   : FOR 
 80494da:       b8 01 00 00 00          mov    $0x1,%eax
 80494df:       93                      xchg   %eax,%ebx
 80494e0:       89 9e 24 01 00 00       mov    %ebx,0x124(%esi)
 80494e6:       b8 0a 00 00 00          mov    $0xa,%eax
 80494eb:       93                      xchg   %eax,%ebx
 80494ec:       53                      push   %ebx

1020   ?=I
 80494ed:       8b 86 24 01 00 00       mov    0x124(%esi),%eax
 80494f3:       e8 ef ed ff ff          call   0x80482e7

1030 @=I+1    : NEXT
 80494f8:       8b 86 24 01 00 00       mov    0x124(%esi),%eax
 80494fe:       93                      xchg   %eax,%ebx
 80494ff:       b8 01 00 00 00          mov    $0x1,%eax
 8049504:       01 c3                   add    %eax,%ebx
 8049506:       93                      xchg   %eax,%ebx
 8049507:       bb 49 00 00 00          mov    $0x49,%ebx
 804950c:       8b 0c 9e                mov    (%esi,%ebx,4),%ecx
 804950f:       89 04 9e                mov    %eax,(%esi,%ebx,4)
 8049512:       5b                      pop    %ebx
 8049513:       53                      push   %ebx
 8049514:       39 d9                   cmp    %ebx,%ecx
 8049516:       0f 85 d1 ff ff ff       jne    0x80494ed
 804951c:       5b                      pop    %ebx

FOR

変数に初期値を代入して、終了条件をスタックに積み、戻りアドレスを登録します。

20080 :----------------------------------------------------------
20090 : For
20100 :----------------------------------------------------------
20110 ^ForCmd
20120    ;=D>1 "<for>"
20130    : T[U*3]:type, T[U*3+1]:var_addr, T[U*3+2]:jmp_addr
20140    I=I+1
20150    U=U+1
20160    T[U*3]=1                       : for
20170    T[U*3+1]=v                     : variable
20180    !=^Expression                  : 終了条件
20190    !=^PushBX                      : PUSH EBX
20200    T[U*3+2]=P+H['B']
20210 ]
20220 :

NEXT

変数の値を読んでスタック上の終了条件と一致しなければ、変数の値を更新して繰り返します。更新後の変数値を終了条件と比較するとループが1回少なくなってしまうため、更新前に変数の値を終了条件と比較します。


変数の増分が正負のどちらも可能なため、「終了条件と一致」しないと終了しません。「終了条件と一致するか超えたら」という実行時の判定は行っていません。

20230 :----------------------------------------------------------
20240 : Next
20250 :----------------------------------------------------------
20260 ^Next
20270    ;=D>1 "<next>"
20280    :I=I+1
20290    !=^Expression                  : 変数値更新
20300    !=^XchgAXBX
20310    +=$BB !=^PutObj1               : MOV EBX, variable
20320    +=Table[U*3+1] !=^PutObj4
20330    +=$8B   !=^PutObj1             : MOV ECX,[ESI+EBX*4]
20340    +=$9E0C !=^PutObj2
20350    +=$89   !=^PutObj1             : MOV [ESI+EBX*4],EAX
20360    +=$9E04 !=^PutObj2
20370    +=$5B   !=^PutObj1             : POP EBX
20380    +=$53   !=^PutObj1             : PUSH EBX
20390    +=$D939 !=^PutObj2             : CMP ECX,EBX
20400    +=$850F !=^PutObj2             : JNZ xxxxx
20410    +=T[U*3+2] !=^PutAddr          :
20420    +=$5B   !=^PutObj1             : POP EBX
20430    U=U-1
20440 ]
20450 :

DO - UNTIL

UNTIL文の式 e の値が偽(0)の場合には @ (DO) に戻ります。真(0以外)なら次の文を実行します。NEXT文と異なり式を括弧で囲む必要があります。繰り返し回数が実行するまでわからない場合に使います。

1040 I=0
1050 @
1060   ?=I
1070   I=I+1
1080 @=(I>10)

上のコードをコンパイルして、逆アセンブルすると次のようになります。

1040 I=0
 804951d:       b8 00 00 00 00          mov    $0x0,%eax
 8049522:       93                      xchg   %eax,%ebx
 8049523:       89 9e 24 01 00 00       mov    %ebx,0x124(%esi)

1050 @        : DO
1060   ?=I
 8049529:       8b 86 24 01 00 00       mov    0x124(%esi),%eax
 804952f:       e8 b3 ed ff ff          call   0x80482e7

1070   I=I+1
 8049534:       8b 86 24 01 00 00       mov    0x124(%esi),%eax
 804953a:       93                      xchg   %eax,%ebx
 804953b:       b8 01 00 00 00          mov    $0x1,%eax
 8049540:       01 c3                   add    %eax,%ebx
 8049542:       89 9e 24 01 00 00       mov    %ebx,0x124(%esi)

1080 @=(I>10) : UNTIL
 8049548:       8b 86 24 01 00 00       mov    0x124(%esi),%eax
 804954e:       93                      xchg   %eax,%ebx
 804954f:       b8 0a 00 00 00          mov    $0xa,%eax
 8049554:       39 c3                   cmp    %eax,%ebx
 8049556:       bb 01 00 00 00          mov    $0x1,%ebx
 804955b:       7f 02                   jg     0x804955f
 804955d:       31 db                   xor    %ebx,%ebx
 804955f:       85 db                   test   %ebx,%ebx
 8049561:       0f 84 c2 ff ff ff       je     0x8049529

DO

現在のアドレスを戻り先アドレスとしてテーブルに登録するだけでコードは生成しません。

24440 :----------------------------------------------------------
24450 : DO NEXT UNTIL
24460 :----------------------------------------------------------
24470 ^Com_DO
24480    c=C(I)
24490    ;=c<>'=' !=^DoCmd #=^NextCom
24500    I=I+1
24510    c=C(I)
24520    ;=c='(' !=^Until #=^NextCom
24530    ;=((c>='A')&(c<='Z'))|((c>='a')&(c<='z')) !=^Next
24540   #=^NextCom
24550 :----------------------------------------------------------
24560 : Do
24570 :----------------------------------------------------------
24580 ^DoCmd
24590    ;=D>1 "<do>"
24600    : T[U*3]:type, T[U*3+1]:var_addr, T[U*3+2]:jmp_addr
24610    U=U+1
24620    Table[U*3+2]=P+H['B']
24630    Table[U*3]=2          : 2 : do
24640 ]

UNTIL

式の処理を行って結果が0の場合、DOで登録されたテーブルの戻りアドレスにジャンプするコードを生成します。

24650 :----------------------------------------------------------
24660 : Until
24670 :----------------------------------------------------------
24680 ^Until
24690    ;=D>1 "<until>"
24700    I=I+1
24710    !=^Expression
24720    +=$DB85 !=^PutObj2             : TEST EBX,EBX
24730    +=$840F !=^PutObj2             : JZ 戻る
24740    +=Table[U*3+2] !=^PutAddr
24750    U=U-1
24760 ]

GOSUB

指定した行番号にジャンプします。飛び先で ](リターン文)が実行されると次の文から実行が再開されます。!=^Label のように飛び先をラベルとした場合はラベル宣言(^Label)した行の次の行にジャンプします。

    例: !=100
    例: !=^LINE

飛び先がラベルの場合はラベル項の結果のアドレス、行番号の場合は行番号:アドレステーブルから得られるアドレスに対する call 命令を生成します。飛び先アドレスは相対アドレスを設定します。

20460 :----------------------------------------------------------
20470 : GOSUB
20480 :----------------------------------------------------------
20490 ^Com_GOSUB
20500    I=I+1                           : skip =
20510    ;=C(I)='^' +=$D3 !=^JumpOrCall #=^NextCom
20520    !=^GetNo n=r
20530    ;=Q>1   !=^SearchLine
20540    +=$E8   !=^PutObj1              : CALL r
20550    +=r     !=^PutAddr
20560  #=^NextCom

GOTO

指定した行番号にジャンプします。#=^Label のように飛び先をラベルとした場合はラベル宣言(^Label)した行の次の行にジャンプします。

    例: #=100
    例: #=^STOP

飛び先がラベルの場合はラベル項の結果のアドレス、行番号の場合は行番号:アドレステーブルから得られるアドレスに対する jmp 命令を生成します。飛び先アドレスは相対アドレスを設定します。

20570 :----------------------------------------------------------
20580 : GOTO
20590 :----------------------------------------------------------
20600 ^Com_GO
20610    I=I+1                           : skip =
20620    ;=C(I)='^' +=$E3 !=^JumpOrCall  #=^NextCom
20630    !=^GetNo n=r
20640    ;=n<0   #=^Com_Exit             : 終了
20650    ;=Q>1   !=^SearchLine
20660    +=$E9   !=^PutObj1              : JMP r
20670    +=r     !=^PutAddr
20680  #=^NextCom
20690 :
20700 ^JumpOrCall
20710    !=^Expression         : ラベル項はアドレスを返す
20720    +=$FF   !=^PutObj1              :
20730    !=^PutObj1                      : call/jmp [ebx]
20740  ]
20750 :

数値取得

ソース内の10進数または16進数を表す文字列を数値に変換します。コンパイラは生成する実行ファイルに行番号の情報を持たないため、GOTO(#=)、GOSUB(!=) ともに計算型GOTO/GOSUBは使用できません。計算を含む式を評価してそこへジャンプした場合、ほぼ確実に暴走します。rvtlc では 行番号を示す定数でなければコンパイラはエラーを表示して終了します。

12320 :----------------------------------------------------------
12330 : ソースから数値を取得 10進  結果:r
12340 :----------------------------------------------------------
12350 ^GetNo
12360    +f
12370    r=0 f=0
12380    c=C(I)
12390    ;=(c='$') #=^GetHex
12400    ;=(c='-') f=1 I=I+1 c=C(I)
12410    @
12420      ;=(c>='0')&(c<='9') r=r*10+(c-'0') I=I+1 c=C(I)
12430    @=((c<'0')|(c>'9'))
12440    ;=D>2 / "getNo:" ?=r /
12450    ;=f>0 r=-r
12460    -f
12470 ]
12480 :
12490 :----------------------------------------------------------
12500 : ソースから数値を取得 16進  結果:r
12510 :----------------------------------------------------------
12520 ^GetHex
12530    I=I+1
12540    c=C(I)
12550    r=0
12560    @
12570      f=1
12580      ;=(c>='0')&(c<='9') r=r*16+(c-'0') I=I+1 c=C(I) f=0
12590      ;=(c>='A')&(c<='F') r=r*16+(c-'A'+10) I=I+1 c=C(I) f=0
12600      ;=(c>='a')&(c<='f') r=r*16+(c-'a'+10) I=I+1 c=C(I) f=0
12610    @=(f=1)
12620    ;=D>2 / "getHex:" ??=r /
12630 ]
12640 :

文字列出力

文字列は文字列直後へのジャンプ命令を生成し、コード内に文字列を埋め込み、文字列表示ルーチンに文字列先頭アドレスを渡します。

      4byte       12byte
       +----+----+----+----+
       | jump 命令         | ---+
       +----+----+----+----+    |
  addr |  S |  t |  r | i  |    |
       +----+----+----+----+    |
       |  n |  g |  s | 0  |    |
       +----+----+----+----+    |
       | call 文字列表示   | <--+
       +----+----+----+----+
20760 :----------------------------------------------------------
20770 : 文字列出力
20780 :----------------------------------------------------------
20790 ^Com_String
20800    ;=D>1 "<String>"
20810    ;=C(I)='"' I=I+1 #=^NextCom
20820    !=^GetString
20830    +=5 !=^CallLib                  : OutAsciiZ
20840    I=I+1
20850   #=^NextCom
20860 :
20870 :----------------------------------------------------------
20880 : 文字列をコードに埋め込み、先頭アドレスをEAXに設定
20890 :----------------------------------------------------------
20900 ^GetString
20910    +qsj
20920    q=Obj+P+2 s=P+2
20930    +=$EB  !=^PutObj1               : JMP SHORT xxxx
20940    j=0;
20950    ;=C(I)='"' #=^gs
20960    @
20970      q(j)=C(I)
20980      I=I+1
20990      j=j+1
21000      ;=j>127 !=^Com_Error
21010    @=((C(I)='"')|(C(I)=0))
21020   ^gs
21030    q(j)=0
21040    +=j+1        !=^PutObj1         : jump here
21050    P=P+j+1
21060    +=$B8        !=^PutObj1         : MOV EAX,
21070    +=s+H['B']   !=^PutObj4         : 文字列先頭
21080    -jsq
21090 ]
21100 :

1文字表示

1文字を表示するコードを生成します。

形式 動作
$=e 式の値を文字コードとして出力。
$$=e 式の値を2文字の文字コードとして出力。
$#=e 式の値を4文字の文字コードとして出力。
$*=e 式の値を先頭アドレスとするASCIIZ文字列を出力。
21110 :----------------------------------------------------------
21120 : 1文字表示
21130 :----------------------------------------------------------
21140 ^Com_OutChar
21150    ;=D>1 "<OutChar>"
21160    c=C(I)
21170    ;=c='=' I=I+1 #=^OutChar1
21180    ;=c='$' I=I+1 #=^OutChar2
21190    ;=c='#' I=I+1 #=^OutChar4
21200    ;=c='*' I=I+1 #=^OutString
21210    #=^Com_Error
21220 ^OutChar1
21230    !=^Expression
21240    !=^XchgAXBX
21250    +=7 !=^CallLib                 : OutChar
21260    #=^NextCom
21270 ^OutChar2
21280    I=I+1                          : skip =
21290    !=^Expression
21300    +=$F888  !=^PutObj2            : MOV AL,BH
21310    +=7 !=^CallLib                 : OutChar
21320    +=$d888  !=^PutObj2            : MOV AL,BL
21330    +=7 !=^CallLib                 : OutChar
21340    #=^NextCom
21350 ^OutChar4
21360    I=I+1                          : skip =
21370    !=^Expression
21380    !=^PushBX
21390    +=$c1    !=^PutObj1            : SHR EBX,16
21400    +=$10eb  !=^PutObj2
21410    +=$F888  !=^PutObj2            : MOV AL,BH
21420    +=7 !=^CallLib                 : OutChar
21430    +=$d888  !=^PutObj2            : MOV AL,BL
21440    +=7 !=^CallLib                 : OutChar
21450    !=^PopBX
21460    +=$F888  !=^PutObj2            : MOV AL,BH
21470    +=7 !=^CallLib                 : OutChar
21480    +=$d888  !=^PutObj2            : MOV AL,BL
21490    +=7 !=^CallLib                 : OutChar
21500    #=^NextCom
21510 :

文字列表示

式を評価して文字列先頭アドレスを取得し、文字列表示ルーチンをcallします。

21520 :----------------------------------------------------------
21530 : $*=
21540 :----------------------------------------------------------
21550 ^OutString
21560    I=I+1                          : skip *=
21570    ;=C(I)='"' #=^OutString2
21580    !=^Expression
21590    !=^XchgAXBX                    : EBX <--> EAX
21600    +=05     !=^CallLib            : call OutAsciiZ
21610  #=^NextCom
21620 :
21630 ^OutString2
21640    ;=C(I)='"' I=I+1 #=^NextCom    : empty string
21650    I=I+1                          : skip "
21660    !=^GetString                   : eax : string top address
21670    +=05     !=^CallLib            : call OutAsciiZ
21680   #=^NextCom
21690 :

エラー

コンパイラが文法のエラーを検出した場合に行番号と位置を行の内容を表示します。

21700 :----------------------------------------------------------
21710 : エラー
21720 :----------------------------------------------------------
21730 ^Com_Error
21740   / "Error (line:" ?=N[1] " at " ?=I ")" /
21750   ;=D>1 i=0 p=N+8 @  " " ?$=p(i) i=i+1 @=(p(i)=0) /
21760   i=0 p=N+8 @  $=p(i) i=i+1 @=(p(i)=0) /
21770   .=I-1 "^"
21780   !=^PrintLabel
21790   #=-1

ファイル出力

{ (ファイル先頭位置) と } (ファイル末位置) で指定した範囲を""に囲まれたファイル名のファイルとして書き込みます。0で終了する文字列の先頭アドレスを渡す形式も使用できます。

    例: (="ファイル名"
        (*=A

ファイル先頭位置「{」 と ファイル末位置「}」を取得して、ランタイムライブラリを呼び出すコードを生成します。

21800 :----------------------------------------------------------
21810 : ファイル出力
21820 :----------------------------------------------------------
21830 ^Com_FileWrite
21840    ;=D>1 "<FileWrite>"
21850    ;=C(I)<>'*' #=^Com_FWrite2
21860    I=I+2                           : skip *=
21870    !=^Expression                   : ebx : &filename
21880    #=^Com_FWrite3
21890    :
21900 ^Com_FWrite2
21910    I=I+2                           : skip ="
21920    : 文字列をコードに埋め込み、先頭アドレスをEAXに設定
21930    !=^GetString
21940    !=^XchgAXBX                     : EBX <--> EAX
21950 ^Com_FWrite3
21960    +=$C931  !=^PutObj2             : XOR ECX,ECX
21970    +=$B1    !=^PutObj1             : MOV CL, '}'
21980    +='}'    !=^PutObj1
21990    +=$8B    !=^PutObj1             : MOV EAX,[ESI+ECX*4]
22000    +=$8E04  !=^PutObj2             :
22010    +=$B1    !=^PutObj1             : MOV CL, '{'
22020    +='{'    !=^PutObj1
22030    +=$8B    !=^PutObj1             : MOV ECX,[ESI+ECX*4]
22040    +=$8E0C  !=^PutObj2             : buffer
22050    +=$C829  !=^PutObj2             : SUB EAX,ECX
22060    : eax:size, ebx:filename, ecx:buffer
22070    +=46     !=^CallLib             : call FileWrite
22080    +=$C931  !=^PutObj2             : XOR ECX,ECX
22090    +=$B1    !=^PutObj1             : MOV CL, '|'
22100    +='|'    !=^PutObj1
22110    +=$89    !=^PutObj1             : MOV [ESI+ECX*4],EAX
22120    +=$8E04  !=^PutObj2
22130    #=^NextCom
22140 :

ファイル入力

""に囲まれたファイル名のファイルを { (ファイル先頭位置) で指定されたメモリアドレスに読み込みます。読み込んだファイルサイズは }-{(ファイル末位置 - ファイル先頭位置) で求めることができます。0で終了する文字列の先頭アドレスを渡す形式も使用できます。

    例: )="ファイル名"
        )*=A

ファイル先頭位置「{」を取得して、ランタイムライブラリを呼び出すコードを生成します。ファイルバッファー最終位置を「}」、読み込みサイズを「)」に設定するコードを生成します。

22150 :----------------------------------------------------------
22160 : ファイル入力
22170 :----------------------------------------------------------
22180 ^Com_FileRead
22190    ;=D>1 "<FileRead>"
22200    ;=C(I)<>'*' #=^Com_FRead2
22210    I=I+2                           : skip *=
22220    !=^Expression                   : ebx : &filename
22230    #=^Com_FRead3
22240 ^Com_FRead2
22250    I=I+2                           : skip ="
22260    : 文字列をコードに埋め込み、先頭アドレスをEAXに設定
22270    !=^GetString
22280    !=^XchgAXBX                     : EBX <--> EAX
22290 ^Com_FRead3
22300    +=$C931  !=^PutObj2             : XOR ECX,ECX
22310    +=$B1    !=^PutObj1             : MOV CL, '{'
22320    +='{'    !=^PutObj1
22330    +=$8B    !=^PutObj1             : MOV ECX,[ESI+ECX*4]
22340    +=$8E0C  !=^PutObj2             : buffer
22350    !=^PushBX
22360    +=47     !=^CallLib             : call FileSize
22370    !=^PopBX
22380    +=$C289  !=^PutObj2             : MOV EDX,EAX
22390    : ebx:filename, ecx:buffer, edx:size
22400    +=48     !=^CallLib             : call FileRead
22410    +=$C289  !=^PutObj2             : MOV EDX,EAX
22420    +=$C801  !=^PutObj2             : ADD EAX,ECX
22430    +=$C931  !=^PutObj2             : XOR ECX,ECX
22440    +=$B1    !=^PutObj1             : MOV CL, '}'
22450    +='}'    !=^PutObj1
22460    +=$89    !=^PutObj1             : MOV [ESI+ECX*4],EAX
22470    +=$8E04  !=^PutObj2             :
22480    +=$B1    !=^PutObj1             : MOV CL, ')'
22490    +=')'    !=^PutObj1
22500    +=$89    !=^PutObj1             : MOV [ESI+ECX*4],EDX
22510    +=$8E14  !=^PutObj2             :
22520    #=^NextCom
22530 :

メモリの拡張

rvtlcは指定しない場合、約256KBのデータ領域を確保します。brk システムコールを実行して使用可能なメモリ終端位置を変更します。

22540 :----------------------------------------------------------
22550 : メモリの拡張
22560 :----------------------------------------------------------
22570 ^Com_BRK
22580    I=I+1
22590    !=^Expression
22600    +=$B8    !=^PutObj1            : MOV EAX, SYS_brk(45)
22610    +=45     !=^PutObj4
22620    +=$80CD  !=^PutObj2            : INT 0x80
22630    +=$DB31  !=^PutObj2            : XOR EBX,EBX
22640    +=$B3    !=^PutObj1            : MOV BL, '*'  RAM末設定
22650    +='*'    !=^PutObj1
22660    +=$89    !=^PutObj1            : MOV [ESI+EBX*4], EAX
22670    +=$9E04  !=^PutObj2
22680   #=^NextCom
22690 :

PUSH 変数スタック

変数は変数専用スタックに格納(プッシュ)することができます。例えば「 +ABC 」の場合は、変数が A、B、C の順にスタックに保存されます。1文字以上の長さの変数名の場合でも先頭の1文字だけを指定します。スタックから戻す(ポップ)場合は「 -CBA 」とプッシュした順の逆に戻します。サブルーチンの先頭で使う変数をプッシュ、リターンの前でポップすることで、変数を局所変数として使うことができ、再帰的にサブルーチンを呼び出すことができます。「+=式」で式の値を変数を介さず変数スタックに格納することができます。また、式中で「;」を参照すると変数スタックトップを返します(ポップ)。


実行時にメモリ中に用意した変数スタックに変数の内容を格納するコードを生成します。変数スタック用のスタックポインタは edi を使用します。

22700 :----------------------------------------------------------
22710 : 変数をスタックへPUSH
22720 :----------------------------------------------------------
22730 ^Com_VarPush
22740    ;=C(I)='=' !=^PushExp #=^NextCom
22750    c=C(I)
22760    +=$C931  !=^PutObj2            : XOR ECX,ECX
22770    @
22780      +=$B1    !=^PutObj1          : MOV CL, VAR
22790      +c       !=^PutObj1
22800      +=$8B    !=^PutObj1          : MOV EAX, [ESI+ECX*4]
22810      +=$8E04  !=^PutObj2
22820      +=$0789  !=^PutObj2          : MOV [EDI],EAX
22830      +=$EF81  !=^PutObj2          : SUB EDI,4
22840      +=$04    !=^PutObj4
22850      I=I+1
22860      c=C(I)
22870    @=(((c<'A')|(c>'Z'))&((c<'a')|(c>'z')))
22880   #=^NextCom
22890 ^PushExp
22900    I=I+1
22910    !=^Expression
22920    +=$1F89 !=^PutObj2             : MOV [EDI],EBX
22930    +=$EF81 !=^PutObj2             : SUB EDI,4
22940    +=$04   !=^PutObj4
22950 ]
22960 :

POP 変数スタック

変数スタックに退避していた値を変数へ戻ひます。プッシュした順の逆に戻します。

23220 :----------------------------------------------------------
23230 : 変数スタックから POP
23240 :----------------------------------------------------------
23250 ^Com_VarPop
23260    c=C(I)
23270    +=$C931  !=^PutObj2            : XOR ECX,ECX
23280    @
23290      +=$C781 !=^PutObj2           : ADD EDI,4
23300      +=04    !=^PutObj4
23310      +=$078B !=^PutObj2           : MOV EAX,[EDI]
23320      +=$B1   !=^PutObj1           : MOV CL, VAR
23330      +c      !=^PutObj1
23340      +=$89   !=^PutObj1           : MOV [ESI+ECX*4],EAX
23350      +=$8E04 !=^PutObj2
23360      I=I+1
23370      c=C(I)
23380    @=(((c<'A')|(c>'Z'))&((c<'a')|(c>'z')))
23390   #=^NextCom

子プロセスの起動

子プロセスを起動します。実行ファイル名はフルパスで指定して下さい。 | を使ったパイプと > による出力のリダイレクトが可能です。

    例: ,="/bin/ls -lt | /usr/bin/grep e >test.txt"  "DONE" /

上記のようにコマンドの後ろに文が続かない場合は、次のような省略記法が使えます。

    例: , /bin/ls -lt | /usr/bin/grep e >test.txt

0で終了する文字列の先頭アドレスを渡す形式も使用できます。文字列の先頭アドレスを変数に設定した後に実行します。

    例: ,*=A

文字列の先頭アドレスを設定して、ランタイムライブラリを呼び出すコードを生成します。

22970 :----------------------------------------------------------
22980 : 外部コマンドの実行
22990 :----------------------------------------------------------
23000 ^Com_Exec
23010    ;=D>1 "<Exec>"
23020    ;=C(I)<>'*' #=^Com_Exec2
23030    I=I+2                           : skip *=
23040    !=^Expression
23050    !=^XchgAXBX                    : EBX <--> EAX
23060    : eax のアドレスからFileNameにコピー
23070    +=40 !=^CallLib                 : call GetString2
23080    : FileName に保存された外部プログラムを実行
23090    +=39 !=^CallLib                 : call ForkExec
23100    #=^NextCom
23110 :
23120 ^Com_Exec2
23130    I=I+2                           : skip ="
23140    : 文字列をコードに埋め込み、先頭アドレスをEAXに設定
23150    !=^GetString
23160    : eax のアドレスからFileNameにコピー
23170    +=40 !=^CallLib                 : call GetString2
23180    : FileName に保存された外部プログラムを実行
23190    +=39 !=^CallLib                 : call ForkExec
23200    #=^NextCom
23210 :

空白出力

「.」は式の値の数の空白を標準出力に書き出します。1文字表示ルーチンにスペースを渡して、式で指定された回数をループするコードを生成します。

23400 :----------------------------------------------------------
23410 : 空白出力
23420 :----------------------------------------------------------
23430 ^Com_Space
23440    ;=D>1 "<Space>"
23450    I=I+1
23460    !=^Expression
23470    +=$D989 !=^PutObj2              : MOV ECX, EBX
23480    +=$20B0 !=^PutObj2              : MOV AL, $20
23490    +=7 !=^CallLib                  : OutChar
23500    +=$F9E2 !=^PutObj2              : LOOP -7
23510   #=^NextCom

改行出力

「/」は改行を標準出力に書き出すライブラリルーチンを call します。

23520 :----------------------------------------------------------
23530 : 改行
23540 :----------------------------------------------------------
23550 ^Com_NewLine
23560    ;=D>1 "<NewLine>"
23570    +=9 !=^CallLib                 : NewLine
23580   #=^NextCom

ラベル宣言

「^」で始まる行はラベル宣言となります。 行番号:ラベルテーブルに登録しますが、コードを生成する必要はありません。ラベルは11文字までが有効でそれ以降は無視されます。ラベルを参照するとラベル宣言の次の行先頭アドレスを返します。

23590 :----------------------------------------------------------
23600 : ラベル処理 ラベル:行番号テーブルの登録
23610 :   adr:int32 label:char[12]
23620 :----------------------------------------------------------
23630 ^Com_Label
23640    ;=D>1 "<Label>" /
23650    ;=Q>1 !=^LineEnd ]             : PASS1のみ
23660    L[K*4]=N[1]                    : 行番号
23670    p=L+(K*16+4)                   : ラベル登録
23680    i=I
23690    j=0
23700    @                              : ラベルを11文字登録
23710      c=C(i)
23720      p(0)=c                       : 1文字登録
23730      ;=j=11 p(0)=0                : 12文字目は0
23740      j=j+1
23750      p=p+1
23760      i=i+1
23770    @=((c=0)|(j=12))
23780    K=K+1 I=i-1
23790    ;=c<>0 !=^LineEnd
23800    #=^NextCom
23810 ^LineEnd
23820    @
23830      c=C(I)
23840      ;=c<>0 I=I+1
23850    @=(c=0)                        : 行末に設定
23860    ]
23870 :

コメント

「:」 の後は、コメントとして行末まで無視されるため、単にソースを行末まで読み飛ばします。

23880 :----------------------------------------------------------
23890 : コメント
23900 :----------------------------------------------------------
23910 ^Com_Comment
23920   @ I=I+1 @=(C(I)=0)  c=0 I=I-1 : 行末に設定
23930   #=^NextCom

IF

式の値が偽(0) なら次の行から実行します。真の場合は次の文を実行します。

    例: ;=e "A=" A=?

式を評価して0ならば次行先頭にジャンプするコードを生成します。

23940 :----------------------------------------------------------
23950 : IF
23960 :----------------------------------------------------------
23970 ^Com_IF
23980   ;=D>1 "<IF>"
23990   I=I+1
24000   !=^Expression
24010   n=N[1]+1
24020   ;=Q>1 !=^SearchLine
24030   +=$DB85 !=^PutObj2              : TEST EBX,EBX
24040   +=$840F !=^PutObj2              : JZ 次行先頭
24050   +=r     !=^PutAddr
24060   #=^NextCom

数値出力

標準出力に数値を出力する命令です。対応するランタイムライブラリを呼び出します。

形式
?=e 式の値を10進数で出力。
?(n)=e 式の値を10進数を n 桁右寄せで出力。
?[n]=+e 式の値を10進数を上位桁を0で埋めた n 桁で出力。
?$=e 式の値を16進数2桁で出力。
?#=e 式の値を16進数4桁で出力。
??=e 式の値を16進数8桁で出力。
?*=e 式の値を符号無し10進数で出力。
?{n}=e 式の値を8進数n桁で出力。
?!n!=e 式の値の下位を2進数n桁で出力。
24070 :----------------------------------------------------------
24080 : 数値出力
24090 :----------------------------------------------------------
24100 ^Com_OutNum
24110    +n
24120    c=C(I)
24130    ;=c='=' n=18 #=^OutNum
24140    ;=c='?' I=I+1 n=15 #=^OutNum
24150    ;=c='#' I=I+1 n=14 #=^OutNum
24160    ;=c='$' I=I+1 n=13 #=^OutNum
24170    ;=c='*' I=I+1 n=17 #=^OutNum
24180    ;=c='(' I=I+1 n=21 #=^RightNum
24190    ;=c='[' I=I+1 n=19 #=^RightNum
24200    ;=c='{' I=I+1 n=12 #=^RightNum
24210    ;=c='!' I=I+1 n=11 #=^RightNum
24220    -n !=^Com_Error
24230 ^OutNum
24240    ;=D>1 "<OutNum>"
24250    I=I+1                          : skip =
24260    !=^Expression
24270    !=^XchgAXBX
24280    +n !=^CallLib                  : PrintLeft
24290    -n
24300    #=^NextCom
24310 ^RightNum
24320    ;=D>1 "<RightNum>"
24330    !=^Expression                  : 桁数 ebx
24340    +=$D989 !=^PutObj2             : MOV ECX,EBX
24350    I=I+2                          : skip x=
24360    +=$51   !=^PutObj1             : PUSH ECX
24370    !=^Expression                  : 数値 ebx
24380    +=$59   !=^PutObj1             : POP  ECX
24390    !=^XchgAXBX
24400    +n !=^CallLib                  : PrintLeft
24410    -n
24420    #=^NextCom
24430 :

リターン

GOSUB文(!=)の次の命令に戻ります。

    例:
    100 ^SUB
    110   "do something"
    120 ]

リターンは単にret 命令(0xC3)を書き込むだけです。

24820 :----------------------------------------------------------
24830 : リターン
24840 :----------------------------------------------------------
24850 ^Com_Return
24860    ;=D>1 "<ret>"
24870    +=$C3 !=^PutObj1               : RET
24880    #=^NextCom

マイクロ秒スリープ

停止時間をマイクロ秒単位で与えてプログラムを一時停止します。マシンの処理速度が異なっても同じタイミングで動作させたい場合などに使用します。

    例: _=e

ランタイムライブラリを呼び出すコードを生成します。

25260 :----------------------------------------------------------
25270 : マイクロ秒スリープ
25280 :----------------------------------------------------------
25290 ^Com_USleep
25300    I=I+1
25310    !=^Expression
25320    !=^XchgAXBX
25330    +=38 !=^CallLib
25340   #=^NextCom

乱数シード設定

乱数は rvtl の起動直後には毎回同じ系列で生成されるため、rvtl の起動後には常に同じ乱数が帰ります。いつも異なる乱数を生成するためには、擬似乱数のシードに別の値を設定します。例えば、擬似乱数のシードを現在時間のマイクロ秒に設定「A=_ `=%」すると起動のたびに予想できない異なる系列の乱数列を生成することができます。

    例: `=e

ランタイムライブラリを呼び出すコードを生成します。

24980 :----------------------------------------------------------
24990 : 乱数シード設定
25000 :----------------------------------------------------------
25010 ^Com_RANDOM
25020    ;=D>1 "<rand>"
25030    I=I+1
25040    !=^Expression                  : 数値 ebx
25050    !=^XchgAXBX
25060    +=35 !=^CallLib                : sgenrand
25070   #=^NextCom

ファイル先頭位置設定

ファイル書き出しコマンドの書き込み先頭位置を設定します。

    例: {=e

変数「{」へ代入するコードを生成します。

25080 :----------------------------------------------------------
25090 : ファイル先頭位置設定
25100 :----------------------------------------------------------
25110 ^Com_FileTop
25120    I=I+1                          : skip =
25130    !=^Expression
25140    +=$9E89 !=^PutObj2             : MOV [ESI+r*4],EBX
25150    +='{'*4   !=^PutObj4
25160   #=^NextCom

ファイル終了位置設定

ファイル書き出しコマンドの書き込み最終位置を設定します。

    例: }=e

変数「}」へ代入するコードを生成します。

25170 :----------------------------------------------------------
25180 : ファイル終了位置設定
25190 :----------------------------------------------------------
25200 ^Com_FileEnd
25210    I=I+1                          : skip =
25220    !=^Expression
25230    +=$9E89 !=^PutObj2             : MOV [ESI+r*4],EBX
25240    +='}'*4   !=^PutObj4
25250   #=^NextCom

プログラム終了

負の行番号へのGOTO(#=-1)は、TERMIOを起動時の設定に戻し、exitシステムコールを実行するコードを生成します。翻訳の最後(ソースプログラムの最後)でも同じコードを出力します。

25260 :----------------------------------------------------------
25270 : プログラム終了
25280 :----------------------------------------------------------
25290 ^Com_Exit
25300   !=^ExitCode
25310   #=^NextCom
25320 :

拡張コマンド

「\」を使う将来の拡張用です。何も行っていません。

24770 :----------------------------------------------------------
24780 : 拡張コマンド
24790 :----------------------------------------------------------
24800 ^Com_Ext
24810   #=^NextCom

[前] [目次] [次]