エピ13.2 FOR ループを更に細かく
***
エピ13.2 FOR ループを更に細かく
***
FOR ループを更に細かく見て行きます。
***
FOR の中間コードは 87h で分岐先は 1D2Fh です。
1D2Fh 番地からの実行ステップを追います。
1D2F: _259Bh()
1D32: _169Ah(B6h) # B6h = '='
1D36: push DE # DE = 2049h = 'I',20h
1D37: _1DBBh()
1D3A: pop HL # HL = 2049h
1D3B: [47F0h] = HL # HL = 2049h
1D3E: ex DE,HL # DE = 2049h; HL = 0000h
1D3F: _23D2h()
1D42: _1818h()
259Bh のルーチンは変数名を DE に設定します。169Ah のルーチンは [HL] が B6h('=')かを調べます。B6h でなければ SYNTAX ERROR です。どちらもエピ12で出て来ましたね。
1DBBh のルーチンは 2251h をコールして値を評価します。結果は数値ワークに入ります。
23D2h のルーチンは DE と同じ名前の数値変数を探索するルーチンです。エピ12で出てきた 23C7h のルーチンの途中からで、数値変数の処理をします。DE は変数の数値部分の番地になります。
1818h のルーチンは数値ワークから DE に5バイト転送します。これもエピ12で出て来ました。
結果1:変数名 'I',20h を 47F0h に転送
:数値変数の領域に変数 'I' を新規追加して、数値ワークから数値を転送
ループ変数は、数値変数の領域に追加されます。同名の変数が既にあれば追加しないでそれを使います。
1D45: _1697h(AEh) # AEh = 'TO'
1D49: _1DBBh()
1D4C: DE = 47F8h
1D4F: _1818h()
1697h のルーチンは、HL=[4801h] して 169Ah に続きます。AEh(='TO')でなければエラーに飛びます。他のルーチンは既出ですね。
結果2:最終値を 47F8h に転送
1D52: _1689h(AFh,1D60h) # AFh = "STEP"
1D60: HL = 1615h
1D63: DE = 47F2h
1D66: A = [HL] # A = C1h
1D67: [47F7h] = A # A = C1h
1D6A: BC = 0005h
1D6D: LDIR # HL = 1615h; DE = 47F2h; BC = 0005h
1689h のルーチンは、HL=[4801h] して 168Ch に続きます。168Ch はメインループから呼び出されて中間コードの分岐処理にジャンプしたルーチンでした。ここでは AFh(='STEP')以外ならば 1D60h にジャンプします。
1615h 番地からの5バイトは C1 00 00 00 80(= 1.0)です。これが STEP を省略した場合の変化幅です。
STEP が省略されていない場合は、1D58h〜1D5Fh のコードで、1DBBh のルーチンで(2251h をコールして)数値を評価して数値ワークに設定、HL=[4644h] して、1D63h に合流します。
結果3:変化幅(の省略時の値 1 )を 47F2h に転送
:変化幅の符号を 47F7h に転送
***
ここまでで、ループ変数に初期値を設定して、47F0h〜47FCh の 13 バイト(変数名・変化幅・変化幅の符号・最終値)の設定が終わりました。
次はネスト数の処理とFOR領域への転送です。
***
ネスト数1(4803h)が0の場合は、...
1D6F: HL = [4648h] # HL = 4787h
1D72: DE = [47F0h] # DE = 2049h = 'I',20h
1D76: A = [4803h] # A = 00h
1D79: inc A # A = 01h
1D7A: dec A # A = 00h, Zf = 1
1D7B: jr Z, 1D9Ch # JR
このように 1D7Bh 番地でジャンプして、ネスト数のチェックとFOR領域への転送と進みます。
ネスト数1が0以外の場合、つまりネストしていた場合の処理(1D7Dh〜1D9Bh)が気になるので、後で調べますね(エピ13.3です)。
1D9C: HL = 4804h
1D9F: A = [HL] # A = 00h
1DA0: if A==0Fh: _139Eh
ネスト数2(4804h)を調べて、15 だったら 139Eh(16FOR ERROR)に飛びます。
1DA5: inc [HL] # HL = 4804h; (HL) = 01h
1DA6: dec HL # HL = 4803h
1DA7: inc [HL] # HL = 4803h; (HL) = 01h
1DA8: dec HL # HL = 4802h
ネスト数1(4803h)とネスト数2(4804h)を +1 します。
1DA9: DE = [4648h] # DE = 4787h
1DAD: BC = 0013h # 13h = 19
1DB0: dec DE # DE = 4786h
1DB1: LDDR # HL = 4802h; DE = 4786h; BC = 0013h
4802h 番地から 4786h 番地(FOR領域の使用中の番地-1)に LDDR で 19 バイト転送します。
1DB3: inc DE # DE = 4774h
1DB4: ex DE, HL # DE = 47EFh; HL = 4774h
1DB5: [4648h] = HL # HL = 4774h
1DB8: jp 199Eh
FOR領域の使用中の番地 4648h を更新して、199Eh にジャンプします。199Eh は LET 命令文の最後でジャンプした番地ですよ(エピ12)。
***
**
*
次は NEXT です。中間コードは 8Dh で、分岐先は 1DC4h です。
1DC4: A = [4803h] # A = 01h ネスト数1
1DC7: if A==01h: _138Ah # err-msg SYNTAX ERROR
ネスト数1が0の場合はエラーです。
1DCB: _2556h() # 変数名チェック
1DCE: [4801h] = HL # HL = 481Bh 命令文への番地
1DD1: HL = [4648h] # HL = 4774h FOR領域の使用中の番地
1DD4: _1E41h() if NC # DE = [HL] ループ変数名
2556h のルーチンでループ変数名(NEXT I の I )を DE に転送します。変数名が省略されている場合は 1E41h のルーチンをコールしてFOR領域のループ変数名を DE に設定します。
1DD7: A = E # A = 49h = 'I' ループ変数の1文字目
1DD8: A -= [HL] # A = 00h
1DD9: inc HL # HL = 4775h
1DDA: B = A # B = 00h
1DDB: A = D # A = 20h ループ変数の2文字目
1DDC: A -= [HL] # A = 00h
1DDD: A |= B # A = 00h
1DDE: jr Z, 1DF5h # JR
DE の変数名をFOR領域のループ変数名と比較します。正常な場合はここで 1DF5h へジャンプします。異常な場合は、...後でね。
1DF5: inc HL # HL = 4776h(変化幅)
1DF6: _23D2h() # 変数を検索
1DF9: push DE # DE = 482Ch(数値変数)
1DFA: push HL # HL = 4776h(変化幅)
1DFB: _2D0Eh() # 加算の処理
1DFE: pop HL # HL = 4776h(変化幅)
1DFF: pop DE # DE = 482Ch(数値変数)
ループ変数に変化幅を加算します。23D2h のルーチンで変数を検索して変数の番地を DE に設定して、HL=4776h の変化幅を 2D0Eh のルーチンで加算します。
1E00: BC = 0005h
1E03: HL += BC # HL = 477Bh(変化幅の符号)
1E04: A = [HL] # A = C1h
1E05: inc HL # HL = 477Ch(最終値)
1E06: push HL # HL = 477Ch(最終値)
1E07: or A # A = C1h
1E08: jp P, 1E23h
変化幅の符号を調べて、符号が0(=マイナス)の場合は 1E23h にジャンプします。ここでは符号は1(=プラス)なので次に続きます。変化幅の符号は変化幅の1バイト目と同じなので変化幅の符号を別途保存する必要は無いよね?
1E0B: ex DE, HL # DE = 477Ch(最終値); HL = 482Ch(数値変数)
1E0C: _3369h() # [DE]と[HL]をBCバイト比較
1E0F: pop HL # HL = 477Ch
1E10: BC = 0005h
1E13: jr C, 1E2Dh
ループ変数と最終値を比較します。(変化幅がプラスで)ループ変数 > 最終値なら FOR ループは終了で 1E2Dh にジャンプします。ループ変数 <= 最終値なら次に続きます。
1E15: HL += BC # HL = 4781h(FOR領域の次文への番地)
1E16: DE = 47FDh # DE = 47FDh(次文への番地)
1E19: inc C # C = 06h
1E1A: LDIR # HL = 4781h; DE=47FDh; BC=0006h
FOR領域の 4781h 番地からの6バイトを 47FDh 以降に転送します。
1E1C: HL = [4801h] # HL = 4813h 命令文への番地
1E1F: A = [HL] # A = 0Dh 命令文(終端)
1E20: jp 19A4h
19A4h へジャンプします。これで FOR 命令文の直後から処理を繰り返すのです。
***
ループ変数 > 最終値の場合は FOR ループの終了です。
1E2D: C = 0Bh # BC = 000Bh
1E2F: HL += BC # HL = 4787h
1E30: [4648h] = HL # HL = 4787h FOR領域の使用中の番地
FOR領域の使用中の番地 4648h を更新します。HL は 477Ch(最終値)を指していたので +11 するとループ1回分を戻したことになります。
FOR領域は、ループが終了した後でもゼロクリアせず、4648h 番地を更新することで使用中の範囲を管理するのですよ。
1E33: dec [4803h] # = 00h ネスト数1
1E37: dec [4804h] # = 00h ネスト数2
4803h(ネスト数1)と 4804h(ネスト数2)を -1 します。
1E39: _1689h(2Ch,199Eh) # 2Ch = ','
1E3F: jr 1DC4h
1689h のルーチンをコールして、[HL] が 2Ch でなければ 199Eh へジャンプします。
[HL] が 2Ch なら 1DC4h へ、...NEXT 命令の先頭にジャンプです。
*
先頭にジャンプ? どういう事?
*
2Ch は ',' ですが調べてみたら、...
FOR I=1 TO 3
FOR J=1 TO 3
FOR K=1 TO 3
:
NEXT K
NEXT J
NEXT I
のように FOR ループがネストしている場合に NEXT を
FOR I=1 TO 3
FOR J=1 TO 3
FOR K=1 TO 3
:
NEXT K,J,I
とまとめることができるのでした。
SP5030の独自仕様ではなくて、F-BASICでも同じです。便利なのかな?
***
**
*
ループ変数は、普通の数値変数と同様に扱えます。値を参照できるのは当然として、値を変更することもできます。つまり、
10 FOR I=1 TO 16
20 PRINT I;
30 IF I=8 THEN I=9
40 NEXT I
> 1 2 3 4 5 6 7 8 10 11 12 13 14 15 16
このようにループしている途中で、今何時や?と尋ねて9をスキップできるのです。
これが古典落語「時そば」の原理です。
***
FOR-NEXT について理解が深まりましたね。続きも読んでね!
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.5.4 微推敲




