エピ30 まとめに代えて --- プログラム入力支援とか
***
エピ30 まとめに代えて --- プログラム入力支援とか
***
前エピでは画面プログラミングの環境について取り組みました。入力できない文字の解消とかプログラム編集のためのコードの整備とかね。
それでね。エピ28で作成したライフゲームのプログラムを書き直してみようと思います。入力支援のスクリプトも準備しましょう。
今回はそんな話です。...、第三部はこれをまとめとします。よろしくお願いします。
***
まずはライフゲームのプログラムです。
書き直したら 110 バイトになりました。これがMZ80で動作する最小コードかもね(個人の意見です)。もちろん完全なトーラスだよ。
src_lifeGame_v41=[
"_m", "1178B02178D0", # ld DE,B078h; ld HL,D078h
"017003EDB0", # ld BC,0370h; LDIR
"2628", # ld H,28h # X-pos 40->1
"_e", "2E19", # ld L,19h # Y-pos 25->4
"_d", "01080411_v__", # ld BC,0408h; ld DE,_v
"_p", "E5", # push HL
"3EFFBC20022627", # ld A,FFh; cp H; jr NZ,+02; ld H,27h # 39
"3E28BC20022600", # ld A,28h; cp H; jr NZ,+02; ld H,00h # 0
"3E02BD20022E18", # ld A,02h; cp L; jr NZ,+02; ld L,18h # 24
"3E19BD20022E03", # ld A,19h; cp L; jr NZ,+02; ld L,03h # 3
"CDB40F7CD62067", # call _0FB4; ld A,H; sub 20h; ld H,A
"AFB6280105", # xor A; or (HL); jr Z,+01; dec B
"1A1332_w__", # ld A,(DE); inc DE; ld (_w),A
"E1", # pop HL
"_w", "000D20_p", # inc/dec H/L; dec C; jr NZ,_p
"E525CDB40F", # push HL; dec H; call _0FB4
"AF10012F", # xor A; djnz +01; CPL
"05280177", # dec B; jr Z,+01; ld (HL),A
"E13E03BD20_d", # pop HL; ld A,03h; cp L; jr NZ,_d
"2520_e", # dec H; jr NZ,_e
"CD1E0020_m", # call _001E; jr NZ,_m
"C38200", # jp _0082
"_v", "25252D2D24242C00" # vec
]
エピ28では 120 バイトが限界でしたから、そこから更に10バイト(8%)小さくできました。しかもこのプログラムは画面の上部(1行目〜)に書けるのです。実質的には 110/125 = 0.88 つまり12%のダイエットです。
左右と上下が連続するトーラスであることは維持してます。...、その判定で28バイト使ってます。Z80の機械語にだいぶんと慣れてきたけど、この28バイトは削れなかった。抜本的な見直しが必要な感じです。アイデアを!
レジスタの割当は幾つか変形パターンがあるけど、コードサイズは同じだったりして。1バイトを削る試みがね。
前バージョンとの違いは使えない文字の制限を無くしたことだけです。
LDIR を使うための自己書換のコードが不要で6バイト減、JR 命令でマイナス方向のジャンプが書けることで1バイト減、PUSH HL と POP HL が使えるので IX レジスタを使わなくてよくて4バイト減、それらの影響で更に数バイト減、...等などと節約できました。
使えない命令の存在が地味にコードサイズに影響していたのを実感しました。
*
プログラム(機械語)は文字列の配列になっています。
個々の文字列は、先頭が "_" ならラベルで、その他は 16 進数のコードです。ラベルは2文字。コードの末尾で "_v__" や "_p" となっている部分はラベルの参照です。"_v__" なら絶対番地の2バイトで、"_p" なら相対番地の1バイトです。
それから "#" の後ろがニモニックです。16 進数だけだと分かり難いのでコメントとして書いています。
これを次のスクリプトで処理します。
src = src_lifeGame_v41; adr = 0xD000; lbl = {}; ref = {}; dst = ""
for c in src:
if c[0]=="_": lbl[c] = adr
else: adr += len(c)/2
if c[-2]=="_": ref[c[-2:]] = adr
for c in src:
if c[0]!="_":
if "__" in c: l = "%04X"%lbl[c[-4:-2]]; c = c[:-4]+l[2:4]+l[0:2]
elif "_" in c: c = c[:-2]+"%02X"%(0x100+lbl[c[-2:]]-ref[c[-2:]])
dst += c
print "l = ",len(dst)/2; print "c = ("
while len(dst)>=40:
print '"%s"+'%(dst[0:40]); dst = dst[40:]
print '"%s")'%(dst[0:40])
変数 adr がプログラムを置く番地です。1行目なら D000h です。21行目なら D000h+20*40 とします。
プログラムの "_p" や "_v__" といったラベルを処理して、出力は次のようになります。
> l = 110
> c = (
> "1178B02178D0017003EDB026282E190108041166"+
> "D0E53EFFBC200226273E28BC200226003E02BD20"+
> "022E183E19BD20022E03CDB40F7CD62067AFB628"+
> "01051A133244D0E1000D20CDE525CDB40FAF1001"+
> "2F05280177E13E03BD20B42520AFCD1E00209DC3"+
> "820025252D2D24242C00")
最初は電卓でラベルの番地を計算していたのですが、だんだんと面倒になって。計算ミスも度々発生したし。
ちゃんとしたアセンブラを使う方がよいのですけど(そもそもハンドアセンブルが間違いの素)、スクリプトでお気楽に済ませました。
***
入力するディスプレイコードは次のようになります。
緑色がメインループの GETL で直に入力できる文字で、赤紫色が入力できない文字です。
EDh は LDIR、E5h と E1h は PUSH HL/POP HL です。使えて嬉しいです。
CDh は CALL nm ですが、1つだけ JR NZ,+n のマイナス方向のジャンプで使っています。CALL nm でなくて CALL NC,nm を使っても良かったのですが。
あと、FFh は LD A,FFh の FFh です。XOR A; DEC A で良いのですけど。えへへ。
エピ29で作った「Q_日ネ木C_→コ_」を画面に書いて、自分で GETL をコールしてアスキーコード変換で入力する場合は、...
緑色が入力できる文字で、赤紫色が入力できない文字です。入力できない文字が目立ちます。
最初の C1h はディスプレイコードとして[↓]を入力すると、アスキーコードに変換されて 11h になるのです。77h「\」なら EDh に、30h「 ̄」なら E5h になります。
*
2回に分けてキー入力すればプログラムを入力できますね!
*
実際にプログラムを入力するときには。大部分は GETL で直に入力できるので、アスキーコード変換するのは最小限にするのが良さそうです。
アスキーコード変換で入力するのは次の緑色の10文字にします。
この10文字をまとめて、1回の GETL で画面の3行目(D000h+2*40 番地)に表示します。それには、
Q^日ネ木C_→コ_(1150D0 AF D40300 C38200)
を適当な行に書いて GOTO して、適当な行で対応する10文字、
1行目用: \,  ̄, π,
2行目用: ┗, ♠, ┗,  ̄, ┗,
3行目用: ♠, ┗
を入力してリターン・キーを押下します。
3行目に10文字が表示されるので[INST]キーと[DEL]キー、それと行の接続を操って各々の文字を1〜3行目に配置します。
あとは GETL で直にディスプレイコードを入力して完成です。
*
行の接続は、行末(X=39)で1文字を表示するときに、
・前行と未接続の場合に限り次行と接続中にして次行は次行未接続にする
というルールです。つまり下から上の行への文字の移動は何行でもできるのです。
上から下の行への移動は、知恵の輪を解くかのように操作すればできるかも?...、です。実質無理よ!
***
**
*
入力支援のスクリプトを用意しましょう。
*
ディスプレイコードを16進数で入力するなら迷いはないのですけど。
文字でキー入力しようとすると、...特にグラフィック文字の場合、...似ている文字が多々あって判別に苦労します。パソコン雑誌のBASICのプログラムを入力した経験があるパソコン少年(当時)なら同意して貰えるはず。
キーボードのレイアウトを示します。
B列のヨコ線たちやC列のタテ線たちは見た目では判別できないよね。3行9列のスラッシュ「/」と2行D列の斜線「/」も難しいね。
だから、キーの位置をn行m列と英数/シフト/カナの状態で示すとよさそうよね。それとアスキーコード変換かディスプレイコードで直に入力するかの表示も。
*
1文字毎にキーボード上の位置を表示します。行は0〜4、列は0〜Fです。それと英数は "E"、シフトは "S"、カナは "K" です。ディスプレイコードで入力できない文字は "___" にします。
10E 3BS 1BK 00E 3BS 0FS 20E 1BS 32E ___ 1BK 05E 07E 38E 15E 20E 25E 22E 10E 05S
0FS ___ 4DE ___ 4BK 09E 34E 05E 06E 4DE 07E 4BK 09E 34E 05E 43E 4DE 34E 4CK 09E
34E 38E 31E 4DE 15E 4CK 09E 34E 38E 32E ___ 2BK 18E 4BS 3FS 09E 06S 37K 2DK 07E
20E 12E 30E 21E 1DE 22S 0FS ___ 43E 36E 09E ___ ___ 04E ___ 2BK 18E 37K 19E 20E
37E 12E 07E 20E 2ES ___ 4DE 32E 4CK 09E 2BK 04E 09E 37K ___ 0DE 43E 09E 0CK 46E
34K 43E 04E 04E 39E 39E 03E 03E 29E 43E
最初の文字は "10E" なので1行0列のキーで英数で入力します。つまり「Q」です。
次は "3BS" で3行B列のキーでシフト付きで入力して「ー」です。ヨコ線は高さが各種ありますが迷いなく入力できますね!
ディスプレイコードで入力できない文字は、アスキーコードで入力します。
___ ___ ___ ___ ___ ___ ___ ___ ___ 2ES ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
___ 1BE ___ 09S ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ 0BE ___ ___ ___ ___ ___ ___ ___ ___ ___
___ ___ ___ ___ ___ ___ ___ 20S ___ ___ ___ 0BE 1BE ___ 0BE ___ ___ ___ ___ ___
___ ___ ___ ___ ___ 20S ___ ___ ___ ___ ___ ___ ___ ___ 0BE ___ ___ ___ ___ ___
___ ___ ___ ___ ___ ___ ___ ___ ___ ___
となります。"2ES" は2行E列のシフト付きで「\」、"1BE" は1行B列の英数で「 ̄」です。良さそうですね。バグがあったらゴメンね。...、ゴメンね。
*
入力支援スクリプト ink_spt() です。入力は変数 c で16進数の文字列です。prn_key() は ink_spt() のサブルーチンです。
def ink_spt(c):
o1 = ""; o2 = ""
for i in range(0,len(c),2):
d = int(c[i:i+2],16)
if d in [0x80,0xC0]:
o2 += "___ "; o1 += "*%02X "%(d)
elif d in [0x40]+range(0xC7,0xD0)+range(0xDF,0x100):
o1 += "___ "; o2 += prn_key(dsp_asc[d])
else:
o2 += "___ "; o1 += prn_key(d)
print o1; print o2
def prn_key(d):
k = key_mat.index(d)
if k>=160: r=(k-160)/8; c=(k-160)%8; s="K"
else: r=k/16; c=k%8; s="E" if (k%16)/8==0 else "S"
r2 = r/2; c2 = c*2+r%2
return "%d%X%s "%(r2,c2,s)
変数 key_mat はROMの 0AC9h 番地からの 240 バイトを配列にしたものです。変数 dsp_asc はROMの 0BD6h 番地からの 240 バイトに先頭 16 バイトを追加して配列にしたものです。参考文献 [R02] を見てね。
* 2026.6.18 バグありました。「and d!=0xF0」を追加。トホホ
* 2026.6.25 バグありました。例外の扱いが甘かったのです
***
**
*
エピ21から始めた第三部はこれで終です。
キー・マトリクスや画面表示の仕組みの解読に取り組み、ほぼ解った感じで満足できました(個人の満足です)。
それでMZ80でしかできないと思われる画面プログラミングを追求してみたのですけど。Z80の機械語プログラムも作ってみて楽しかったのだけど。ライフゲームを実装できたのでZ80はチューニング完全であることを照明、...ピカッー
あ。...どうだったかな? 面白くない?、...。それは、...自己満足だから止むなしか。シクシク
それでも! イイねと思った方はイイねボタンのご検討を!
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.6.11 微推敲
2026.6.13 微推敲とエピタイ修正
2026.6.18 デバックと微推敲
2026.6.25 デバックと微推敲
2026.6.30 微推敲




