2025年06月29日

Z80での全メモリ同一値設定処理


1.はじめに
 散歩中にZ80のクイズを思いついたのでX(旧Twitter)で問いかけてみました。
 いつもはタイムラインに流れてくる課題(お題)について考える側の人間ですが、たまにはお題の提起をしてみようと思った次第です。あまり反応はないだろうと思っていましたが、予想に反して具体的なコードを示してくれる人もいらして楽しかったと同時に小規模な募集ではありましたが出題する側の大変さも実感しました。



2.お題の内容
 今回のお題の内容は上記のメッセージに書いてあるように下記のとおりです(Xのリンクは後でリンク切れになることがあったので念のために再掲)。

全メモリがRAMのZ80で全メモリを00hにするプログラムはPUSHやRSTを使って作ることができます。
それではPUSHもRSTも使わずに全メモリを同じ値にするプログラムを作れるでしょうか?

 メモリをゼロクリアするプログラムは例えば下記のようなものです。
 尚、以降に掲載するリストはAKI-80用モニターROM&Cコンパイラーセットの購入」の記事で書いたコンパイラに添付されているアセンブラの出力です 講談社のBLUE BACKSシリーズ「動かしながら理解するCPUの仕組み」に添付されているZ-Visionに同胞のアセンブラ(XA80W)の出力です。※訂正 2025/06/29
※追記 2026/07/11 全メモリクリアの最短コードを「Z80での全メモリクリアプログラム」の記事に記載しました。

全メモリクリアプログラム例(Z80アセンブラ)
                        ;++++++++++++++++++++++++
                        ; Z80 all memory clear
                        ;  2025/06/27 skyriver
                        ;++++++++++++++++++++++++
                        
[0000] :                	ORG	0000H
                        
[0000] : 21 01 00       START:	LD	HL,0001H
[0003] : 36 E9          	LD	(HL),0E9H	; JP (HL)
[0005] : 2B             	DEC	HL
[0006] : 36 E5          	LD	(HL),0E5H	; PUSH HL
[0008] : F9             	LD	SP,HL
[0009] : E9             	JP	(HL)
                        
                        	END

 PUSH や RST を使用すれば2バイトでメモリを書き換えるループ処理を作成でき、PUSH もしくは コールスタック で一度に2バイトを書き換えることが可能なことから2バイトのループを一度に上書きできます。
 そこで PUSH や RST を使わないことを条件にしました。流石にこの条件下で全メモリをクリアするのは回答が1方式に集中しそうなので「全メモリを同じ値にする」というように条件を和らげたお題にしました。


3.考える上でのポイント
 今回のお題ではメモリ書き換え部分のプログラム自身も削除する必要があるので効率よくメモリを書き換える必要があります。
 そのためには次の3つの方向から検討できるのではないかと思います。
  1. ブロック転送方式
     2バイト命令であり、この命令単独でループ処理が可能なので多くの人が最初にこの方式を思い浮かべるのではないでしょうか。今回のようにゼロクリアでなくてもいいのであればできそうですね。

  2. コールスタック方式
     コール時の戻りアドレスのスタックへの書き込みを利用する方式で一度に2バイトの書き換えができます。コール処理のアドレス次第で戻り値を決定できるのでメモリを満たすデータは 00h でも可能になるはずです。

  3. 単純ループ方式
     メモリフルするデータを00h以外でも可としたことでこの方式でも可能そうです。


