SSブログ
English Version

メモリ上で実際に動くプログラム [Z80]

 下記のX(旧Twitter)に投稿したメッセージではプログラムリストの一部が画面上を動き回ることで実際に「動く」プログラムを実現してみました。



 上記のメッセージにも書いたように学生の頃に自身のコードをメモリの少し先のアドレスにコピーし、コピーしたコードにジャンプすることを繰り返すことでメモリ上で実際に「動く」プログラム(以降、自走プログラムと記す)を書いたことがあります。
 その時使ったマイコンは自作の Z80 を使用したマイコンで起動後は全メモリが RAM になり、メモリ空間の一部を画面表示用の VRAM として使っていました。自走プログラムが VRAM を通過する際、コードに対応したキャラクタが一瞬表示されると共にコード実行時の VRAM のアクセスが CRT コントローラと競合することで画面にノイズが発生することから、VRAM 上を走っているタイミングが画面上で確認できました。

 以前作成した上記の自走プログラムのコード内容を細かくは覚えていないのですが久々にメモリ上を動くプログラムを作ってみることにしました。

 作成するプログラムの条件としては
  • メモリは全て RAM とする
  • 移動後のメモリは 00H にすること(自走プログラムがメモリを一周した後は実行中の自走プログラムに無関係なメモリ領域は全て 00H になるようにする)
  • 一回のコピーでのメモリ上の移動量はなるべく小さくする(数バイト程度)
です。

 中々面白そうなお題目ですね^^

 実際に作ったプログラムの一例を以降に記載しますが、まずは上記のお題目を自分で少し考えてみてから以降を読んだ方がこの記事の内容をより楽しめるかもしれません。



 上記の条件を満たすプログラムを作成するに当たり、最初に考えた方針が下記です。
  1. コードのコピー
     Z80 なのでブロック転送を使用する。コピー対象のソースとディスティネーションのアドレスが変化するので、HL と DE の値は再設定せずに継続して使用するようにする。

  2. コピー後のメモリクリア
     PUSH 命令でコピー元のコードを 00H で上書きする。コピー後は HL がコピー元の最終コードの次のアドレス値になっているのでスタックに HL の値を入れてから必要回数 0000H を PUSH する。

  3. プッシュ用のレジスタ
     ブロック転送で BC,DE,HL のレジスタペアを使用してしまうので 0000H PUSH用のレジスタペアをどうするか・・
    ⇒ ブロック転送後は BC レジスタが零になるのでこれを利用する。

 上記の方針で作成したコードが下記になります。以前作成したものは相対ジャンプを使っていた記憶があるので以前のものより進化したプログラムかもしれません(その分私自身も進化したということかw)。

メモリ上で「動く」プログラムの一例(Z80 アセンブラ)
;+++++++++++++++++++++++++++++++++++++ ; runner code on memory ; Ver 0.01 2024/01/16 by skyriver ; Ver 0.01a 2024/01/30 by skyriver ;+++++++++++++++++++++++++++++++++++++ 0000' ASEG ORG 0100H 0100 21 010B START: LD HL,CSTART 0103 11 0113 LD DE,DST 0106 06 00 LD B,0 0108 3E 08 LD A,CEND - CSTART + 1 010A F3 DI 010B F9 CSTART: LD SP,HL 010C C5 PUSH BC 010D C5 PUSH BC 010E C5 PUSH BC 010F C5 PUSH BC 0110 4F LD C,A ; B=0 0111 ED B0 LDIR 0112 CEND EQU $ - 1 0113 DST EQU $ END
★2023/01/30 若干最適化しました

 前述したメモリの一部を VRAM で使用しているようなマイコン環境は今はないので自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。

 確認手順は下記の通りです。
  1. メモリの設定
     自走プログラムが自コード以外のメモリをゼロクリアしていることを確かめるためにディバッガ(ZSID)を使ってメモリを E5H に設定する。

  2. 自走プログラムの実行
     Cドライブにある自走プログラム(runcode)を実行する。

  3. CP/M の再起動
     CPU をリセットし、CP/M を再起動する。TPA 領域のメモリはリセット時の値が保持されている。

  4. サーチプログラムの実行
     GAME インタープリタを起動し、GAME 言語で記述した自走プログラムのコードサーチプログラムを実行する。

  5. 自走プログラム周辺のメモリ値の確認
     サーチプログラムがメモリ上に残っている自走プログラムのコードを発見し、周辺のメモリも含めたメモリのダンプ表示をするので、メモリがクリアされていることを確認する。下記のログ例では LDIR で3バイトコピー直後にリセットされたようです。周辺のメモリは想定通り零クリアされていますね。


Z80GalCompact での自走プログラムの検証時のログ
Z80GAL Ver 0.01 2020/11/26 by skyriver CP/M loading ... ok 62k CP/M Version 2.2 COPYRIGHT (C) 1979, DIGITAL RESEARCH a>zsidm ZSIDM Ver 1.4 #f100 cfff e5 #d9000 9000: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9010: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9020: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9030: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9040: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9050: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9060: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9070: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9080: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9090: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 90A0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ #g0 a>c:runcode Z80GAL Ver 0.01 2020/11/26 by skyriver CP/M loading ... ok 62k CP/M Version 2.2 COPYRIGHT (C) 1979, DIGITAL RESEARCH a>gamec GAME80 for CP/M-80(compiler 0200-1B38) Ver0.05d *READY :¥<findrun 8E00-8F24 *READY :0 1' *** find runner code 10 A=0 100 @ 110 ;=(A:0)=$F9)*(A:1)=$C5)*(A:2)=$C5) " *** find at $" ??=A #=1000 120 ;=%(A/$1000)=0 / "Searching " ??=A "H" 130 A=A+1 140 @=(A=0) 150 #=-1 1000 B=A/256*256 ;=B<0 B=B-$100 1010 I=0,$10 1020 / ??=B ":" 1030 J=0,15 1040 " " ?$=B:J) 1050 @=J+1 1060 B=B+16 1070 @=I+1 *READY :#=1 Searching 0000H Searching 1000H Searching 2000H Searching 3000H Searching 4000H Searching 5000H *** find at $5BBB 5B00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5B90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5BA0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5BB0: 00 00 00 00 00 00 00 00 00 00 00 F9 C5 C5 C5 C5 5BC0: 4F ED B0 F9 C5 C5 00 00 00 00 00 00 00 00 00 00 5BD0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5BE0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5BF0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5C00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 *READY :


 上記のログの最後の部分と同様ですが、自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチした際の画面コピーも貼っておきます。このような時にはお手軽に使える GAME 言語が役立ちますね^^

