メモリ上で実際に動くプログラム(その2) [Z80]
メモリ上で実際に動き(移動し)通過した後にはNOP命令のみを残してひたすらメモリ上を駆け回る自走プログラムで新たな展開があったので記録しておきたいと思います。
前回の記事で追記したブロック転送のみによる自走プログラムはアイディアは面白いのですが、前回の記事に書いたように今回の自走プログラムの条件の一つである「一回のコピーでのメモリ上の移動量はなるべく小さくする(数バイト程度)」という点では初版のものより劣っていたのでもっと小刻みに移動するものはできないか考えてみました。
前回記事に追記したプログラムは PUSH を使わずブロック転送のみで自走プログラムを実現したので逆の発想で PUSH のみで自走プログラムができないかも試みてみます。
これは結構難しく、できそうではあるけれどもう一歩のところでなかなかできません。
Z80 はレジスタは多いのですが、PUSH 対象のレジスタペア数は半減しそれほど多くないので 裏レジスタと AF レジスタペアまで利用しました。
AF レジスタペアを利用するにあたり、フラグレジスタの未使用ビットに値を設定できるのかを検索してみると「Z80のフラグレジスタについて」がヒットしましたが回答は「~~の場合もありうる」という内容で質問に対する回答(できるかできないか)が書いてありません。
海外のネット情報では例えば「What is the purpose of the reserved/undefined bit in the flag register?」にあるように Z80 のフラグの未使用ビットには値を設定できる旨が書いてありました。
念のためにザイログ製の Z80 を使って下記のプログラムで確認してみた結果、出力は Ok だったのでフラグの未使用ビットも値を保持できることを確認できました。
※2024/02/04 improved slightly
今回のプログラムも自身のコードを上書きするのでディバッガでのステップ動作等は使えず、机上ディバッグメインで動かしました。通常のプログラムを組む時の思考とは違う考え方が必要なので結構大変でした。普通に考えると PUSH 対象のレジスタペアが足りないのでレフレックスラジオが一つのトランジスタを高周波と低周波で2回使うように二つのレジスタペアを2回づつ使っています。この共通コードができるようにし、レジスタの使用を節約するように調整するのが難しいパズルでした。
完成したソースを以下に示します。メモリ上の移動幅は究極の1バイトで、メモリを逆走します。このソースを見て机上で動作を追えるでしょうか? PC と SP がクロスする部分の仕掛け等を堪能してください。
★追記 2024/01/28 {
コード生成に必要な PUSH 回数について考察してみると
しかし、生成コードの中には順番を逆転できない制約がある部分があり、今回の自走プログラムはかなり厳しい条件の中で辛うじて達成できたと言えると思います。
}
前回の記事と同様に自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。
今回作成した自走プログラムは runcode3 で実行後にリセットし、メモリ上に残っているプログラムのコードを GAME 言語で作成したサーチプログラムで探しています。
X(旧Twitter)で教えてもらったメモリの全空間が RAM で メモリ上に VRAM をアサインできる MZ-2200 のエミュレータで自走プログラムが VRAM 上を通る時の画面キャプチャを貼っておきます。
下記の例では PUSH 命令によって PUSH 命令自身を書き換えています。
以下がステップ動作時の画面表示です。「done 00000009 PUSH HL」と表示されずに「done 00000009 INC A」と表示されていますね。Aregはゼロのままでメモリは HL の値に書き換わっているので実際は PUSH HL を実行し、done 表示ではメモリから再読みこみして表示しているようです(つまり実行動作は問題なくて表示が違っているだけ)。極めてレアなケースなので通常の使用には問題ないと思われます。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
前回の記事で追記したブロック転送のみによる自走プログラムはアイディアは面白いのですが、前回の記事に書いたように今回の自走プログラムの条件の一つである「一回のコピーでのメモリ上の移動量はなるべく小さくする(数バイト程度)」という点では初版のものより劣っていたのでもっと小刻みに移動するものはできないか考えてみました。
前回記事に追記したプログラムは PUSH を使わずブロック転送のみで自走プログラムを実現したので逆の発想で PUSH のみで自走プログラムができないかも試みてみます。
これは結構難しく、できそうではあるけれどもう一歩のところでなかなかできません。
Z80 はレジスタは多いのですが、PUSH 対象のレジスタペア数は半減しそれほど多くないので 裏レジスタと AF レジスタペアまで利用しました。
AF レジスタペアを利用するにあたり、フラグレジスタの未使用ビットに値を設定できるのかを検索してみると「Z80のフラグレジスタについて」がヒットしましたが回答は「~~の場合もありうる」という内容で質問に対する回答(できるかできないか)が書いてありません。
海外のネット情報では例えば「What is the purpose of the reserved/undefined bit in the flag register?」にあるように Z80 のフラグの未使用ビットには値を設定できる旨が書いてありました。
念のためにザイログ製の Z80 を使って下記のプログラムで確認してみた結果、出力は Ok だったのでフラグの未使用ビットも値を保持できることを確認できました。
POP AF でのフラグの未使用ビットの値の確認(Z80アセンブラ) |
|
今回のプログラムも自身のコードを上書きするのでディバッガでのステップ動作等は使えず、机上ディバッグメインで動かしました。通常のプログラムを組む時の思考とは違う考え方が必要なので結構大変でした。普通に考えると PUSH 対象のレジスタペアが足りないのでレフレックスラジオが一つのトランジスタを高周波と低周波で2回使うように二つのレジスタペアを2回づつ使っています。この共通コードができるようにし、レジスタの使用を節約するように調整するのが難しいパズルでした。
完成したソースを以下に示します。メモリ上の移動幅は究極の1バイトで、メモリを逆走します。このソースを見て机上で動作を追えるでしょうか? PC と SP がクロスする部分の仕掛け等を堪能してください。
完成した自走プログラム3(Z80アセンブラ) |
|
★追記 2024/01/28 {
コード生成に必要な PUSH 回数について考察してみると
- PUSH 命令(1バイト)で2バイトのメモリを設定できるので PUSH 命令1回につき1バイトのコードを生成できる
- 今回の自走プログラムでは先頭の LD SP,HL と DEC HL の2バイトと末尾の JR xx と NOP の3バイト及び前進するための伸長分の1バイトの合計6バイトのコード生成が必要となる
- PUSH 用に使用できるレジスタペアが AF BC DE の3つ(HL レジスタはスタック再設定用に使用)では不足するので更に3つの裏レジスタペアを使用する必要がある
- 裏レジスタを使用するためには EXX を2回実行する必要があり、必要コードにこの2バイトを加えると8バイトのコード生成(8回の PUSH)が必要となる
しかし、生成コードの中には順番を逆転できない制約がある部分があり、今回の自走プログラムはかなり厳しい条件の中で辛うじて達成できたと言えると思います。
}
前回の記事と同様に自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。
今回作成した自走プログラムは runcode3 で実行後にリセットし、メモリ上に残っているプログラムのコードを GAME 言語で作成したサーチプログラムで探しています。
Z80GalCompact での自走プログラム3の検証時のログ |
|
X(旧Twitter)で教えてもらったメモリの全空間が RAM で メモリ上に VRAM をアサインできる MZ-2200 のエミュレータで自走プログラムが VRAM 上を通る時の画面キャプチャを貼っておきます。
EmyuZ-2200 エミュレータでの自走プログラム2表示 |
|
★追記 2024/01/27
EmyuZ-2200 エミュレータのディバッガは RST 命令を使用したソフトウェアディバッガよりも ICE に近いので実行命令直下のメモリを書き換えてもステップ動作が行えたり、スタック直下のメモリを壊したりしません。
今後ディバッグに活用する場面がありそうで有用なソフトを公開された作者の方に感謝です。
しかし、上記の自走プログラムで使用しなかったのは奇妙な動きがあったからです。改めて動作を確認してみましたので記録しておきます。
EmyuZ-2200 エミュレータのディバッガは RST 命令を使用したソフトウェアディバッガよりも ICE に近いので実行命令直下のメモリを書き換えてもステップ動作が行えたり、スタック直下のメモリを壊したりしません。
今後ディバッグに活用する場面がありそうで有用なソフトを公開された作者の方に感謝です。
しかし、上記の自走プログラムで使用しなかったのは奇妙な動きがあったからです。改めて動作を確認してみましたので記録しておきます。
下記の例では PUSH 命令によって PUSH 命令自身を書き換えています。
PUSH 命令による自己書き換えプログラム |
|
以下がステップ動作時の画面表示です。「done 00000009 PUSH HL」と表示されずに「done 00000009 INC A」と表示されていますね。Aregはゼロのままでメモリは HL の値に書き換わっているので実際は PUSH HL を実行し、done 表示ではメモリから再読みこみして表示しているようです(つまり実行動作は問題なくて表示が違っているだけ)。極めてレアなケースなので通常の使用には問題ないと思われます。
EmyuZ-2200 のディバッガでのステップ動作結果 |
|
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
最近遊んだMPU [CH32V]
最近遊んでみた MPU について書いておきたいと思います。
★追記 2024/07/15
「CH32V203で遊ぶ」の記事に MounRiver Studio の開発環境で CH32V203 のソフトウェアを作ってみる内容を記載しました。
- ルネサス製 RA4M1
ルネサスさんの Arduino UNO R4 MINIMA を無料で配布し、教育用のビデオで勉強できるという太っ腹企画に応募して最新の UNO R4 を入手出来ました。
Arduino UNO R4 MINIMA での実験中の様子
教育用の資料とビデオで Arduino IDE で数個のサンプルプログラムを少しカスタマイズしながら動かしてみました。Arduino は楽でいいですねぇ。
Arduino IDE は至れり尽くせり感が滲み出てきますがソースを作るより探す面が多くなりがちなことや内部の仕掛けが見えにくい等から個人的にはあまり好きではありません。また RA4M1 は MPU 単体を安価に入手できないのも使用する上で大きなネックになりますね。
PWM でのLEDジンワリ点滅
- WCH製 CH32V
近年、秋月さんでも取り扱いを始めている安価に入手可能な 32 ビット RISCV MPUです。秋月さんで扱うと電子工作界隈に浸透すると言われますが CH32V はどうでしょうね。私のお気に入りだった PIC24FJ は残念なことにすっかり値段が高くなってしまい使い辛くなってしまいましたが CH32V は円安にも関わらず安価に入手できます。書込みにはWCH-LinkE エミュレータが必要ですが、秋月さんで ¥750 で入手できます。
最初の設定等は「きょうのかんぱぱ」サイトの「40円RISC-Vマイコン(CH32V003)をArduino IDEでLチカをしてみました」のブログ記事を参考にさせて頂き、すんなり動きました。
CH32V003J4M6 でのLチカ実験用ハード
CH32V003J4M6 は安価(¥40)で良いのですが、もっと高機能な CH32V203K8T6(以降CH32V203 と記す)でも遊んでみました。高機能といっても秋月さんで安価(¥120)に入手できます。
簡単な仕様比較を下表に示します。CH32V203 は SPI 1CH、CAN 1CH、USB 機能等も実装しています。
clock flash RAM GPIO ADC UART/USART I2C timer Pakage CH32V003J4M6 48MHz 16KB 2KB 6pin 6ch 1ch 1ch 4ch SOP8 CH32V203K8T6 144MHz 64KB 20KB 26pin 10ch 2ch 1ch 6ch LQFP32
CH32V203 は 32 ピンの LQFP パッケージなのでブレッドボードで使えるように DIP 変換基板も購入しました。
CH32V203 と DIP 変換基板
ピン間隔が 0.8mm の QFP パッケージなので上下左右の位置合せをしっかるやる必要があり、下図のようにマスキングテープで固定してから半田付けしました。
CH32V203 の固定
半田付け後の状態が下の写真になります。うまく半田付けできているみたいですね。
DIP 変換基板に半田付け後の状態
Lチカ実験用の回路を下図に示します。ディバッグ用のシリアル受信のために Link-E の Rx も接続しています。
CH32V203 でのLチカ回路
ブレッドボードを使って LinkE と LED を接続した状態が下の写真です。右にあるチップは CH32V003J4M6 です。
CH32V203 でのLチカ実験環境
Arduino IDE ではすんなりLチカが動きました。
CH32V203 でのLチカ実験(Arduino IDE)
次に CH32V の IDE 環境である MounRiver Studio を動かしてみました。
”mcuXfamikyのブログ”というブログの「CH32V203(1)資料集め」から始まる一連の記事が大変参考になりました。
下図が MounRiver Studio でLチカを実行している様子です。ブレークやステップもできるのでこの開発環境は中々いいですね^^
MounRiver Studio でのLチカ実行画面
★追記 2024/07/15
「CH32V203で遊ぶ」の記事に MounRiver Studio の開発環境で CH32V203 のソフトウェアを作ってみる内容を記載しました。
メモリ上で実際に動くプログラム [Z80]
下記のX(旧Twitter)に投稿したメッセージではプログラムリストの一部が画面上を動き回ることで実際に「動く」プログラムを実現してみました。
上記のメッセージにも書いたように学生の頃に自身のコードをメモリの少し先のアドレスにコピーし、コピーしたコードにジャンプすることを繰り返すことでメモリ上で実際に「動く」プログラム(以降、自走プログラムと記す)を書いたことがあります。
その時使ったマイコンは自作の Z80 を使用したマイコンで起動後は全メモリが RAM になり、メモリ空間の一部を画面表示用の VRAM として使っていました。自走プログラムが VRAM を通過する際、コードに対応したキャラクタが一瞬表示されると共にコード実行時の VRAM のアクセスが CRT コントローラと競合することで画面にノイズが発生することから、VRAM 上を走っているタイミングが画面上で確認できました。
以前作成した上記の自走プログラムのコード内容を細かくは覚えていないのですが久々にメモリ上を動くプログラムを作ってみることにしました。
作成するプログラムの条件としては
中々面白そうなお題目ですね^^
実際に作ったプログラムの一例を以降に記載しますが、まずは上記のお題目を自分で少し考えてみてから以降を読んだ方がこの記事の内容をより楽しめるかもしれません。
上記の条件を満たすプログラムを作成するに当たり、最初に考えた方針が下記です。
上記の方針で作成したコードが下記になります。以前作成したものは相対ジャンプを使っていた記憶があるので以前のものより進化したプログラムかもしれません(その分私自身も進化したということかw)。
★2023/01/30 若干最適化しました
前述したメモリの一部を VRAM で使用しているようなマイコン環境は今はないので自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。
確認手順は下記の通りです。
上記のログの最後の部分と同様ですが、自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチした際の画面コピーも貼っておきます。このような時にはお手軽に使える GAME 言語が役立ちますね^^
★追記 2024/01/18
メモリを移動しながら通った後のメモリをゼロクリアする自走プログラムで PUSH を使用しない方法を思い付きました。
ブロック転送で足跡を消すのは難しいと思っていましたが実行中のブロック転送命令をブロック転送命令で上書きすることでブロック転送処理を継続し、ブロック転送により足跡も消してしまう・・という方法です。
自分自身のコードを上書きするのでステップやブレークが使えず机上ディバッグしたのでディバッグに少し苦労しました。
ソースを以下に示します(このソースを見て動作を追えるでしょうか?)
★2023/01/29 若干最適化しました
前回と同様の確認手順を実行した際のログを以下に示します。今回の自走プログラム名は runcode2 です。メモリ上に残っている自走プログラムの痕跡を見るとブロック転送で次のコードを生成している最中にリセットが掛かったようですね。
前回と同様に上記のログの最後の部分と同じ「自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチ」した際の画面コピーも貼っておきます。
★追記 2024/01/21
自走プログラムが走る様子を MZ-2200 のエミュレータの画面により可視化できました。X(旧Twitter)に投稿したメッセージに添付した動画を貼っておきます。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
CP/M用GAME言語にロード/セーブ/filesコマンドを追加したので少し遊んで見た
— skyriver (@wcinp) December 1, 2023
以前自身のコードを少し先にコピーしジャンプすることでメモリ上を暴走するコードを作ったことがありますがこれもある意味動くプログラムw
尚、CP/M用のGAME言語のDLは下記からどうぞhttps://t.co/ftoKmArOU5#GAME言語 pic.twitter.com/uDPBteBiCc
上記のメッセージにも書いたように学生の頃に自身のコードをメモリの少し先のアドレスにコピーし、コピーしたコードにジャンプすることを繰り返すことでメモリ上で実際に「動く」プログラム(以降、自走プログラムと記す)を書いたことがあります。
その時使ったマイコンは自作の Z80 を使用したマイコンで起動後は全メモリが RAM になり、メモリ空間の一部を画面表示用の VRAM として使っていました。自走プログラムが VRAM を通過する際、コードに対応したキャラクタが一瞬表示されると共にコード実行時の VRAM のアクセスが CRT コントローラと競合することで画面にノイズが発生することから、VRAM 上を走っているタイミングが画面上で確認できました。
以前作成した上記の自走プログラムのコード内容を細かくは覚えていないのですが久々にメモリ上を動くプログラムを作ってみることにしました。
作成するプログラムの条件としては
- メモリは全て RAM とする
- 移動後のメモリは 00H にすること(自走プログラムがメモリを一周した後は実行中の自走プログラムに無関係なメモリ領域は全て 00H になるようにする)
- 一回のコピーでのメモリ上の移動量はなるべく小さくする(数バイト程度)
中々面白そうなお題目ですね^^
実際に作ったプログラムの一例を以降に記載しますが、まずは上記のお題目を自分で少し考えてみてから以降を読んだ方がこの記事の内容をより楽しめるかもしれません。
上記の条件を満たすプログラムを作成するに当たり、最初に考えた方針が下記です。
- コードのコピー
Z80 なのでブロック転送を使用する。コピー対象のソースとディスティネーションのアドレスが変化するので、HL と DE の値は再設定せずに継続して使用するようにする。
- コピー後のメモリクリア
PUSH 命令でコピー元のコードを 00H で上書きする。コピー後は HL がコピー元の最終コードの次のアドレス値になっているのでスタックに HL の値を入れてから必要回数 0000H を PUSH する。
- プッシュ用のレジスタ
ブロック転送で BC,DE,HL のレジスタペアを使用してしまうので 0000H PUSH用のレジスタペアをどうするか・・
⇒ ブロック転送後は BC レジスタが零になるのでこれを利用する。
上記の方針で作成したコードが下記になります。以前作成したものは相対ジャンプを使っていた記憶があるので以前のものより進化したプログラムかもしれません(その分私自身も進化したということかw)。
メモリ上で「動く」プログラムの一例(Z80 アセンブラ) |
|
前述したメモリの一部を VRAM で使用しているようなマイコン環境は今はないので自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。
確認手順は下記の通りです。
- メモリの設定
自走プログラムが自コード以外のメモリをゼロクリアしていることを確かめるためにディバッガ(ZSID)を使ってメモリを E5H に設定する。
- 自走プログラムの実行
Cドライブにある自走プログラム(runcode)を実行する。
- CP/M の再起動
CPU をリセットし、CP/M を再起動する。TPA 領域のメモリはリセット時の値が保持されている。
- サーチプログラムの実行
GAME インタープリタを起動し、GAME 言語で記述した自走プログラムのコードサーチプログラムを実行する。
- 自走プログラム周辺のメモリ値の確認
サーチプログラムがメモリ上に残っている自走プログラムのコードを発見し、周辺のメモリも含めたメモリのダンプ表示をするので、メモリがクリアされていることを確認する。下記のログ例では LDIR で3バイトコピー直後にリセットされたようです。周辺のメモリは想定通り零クリアされていますね。
Z80GalCompact での自走プログラムの検証時のログ |
|
上記のログの最後の部分と同様ですが、自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチした際の画面コピーも貼っておきます。このような時にはお手軽に使える GAME 言語が役立ちますね^^
自走プログラムのサーチ結果の画面 |
|
★追記 2024/01/18
メモリを移動しながら通った後のメモリをゼロクリアする自走プログラムで PUSH を使用しない方法を思い付きました。
ブロック転送で足跡を消すのは難しいと思っていましたが実行中のブロック転送命令をブロック転送命令で上書きすることでブロック転送処理を継続し、ブロック転送により足跡も消してしまう・・という方法です。
自分自身のコードを上書きするのでステップやブレークが使えず机上ディバッグしたのでディバッグに少し苦労しました。
ソースを以下に示します(このソースを見て動作を追えるでしょうか?)
メモリ上で「動く」プログラムのもう一つの例(Z80 アセンブラ) |
|
前回と同様の確認手順を実行した際のログを以下に示します。今回の自走プログラム名は runcode2 です。メモリ上に残っている自走プログラムの痕跡を見るとブロック転送で次のコードを生成している最中にリセットが掛かったようですね。
Z80GalCompact での自走プログラム2の検証時のログ |
|
前回と同様に上記のログの最後の部分と同じ「自走プログラム実行中にリセットし、メモリ上にある自走プログラムのコードをサーチ」した際の画面コピーも貼っておきます。
自走プログラム2のサーチ結果の画面 |
|
★追記 2024/01/21
自走プログラムが走る様子を MZ-2200 のエミュレータの画面により可視化できました。X(旧Twitter)に投稿したメッセージに添付した動画を貼っておきます。
自走プログラムが走る様子をエミュの画面上で額認できました^^ pic.twitter.com/BYlOw5nqS6
— skyriver (@wcinp) January 21, 2024
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]