4.回答案
 上記のポイントに沿って事前に考えた回答案は次の3つです。

  1. ブロック転送方式
     ブロック転送命令を利用したサンプルが下記になります。ブロック転送命令の後半のバイトである B0h をアドレスが増加する方向にコピーしていって、ブロック転送命令の最初のバイトの EDh が B0h に書き換えられた直後にブロック転送命令が終了します。
     結果として全メモリが B0h(OR B)で満たされた状態になります。

    Z80全メモリ同値設定プログラム(ブロック転送方式)
                            ;++++++++++++++++++++++++++++++++++
                            ; Z80 fill memory(LDIR method)
                            ;  Ver 0.01 2025/06/27 by skyriver
                            ;++++++++++++++++++++++++++++++++++
                            
    [0000] :                	ORG	0000H
                            
    [0000] : 21 0A 00       START:	LD	HL,CPCODE
    [0003] : 11 0B 00       	LD	DE,CPCODE + 1
    [0006] : 01 00 00       	LD	BC,0
    [0009] : ED             LOOP:	DB	0EDH	; LDIR
    [000A] : B0             CPCODE:	DB	0B0H	;
                            
                            	END
    


  2. コールスタック方式
     FFFDh に自分自信をコールするコール命令を設定し、実行します。コール命令の直前からアドレスが減少する方向に戻り値である 0000h がスタックに積まれていくようにします。最後にコール命令自体が CD FD 00 に上書きされますが 00FDh 以降コール命令までが既に 00h(NOP)なのでコール命令が実行され CD FD も 00 00 に上書きされます。
     結果として全メモリが 00h(NOP)で満たされた状態になります。

    Z80全メモリ同値設定プログラム(コールスタック方式)
                            ;++++++++++++++++++++++++++++++++++
                            ; Z80 fill memory(CALL method)
                            ;  Ver 0.01 2025/06/27 by skyriver
                            ;++++++++++++++++++++++++++++++++++
                            
    [0000] :                	ORG	0000H
                            
    [0000] : 21 FD FF       START:	LD	HL,0FFFDH
    [0003] : 22 FE FF       	LD	(0FFFEH),HL
    [0006] : 36 CD          	LD	(HL),0CDH
    [0008] : F9             	LD	SP,HL
    [0009] : E9             	JP	(HL)
                            
                            	END
    


  3. 単純ループ方式
     HL レジスタを使った4バイトのメモリ書き込みループにより、ループ直前のメモリからアドレスが減少する方向に 77h が設定されていきます。ジャンプ命令が上書きされても連続する 77h を実行してループの先頭まで辿り着きます。
     77h に 77h が上書きされてもメモリ変化はないので、最後に 2Bh が 77h に置き換わります。
     結果として全メモリが 77h で満たされた状態になります。

    Z80全メモリ同値設定プログラム(単純ループ方式)
                            ;++++++++++++++++++++++++++++++++++++
                            ; Z80 fill memory(simple loop method)
                            ;  Ver 0.01 2025/06/27 by skyriver
                            ;++++++++++++++++++++++++++++++++++++
                            
    [0000] :                	ORG	0000H
                            
    [0000] : 21 05 00       START:	LD	HL,LOOP
    [0003] : 3E 77          	LD	A,77H	; LD (HL),A
    [0005] : 2B             LOOP:	DEC	HL
    [0006] : 77             	LD	(HL),A
    [0007] : 18 FC          	JR	LOOP
                            
                            	END
    


    ★追記 2025/07/01
     1バイト短縮してみました。

    Z80全メモリ同値設定プログラム その2(単純ループ方式)
                            ;++++++++++++++++++++++++++++++++++++
                            ; Z80 fill memory(simple loop method)
                            ;  Ver 0.02 2025/07/01 by skyriver
                            ;++++++++++++++++++++++++++++++++++++
                            
    [0000] :                	ORG	0000H
                            
    [0000] : 21 04 00       START:	LD	HL,LOOP
    [0003] : 3E             	DB	3EH	; LD A,xx
    [0004] : 77             LOOP:	LD	(HL),A
    [0005] : 2B             	DEC	HL
    [0006] : 18 FC          	JR	LOOP
                            
                            	END
    

    ※追記 2025/07/02
     開始位置変更&処理時間の増大を許容すれば6バイトに短縮可能 Xのリンク
    ※追記 2025/07/03
     3Eh ⇒ 7Eh でも動きますね。


5.予想外だった回答
 Xで予想外の回答を頂いたので記録しておきたいと思います。以下の回答はタイムラインでの時系列順に記載しています。
 尚、本クイズに寄せられた回答は冒頭のXでの問い掛けメッセージに対するリプライ及び、「引用を表示」(寧ろ引用での回答の方が多い)を表示することで確認できます。
※Xでの回答の確認方法を追記 2025/06/30

  1. 書き込み処理コピー方式 Xのリンク
  2.  前述の「単純ループ方式」では HL を使ったメモリ書き込み処理をループで実行しましたが、この方式ではループ実行ではなく、書き込み処理自体を多数コピーした後に書き込み処理を実行するというように2段階で処理が実行されます。
     一つの書き込み処理が2バイトで新たに1バイトのメモリが設定されるのでメモリを周回する度に書き込み処理の総バイト数の1/2のサイズのメモリが設定されていきます。書き込み処理が残存する間はメモリ書き込みが継続されるので最終的には全ての書き込み処理が上書きされます。
     更に特徴的なのは複数の書き込み処理を最初に実行する際に書き込まれるメモリ領域には書き込み処理をコピーする必要が無いことから、書き込み処理をコピーするためのブロック転送時に B レジスタのみを設定することで処理自体のサイズを1バイト短くしている点です。

    Z80全メモリ同値設定プログラム(書き込み処理コピー方式)
    [0000] :                	ORG	00000H
                            
    [0000] : 21 0B 00       START:	LD	HL,ST
    [0003] : 11 0D 00       	LD	DE,ST+2
    [0006] : 06 FE          	LD	B,0FEH
    [0008] : 7E             	LD	A,(HL)
    [0009] : ED B0          	LDIR
    [000B] : 77             ST:	LD	(HL),A
    [000C] : 23             	INC	HL
                            
                            
                            	END
    



  3. EX (SP),HL方式 Xのリンク
     スタックを使った2バイト書き込みには PUSH, CALL 以外もあることを見落としていましたorz
     本方式はブロック転送後に残った2バイトをEX命令を使って消し去る方式です。
     ブロック転送により末尾にある 00h がアドレスが増える方向にコピーされていき、メモリを一周してブロック転送の前半のバイト(EDh)を00hに書き換えた直後にブロック転送命令から抜け出します。この時点で HL が0000hになっていて、 残った B0 E3 がEX命令により 00 00 に書き換えられます。
    ※動作説明を追記 2025/06/30
     尚、下記のソースは僭越ながら当方でコンパクト化したものです。

    Z80全メモリ同値設定プログラム(EX (SP),HL方式)
    [FFF4] :                	ORG	0FFF4H
                            
    [FFF4] : 21 03 00       START:	LD	HL,CPCODE
    [FFF7] : 11 04 00       	LD	DE,CPCODE+1
    [FFFA] : 01 00 00       	LD	BC,0
    [FFFD] : 31 01 00       	LD	SP,CPCODE-2
    [0000] : ED B0          	LDIR
    [0002] : E3             	EX	(SP),HL
    [0003] : 00             CPCODE:	NOP
                            
                            
                            	END
    


posted by skyriver at 21:26| Comment(0) | Z80 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください