SSブログ
English Version

メモリ上で実際に動くプログラム(その3) [Z80]

 メモリ上で実際に動き(移動し)通過した後にはNOP命令のみを残してひたすらメモリ上を駆け回る自走プログラムで新たな案を思い付いたので記録しておきたいと思います。

 前回の記事で書いた PUSH によるコード生成方式は自走プログラムの移動量が究極の1バイトであり、自走プログラムの移動量という点ではこれ以上小さくするのは不可能ということになります(プログラムの仕掛けという観点でも素晴らしいと思う)。
 本連載の初回記事「メモリ上で実際に動くプログラム」に PUSH を使わずブロック転送のみを使用した自走プログラムを追記しましたが、同様にブロック転送のみの場合でも移動量をもっと小さくできないか考えてみました。

 インクリメント方式のブロック転送である LDIR 命令では次のブロック転送のために16ビットの減算をする必要があり、キャリーのクリア等が必要になることを考えるとデクリメント方式の LDDR を使いたいものです。ブロック転送を利用した場合の最小移動量は2バイトになりそうですが、ブロック転送命令(LDDR=EDH,B8H)を二つ並べてブロック転送命令をブロック転送命令で上書きすることでブロック転送を中断しないようにするとブロック転送終了直後にコピーしたブロック転送を実行してしまうのでうまくいきません・・・

 しかし、巧妙な仕掛けを思い付きました^^ 
 方式のアイディアさえ固まれば、ブロック転送を利用した自走プログラムには慣れてきたのでほぼ机上コーディングのみで完成しました。完成したソースが下記で移動量は2バイトです。このソースを見て今回のアイディアの仕掛けが判りますか?