自走プログラムのサーチ結果の画面



★追記 2024/01/18
 メモリを移動しながら通った後のメモリをゼロクリアする自走プログラムで PUSH を使用しない方法を思い付きました。
 ブロック転送で足跡を消すのは難しいと思っていましたが実行中のブロック転送命令をブロック転送命令で上書きすることでブロック転送処理を継続し、ブロック転送により足跡も消してしまう・・という方法です。
 自分自身のコードを上書きするのでステップやブレークが使えず机上ディバッグしたのでディバッグに少し苦労しました。

 ソースを以下に示します(このソースを見て動作を追えるでしょうか?)

メモリ上で「動く」プログラムのもう一つの例(Z80 アセンブラ)
;+++++++++++++++++++++++++++++++++++++ ; runner code on memory ; Ver 0.02 2024/01/18 by skyriver ; Ver 0.02a 2024/01/28 by skyriver ; Ver 0.02b 2024/01/29 by skyriver ;+++++++++++++++++++++++++++++++++++++ 001D BACKAD EQU (NEXT - CSTART + CLDR2 - CLDR1) 0000' ASEG ORG 0100H 0100 21 0103 START: LD HL,CEND - BACKAD 0103 11 010C LD DE,CEND - BACKAD + (CLDR2 - CLDR1) 0106 06 00 LD B,0 0108 3E 1D LD A,BACKAD 010A F3 DI 010B 18 0B JR SETC 010D 00 00 00 00 CSTART: DB 0,0,0,0 0111 00 00 00 00 DB 0,0,0,0 0115 00 DB 0 0116 ED B8 CLDR1: LDDR 0118 4F SETC: LD C,A 0119 09 ADD HL,BC 011A EB EX DE,HL 011B 09 ADD HL,BC 011C EB EX DE,HL 011D 0E 14 LD C,NEXT - CSTART 011F ED B8 CLDR2: LDDR 0120 CEND EQU $ - 1 0121 NEXT EQU $ END
★2023/01/29 若干最適化しました

 前回と同様の確認手順を実行した際のログを以下に示します。今回の自走プログラム名は runcode2 です。メモリ上に残っている自走プログラムの痕跡を見るとブロック転送で次のコードを生成している最中にリセットが掛かったようですね。

Z80GalCompact での自走プログラム2の検証時のログ
Z80GAL Ver 0.01 2020/11/26 by skyriver CP/M loading ... ok 62k CP/M Version 2.2 COPYRIGHT (C) 1979, DIGITAL RESEARCH a>zsidm ZSIDM Ver 1.4 #f100 cfff e5 #d9000 9000: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9010: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9020: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9030: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9040: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9050: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9060: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9070: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9080: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 9090: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ 90A0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 ................ #g0 a>c:runcode2 Z80GAL Ver 0.01 2020/11/26 by skyriver CP/M loading ... ok 62k CP/M Version 2.2 COPYRIGHT (C) 1979, DIGITAL RESEARCH a>gamec GAME80 for CP/M-80(compiler 0200-1B38) Ver0.05d *READY :¥<c:find2c 8E00-8F34 *READY :0 1' *** find runner code 10 A=0 100 @ 110 ;=(A:0)=$ED)*(A:1)=$B8)*(A:2)=$4F) " *** find at $" ??=A #=1000 120 ;=%(A/$1000)=0 / "Searching " ??=A "H" 130 A=A+1 140 @=(A=0) 150 #=-1 1000 B=A/256*256 ;=B<0 B=B-$100 1010 I=0,$10 1020 / ??=B ":" 1030 J=0,15 1032 ;=J=8 " -" 1040 " " ?$=B:J) 1050 @=J+1 1060 B=B+16 1070 @=I+1 *READY :#=1 Searching 0000H Searching 1000H Searching 2000H Searching 3000H Searching 4000H Searching 5000H Searching 6000H Searching 7000H Searching 8000H Searching 9000H Searching A000H *** find at $A26C A200: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A210: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A220: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A230: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A240: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A250: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A260: 00 00 00 00 00 00 00 00 - 00 00 00 00 ED B8 4F 09 A270: EB 09 EB 0E 14 ED B8 00 - 00 00 00 EB 0E 14 ED B8 A280: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A290: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2A0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2B0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2C0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2D0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2E0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A2F0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 A300: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 *READY :


 前回と同様に上記のログの最後の部分と同じ「自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチ」した際の画面コピーも貼っておきます。

自走プログラム2のサーチ結果の画面



★追記 2024/01/21
 自走プログラムが走る様子を MZ-2200 のエミュレータの画面により可視化できました。X(旧Twitter)に投稿したメッセージに添付した動画を貼っておきます。




[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。