↑ページトップへ
表示調整
閉じる
挿絵表示切替ボタン
▼配色
▼行間
▼文字サイズ
▼メニューバー
×閉じる

ブックマークに追加しました

設定
0/400
設定を保存しました
エラーが発生しました
※文字以内
ブックマークを解除しました。

エラーが発生しました。

エラーの原因がわからない場合はヘルプセンターをご確認ください。

ブックマーク機能を使うにはログインしてください。
PR
42/50

補足 MZ80で動くコードサイズ最小のライフゲーム


 ***


 補足 MZ80で動くコードサイズ最小のライフゲーム


 ***


 ちょっとした興味からMZ80で動くライフゲームのプログラムのコードサイズを追求してみました。


 *


 MZ80で動くライフゲームという条件を具体的にすると、


 ・画面は40x25の範囲(X=0〜39, Y=0〜24)でトーラス構造とする。トーラスとは左右と上下が各々連続することです。つまり、右端(X=39,Y=n)の右は同じ行の左端(X=0,Y=n)と扱い、同様に左端の左は右端で、下端の下は上端、上端の上は下端とする。


 ・メモリは 1200h〜CFFFh と D000h〜D3FFh を利用可能とする。D000h〜D3FFh は画面用メモリ。


 ・画面用メモリは D000h+X+Y*40 番地が 00h ならオフ(死滅)、00h 以外ならオン(生存or誕生)とする。


 ・ROMモニタ(SP1002)を利用可能とする。ROMは 0000h〜0FFFh で、1000h〜11FFh はROMモニタのワークエリア。スタックポインタはROMモニタが 10F0h に設定。ROMはコードサイズに含めない。


 ・Z80の機械語で記述する。


 ...、かな。あと何かある? ...、思い付いたら追加すればよいか。


 ***


 さて。


 画面プログラミングでは画面(D000h〜)にプログラムを書き込んで実行するのですが、プログラムのコードが邪魔です。そこでエピ29で整備したバックアップのコード(D348h)を利用して、バックアップ先(9000h)のコードを実行することにします。

 つまり、GOTO$D348 でバックアップして、GOTO$9000 でゲームスタートです。これで画面全体でライフゲームできます。


 ただ、GOTO$9000 が画面に残るのは、...やむなし? うーん、なら GETL をコールしてリターンしたら 9000h にジャンプするコードを用意するかな。


   Q_ワ : 1100A0 ; LD DE,A000h

   ネ   : AF____ ; XOR A

   木C_ : D40300 ; CALL NC,0003h

   →_セ : C30090 ; JP 9000h


 これを適当な行に書いて、そこに GOTO するのです。

 カーソル制御 [C] で画面クリアして、好きなパターンを書いてリターンすればゲームスタートします。OK?


 プログラムを 9000h で動かすには、絶対番地の修正が必要です。これはエピ30のスクリプトで、adr=0x9000 と指定すればよいですね。


 それから、前回(Ver4.1)までは SHIFT+BREAK を検出してゲームを止めるようにしていましたが、コードサイズ最小化のために、外してしまいます。6バイト違うのです。


 *


 画面全部を使うと、0 とか -1 との境界判定になるので、これがコードに微妙な影響を与えます。


 あれをコウするとこちらが、...。うむむ、...。


 *


 * ポク、ポク、ポク、...。チーン


 *


 できました! ソースコードは以下になります。


   _m:  ld DE,B000h; ld HL,D000h; ld BC,0400h; LDIR

      ld H,28h

   _e:  ld L,19h

   _d:  ld BC,0804h; ld DE,_v

   _p:  push HL

      ld A,H; inc A; jr NZ,+02; ld H,27h

      ld A,L; inc A; jr NZ,+02; ld L,18h

      ld A,28h; sub H; jr NZ,+01; ld H,A

      ld A,19h; sub L; jr NZ,+01; ld L,A

      call NC,0FB4h; ld A,H; sub 20h; ld H,A

      xor A; or (HL); jr Z,+01; dec C

      ld A,(DE); inc DE; ld (_w),A; pop HL

   _w:  nop; djnz _p

      push HL

      dec L; dec H; call NC,0FB4h; xor A

      dec C; jr NZ,+01; CPL

      dec C; jr Z,+01; ld (HL),A; pop HL

      dec L; jr NZ,_d

      dec H; jr NZ,_e

      jr _m

   _v:  db 25h,25h,2Dh,2Dh,24h,24h,2Ch,2Ch


 トーラスの処理で FFh との比較を止めて inc A にする。ディスプレイコードで入力できるし1バイト減。それから 40 と 25 との比較は cp でなくて sub にすれば H や L を 00h にするときに1バイト減。トーラス処理が4バイト減して24バイトになります。それと、


  ld A,28h; sub H; jr NZ,+01; ld H,A


 は


  ld A,L; sub 28h; jr NZ,+01; ld H,A


 としたいかもだけど。これだとキャリーフラグが立つからダメなんだな。


 レジスタ B と C を入れ替えて、DJNZ を _p のループ内で使う。バイト数は同じだけど生死の判定が綺麗に書けた。DJNZ B,+n や DJNZ C,+n などの命令があったら良かったよね。


 _d のループで、LD A,03h が不要になってコードを調整していたら1バイト減りました。それと L の判定を見直した。バイト数は同じだけど DEC L と DEC H が並んで。こっちの方が好きです。...、趣味の話ですまぬな。


 ディスプレイコードで入力できるように CALL nm は CALL NC,nm に戻した。キャリは立たないので大丈夫なはず、...多分。

 1つ目の CALL は、SUB L でキャリが0になって、JR と LD はキャリを変更しない。

 2つ目の CALL は、OR (HL) でキャリが0になって、以後の JR, DJNZ, INC, DEC, LD, PUSH, POP ではキャリを変更しない。

 ...、というかキャリが立つ命令って使ってなくない? つまり無借金経営?で堅実なコードだった。イイね!


 *


 これをスクリプトで処理すると、


   l = 99

   c = ( # VER 4.3

   "1100B02100D0010004EDB026282E19010408115B"+

   "90E57C3C200226277D3C20022E183E2894200167"+

   "3E199520016FD4B40F7CD62067AFB628010D1A13"+

   "324090E10010D2E52D25D4B40FAF0D20012F0D28"+

   "0177E12D20B92520B418A525252D2D24242C2C")


 となります。99バイトです。110バイトから11バイトのダイエットです。


 Z80機械語で書いた完全トーラスなライフゲームが99バイトってスゴくない?


 でも。あと1バイト削りたいな。1バイトを。


 *


 入力するディスプレイコードは次のようになります。緑色は GETL で直に入力できる文字で、赤紫色マゼンダは直には入力できない文字です。


 挿絵(By みてみん)


 ディスプレイコードで直に書けない文字は6個あります! 40h に注意して!


 キー入力支援のためのn行m列の値とエイスウシフトカナは次の通りです。


