エピ15 算術演算子の分岐処理を確かめよう
***
エピ15 算術演算子の分岐処理を確かめよう
***
数式、...例えば 0+1 のような、...を入力すると 2251h をコールして処理をします。その処理中の話ですが。数値 0 を評価したら、次が算術演算子の "+" だと 22D4h をコールするようです。
それで 22D4h では算術演算子の処理をしているに違いないと、エピ14では推測しました。
その通りなのか確認します。
***
早速に。22D4h〜22F6h のコードを見ます。
_22D4h:
[4644h] += 0005h # 数値ワーク +5
push BC # BC=226Ah +++ BC
push [25ADh+(A-B0h)*2] # A=BCh +++ 分岐先の番地
push 22F7h # +++ 22F7h
push DE # DE=22B1h +++ DE
exx
if D==00h: ret # D=00h, RET to 22B1h
_22F4h:
jp 1399h # err-msg MISMATCH
数値ワークを +5 して、PUSH を4回して、RET しているのですね。
22D4h をコールする前には 0+1 の 0 を評価していて、その結果が数値ワークに残っています。それで次の値を評価するために数値ワークを +5 するのです。エピ11で見た通りです。
それから4回の PUSH。
BC と DE の値はコール元で設定しています。
1) 21FFh: BC = 2202h, DE = 2250h
2) 2251h: BC = 226Ah, DE = 22B1h
3) 22B2h: BC = 22B5h, DE = 22C4h
4) 22C5h: BC = 22C8h, DE = 231Ch
A の値は算術演算子の中間コードです。25ADh 番地にあるジャンプテーブルを参照して PUSH しています。中間コードの分岐先の番地なのでしょう。
BC と DE、それと 227Fh という謎の値は RET するときに出てきます。
2251h からの処理の流れを整理すると。
・2) のルーチンは 3) をコールして、3) は 4) をコールして、4) は 231Dh をコールして、231Dh で数値 0 を評価して数値ワークに設定して、リターンする。
・2) のルーチンに戻ってきたら、A の値を調べて中間コード BCh("+")だったら、BC=226Ah, DE=22B1h を設定して 22D4h にジャンプする。
・22D4h では、push DE して RET する。つまり、DE=22B1h へジャンプする。
・22B1h 番地では inc HL して 3) に続き、4)、231Dh と行って、1 を評価して数値ワークに設定して、リターンすると、...スタックには 22F7h が入っていて、22F7h にジャンプすることになる。
・スタックには、分岐先の番地と DE=226Ah の2つが残っていることを覚えておいて!
ふむふむ。
*
では 22F7h〜2310h のコードを見ます。
_22F7h:
if D!=00h: jr 22F4h # D=00h
pop IY # IY=2D0Eh --- 分岐先の番地
push HL # HL=445Eh +++ HL
push 2314h # +++ 2314h
HL = [4644h] # HL=481Dh
[4644h] += FFFBh # 数値ワーク -5
DE = [4644h] # DE=4818h
JP [IY] # IY=2D0Eh ジャンプ!
22F7h のルーチンでは、pop IY してスタックから分岐先の番地を取り出して、PUSH を2回する。この時点でスタックには(22D4h で PUSH した)BC=226Ah と、HL と 2314h が入っている。
それから数値ワークを -5 する。HL に 1 を評価した数値の番地、DE には 0 を評価した数値の番地を設定する。
そして、IY の番地にジャンプする。ここが中間コード BCh の分岐先だ!
*
算術演算子の中間コードでジャンプテーブルを参照して分岐先を得て、演算子の左辺と右辺が入った番地を DE と HL に設定して、分岐先に飛んで行きます。
中間コード BCh('+')なら 2D0Eh 番地に行くはずです。DE と HL から2つの数値を読み出して、加算して、結果を数値ワーク [4644h] に転送して、リターンするのでしょう。
すると、スタックには 2314h が入っているので、そこに行きます。
*
最後のコードです。2314h〜231Bh を見ます。
_2314:
pop HL # HL=445Eh
BC = 0005h
DE = 0000h
A = [HL] # A=0Dh
ret # RET to 226Ah
スタックから HL を POP して、BC,DE,A を設定して、リターンします。
...、ってスタックには(22D4h で PUSH した)BC=226Ah が残っているので、そこに行くのです。
226Ah は 2251h のルーチンの中で、A の値を判定するところです。0+1 の場合は終端 0Dh に来ているので、リターンします。
... 2251h をコールした元に戻ります。LET 命令文なら 1B34h 番地ですね。
*
予定調和的な結論ですけど。綺麗に収まりました。
エピ12で出てきた 05 00 00 00 は 2314h が設定した値か。...、ちょっと懐かしいです。
***
22D4h のルーチンで参照したジャンプテーブルを確認します。
push [25ADh+(A-B0h)*2]
25ADh 番地からのデータですね。
25AD: 3465h # >< B0
25AF: 3465h # <> B1
25B1: 348Ah # =< B2
25B3: 348Ah # <= B3
25B5: 348Eh # => B4
25B7: 348Eh # >= B5
25B9: 3482h # = B6
25BB: 3476h # > B7
25BD: 347Ah # < B8
25BF: 268Ah # AND B9
25C1: 268Dh # OR BA
25C3: 268Fh # NOT BB
25C5: 2D0Eh # + BC
25C7: 2D0Bh # - BD
25C9: 2E08h # * BE
25CB: 2ED6h # / BF
25CD: 3A5Ah # ^ C0 <-- CF
A=BCh なら 25ADh + (BCh-B0h)*2 = 25C5h ... 分岐先は 2D0Eh で間違いないです。
*
余談になりますが。
ジャンプテーブルに AND, OR, NOT があるので分岐先のコードを見に行ったら、
268A: jp 138Ah # err-msg SYNTAX ERROR
268D: jr 268Ah # = err-msg
268F: jr 268Ah # = err-msg
でした。なんなのー
*
余談になりますが(その2)
べき乗 ^ の中間コードは CFh なのですが、4) 22C5h の中で C0h に変換しています。中間コードが B0h〜BFh まで埋まってしまったので苦肉の策ですか。
ところで >< と <> の分岐先は同じですね。不等号が2種類あるのはBASICだからかな。...、これを削れば、...。あるいは実態がない AND, OR, NOT はキーワードから外したらどうかな?
***
**
*
スタックに複数個のリターン番地を設定(PUSH)して、サブルーチン(をコールせず)へジャンプする。
サブルーチンはコール元が偽装されているかは知るすべもなくリターンして、設定された番地に行く。そこがまたサブルーチンだったらリターンして、設定された次の番地に行く。
これを何個でもつなげることができる。スタック領域の範囲内でね。
リターン番地はサブルーチンの先頭である必要はなくて、RET の数ステップ前で良い。RET の直前付近で、都合の良いシステムコールをしているコードがあればラッキーだ。
するとプログラム領域のコード(の断片)を繋ぎ合わせて、所望のプログラムを得ることができる。書換えが禁止されている領域であってもだ。
...、そんなことを連想させる技法が 2251h に入っていました。
*
この技法は2000年代に実用(悪用?)されて一部の業界で話題になっていたようです。
SP5030は1980年か1981年頃のリリースで今から45年前。
温故知新と言う言葉を思い出す。工学分野での考古学かな?
電子計算機において、...あるいはパソコンか、CPUを組み込んだ機器、...スマホ等において...、当り前で、当然だったこと、...を現在の開発者が知っていることは大切よね。
***
**
*
いやはや(死語?)
コールしたら RET で戻ってこないし、一方でどこからコールされるか不明なコードがゴロゴロしていて悩みましたよ。
PUSH と POP の数が合わないと気付いたのが最初のヒントでした。機械語は気難しいです。
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.3.10 誤記訂正と微推敲
2026.3.11 推敲
2026.4.17 微推敲
2026.5.4 コピペミスを修正。痛恨。




