エピ29 MZ80のプログラミング環境を整備
***
エピ29 MZ80のプログラミング環境を整備
***
前エピでは画面にディスプレイコードを書込むタイプのプログラミングを試みました。
画面でプログラミングするにはキーボードから入力できない命令があることが制限となり、これを如何に回避するかがポイントでした。
でも実際にプログラムを書いてみて、ライフゲーム程度なら可能だとの感触を得ました。
コードサイズとスピードは多少犠牲になりますが、どちらかと言うと機械語の技量に依存する部分の方が大きい感じです(個人の印象です)。
それでも、入力できない文字という制限を無くせるなら無い方が良いですし、画面でプログラミングと言うからには多少はプログラムの編集ができると良いのです。
画面プログラミングをもっと楽しめるようにできないかな? ほら。MZ700には不可能はないって言う方もいて。MZ80だって、...。
...、と、そんなことに取り組んでみたと言う話です。
***
入力できない文字を画面に表示する方法。
*
エピ28で前提としたプログラム入力時の条件は、SP1002のメインループからコールされた GETL(ROMルーチン 0003h)でキー入力を行うことです。
GETL のキー入力中はカーソルキーで上下左右に移動して好きな場所に入力できて、入力したキーに対応するディスプレイコードが画面に書き込まれます。
カーソル制御 [C] で画面をクリアして1行目の先頭(D000h 番地)から書き込むのがよくあるパターンです。
GETL で入力できない、あるいは画面表示できない文字は 40h, 80h, C0h, C7h〜CFh, DFh, E0h〜FFh の45個です。
そのうち、40h, 80h, C0h, CCh, CEh, CFh, DFh, E0h〜FFh の39個はキーボードに対応するキーがないから入力できない文字です。
C7h〜CBh と CDh の6個は対応するキーがあるのですが、...例えば C7h はキーボード上の INST キーに対応しますが、INST キーを押下すると INST のカーソル制御(カーソル位置に空白を挿入する)をするので、C7h は画面表示できないのです。
キー入力後、リターン・キーを押下するとカーソル位置の行を [DE] に転送し、転送先でディスプレイコードをアスキーコードに変換します。それから行末までの20h(ディスプレイコードでは 00h)を 0Dh に変換します。詳しくはエピ23を見てね。
SP1002のメインループでは DE を 11A3h に設定して GETL をコールします。GETL からリターン後、11A3h 番地以降を調べて、入力された文字がコマンド("LOAD" や "GOTO" など)と一致するなら実行します。コマンド以外なら次行に「*」を表示して、GETL をコールして入力待ちになります。
*
...、と言うことは。ピコーン!
Q8日 : 1128D0 ; LD DE,D028h
ネ : AF____ ; XOR A
木C_ : D40300 ; CALL NC,0003h
→コ_ : C38200 ; JP 0082h
を画面の1行目に入力して、GOTO$D000 すると、...どうなるのでしょう?
カーソルを適当な行に移動して「π」を入力してリターンキーを押下すると、2行目(= D028h)に FFh の文字(白黒反転した "田")が表示されるはずです。「π」のアスキーコードは FFh だから。
やや! 入力できない文字が表示できる? 「@」なら 40h を表示できるの?
ただし。GETL はカーソル位置の1行(あるいは2行)を行末までのスペースも含めて [DE] に転送します。[DE] 番地以降に 41 バイト等が転送されるのです。行頭に「π」を入力して、残り 39 文字がスペースの場合、転送先では(アスキーコードに変換した)FFh が1バイト、0Dh が 40 バイトで計 41 バイトになります。0Dh は「M」です。
プログラムの途中に1文字を入力したい場合には INST で 41 バイト等の隙間を空けないと、上書きされますです。DE の値と [1173h] に注意ね。
*
実用になるかは試して見ないと、...いえ、試すまでもなくアレなことですが。
...ですが。アスキーコードの 40h と C1h〜FFh は(対応するディスプレイコードを)キー入力できるから、使えない命令がほぼ解消します。
残るのは 80h と C0h の2文字です。機械語では 80h は ADD A,B で C0h は RET NZ です。ADD A,B は使いたいことがあるかも。
この2文字だけなら +1 した 80h ⇒ 81h と C0h ⇒ C1h を入力して、次のコードで -1 すれば 80h と C0h になりますね。
Q_日 : 1100D0 ; LD DE,D000h
Z : 1A____ ; LD A,(DE)
土A : D601__ ; SUB 01h
R : 12____ ; LD (DE),A
→コ_ : C38200 ; JP 0082h
A の値を -1 するなら普通は DEC A を使うのですが、ここでは -10h 等に変更したい場合のことを考慮して、SUB 01h 命令にしました。
*
全ての文字を画面表示するには。
画面でプログラミングしているときに下の方にこのコードを置いておくと便利かもよ?
Q8日ネ木C_→コ_ (1128D0 AF D40300 C38200)
Q_日Z土AR→コ_ (1100D0 1A D601 12 C38200)
D000h や D028h は文字の場所に応じて修正してね。
***
**
*
プログラムを入力するときの手順。
*
GETL でアスキーコードに変換できる文字を最初に入力した後、アスキーコードに変換できない文字は(メインループの)GETL で直にディスプレイコードで上書きすることを考えます。
入力できない文字はスペース(00h)のままにしておくと、ディスプレイコードの 00h はアスキーコードの 20h に変換されて画面では「0」と表示されますから、後で「0」を直すのです。
スペースではなくて「π」を入力して、変換後の「田」の文字を直す方が分かり易いかな。
*
GETL でアスキーコードに変換できない文字とは、00h〜0Fh, 10h, 17h〜1Fh, 60h〜6Fh, 7Fh, 80h, C0h の45個です。
入力できない文字の個数は同じですが、E0h 〜 FFh を入力できてマイナス方向の相対ジャンプを記述できるし、EX (SP),HL 命令や LDIR 命令を入力できるのは嬉しい。...、よね?
入力できない文字に対応する機械語は次のとおり。
60h-67h: LD H,rr 68h-6Fh: LD L,rr ... rr = B,C,D,E,H,L,(HL),A
7Fh: LD A,A 1Ah: LD A,(DE) 02h: LD (BC),A 0Ah: LD A,(BC)
06h: LD B,nn 0Eh: LD C,nn 1Eh: LD E,nn 01h: LD BC,nm
80h: ADD A,B 09h: ADD HL,BC 19h: ADD HL,DE
04h: INC B 05h: DEC B 0Ch: INC C 0Dh: DEC C
1Ch: INC E 1Dh: DEC E
03h: INC BC 0Bh: DEC BC 1Bh: DEC DE
18h: JR +nn 10h: DJNZ +nn C0h: RET NZ
07h: RLCA 17h: RLA 0Fh: RRCA 1Fh: RRA
08h: EX AF,AF' 00h: NOP
LD や INC/DEC 等で使える命令/使えない命令を覚えるのは大変だけど、2回に分けて入力するなら全ての命令が使えるものとして良いよね。80h と C0h の2つだけが例外。
2回のどちらで入力するか(アスキーコードかディスプレイコードか)等は、多分スクリプトで処理して入力すべきキーを表示するから覚えなくて大丈夫。
*
プログラムの入力手順。
(1) 次のコードを適当な行に書く。
Q_日ネ木C_→コ_ (1100D0 AF D40300 C38200)
このコードは出力を1行目の先頭(D000h)にする。2行目や3行目に出力するなら、2〜3文字目を「8日」(2行目)や「^日」(3行目)とする。「^」はMZでは「↑」ですね。
80h や C0h を使う場合には次のコードを書くね。2〜3文字目が修正する文字の番地です。
Q_日Z土AR→コ_ (1100D0 1A D601 12 C38200)
(2) (1)のコードに GOTO する。20 行目に書いたなら D000h + 19*40 = D2F8h で GOTO$D2F8 とする。
(3) GETL による1行入力になるので適当な行に移動して入力する(1回目の入力)。入力できない文字はスペースなり「π」にする。入力できるのは2行分まで。
(4) リターン・キーを押下する。アスキーコードに変換した文字が1行目(あるいは2行目や3行目)に表示される。次行の先頭が 0Dh で上書きされる点に注意!
(5) (4)の後、メインループからコールされた GETL の1行入力待ちになるので、残りの文字を入力する(2回目の入力)。それと行末の 0Dh を削除する。
***
**
*
入力したプログラムの編集。
*
プログラム入力後に間違いが見つかったときの対処を主な目的としましょう。
プログラムを別の行に移動して絶対番地の修正を自動的にできたら本格的だけど、それは無理だよね。削除と挿入とコピペが出来ればよいかな。
*
GETL の入力中はカーソルの移動と INST/DEL により、ある程度の編集ができます。1行あるいは2行の範囲内という制限がありますが画面上の文字を左右に移動できます。
削除と挿入はこれで十分でしょう。
コピペはできないので、...。コピー用のコードを用意しよう。
3行目を4行目にコピーする場合は(「※」はグラフィック文字)、
1※日 : 2178D0 ; LD HL,D078h ... コピー元 3行目
Qヮ日 : 11A0D0 ; LD DE,D0A0h ... コピー先 4行目
A8_ : 012800 ; LD BC,0028h ... 40バイト
※ム : EDB0__ ; LDIR
→コ_ : C38200 ; JP 0082h
1行を再入力するとき、GETL すると次行の先頭が壊れるので、別の行(ワーク用の領域)に一時出力して目的の行にコピーする、...ときに使えるかな。1行の一部だけをコピーすることもあるかも。
1文字のコピーなら、
※E日 : 3A05D0 ; LD A,(D005h)
※F日 : 3206D0 ; LD (D006h),A
→コ_ : C38200 ; JP 0082h
1文字なら、削除してもう一度入力する方が簡単かもだけど。
ディスプレイコードで直に入力できない文字はアスキーコード経由でワーク用領域に表示して1文字コピーするのです。いっそのこと、入力できない45文字をワーク用領域に表示しておいてコピー元にすると良いかも。
あるいは 80h,C0h 用の1文字修正(-1)コードを何回かコールすれば任意の文字にできるから(最大 255 回コールで!)これで文字を修正しても良いかも(よくない!)。
...。1文字修正のコードを -10h に変更して、F0h〜FFh の16文字を表示しておいて、1文字コピーして、-10h を最大3回コールするのが現実的だね。
F0h〜FFh を表示するには GETL をコールしてアスキーコードで入力すればよいのだけど。コードを用意するなら、D2A8h 番地にコードを置いて、D208h 番地に表示するとして、
QX火 : 2118D2 ; LD DE,D208h+10h
ネ : AF____ ; XOR A
FP : 0610__ ; LD B,10h
| : 3D____ ; DEC A
R : 77____ ; LD (DE),A
╋ : 1B____ ; DEC DE
E : 05____ ; DEC B
↑ル火 : 10AED2 ; JP NZ,D2A8h+06h
→コ_ : C38200 ; JP 0082h
かな。DJNZ を使うと FBh が入力できなくてね。...。多分このコードは使わないね。
*
あれ? D040h 番地に1文字コピーするには、コピー先の番地の D040h を入力することになるけど、40h は直には入力できないからコピーできないよ? 41h を入力して1文字修正してから1文字コピーするの? ...、すごく面倒!笑える。
D03Fh か D041h 番地へなら1文字コピーできるから、そこにコピーしてから GETL 入力中に INST か DEL で D040h に移動すれば良いかな。...。
***
**
*
プログラミング環境のバックアップ。
*
プログラムを実行するとプログラミング環境が壊れることがあります。例えばライフゲームを実行したときね。
プログラムの実行の前に D000h〜 をメモリの何処かにコピーしておき、プログラムの終了後に復活できると良いですね。
コピーは行コピーを流用するか、専用のコードを用意するかな。
専用のコードを用意するならコピー(D000h から 9000h へ)は、
1_日 : 2100D0 ; LD HL,D000h
Q_セ : 110090 ; LD DE,9000h
A_D : 010004 ; LD BC,0400h
※ム : EDB0__ ; LDIR
→コ_ : C38200 ; JP 0082h
で、復活のコードは、9000h〜 にコピーしたとして、
1_セ : 210090 ; LD HL,9000h
Q_日 : 1100D0 ; LD DE,D000h
(以下同文)
とする。復活のコードを D320h 番地に置くなら、GOTO$9320 で復活する。
***
**
*
プログラムのセーブとロード。
*
プログラムが完成したら保存したいよね。
セーブは 10F0h 番地以降に設定して「ネ木1ネ木4」すればよい。ロードはSP1002の LOAD コマンドでOK。楽勝。
*
10F0h 番地以降に設定する内容は次の24バイト。ファイル名は付けたいよね。
10F0: 01h (種別)
10F1: 41414141414141414141414141414141h(ファイル名,16文字)
1101: 0Dh(終端)
1102: 0400h (バイト数)
1104: D000h (開始番地)
1106: 0000h (実行番地)
画面のどこかにこの24バイトを入力しておいて、10F0h にコピーすればよい。...。のだけど。1行コピーのコードで、コピー先の番地に 10F0h の F0h は入力できないのです。難儀。
なら、10F0h+24-1 = 1107h 番地からマイナス方向に24バイトをコピればよいかな。
1ネ水 : 21AFD3 ; LD HL,D3AFh # = D398h+24-1
QGQ : 110711 ; LD DE,1107h # = 10F0h+24-1
AX_ : 011800 ; LD BC,0018h
※※ : EDB8__ ; LDDR
Qコ_ :118200 :LD DE,0082h
金 :D5____ :PUSH DE
┣4 :1E24__ :LD E,24h
金 :D5____ :PUSH DE
→1_ :C32100 :JP 0021h
21文字とちょっと長いけど。カセットテープに保存するなら入力は1度でよいから。...、頑張って。
*
LOAD や SAVE の処理中にはメッセージが画面に表示されることに注意ね。画面の下の方で実行したら画面がスクロールアップしてプログラムが壊れてしまうよ。
LOAD コマンドなら「↓PLAY」と、LOADING の2行が表示される。SAVE も2行(多分)
***
**
*
まとめです。プログラミング環境のイメージ図です。
プログラムは1行目からに書くことにします。5行あれば十分かな? 図ではライフゲームのプログラムを書きました。
6行目からをプログラミング用に使う。ROMコマンドの実行や作業領域などね。GETL コールにて、13行目でキー入力して11行目に出力した後、必要部分を1〜5行目にコピーする感じで?
16行目からコードを置く。最初に GETL コール(16行目)と1文字修正(17行目)のコードを入力して、これを使って他のコードを入力する。20〜23行目の直に入力できない4文字(EDh)を GETL コールで23行目にまとめて入力して、20〜23行目に移動して、残りを入力すると良いかも。
コードの中で番地を指定する部分は適宜修正して使います。具体的には、
・GETL コールの2〜3文字目(出力する行の指定)
・1字修正の2〜3文字目(修正する番地)
・1字コピーの2〜3、5〜6文字目(コピ元とコピ先)
・1行コピーの2〜3、5〜6、8〜9文字目(コピ元、コピ先、コピ文字数)
です。番地の指定で使いそうなのは、...
1行目 D000 「 日」
2行目 D028 「8日」
3行目 D050 「↑日」
4行目 D078 「ー日」
5行目 D0A0 「ワ日」
11行目 D190 「セ月」
23行目 D370 「 ̄水」
かな。あとはエピ28の図で調べて。
画面の19行目は D2F8h 番地で、F8h は直には入力できないから面倒です。とりあえず空行で放置。
行の接続を操るには [1173h]〜 をクリアするコードがあると良いかもだけど。ルーチンが増えたら1行に複数のコードを書いてもよいかな。2個くらいは置けるはず。
***
**
*
画面でプログラミングするための環境整備を試みました。...、思考実験的なお遊びですけど。
まあ。そもそもZ80の機械語を16進数ではなくてディスプレイコードで入力しようなんてアレなことですけど。面白いかなと思ったから。
*
画面をみて「ネ」なら XOR A だとか、JP 命令は(C3h ではなくて)「→」だと連想できたら、あなたも画面プログラマです。笑笑笑
*
キー入力が結構面倒なことは課題かな。
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.6.13 推敲
2026.6.15 推敲
2026.6.18 推敲と図追加
2026.6.19 誤記訂正と推敲
2026.6.20 推敲と図差替
2026.6.25 推敲
2026.7.16 微推敲