10E 43E 1BK 00E 43E 0FS 20E 43E 22E ___ 1BK 05E 07E 38E 15E 20E 22E 25E 10E 2AS

19K ___ 4BS 4BE 09E 34E 05E 06E 4CS 4BE 09E 34E 38E 31E 4DE 07E 14K 09E 20E 06S

4DE 15E 16K 09E 20E 37S 2FS 2BK 18E 4BS 3FS 09E 06S 37K 2DK 07E 20E 36E 30E 21E

1DE ___ 19K ___ 43E 19E 1FS ___ 39E 04E 2FS 2BK 18E 37K 36E 09E 20E 37E 36E 07E

20E 2ES ___ 39E 09E 3CK 04E 09E 2BK 31E 04K 04E 04E 39E 39E 03E 03E 29E 29E


___ ___ ___ ___ ___ ___ ___ ___ ___ 2ES ___ ___ ___ ___ ___ ___ ___ ___ ___ ___

___ 1BE ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___

___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___

___ 16S ___ 20S ___ ___ ___ 1BE ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___

___ ___ 20S ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___


 MZ80の実機をお持ちの方は是非とも入力してみて下さいな。


 ***

 **

 *


 画面プログラミングでディスプレイコードで直に書けない文字は、ライフゲームの場合は LDIR(EDh)と、PUSH HL(E5h)、POP HL(E1h)、それから番地(40h)になりました。


 マイナス方向32バイト内の相対ジャンプも制約なのですけど該当しなかったのは幸運。...、40h が該当したのは不運です。コードを 9001h 以降に配置すれば回避できますけど。...。


 ***

 **

 *


 ただ、ただ追求したかったから。書いて見たかったから書いただけです。


 もっと追求できるのか。なんなら最小コードであることのタイトな証明を付けてみるのも良いかも。


 でも。上限はコードの存在を示せば(99バイトで書けることの)証明になるけど、下限の証明はどうするのだろう? 1〜2バイトでは無理ですってナイーブな主張はできるけど。90バイトでは、...どうだろう?


 計算量の評価は(死ぬほど)色々あるけど。コードサイズの評価は、...そう言えば何かあった気がするけど。空間メモリの評価ではなくて、...回路計算量は、...ちょっと違う?


 調べてみないとだけど。なんだか下限の証明はナップザック問題みたいなNP困難な感じもする。


 ***


 間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!


 ***

2026.7.13 微推敲



評価をするにはログインしてください。
ブックマークに追加
ブックマーク機能を使うにはログインしてください。
― 新着の感想 ―
このエピソードに感想はまだ書かれていません。
感想一覧
+注意+

特に記載なき場合、掲載されている作品はすべてフィクションであり実在の人物・団体等とは一切関係ありません。
特に記載なき場合、掲載されている作品の著作権は作者にあります(一部作品除く)。
作者以外の方による作品の引用を超える無断転載は禁止しており、行った場合、著作権法の違反となります。