メモリ上で実際に動くプログラム(その3) [Z80]
メモリ上で実際に動き(移動し)通過した後にはNOP命令のみを残してひたすらメモリ上を駆け回る自走プログラムで新たな案を思い付いたので記録しておきたいと思います。
前回の記事で書いた PUSH によるコード生成方式は自走プログラムの移動量が究極の1バイトであり、自走プログラムの移動量という点ではこれ以上小さくするのは不可能ということになります(プログラムの仕掛けという観点でも素晴らしいと思う)。
本連載の初回記事「メモリ上で実際に動くプログラム」に PUSH を使わずブロック転送のみを使用した自走プログラムを追記しましたが、同様にブロック転送のみの場合でも移動量をもっと小さくできないか考えてみました。
インクリメント方式のブロック転送である LDIR 命令では次のブロック転送のために16ビットの減算をする必要があり、キャリーのクリア等が必要になることを考えるとデクリメント方式の LDDR を使いたいものです。ブロック転送を利用した場合の最小移動量は2バイトになりそうですが、ブロック転送命令(LDDR=EDH,B8H)を二つ並べてブロック転送命令をブロック転送命令で上書きすることでブロック転送を中断しないようにするとブロック転送終了直後にコピーしたブロック転送を実行してしまうのでうまくいきません・・・
しかし、巧妙な仕掛けを思い付きました^^
方式のアイディアさえ固まれば、ブロック転送を利用した自走プログラムには慣れてきたのでほぼ机上コーディングのみで完成しました。完成したソースが下記で移動量は2バイトです。このソースを見て今回のアイディアの仕掛けが判りますか?
★追記 2024/02/09
X(Twitter)に投稿した自走状況を可視化した動画を貼っておきます。
★追記 2024/02/10
X での書込みメッセージに対して返信を頂きました。LDIR を使用し、移動量が1バイトというものです。コピー後の LDIR を再利用する部分とキャリーによる条件ジャンプの部分が味わい深いですね。LDIR で検討してみました(下記)が DI を除けば同じサイズになりました。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
前回の記事で書いた PUSH によるコード生成方式は自走プログラムの移動量が究極の1バイトであり、自走プログラムの移動量という点ではこれ以上小さくするのは不可能ということになります(プログラムの仕掛けという観点でも素晴らしいと思う)。
本連載の初回記事「メモリ上で実際に動くプログラム」に PUSH を使わずブロック転送のみを使用した自走プログラムを追記しましたが、同様にブロック転送のみの場合でも移動量をもっと小さくできないか考えてみました。
インクリメント方式のブロック転送である LDIR 命令では次のブロック転送のために16ビットの減算をする必要があり、キャリーのクリア等が必要になることを考えるとデクリメント方式の LDDR を使いたいものです。ブロック転送を利用した場合の最小移動量は2バイトになりそうですが、ブロック転送命令(LDDR=EDH,B8H)を二つ並べてブロック転送命令をブロック転送命令で上書きすることでブロック転送を中断しないようにするとブロック転送終了直後にコピーしたブロック転送を実行してしまうのでうまくいきません・・・
しかし、巧妙な仕掛けを思い付きました^^
方式のアイディアさえ固まれば、ブロック転送を利用した自走プログラムには慣れてきたのでほぼ机上コーディングのみで完成しました。完成したソースが下記で移動量は2バイトです。このソースを見て今回のアイディアの仕掛けが判りますか?
ブロック転送での自走プログラム例(Z80 アセンブラ) |
|
★追記 2024/02/09
X(Twitter)に投稿した自走状況を可視化した動画を貼っておきます。
メモリ上で実際に動き(移動し)通過した後にはNOP命令のみを残してメモリ上を駆け回る自走プログラムでブロック転送方式で移動量が2byteのものができました
— skyriver (@wcinp) February 9, 2024
動画はEmyuZ-2200で自走の様子を可視化したものですhttps://t.co/ZtFauNTAdM#Z80 #自走プログラム pic.twitter.com/kXaYLmE6Q9
★追記 2024/02/10
X での書込みメッセージに対して返信を頂きました。LDIR を使用し、移動量が1バイトというものです。コピー後の LDIR を再利用する部分とキャリーによる条件ジャンプの部分が味わい深いですね。LDIR で検討してみました(下記)が DI を除けば同じサイズになりました。
LDIR による1バイト移動の自走プログラム例(Z80 アセンブラ) |
|
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
HD64180Compact(その1)構想 [Z80]
X(旧Twitter)のタイムラインで日立製の Z80 系 CPU である HD64B180ROP が共立エレショップさんで販売されていることを知り2個確保できました。
パッケージは 64 ピンのシュリンク DIP なので秋月さんからシュリンク DIP のソケットと 1.778mm ピッチの汎用基板を購入しました。
かなり前に 旧Twitter のタイムラインで HD64180 はハーフピッチの汎用基板に斜めに刺さるという情報を見た覚えがあります。しかし、今回購入した基板には逆に通常の DIP IC を斜めに刺すことができます。PIC を使って使用 IC 数を少なくすれば 大きな HD64180 をきれいに収めた方が作り易いと思ったからです。秋月さんでは通常ピッチとシュリンクピッチが縦横で混在している汎用基板も販売していますが最大でC基板サイズなので HD64180 を搭載するには残念ながら小さすぎます。
今回開発するワンボードを HD64180Compact と命名します。使用 IC 数を最小限にするように設計したいと考えていて、CPU 以外の IC としては SRAM と USB 機能を有した 20 ピンの PIC(PIC18F14K50)の3チップ構成にする予定です。以前に同様の構成でほぼ Z80 のチップサイズの CP/M が動くワンボードマイコンの Z80PicCompact を開発しましたが、今回は下記の課題についても対処したいと思います(まったく同じ仕掛けではつまらないですからね)。
下表に Z80Compact での PIC のピンアサインを再掲します。
HD46180ROP には RTS0/ のような Z80 にはなかった出力ピンがあるので、これを PIC アクセス用のコントロール信号として利用することで PIC を I/O アクセスする時のみ、 IORQ/ で wait 状態になるようにします。
初めに考案したトランジスタを使った回路をシミュレータで検証してみました。CNT がコントロール信号です。
想定通り CNT が low の時は WAIT/ はマスクされ、CNT が high の時に IORQ/ により、WAIT/ がアクティブになります。しかし、WAIT/ の low レベルが 0.8V 程度であり、HD64180 の規格である 0.8V 以下に対してマージンがありません。
下図はベースとエミッタの抵抗を小さくした場合のシミュレーション結果です。今度は WAIT/ の low レベルは規格を満たせていますね。
別の案としてショットキーバリアダイオードを使った OR 回路構成の案もシミュレートしてみました。これも WAIT/ 信号の low レベルが高すぎますね。
プルダウン抵抗の値を小さくしてシミュレートした結果が下図です。これなら行けそうですね。
部品数から考えてまずはショットキー案2で行こうかと思います。PIC の RB7 が抵抗を介して R2 へ接続することになるかと思います。
今回はプライオリティを下げて気が向いた時に開発を進める予定なので進捗はゆっくりになりそうです。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
入手した HD46B180ROP |
|
パッケージは 64 ピンのシュリンク DIP なので秋月さんからシュリンク DIP のソケットと 1.778mm ピッチの汎用基板を購入しました。
HD64180 と汎用基板とソケット |
|
かなり前に 旧Twitter のタイムラインで HD64180 はハーフピッチの汎用基板に斜めに刺さるという情報を見た覚えがあります。しかし、今回購入した基板には逆に通常の DIP IC を斜めに刺すことができます。PIC を使って使用 IC 数を少なくすれば 大きな HD64180 をきれいに収めた方が作り易いと思ったからです。秋月さんでは通常ピッチとシュリンクピッチが縦横で混在している汎用基板も販売していますが最大でC基板サイズなので HD64180 を搭載するには残念ながら小さすぎます。
今回開発するワンボードを HD64180Compact と命名します。使用 IC 数を最小限にするように設計したいと考えていて、CPU 以外の IC としては SRAM と USB 機能を有した 20 ピンの PIC(PIC18F14K50)の3チップ構成にする予定です。以前に同様の構成でほぼ Z80 のチップサイズの CP/M が動くワンボードマイコンの Z80PicCompact を開発しましたが、今回は下記の課題についても対処したいと思います(まったく同じ仕掛けではつまらないですからね)。
- I/O の拡張性
Z80PicCompact では I/O 空間に PIC だけを配置していて、PIC はアドレスの A0 だけを見ており、コマンド/データのポートの識別に A0 を使っていたので I/O 空間に空きがなかった
- 割込み対応
Z80 は IORQ/ をアクティブにすると自動的に wait するようにしていたので割込み(Z80Compactでは未使用)時に wait してしまうので PIC が割込みの都度 wait を解除する必要がある
下表に Z80Compact での PIC のピンアサインを再掲します。
Z80Compactのピンアサイン |
|
HD46180ROP には RTS0/ のような Z80 にはなかった出力ピンがあるので、これを PIC アクセス用のコントロール信号として利用することで PIC を I/O アクセスする時のみ、 IORQ/ で wait 状態になるようにします。
初めに考案したトランジスタを使った回路をシミュレータで検証してみました。CNT がコントロール信号です。
想定通り CNT が low の時は WAIT/ はマスクされ、CNT が high の時に IORQ/ により、WAIT/ がアクティブになります。しかし、WAIT/ の low レベルが 0.8V 程度であり、HD64180 の規格である 0.8V 以下に対してマージンがありません。
トランジスタ案1 |
|
下図はベースとエミッタの抵抗を小さくした場合のシミュレーション結果です。今度は WAIT/ の low レベルは規格を満たせていますね。
トランジスタ案2 |
|
別の案としてショットキーバリアダイオードを使った OR 回路構成の案もシミュレートしてみました。これも WAIT/ 信号の low レベルが高すぎますね。
ショットキー案1 |
|
プルダウン抵抗の値を小さくしてシミュレートした結果が下図です。これなら行けそうですね。
ショットキー案2 |
|
部品数から考えてまずはショットキー案2で行こうかと思います。PIC の RB7 が抵抗を介して R2 へ接続することになるかと思います。
今回はプライオリティを下げて気が向いた時に開発を進める予定なので進捗はゆっくりになりそうです。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
メモリ上で実際に動くプログラム(その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] [ 前へ ] 連載記事一覧 [ 次へ ]
メモリ上で実際に動くプログラム [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] [ 前へ ] 連載記事一覧 [ 次へ ]
リアルタイムモニタ(ZealMon)の製作(その2)セマフォの実装 [Z80]
前回の記事でライトウェイトである程度高速なリアルタイムモニタ/リアルタイムOS(以降、モニタと記す)の製作について書きましたが、今回はこれにセマフォ管理機能を追加したので記録して置きたいと思います。
前回の記事でも書いたように今回制作したモニタの特徴の一つとしてタスクの状態の情報をTCB(タスクコントロールブロック)には持たずにキューで管理することでTCBの更新箇所を最小限にするというのがあります。今回追加するセマフォ機能もこのコンセプトを継承します。
セマフォは Wikipedia にも書いてあるようにカウンティングセマフォとバイナリセマフォがあります。今回は汎用性の高いカウンティングセマフォを実装することにします。
セマフォ管理データは一つのセマフォに付き2バイトで初期値を管理対象のリソース数とします。
セマフォの操作は次のようにしました。
実装はメチャ簡単そうですね。それでは実際にセマフォ機能を使ったデモを作ってみましょう(デモ作りの方が遥かに大変でした)。
今回のデモは前回と同様にネット上で公開されているブラウザで動作するMSX環境のMSXPenを使ってGAME言語インタープリタを同時に二つ動かしてみます。
以前CP/Mに移植したGAME言語のインタープリタ&コンパイラをGAMECとして公開しましたが、今回はコンカレントなGAME言語インタープリタなのでCGAMEですw
画面表示制御は高速化のためVDPを直に制御しています。TEXT2(80字モード)の画面での実行を前提にしているので実行前に mode 80 のコマンドで80文字モードにしています。
「コントロールA」の操作でキー所有のタスクが変わります。キーの所有管理は上記のセマフォで行っています。
今回作成したデモソフトを下記のリンクからダウンロードできます。
Twitter(X)に投稿した動画付きメッセージを貼っておきます。
★追記 2023/10/15
上記の動画の動作について若干補足します。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]
前回の記事でも書いたように今回制作したモニタの特徴の一つとしてタスクの状態の情報をTCB(タスクコントロールブロック)には持たずにキューで管理することでTCBの更新箇所を最小限にするというのがあります。今回追加するセマフォ機能もこのコンセプトを継承します。
セマフォは Wikipedia にも書いてあるようにカウンティングセマフォとバイナリセマフォがあります。今回は汎用性の高いカウンティングセマフォを実装することにします。
セマフォ管理データは一つのセマフォに付き2バイトで初期値を管理対象のリソース数とします。
セマフォ管理データの構造 |
|
セマフォの操作は次のようにしました。
- セマフォの獲得(P操作)
セマフォ管理データの上位バイトがゼロ※1で下位バイトが1以上の場合は使用可能なリソースが残っていることを示し、リソース確保のためにこの値をディクリメントします。
上記以外の場合はセマフォ管理データを待ちキューとして使い、セマフォを要求したタスクのTCBをリンクに追加し、ウェイト状態として扱います。
※1 TCBの上位アドレスは零ではない前提を利用した管理方法です
- セマフォの解放(V操作)
セマフォ管理データのキューにTCBがある場合(上位バイトがゼロではない場合)は末尾のTCBをキューから外してレディーキューに追加します。セマフォのキューが空になった時はポインタが NULL(=ゼロ)になるのでカウントデータとしてシームレスに使用できます。
セマフォのキューにTCBが無い場合はセマフォー管理データをカウンタとして使用し、インクリメントします。
実装はメチャ簡単そうですね。それでは実際にセマフォ機能を使ったデモを作ってみましょう(デモ作りの方が遥かに大変でした)。
今回のデモは前回と同様にネット上で公開されているブラウザで動作するMSX環境のMSXPenを使ってGAME言語インタープリタを同時に二つ動かしてみます。
以前CP/Mに移植したGAME言語のインタープリタ&コンパイラをGAMECとして公開しましたが、今回はコンカレントなGAME言語インタープリタなのでCGAMEですw
画面表示制御は高速化のためVDPを直に制御しています。TEXT2(80字モード)の画面での実行を前提にしているので実行前に mode 80 のコマンドで80文字モードにしています。
「コントロールA」の操作でキー所有のタスクが変わります。キーの所有管理は上記のセマフォで行っています。
コンカレントGAMEインタープリタ動作中の様子 |
|
今回作成したデモソフトを下記のリンクからダウンロードできます。
Twitter(X)に投稿した動画付きメッセージを貼っておきます。
先日作成のZ80用リアルタイムモニタにセマフォを追加しました
— skyriver (@wcinp) October 13, 2023
前回同様MSXPen上で動くデモを作成しました
GAME言語を二つ同時に動かしてみました^^
CPUが一つなので片方の表示を止めるともう一方が速くなります
詳細は下記URLを参照https://t.co/GkkczEnVIk#RealTimeMonitor #ZealMon #Z80 #MSXPen https://t.co/9pZGOttzus pic.twitter.com/JzJugqqfyq
★追記 2023/10/15
上記の動画の動作について若干補足します。
- 連続表示中にキー資源の所有を失うと少し速くなる理由
連続表示中にも常にキーセンスをしていてキー資源を所有している場合は BIOS のキーセンス処理をコールしますが、キー資源を所有していない場合は実際のキーセンス処置を呼ばずにキー入力無しと判断するようにしているためです(つまりキーセンス処理が軽くなるので速くなる)。
- 連続表示中にもう一方のタスクがキー入力状態になると表示が速くなる理由
キー入力処理ではキーセンス後、キー入力が無い場合、タイマー待ち状態になり、タイムアウト後に再度キーセンスをコールする処理を繰り返しています。このためタスクは多くの時間タイマー待ち状態で止まっていて CPU 資源を殆ど消費しないためです。
[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]