エピ25 MZ80のカーソル制御のこと
***
エピ25 MZ80のカーソル制御のこと
***
さて。
0DB5h: ディスプレイ表示
0DDCh: カーソル制御
について調べてみます。
***
ディスプレイ表示のルーチン 0DB5h です。
def _0DB5(A):
push AF; push BC; push DE; push HL
HL=_0FB1h(); [HL]=A
if [1171h]==27h and [1173h+[1172h]]==00h:
[1173h+[1172h]+1]=01h # 次行と接続
[1173h+[1172h]+2]=00h # 次行の次は切断
jp _0E90h # [→] カーソル移動
引数の A をカーソル位置に表示します。
カーソル位置のX座標は 1171h 番地、Y座標は 1172h 番地にあります。0FB1h のルーチンは(X,Y)から D000h 以降の対応する番地を計算します。つまり、
HL = D000h+X+Y*40
です。ここに A を転送します。これで画面に反映されます。
次には、1173h 番地が登場します。
X==39, [1173h+Y]==00h なら、[1173h+Y+1]=01h にするのです。
[1173h+Y] は 00h なら次行と未接続で、00h 以外なら接続中、...だと思っているのですが。
Y=0, [1173h+0] = ?
Y=1, [1173h+1] = ?
Y=2, [1173h+2] = 0 # Now here
Y=3, [1173h+3] = ? <-- set 1
Y=4, [1173h+4] = ? <-- set 0
:
X=39, Y=2 のとき、[1173h+2]==00h なら [1173h+3] を 01h にする。ちょっと不思議ですね。調査必要な感じですが。1173h のことはカーソル制御を見ないと分らないかもですね。
あとは、カーソル位置を右に移動します。0E90h はカーソル制御の [→] を処理するルーチンです。要は [1171h] を +1 するのですが、後述します。POP は 0E90h の方で行います。
***
**
*
カーソル制御のルーチン 0DDCh です。
def _0DDCh(A):
push AF; push BC; push DE; push HL
if (A&F0h)!=C0h: jp _0FDEh
jp (0DF3h+(A&0Fh)*4)
_0FDE: pop HL
_0FDF: pop DE; pop BC; pop AF; ret
引数 A が C0h〜CFh 以外なら 0FDEh にジャンプして POPs & RET で、C0h〜CFh ならテーブルジャンプ、...ではなくて、...算術型のジャンプですよ。
A の値が 0〜(N-1) のとき、算術型なら
1, 4: rlca
1, 4: rlca
1, 4: ld C,A
2, 7: ld B,00h
3, 10: ld HL,0DF3h
1, 11: add HL,BC
1, 4: jp (HL)
4, 10: jp 0E32; nop <-- 分岐先
なので、10+(4*N-1) バイトと 54 ステートです。分岐先が4つなら 10+4*4-1 = 25 バイト、54 ステート。
テーブルジャンプなら、SP5030の 19FEh のルーチンを参考に条件を同じにして整理すると、13+2*N バイト、77 ステート。分岐先4なら 13+2*4 = 21 バイト、77 ステート。
算術型の方が速いから。N=16 だけど速度優先で採用したのか。
*
カーソル制御の分岐先を見ましょう。
_0DF3: jp 0E32; nop # C0h [UP]
_0DF7: jp 0E74; nop # C1h [↓]
_0DFB: jp 0E84; nop # C2h [↑]
_0DFF: jp 0E90; nop # C3h [→]
_0E03: jp 0EAE; nop # C4h [←]
_0E07: jp 0EBF; nop # C5h [H]
_0E0B: jp 0EC5; nop # C6h [C]
_0E0F: jp 0EF8; nop # C7h [DEL]
_0E13: jp 0F49; nop # C8h [INS]
_0E17: jp 0EE1; nop # C9h [英数]
_0E1B: jp 0EEE; nop # CAh [カナ]
_0E1F: jp 0FDE; nop # CBh []
_0E23: jp 0FDE; nop # CCh []
_0E27: jp 0F8B; nop # CDh [CR]
_0E2B: jp 0FDE; nop # CEh []
_0E2F: jp 0FDE; # CFh []
分かり易いところからね。
*
C9h [英数] と CAh [カナ] です。カナ/英数の状態を切り替えます。
_0EE1: #C9 [英数]
[E003h]=05h; [1170h]=00h; jp _0FDEh
_0EEE: #CA [カナ]
[E003h]=04h; [1170h]=01h; jp _0FDEh
E003h 番地はメモリマップドIOで、04h/05h を転送するとLEDを赤色/緑色にできます。
1170h はカナ/英数の状態を管理するワークエリアです。
0FDEh 番地のルーチンは POPs&RET する共通のルーチンです。
簡単ですね。
*
機械語なら少しでも同じコードがあれば共有するので、0EEEh の後半は 0EE1h にジャンプしますが、分かり易さを優先した整理をしてます。
*
次は C5h [H] と C6h [C] です。[H] はカーソル位置を(0,0)にします。[C] は画面をクリヤしてカーソル位置を(0,0)します。
_0EBF: #C5h [H]
[1171h]=0000h; jp _0FDEh # POPs&RET
_0EC5: #C6h [C]
_0DA6h(); C=19h; HL=D000h
while(C>0):
HL=_0FD8h(HL,B=28h); dec C
HL=1173h; HL=_0FD8h(HL,B=1Bh) # 1Bh=27 ?
jp _0EBFh # [H]へ
_0FD8: A=00h
_0FD9: [HL]=A; inc HL; djnz _0FD9h; ret
[H] はそのままですね。
[C] では 0DA6h のルーチンで垂直ブランク検出して、0FD8h のルーチンで D000h〜 と 1173h〜 をゼロクリアします。
D000h〜 は 28h = 40 バイトのクリアを 19h = 25 回行いますが、200 バイトのクリアを5回行った方が良くないかな?
あとは [H] にジャンプです。
*
次は C0h [UP] です。画面を1行分スクロールアップします。
_0E32: #C0 SCR-UP
_0DA6h(); [E003h]=00h
_0E39:
BC=03C0h;DE=D000h;HL=D028h;LDIR;ex DE,HL;HL=_0FD8h(HL,B=28h)
BC=001Ah;DE=1173h;HL=1173h;inc HL;LDIR;[HL]=00h
if [1173h]!=00h: [1172h]-=1; jp _0E39h #
_0DA6h(); [E003h]=01h; jp _0FDEh # POPs&RET
最初に垂直ブランク検出して、[E003h] に 00h を転送します。すると画面オフします。
D000h と 1173h を1行分移動します。
D000h+40 から D000h へ 40*24 バイト転送して、0FD8h のルーチンで最後の行をゼロクリアします。
次は 1174h から 1173h へ 26 バイト転送して、[1174h+26] に 00h を転送します。...。でもね。何故そこをゼロクリアするのかな。ex DE,HL か dec HL が抜けてない? そも 「HL=1173h; inc HL」って変だよ。もしかしてバG、...。
[1173h]!=00h の場合、カーソル位置のY座標を -1 して、もう一度スクロールアップ。...、だけどY座標が0だったらバグらない?
最後に垂直ブランク検出して [E003h] に 01h を転送して画面オンします。
*
謎が蓄積していますが解けるのでしょうか
*
次は C1h [↓] と C2h [↑] です。
_0E74: #C1h [↓]
if [1172h]==18h: jp _0E32h # SCR-UPへ
else: [1172h]+=1; jp _0FDEh # POPs&RET
_0E84: #C2h [↑]
if [1172h]==00h: jp _0FDEh # POPs&RET
else: [1172h]-=1; jp _0FDEh # POPs&RET
カーソル位置のY座標を増減するのですが。最初の行(Y=0)で [↑] なら移動なし。最後の行(Y=24)で [↓] ならスクロールアップです。
*
続いて C3h [→] と C4h [←] です。
_0E90: #C3h [→]
if [1171h]>=27h: #27h=39
if [1172h]<18h: [1171h]=00h; [1172h]+=1
else: [1171h]=00h; [1172h]=18h; jp _0E32h # SCR-UPへ
else: [1171h]+=1
jp _0FDEh
_0EAE: #C4h [←]
if [1171h]==00h
if [1172h]>=01h: [1171h]=27h; [1172h]-=1
else: [1171h]=00h; [1172h]=00h
else: [1171h]-=1
jp _0FDEh
[←] は簡単ですね。カーソル位置のX座標を -1 するのですが、X=0 のときは上の行の末(X=39,Y-=1)に移動します。Y=0 だったらそれ以上は移動できないので(X=0,Y=0)にする。ってか、元から(0,0)なのでは。
[→] も同様ですが。行末(X=39)にいたときに、最後の行(Y=24)の場合はスクロールアップするのです。
*
はい。CDh [CR] です。
_0F8B: #CDh [CR]
if [1173h+[1172h]+1]==00h: #次行未接続
if [1172h]<18h: [1171h]=00h; [1172h]+=1
else: [1171h]=00h; [1172h]=18h; jp _0E32h # SCR-UPへ
elif [1172h]!=17h: #17h=23
[1171h]=00h; [1172h]+=2
else:
[1171h]=00h; [1172h]+=1
jp _0E32h # スクロールアップへ
jp 0FDEh
次行未接続の場合は 0E9Dh([→]の一部)にジャンプするのですがコピペしました。
次行未接続なら次行の先頭(X=0,Y+=1)にカーソル位置を移動するのです。最後の行(Y=24)ならスクロールアップです。
次行接続中なら2行下の先頭(X=0,Y+=2)に移動だけど、Y=23 のときは Y=24 にしてスクロールアップ。むむ。Y=24 のときは必ず次行未接続なのかな。Y=25 の行はないから良いのか。
あれ? [1173h+[1172h]+1] の +1 って何? 次行が次行未接続? あれ、あれ? ッー
*
**
***
ピポッ! 謎の蓄積によりヒープメモリがオーバーフローエラーしました。強制シャットダウンしました。
***
C7h [DEL] と C8h [INS] が手付かずですが。少し休憩して。ここまでを見直します。
次回、1173h は思うてたのと違うのか。巨大な不発弾。自爆と誘爆にご用心!
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.4.28 推敲
2026.5.8 エピタイ修正