ブロック転送での自走プログラム例(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 ; Ver 0.02c 2024/02/09 by skyriver ;+++++++++++++++++++++++++++++++++++++ 0000' ASEG ORG 0100H 0100 21 011A START: LD HL,CEND 0103 11 011C LD DE,CEND + 2 0106 06 00 LD B,0 0108 3E 10 LD A,CNEXT - CSTART + 2 010A F3 DI 010B 18 0A JR SETC 010D 00 00 CSTART: DB 0,0 010F 47 LD B,A ; ED 47 = LD I,A   0110 ED B8 CLDR: LDDR 0112 4F LD C,A 0113 09 ADD HL,BC 0114 EB EX DE,HL 0115 09 ADD HL,BC 0116 EB EX DE,HL 0117 0E 0E SETC: LD C,CNEXT - CSTART 0119 18 F5 JR CLDR 011A CEND EQU $ - 1 011B CNEXT EQU $ END


★追記 2024/02/09
 X(Twitter)に投稿した自走状況を可視化した動画を貼っておきます。


★追記 2024/02/10
 X での書込みメッセージに対して返信を頂きました。LDIR を使用し、移動量が1バイトというものです。コピー後の LDIR を再利用する部分とキャリーによる条件ジャンプの部分が味わい深いですね。LDIR で検討してみました(下記)が DI を除けば同じサイズになりました。

LDIR による1バイト移動の自走プログラム例(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 ; Ver 0.02c 2024/02/09 by skyriver ; Ver 0.02d 2024/02/09 by skyriver ;+++++++++++++++++++++++++++++++++++++ 000E CSIZE EQU CEND - CSTART 0000' ASEG ORG 0100H 0100 11 0117 START: LD DE,CSTART + CSIZE 0103 06 00 LD B,0 0105 3E 0E LD A,CSIZE 0107 F3 DI 0108 2E DB 2EH ; LD L,xx 0109 ED B0 CSTART: LDIR 010B EB EX DE,HL 010C 4F LD C,A 010D ED 42 SBC HL,BC 010F 5D LD E,L 0110 54 LD D,H 0111 1B DEC DE 0112 ED B0 LDIR 0114 18 F2 JR CSTART - 1 0116 00 NOP 0117 CEND EQU $ END



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

HD64180Compact(その1)構想 [Z80]

 X(旧Twitter)のタイムラインで日立製の Z80 系 CPU である HD64B180ROP が共立エレショップさんで販売されていることを知り2個確保できました。

入手した 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] [ 前へ ] 連載記事一覧 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

メモリ上で実際に動くプログラム(その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 だったのでフラグの未使用ビットも値を保持できることを確認できました。

POP AF でのフラグの未使用ビットの値の確認(Z80アセンブラ)
;++++++++++++++++++++++++++++++++++++ ; check POP AF flag value ; Ver 0.01 2024/01/25 by skyriver ; Ver 0.02 2024/02/04 by skyriver ;++++++++++++++++++++++++++++++++++++ 0005 BDOS EQU 0005H 0002 COUT EQU 02H ; E<-data 0000' ASEG ORG 0100H 0100 2E 00 START: LD L,0 0102 E5 LOOP: PUSH HL 0103 F1 POP AF 0104 F5 PUSH AF 0105 D1 POP DE 0106 7B LD A,E 0107 BD CP L 0108 20 14 JR NZ,ERR 010A 2C INC L 010B 20 F5 JR NZ,LOOP 010D 21 0123 LD HL,MgOk ; +++ go through +++ 0110 7E Puts: LD A,(HL) 0111 B7 OR A 0112 C8 RET Z 0113 E5 PUSH HL 0114 0E 02 LD C,COUT 0116 5F LD E,A 0117 CD 0005 CALL BDOS 011A E1 POP HL 011B 23 INC HL 011C 18 F2 JR PUTS 011E 21 0126 ERR: LD HL,MgNg 0121 18 ED JR Puts 0123 4F 6B 00 MgOK: "Ok", 0 0126 4E 67 00 MgNg: "Ng", 0 END
※2024/02/04 improved slightly

 今回のプログラムも自身のコードを上書きするのでディバッガでのステップ動作等は使えず、机上ディバッグメインで動かしました。通常のプログラムを組む時の思考とは違う考え方が必要なので結構大変でした。普通に考えると PUSH 対象のレジスタペアが足りないのでレフレックスラジオが一つのトランジスタを高周波と低周波で2回使うように二つのレジスタペアを2回づつ使っています。この共通コードができるようにし、レジスタの使用を節約するように調整するのが難しいパズルでした。

 完成したソースを以下に示します。メモリ上の移動幅は究極の1バイトで、メモリを逆走します。このソースを見て机上で動作を追えるでしょうか? PC と SP がクロスする部分の仕掛け等を堪能してください。

完成した自走プログラム3(Z80アセンブラ)
;+++++++++++++++++++++++++++++++++++++ ; runner code on memory ; Ver 0.03 2024/01/26 by skyriver ;+++++++++++++++++++++++++++++++++++++ 0000' ASEG ORG 0100H 0100 31 0100 START: LD SP,START 0103 21 DB 21H ; LD HL,xxxx 0104 F5 PUSH AF 0105 E5 PUSH HL 0106 E5 PUSH HL 0107 F1 POP AF ; set AF 0108 21 DB 21H ; LD HL,xxxx 0109 D9 EXX 010A C5 PUSH BC 010B 11 DB 11H ; LD DE,xxxx 010C F9 LD SP,HL 010D 2B DEC HL 010E 01 DB 01H ; LD BC,xxxx 010F C5 PUSH BC 0110 D5 PUSH DE 0111 D9 EXX 0112 01 DB 01H ; LD BC,xxxx 0113 F1 DB -(HLVAL - TOP) 0114 00 NOP 0115 11 DB 11H ; LD DE,xxxx 0116 D9 EXX 0117 18 DB 18H ; JR 0118 21 012B LD HL,HLVAL 011B F3 DI 011C F9 TOP: LD SP,HL 011D 2B DEC HL 011E C5 PUSH BC ; NOP : xx 011F D5 PUSH DE ; JR : EXX 0120 D9 EXX 0121 C5 PUSH BC ; PUSH DE : PUSH BC 0122 F5 PUSH AF ; PUSH HL : PUSH AF ; ----------------------- 0123 F5 PUSH AF ; PUSH HL : PUSH AF 0124 E5 PUSH HL ; PUSH BC : EXX 0125 C5 PUSH BC ; PUSH DE : PUSH BC 0126 D5 PUSH DE ; DEC HL : LD SP,HL 0127 D9 EXX 0128 18 FE JR $ 012A 00 NOP 012B HLVAL EQU $ END


★追記 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)が必要となる
以上から8回の PUSH が必要となるのに対して実際に PUSH 可能なレジスタペアは6つ( AF BC DE BC' DE' HL')なので生成コード中に共通部分を作り2回分の PUSH は使用済みのレジスタペアを再利用できるようにする必要があります。
 しかし、生成コードの中には順番を逆転できない制約がある部分があり、今回の自走プログラムはかなり厳しい条件の中で辛うじて達成できたと言えると思います。
}
 前回の記事と同様に自作の Z80 ボードである Z80GalCompact を使って動作確認してみました。
 今回作成した自走プログラムは runcode3 で実行後にリセットし、メモリ上に残っているプログラムのコードを GAME 言語で作成したサーチプログラムで探しています。

Z80GalCompact での自走プログラム3の検証時のログ
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:runcode3 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:findrun3 8E00-8F36 *READY :0 1' *** find runner code 3 10 A=0 100 @ 110 ;=(A:0)=$F9)*(A:1)=$2B)*(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 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 *** find at $7A89 7A00: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A10: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A20: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A30: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A40: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A50: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A60: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A70: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7A80: 00 00 00 00 00 00 00 00 - 00 F9 2B C5 D5 D9 C5 F5 7A90: E5 F5 E5 C5 D5 D9 18 F1 - 00 00 00 00 00 00 00 00 7AA0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7AB0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7AC0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7AD0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7AE0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7AF0: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 7B00: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 *READY :


 X(旧Twitter)で教えてもらったメモリの全空間が RAM で メモリ上に VRAM をアサインできる MZ-2200 のエミュレータで自走プログラムが VRAM 上を通る時の画面キャプチャを貼っておきます。

EmyuZ-2200 エミュレータでの自走プログラム2表示



★追記 2024/01/27
 EmyuZ-2200 エミュレータのディバッガは RST 命令を使用したソフトウェアディバッガよりも ICE に近いので実行命令直下のメモリを書き換えてもステップ動作が行えたり、スタック直下のメモリを壊したりしません。
 今後ディバッグに活用する場面がありそうで有用なソフトを公開された作者の方に感謝です。
 しかし、上記の自走プログラムで使用しなかったのは奇妙な動きがあったからです。改めて動作を確認してみましたので記録しておきます。

 下記の例では PUSH 命令によって PUSH 命令自身を書き換えています。

PUSH 命令による自己書き換えプログラム
;+++++++++++++++++++++++++++++++++++++ ; runner code on memory ; Ver 0.03 2024/01/26 by skyriver ;+++++++++++++++++++++++++++++++++++++ 0000' ASEG ORG 0 0000 F3 START: DI 0001 31 000A LD SP,SPVAL 0004 AF XOR A 0005 21 DB 21H ; LD HL,xxxx 0006 3C INC A 0007 3C INC A 0008 00 NOP 0009 E5 PUSH HL 000A 00 SPVAL: NOP 000B 00 NOP 000C 00 NOP 000D 00 NOP END


 以下がステップ動作時の画面表示です。「done 00000009 PUSH HL」と表示されずに「done 00000009 INC A」と表示されていますね。Aregはゼロのままでメモリは HL の値に書き換わっているので実際は PUSH HL を実行し、done 表示ではメモリから再読みこみして表示しているようです(つまり実行動作は問題なくて表示が違っているだけ)。極めてレアなケースなので通常の使用には問題ないと思われます。

EmyuZ-2200 のディバッガでのステップ動作結果



[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

メモリ上で実際に動くプログラム [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) 
共通テーマ:趣味・カルチャー

リアルタイムモニタ(ZealMon)の製作(その2)セマフォの実装 [Z80]

 前回の記事でライトウェイトである程度高速なリアルタイムモニタ/リアルタイムOS(以降、モニタと記す)の製作について書きましたが、今回はこれにセマフォ管理機能を追加したので記録して置きたいと思います。

 前回の記事でも書いたように今回制作したモニタの特徴の一つとしてタスクの状態の情報を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)に投稿した動画付きメッセージを貼っておきます。



★追記 2023/10/15
 上記の動画の動作について若干補足します。
  • 連続表示中にキー資源の所有を失うと少し速くなる理由
     連続表示中にも常にキーセンスをしていてキー資源を所有している場合は BIOS のキーセンス処理をコールしますが、キー資源を所有していない場合は実際のキーセンス処置を呼ばずにキー入力無しと判断するようにしているためです(つまりキーセンス処理が軽くなるので速くなる)。

  • 連続表示中にもう一方のタスクがキー入力状態になると表示が速くなる理由
     キー入力処理ではキーセンス後、キー入力が無い場合、タイマー待ち状態になり、タイムアウト後に再度キーセンスをコールする処理を繰り返しています。このためタスクは多くの時間タイマー待ち状態で止まっていて CPU 資源を殆ど消費しないためです。



[TOP] [ 前へ ] 連載記事一覧 [ 次へ ]

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー