SSブログ
English Version

Z80GALの構想(その11)IPLの製作 [Z80]

 前回の「Z80GALの構想(その10)回路図とパターン設計」で書いたようにプリント基板のパターン設計ができたのでseeedさんへ発注しましたが、その後2ヶ所ミスを発見orz
 1つはシルクが2022年になっていました(未来から来た基板w)、あと1つはパターンをカットすれば対応できそうですがプリント基板が届いてから具体的な対処を検討します。

 今回は未作成だったIPLを作ったので書いてみます。IPLを先頭セクタに保存し、リセット時に先頭セクタをメモリに読込み、実行することでCP/Mが立ち上るようになります。
 IPLは1つのセクタ(128バイト)に入れるため、SDカードのブロック読込みはEEPROMからRAMへコピーされたコード内の処理を呼び出すようにしました。
 また、割込み処理の切替えは少し面倒なのでシリアル出力は割込みを使用せずソフトでタイミングを取った専用のものを使用するようにしました。

 短いので全ソースを貼っておきます。

Z80GAL IPL(Initial Program Loader)
;+++++++++++++++++++++++++++++++++++++ ; Z80GAL IPL(Initial Program Loader) ; Ver 0.02 2020/11/29 by skyriver ;+++++++++++++++++++++++++++++++++++++ 8000 IPLENT EQU 08000H ; IPL start address 0080 SECSIZE EQU 128 ; sector size 0200 SDBLKSZ EQU 512 ; SD card block size 0004 SECBLK EQU (SDBLKSZ / SECSIZE) ; sec par block 003E MSIZE EQU 62 ; CP/M memory size A800 BIAS EQU (MSIZE-20) * 1024 DC00 CCP EQU 3400H + BIAS ; CCP start address E406 BDOS EQU CCP + 806H ; BDOS start address F200 BIOS EQU CCP + 1600H ; BIOD start address F200 BOOT EQU BIOS FE00 BIOSEND EQU 0FE00H ; BIOS end addtress 0045 RDSEC EQU (BIOSEND - CCP) / SECSIZE + 1 ; add 1sec for IPL 0012 RDBLK EQU (RDSEC + SECBLK -1) / SECBLK 0380 BlkRd EQU 00380H ; SD card block read func HL<-read adrs 0384 SdBlkNo EQU 00384H ; SD block number(big endian) 0386 SdBlkL EQU 00386H ; SD block low number(big endian) 000D CR EQU 13 000A LF EQU 10 ;*** Z80GAL I/O address *** 0080 adBASE EQU 080H ; I/O base address 00E0 adRAM EQU (adBASE + 60H) ; select memory RD D0=1:RAM, 0:ROM 00C0 adSdCs EQU (adBASE + 40H) ; set SD's CS D0=1:active 00A0 AdSd EQU (adBASE + 20H) ; write D0:SDO D1:CLK, read D0:SDI D1:CS 0080 AdSeri EQU (adBASE + 00H) ; serial D0 write:Tx read:Rx 0001 SD_DAT EQU 1 0002 SD_CLK EQU 2 0001 SD_CS EQU 1 .Z80 0000' ASEG ORG IPLENT 8000 F3 IPL: DI 8001 21 8034 LD HL,MSGLD 8004 CD 8054 CALL Puts 8007 21 0000 LD HL,0 800A 22 0384 LD (SdBlkNo),HL 800D 22 0386 LD (SdBlkL),HL 8010 21 DB80 LD HL,CCP - SECSIZE 8013 01 1200 LD BC,RDBLK * 256 + 0 ; B:Blk counter, C:Blk No. 8016 79 IPL10: LD A,C 8017 32 0387 LD (SdBlkL + 1),A ; set Block No. 801A 0C INC C 801B CD 0380 CALL BlkRd 801E B7 OR A 801F 20 0B JR NZ,IPLERR 8021 10 F3 DJNZ IPL10 8023 21 8049 LD HL,MSGOK 8026 CD 8054 CALL Puts 8029 C3 F200 JP BOOT 802C 21 804C IPLERR: LD HL,MSGERR 802F CD 8054 CALL Puts 8032 18 FE JR $ 8034 0D 0A 20 43 MSGLD: DB CR,LF," CP/M loading ... ",0 8038 50 2F 4D 20 803C 6C 6F 61 64 8040 69 6E 67 20 8044 2E 2E 2E 20 8048 00 8049 6F 6B 00 MSGOK: DB "ok",0 804C 49 50 4C 20 MSGERR: DB "IPL err",0 8050 65 72 72 00 8054 7E Puts: LD A,(HL) 8055 23 INC HL 8056 B7 OR A 8057 C8 RET Z 8058 CD 805D CALL Putc 805B 18 F7 JR Puts ;*** softqare serial *** ; 9600bps 1bit length = 1000000/9600 = 104.17 us ; = (1000000/9600)/(1/20) = 2083.3 states(20MHz) 0823 STIMST EQU 2083 ; (10000/96*20) ; state par serial 1bit 009D STICNT EQU (STIMST-11-4-4-7-8+7-4-12)/13+1 ; serial out ; A <- data 805D B7 Putc: OR A ; 4 clear Cy(start bit) 805E 17 RLA ; 4 805F 0E 0A LD C,10 ; 7 set bit counter 8061 D3 80 Putc10: OUT (AdSeri),A ; 11 8063 1F RRA ; 4 8064 37 SCF ; 4 for stop bit 8065 06 9D LD B,STICNT ; 7 8067 10 FE DJNZ $ ; 13/8 8069 0D DEC C ; 4 806A 20 F5 JR NZ,Putc10 ; 12/7 806C C9 RET ; 10 END
★変更 2020/11/29 Putc処理のコードを短縮

 また、リセット後の立上り処理として、SDカードを挿している場合はCP/Mを起動し、SDカードが無い場合にはGAMEインタープリタを起動するようにしました。

 更にCP/M起動後にGAMEの世界に戻ったりGAMEからCP/Mを起動できるようにしてみました。下図がリセット後のCP/M起動から切替え操作例のキャプチャです。

CP/MとGAMEの切替え


 CP/Mが動作している状態でEEPROM内コードをRAMにコピー後に、0003Hから実行するとGAMEインタープリタが起動するようにしています。
 CP/MからGAMEへ移行するためには下記のリストのようにROM/RAM切替の影響を受けないメモリの後半(下記の場合は8000H)で動作するプログラムでEEPROMをアクティブにしてコードをRAMにコピー後、0003HへジャンプすることでGAMEインタープリタが立ち上がります。
 また、GAMEが動作している状態では、単に0000HへジャンプするだけでCP/Mが起動します。

CP/MからGAMEインタープリタ起動コマンドのソース
;+++++++++++++++++++++++++++++++++++++++ ; exit from CP/M-80 in Z80GAL ; Ver 0.01 2020/11/27 by skyriver ;+++++++++++++++++++++++++++++++++++++++ ;*** Z80GAL I/O address *** 0080 adBASE EQU 080H ; I/O base address 00E0 adRAM EQU (adBASE + 60H) ; select memory RD D0=1:RAM, 0:ROM 00C0 adSdCs EQU (adBASE + 40H) ; set SD's CS D0=1:active 00A0 AdSd EQU (adBASE + 20H) ; write D0:SDO D1:CLK, read D0:SDI D1:CS 0080 AdSeri EQU (adBASE + 00H) ; serial D0 write:Tx read:Rx 0001 SD_DAT EQU 1 0002 SD_CLK EQU 2 0001 SD_CS EQU 1 4000 CPYSIZE EQU 4000H ; ROM -> RAM copy size 8000 PROGADR EQU 8000H ; program execute address 0003 GOGAME EQU 0003H ; GAME exute entry .Z80 0000' ASEG ORG 0100H 0100 21 010E LD HL,RELPRG 0103 11 8000 LD DE,PROGADR 0106 01 0011 LD BC,ENDAD - EXTCPM 0109 ED B0 LDIR 010B C3 8000 JP PROGADR 010E RELPRG EQU $ .PHASE PROGADR 8000 F3 EXTCPM: DI 8001 AF XOR A 8002 D3 E0 OUT (adRAM),A ; select ROM 8004 21 0000 LD HL,0 8007 54 LD D,H 8008 5D LD E,L 8009 01 4000 LD BC,CPYSIZE 800C ED B0 LDIR 800E C3 0003 JP GOGAME 8011 ENDAD EQU $ END


★追記 2021/01/22
 CP/MからGAMEインタープリタの世界に移行するためには、ROMがアクティブな状態で0003Hから実行すればいいので、上記のGAMEインタープリタ起動コマンドをスリム化しました。

CP/MからGAMEインタープリタ起動コマンド改善版のソース
;+++++++++++++++++++++++++++++++++++++++ ; exit from CP/M-80 in Z80GAL ; Ver 0.02 2021/01/22 by skyriver ;+++++++++++++++++++++++++++++++++++++++ ;*** Z80GAL I/O address *** 0080 adBASE EQU 080H ; I/O base address 00E0 adRAM EQU (adBASE + 60H) ; select memory RD D0=1:RAM, 0:ROM 00C0 adSdCs EQU (adBASE + 40H) ; set SD's CS D0=1:active 00A0 AdSd EQU (adBASE + 20H) ; write D0:SDO D1:CLK, read D0:SDI D1:CS 0080 AdSeri EQU (adBASE + 00H) ; serial D0 write:Tx read:Rx 0001 SD_DAT EQU 1 0002 SD_CLK EQU 2 0001 SD_CS EQU 1 .Z80 0000' ASEG ORG 0100H 0100 21 DB 021H ; ld hl 0101 D3 E0 OUT (adRAM),A 0103 22 0001 LD (0001H),HL 0106 AF XOR A 0107 C3 0001 JP 1 END



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

nice!(0)  コメント(0) 

Z80GALの構想(その10)回路図とパターン設計 [Z80]

 Z80を高速(20MHz)で動作させることで、周辺ICの機能をソフトで代行し、チップ数の少ないZ80シングルボードを作ろうと始めた今回の試みですが、CP/Mも快適に動くようになりゴールに近づいてきたので回路図の整理とパターン設計を行ってみました。

 全体の概要を改めてまとめてみると

  1. チップ構成
     当初の予定通り、8ピンのICも含めて5チップ構成でCP/Mが快適に動くようになりました。
     使用したICは次の通りで、全てDIPパッケージのICです。

    1. Z80
       Z84C0020PEG(40PIN)をクロック上限の20MHzで動作させています。
       メモリも下記のように高速なものを使い、wait無しで動作しています。9600bpsの調歩同期式シリアル通信をソフトで実現するために34.7us間隔で割込み処理を実行していることから処理能力は14.516MHz相当になります(今後改善する可能性もある)。
       当然ですが、シリアル入力をバックグラウンドでセンスしなければ(タイマー割込みを禁止すれば)20MHz相当のスピードになります。
      ★変更 2020/12/30
       処理能力を16MHz相当に変更。詳細は「Z80GALの構想(その8)Blocking/Deblockingの実装」を参照してください。

    2. RAM
       UM61512-15(32PIN)を使用しました。ピン数が少し多いですが、64Kx8bitのスタティックRAMでアクセスタイムは15nsと高速です。

    3. EEPROM
       W27C512-45(28PIN)でアクセスタイムが45nsでROMではかなり速い方だと思います。Z80のメモリ空間の後半は常にRAMをアクセスできるようにしているので、EEPROMのサイズは64Kx8bitですがZ80のメモリ空間の前半の32KBにマッピングしています。
       ジャンパーピンの設定によりメモリ空間にマッピングされるのがどちらか(EEPROMの前半か後半か)を選択可能としました。
       メモリライトは常にRAMが対象となり、メモリリードはEEPROMかRAMかをI/Oでコントロールできるようにしています。

    4. GAL
       GAL22V10D(24PIN)を使用しています。詳細は「Z80GALの構想(その2)GALの設計」の記事を参照してください。

    5. クロック発振&タイマー割込み信号生成
       PIC12F683(8PIN)で水晶発振子用バッファとタイマー割込み用のインターバル信号を生成しています。
       発振部分は後段に一段バッファを設けたかったのですがICが追加になってしまうので後段バッファは無しにしています(PIC12F683の内蔵コンパレータをバッファにできないか試みましたが20MHzでは利得が落ちて駄目でした)

  2. ソフトで代行した機能
     調歩同期式シリアル通信(9600bps)をタイマー割込みを使ってバックグラウンドでの受信センスも含めて実現しています。詳細は「Z80GALの構想(その5)タイマ割込みによるシリアル通信」を参照してください。
     また、SDカードとのインターフェースであるSPIインターフェースをソフトウェアにより実現しています。こちらも詳細は「Z80GALの構想(その6)SPIインターフェースの実装」を参照願います。

  3. 回路図
     今後変更する可能性もありますが、現時点での回路図は下図の通りです。

    ★変更 2021/01/06
     ATF22V10C対応(リセット部の抵抗を10Kから33Kに変更)
    ★変更 2021/02/05
     リセット時の動作の違いはSDカード依存だったのでリセット部抵抗を10Kに戻した


  4. パターン設計
     下図が今回設計した基板のパターン図でトップ面とボトム面を重ねて表示したものです。黄色の細い線はグランドの接続を示していて、グランドをベタパターン化した際に接続されます(接続されない場合はviaを打つ)。

    トップとボトムのパターン(ベタグランド化前)

     トップ面のグランドをベタパターン化したものが下図です。
     今回はMicroSDカードを使って小型化を図りました。SDカードコネクタは秋月さんから購入したヒロセ・マイクロSDカードコネクタを使用しています。
     11ピン~14ピン(周囲の淵にあるピン)は未使用ですが強度を高めるために周りをベタパターン化しました。

    トップパターン(ベタグランド化後)

     下図がボトム面のパターンです。トップ面もそうですが今回はチップ数が多いので結構混雑度の高いパターンになっています。

    ボトムパターン(ベタグランド化後)


★追記 2020/12/11 {
 試作基板評価結果を反映して回路図とパターン図をアップデートしました。


 【主な変更点】
  • Versionを0.01aに変更
  • microSDの配線を修正
  • R7を10Kから1Kに修正
  • シルクの日付を修正
  • ダイオードの極性が判り易いシルクに変更
  • ダイオードを1種類に統一


試作基板(Ver0.01)

}


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

nice!(0)  コメント(0) 

Z80GALの構想(その9)簡易モニタの製作(その2)ブレーク機能の実装 [Z80]

 「Z80GALの構想(その4)簡易モニタの製作」の記事で書いたように今回はZ80GALに移植したGAME言語を使って作成した簡易モニタを使ってプログラムをダウンロードしディバッグを行いました。
 始めからブレーク機能を実装してディバッグに使えば良かったようなものですが、ブレーク機能無しでも今回の開発作業は問題ないと考えていました。
 今回のプログラム開発で残りはIPLくらいしかありませんが、実装方法を考えるのが面白そうだったのでブレーク機能を追加してみました。

 まずはモニタにおけるテスト対象プログラム実行時に期待する機能として、RET命令でモニタに戻ってきた際のレジスタの値などを確認する使い方と、ブレークポイントを設定して実行プログラムがブレークポイントに到達した時点でのレジスタ内容などを確認したい場合の二通りあるのではないかと思います。

 また、いずれの場合もプログラム実行前にはレジスタの値を事前に設定したいものです。

 今回は上記の2つの使い方に対応できるようにしました。それぞれの機能について以下に概要を説明します。

  1. 実行プログラムリターン時のレジスタ確認機能
     事前に任意のレジスタの値を設定して指定した番地からプログラムを実行し、RET命令でモニタに戻ってくる。
     この場合にスタック上にはモニタへの戻り値を積んたうえで対象プログラムを実行します。
     対象プログラムから戻ってきたら、その時点のレジスタを保存し、確認できるようにします。この戻り時の処理は次に述べるブレーク機能と同じ処理になります。
     試験プログラムの実行方法としては「Gaaaa」になります(aaaaは開始アドレス)。

  2. ブレーク機能
     試験対象プログラムの複数個所にブレークポイント(今回はRST 30H(F7H)を使用)を設定後、プログラムを実行し、ブレークポイントに達した時点でモニタに戻し、レジスタ内容の確認などができるようにする。
     今回はブレークポイントを5個まで設定可能で、ブレークした時点で置き換えたF7Hのコードを元のコードに戻す(ブレークポイントを自動的にクリアする)ことで継続して試験プログラムを実行できるようにしました。
     試験プログラムの実行方法はPC(プログラムカウンタ)に実行アドレスを設定後、「G」コマンドで試験対象プログラムを実行します。


 下記のサンプルプログラムを用いて、今回追加した機能についての概要を書いてみます。

使用したサンプルプログラム
  8000    32 800F	LD	(800FH),A
  8003    3E 55  	LD	A,55H
  8005    06 11  	LD	B,11H
  8007    CD 8010	CALL	8010H
  800A    D9     	EXX
  800B    C9     	RET
  800C    00     	NOP
  800D    00     	NOP
  800E    00     	NOP
  800F    00     	NOP
  8010    0E 22  	LD	C,22H
  8012    21 1234	LD	HL,1234H
  8015    C9     	RET


 文字でいろいろ書くよりも操作例を示した方が判り易いと思います。
 ブレークを設定後に最初だけ「Gxxxx」コマンドで対象プログラムを実行し、ブレーク後は「G」コマンドで継続して実行しています。
 最後にRET命令によりモニタに戻ってきますが、この場合、PCの値が不明(ステップ動作すれば判るようになるがそのためには命令長情報が必要になる)なのでPCの表示値は実行開始のアドレス値を表示するようにしました。

 また、今回追加したレジスタ管理機能とブレーク設定/解除機能はいずれもマルチステートメント対応にしているで複数命令を一度に(場合によってはコピペで)実行可能です。

モニタでのブレーク操作例(水色:入力部)
]h *DUMP: D[aaaa[,aaaa]] *FILL : Faaaa,aaaa,dd *GO : G[aaaa] *SET : S[aaaa[,dd]] *IN : Iaa *OUT : Oaa,dd *Reg : X[reg[dddd]] *Break: B[Sn[,aaaa]|Cn|L] n=0..4 READ: R WRITE: Waaaa,aaaa QUIT: Q HELP : H *:can use multi statement separated with ':' command set:= exec:! display:? ]=d8000,801f ]? d8000,801f ]! 8000 : 32 0F 80 3E 55 06 11 CD - 10 80 D9 C9 00 00 00 00 2..>U... - ........ 8010 : 0E 22 21 34 12 C9 00 00 - 00 00 00 00 00 00 00 00 ."!4.... - ........ ]bs,8003:bs1,8007:bs2,8012 adr0:8003 adr1:8007 adr2:8012 ]bs3 adr3:800a ]bl adr0:8003(3E) adr1:8007(CD) adr2:8012(21) adr3:800A(D9) ]! 8000 : 32 0F 80 F7 55 06 11 F7 - 10 80 F7 C9 00 00 00 00 2...U... - ........ 8010 : 0E 22 F7 34 12 C9 00 00 - 00 00 00 00 00 00 00 00 .".4.... - ........ ]xa --.-.--- AF=0000:aa00 ]x --.-.--- AF=AA00 BC=0000 DE=0000 HL=0000 PC=8000 SP=0340 --.-.--- AF'0000 BC'0000 DE'0000 HL'0000 IX=0000 IY=0000 ]g8000 G:8000 at adr0 --.-.--- AF=AA00 BC=0000 DE=0000 HL=0000 PC=8003 SP=033C --.-.--- AF'0000 BC'0000 DE'0000 HL'0000 IX=0000 IY=0000 ]! 8000 : 32 0F 80 3E 55 06 11 F7 - 10 80 F7 C9 00 00 00 AA 2..>U... - ........ 8010 : 0E 22 F7 34 12 C9 00 00 - 00 00 00 00 00 00 00 00 .".4.... - ........ ]bl adr1:8007(CD) adr2:8012(21) adr3:800A(D9) ]g G:8003 at adr1 --.-.--- AF=5500 BC=1100 DE=0000 HL=0000 PC=8007 SP=033C --.-.--- AF'0000 BC'0000 DE'0000 HL'0000 IX=0000 IY=0000 ]g G:8007 at adr2 --.-.--- AF=5500 BC=1122 DE=0000 HL=0000 PC=8012 SP=033A --.-.--- AF'0000 BC'0000 DE'0000 HL'0000 IX=0000 IY=0000 ]g G:8012 at adr3 --.-.--- AF=5500 BC=1122 DE=0000 HL=1234 PC=800A SP=033C --.-.--- AF'0000 BC'0000 DE'0000 HL'0000 IX=0000 IY=0000 ]! 8000 : 32 0F 80 3E 55 06 11 CD - 10 80 D9 C9 00 00 00 AA 2..>U... - ........ 8010 : 0E 22 21 34 12 C9 00 00 - 00 00 00 00 00 00 00 00 ."!4.... - ........ ]g G:800A --.-.--- AF=5500 BC=0000 DE=0000 HL=0000 PC=8000 SP=0340 --.-.--- AF'0000 BC'1122 DE'0000 HL'1234 IX=0000 IY=0000 ]■

★変更 2011/11/22 操作例をアップデート

 このような機能はGAME言語だけでは記述できないので上記の二通りの実行機能(GO:がブレーク用、GOxx:がRET時レジスタ確認用の実行開始モジュール)とモニタへリターン時のレジスタ格納処理(RSTENT:)をアセンブラで記述しました。
 先頭のジャンプ命令とワークメモリアドレスはGAME言語とのインターフェース用です。

ブレーク処理用のアセンブラ記述部分(Z80アセンブラ)
ORG HEXST 0200 C3 0283 RSTJMP: JP RSTENT 0203 C3 02A6 enGO: JP GO 0206 C3 02C7 enGOxx: JP GOXX 0209 00 DB 0 020A 02ED RegWrk: DW SavAFb  ~~ 途中省略 !! Omit the middle!! ;+++++++++++++++++++++++++++++++++++ ; RST break test ; 2020/11/17 by skyriver ;+++++++++++++++++++++++++++++++++++ 0283 22 02FF RSTENT: LD (SavHL),HL 0286 E1 POP HL 0287 2B DEC HL 0288 22 0301 LD (SavPC),HL 028B ED 73 0303 LD (SavSP),SP 028F F3 DI 0290 31 02FF LD SP,SavHL 0293 D5 PUSH DE 0294 C5 PUSH BC 0295 F5 PUSH AF 0296 FD E5 PUSH IY 0298 DD E5 PUSH IX 029A D9 EXX 029B E5 PUSH HL 029C D5 PUSH DE 029D C5 PUSH BC 029E 08 EX AF,AF' 029F F5 PUSH AF 02A0 FB EI 02A1 ED 7B 0305 LD SP,(SavGSP) 02A5 C9 RET 02A6 ED 73 0305 GO: LD (SavGSP),SP 02AA F3 DI 02AB 31 02ED LD SP,SavAFb 02AE F1 POP AF 02AF C1 POP BC 02B0 D1 POP DE 02B1 E1 POP HL 02B2 08 EX AF,AF' 02B3 D9 EXX 02B4 DD E1 POP IX 02B6 FD E1 POP IY 02B8 F1 POP AF 02B9 C1 POP BC 02BA D1 POP DE 02BB E1 POP HL 02BC E1 POP HL ; PC 02BD FB EI 02BE ED 7B 0303 LD SP,(SavSP) 02C2 E5 PUSH HL 02C3 2A 02FF LD HL,(SavHL) 02C6 C9 RET ; Gxxxx command ; SavPC <- xxxx 02C7 ED 73 0305 GOxx: LD (SavGSP),SP 02CB F3 DI 02CC 31 02ED LD SP,SavAFb 02CF F1 POP AF 02D0 C1 POP BC 02D1 D1 POP DE 02D2 E1 POP HL 02D3 08 EX AF,AF' 02D4 D9 EXX 02D5 DD E1 POP IX 02D7 FD E1 POP IY 02D9 F1 POP AF 02DA C1 POP BC 02DB D1 POP DE 02DC FB EI 02DD ED 7B 0303 LD SP,(SavSP) 02E1 21 0283 LD HL,RSTENT 02E4 E5 PUSH HL 02E5 2A 0301 LD HL,(SavPC) 02E8 E5 PUSH HL 02E9 2A 02FF LD HL,(SavHL) 02EC C9 RET 02ED SavAFb: DS 2 ; 0 02EF SavBCb: DS 2 ; 1 02F1 SavDEb: DS 2 ; 2 02F3 SavHLb: DS 2 ; 3 02F5 SavIX: DS 2 ; 4 02F7 SavIY: DS 2 ; 5 02F9 SavAF: DS 2 ; 6 02FB SavBC: DS 2 ; 7 02FD SavDE: DS 2 ; 8 02FF SavHL: DS 2 ; 9 0301 SavPC: DS 2 ;10 0303 0340 SavSP: DW PRGST ; save SP 0305 SavGSP: DS 2 ; GAME's SP


 下のリストが今回機能追加したGAME言語のモニタ(Gamon)のソースです。GAME言語を移植できる環境があるのであれば、モニタの移植も容易だと思います。

Gamon(Game Monitor)ソース(GAME言語)
1' simple monitor(Gamon) V003a 2020/11/19 by skyriver 10 #=10000 100'*** init 110 M=&+1 120 M:1)=$DB M:3)=$32 M(2)=M M:6)=$C9 130 M:7)=$3E M:9)=$D3 M:11)=$C9 140 M:12)=$3E M:14)=$E6 M:15)=$0F M(8)=$3200 M(9)=M M:20)=$C9 150 M:21)=$3E M:23)=$0F M(12)=$0F0F M(13)=$320F M(14)=M M:30)=$C9 160 W=M+31 B=W+20 I=0,9 W(I)=0 @=I+1 162 Y=B+256 164 R=$30 R:0)=$C3 R=R+1 R(0)=$200 R=$20A R=R(0) 170 /"** Gamon(Game Monitor) V0.03a 2020/11/19 by skyriver H:Help **"// 180 ] 200'*** get line 210 P=B Q=B $=Z 220 @ 230 C=$ ;=C<>8 #=260 240 ;=Q=B $=Z #=260 250 Q=Q-1 " " $=8 260 ;=(C=$18)*(Q>B) @ $=8 " " $=8 Q=Q-1 @=(Q=B) 262 ;=Q-B>253 $=8 " " $=8 #=280 270 ;=C>=" " Q:0)=C Q=Q+1 280 @=(C=13) 290 ] 300'** Getc from buf 310 C=0 ;=P<Q C=P:0) P=P+1 ;=(C>="a")*(C<="z") C=C-("a"-"A") 320 ] 350'unpuc 360 ;=C P=P-1 370 ] 400'** Get Hex byte D-> 0..15, -1:NG 410 D=-1 420 !=300 430 ;=(C>="0")*(C<="9") D=C-"0" 440 ;=(C>="A")*(C<="F") D=C-"A"+10 450 ] 500'** Get value A->value F->1:OK,0:NG 510 A=0 F=0 520 @ 530 !=400 ;=D>=0 A=16*A+D F=1 540 @=(D=-1) 550 ] 600'** PutHex byte D<-data,G<-sum 610 M:22)=D >=M+21 M:13)=M:0) >=M+12 620 M:13)=M:0) >=M+12 C=M:0) 630 C=C+"0" ;=C>"9" C=C+"A"-"0"-10 640 $=C 650 M:13)=D >=M+12 C=M:0) 660 C=C+"0" ;=C>"9" C=C+"A"-"0"-10 670 $=C G=G+D 680 ] 710 J=R-U*2+31 I=0,V $=M:J) $=M:J+1) ":" ??=U(0) " " U=U+2 J=J+2 @=I+1 720 ] 800'** Dsp reg 802 ;=U=0 !=880 "AF'" 803 ;=U=1 "BC'" 804 ;=U=2 "DE'" 805 ;=U=3 "HL'" 806 ;=U=4 "IX=" 807 ;=U=5 "IY=" 808 ;=U=6 !=880 "AF=" 809 ;=U=7 "BC=" 810 ;=U=8 "DE=" 811 ;=U=9 "HL=" 812 ;=U=10 "PC=" 813 ;=U=11 "SP=" 820 ??=R(U) 830 ] 880 M:13)=R(U) 882 M:15)=$80 >=M+12 T="-" ;=M:0) T="S" 884 $=T M:15)=$40 >=M+12 T="-" ;=M:0) T="Z" 886 $=T "." M:15)=$10 >=M+12 T="-" ;=M:0) T="H" 888 $=T "." M:15)=$04 >=M+12 T="-" ;=M:0) T="P" 890 $=T M:15)=2 >=M+12 T="-" ;=M:0) T="N" 892 $=T M:15)=1 >=M+12 T="-" ;=M:0) T="C" 894 $=T " " M:15)=$F 896 ] 1000'** dump 1010 ;=P<Q !=500 S=A 1020 U=S M:13)=S >=M+12 S=S-M:0) 1030 E=U+$7F ;=P<Q !=500 ;=A>S E=A 1040 @ ??=S " :" 1050 I=0,15 1060 " " 1070 ;=(S+I>E)+(S+I<U) " " #=1090 1080 ?$=S:I) 1090 ;=I=7 " -" 1100 @=I+1 1110 " " 1120 I=0,15 1130 T=S:I) ;=(T<" ")+(T>"z") T="." 1140 ;=S+I<U T=" " 1150 $=T ;=I=7 " - " 1160 ;=S+I>=E I=15 1170 @=I+1 1180 / S=S+I 1190 @=(S>E) 1200 S=E+1 1210 ] 2000'** Set Memory 2010 ;=P<Q !=500 S=A 2012 ;=P<Q !=500 ;=F ??=S " " ?$=S:0) S:0)=A " - " ?$=A / #=2100 2020 @ 2030 ??=S " " ?$=S:0) " -" 2040 Z=" " !=200 / 2050 ;=P=Q S=S+1 F=1 #=2090 2060 !=500 2070 ;=F S:0)=A S=S+1 2080 ;=C="-" S=S-1 F=1 2090 @=(F=0) 2100 ] 3000'** Fill 3010 G=-1 3020 ;=P<Q !=500 S=A ;=P<Q !=500 ;=A>S E=A+1 !=500 ;=F G=A 3030 ;=G=-1 " ?"/ 3040 ;=G>=0 @ S:0)=G S=S+1 @=(S=E) 3050 ] 4000'** Go 4010 F=0 4012 ;=P=Q "G:" ??=R(10) >=$203 !=4200 !=9394 / ] 4020 ;=P<Q !=500 ;=F "G:" ??=A R(10)=A >=$206 !=4200 !=9394 / ] 4030 " ?"/ 4040 ] 4200'** clear BP 4210 T=R(10) 4220 I=0,9 4230 ;=W(I)=T T:0)=W(I+1) W(I)=0 " at adr" ?=I/2 I=9 4240 @=I+2 / 4250 ] 5000'** IN 5010 !=500 ;=F M:2)=A >=M+1 ?$=A " -> " ?$=M:0) / 5020 ] 6000'** OUT 6010 !=500 ;=F S=A !=500 ;=F M:8)=A M:10)=S >=M+7 #=6030 6020 " ?"/ #=6040 6030 ?$=S " <- " ?$=A / 6040 ] 7000'** Help 7010 " *DUMP: D[aaaa[,aaaa]] *FILL : Faaaa,aaaa,dd"/ 7020 " *GO : G[aaaa] *SET : S[aaaa[,dd]]"/ 7030 " *IN : Iaa *OUT : Oaa,dd"/ 7040 " *Reg : X[reg[dddd]] *Break: B[Sn[,aaaa]|Cn|L]" 7042 " n=0..4"/ 7050 " READ: R WRITE: Waaaa,aaaa"/ 7060 " QUIT: Q HELP : H"/ 7070 " *:can use multi statement separated with ':'"/ 7080 " command set:= exec:! display:?"/ 7090 ] 8000'** Write 8010 !=500 ;=F=0 #=8900 8020 S=A !=500 ;=F=0 #=8900 8030 ;=S>A #=8900 8040 @ 8050 G=0 E=32 ;=A-S+1<E E=A-S+1 8060 ":" D=E !=600 D=S/256 !=600 D=S !=600 D=0 !=600 8070 @ D=S:0) S=S+1 E=E-1 !=600 @=(E=0) 8080 D=-G !=600 / 8090 @=(S>A) 8100 ":00000001FF"/ 8110 ] 8900 " ?"/ 8910 ] 9000'** set cmd 9010 Y:0)=Q-P ;=P<Q I=1 @ Y:I)=P:0) I=I+1 P=P+1 @=(P=Q) 9020 ] 9100'** Display cmd 9110 ;=Y:0)>0 " " I=1,Y:0) $=Y:I) @=I+1 / 9120 ] 9200'** exec cmd 9210 P=B+1 Q=P 9220 ;=Y:0)>0 I=1,Y:0) Q:0)=Y:I) Q=Q+1 @=I+1 9230 C=":" 9240 ] 9300'** Xcmd 9312 !=300 9314 ;=(C=0)+(C=":") !=9394 / ] 9316 U=-1 ;=(C>="A")*(C<="B") U=C-"A"+6 9317 ;=C="D" U=8 9318 ;=C="H" U=9 9320 ;=U<0 #=9330 9322 !=300 ;=C="'" U=U-6 #=9324 9323 !=350 9324 #=9370 9330 ;=C<>"I" #=9350 9332 !=300 ;=C="X" U=4 #=9370 9340 ;=C<>"Y" "?"/ ] 9342 U=5 9350 ;=C="P" U=10 9352 ;=C="S" U=11 9360 ;=U<0 "?"/ ] 9370 ;=P<Q !=500 S=A ;=F !=800 ":" ??=A R(U)=A / ] 9372 !=800 Z=":" !=200 !=500 ;=F R(U)=A 9380 !=300 / 9390 ] 9394 U=6,11 " " !=800 @=U+1 / U=0,5 " " !=800 @=U+1 ] 9400'** Break 9410 !=300 ;=C<>"L" #=9420 9412 I=0,9 9414 ;=W(I) " adr" ?=I/2 ":" ??=W(I) "(" ?$=W(I+1) ")" / 9416 @=I+2 !=300 9418 ] 9420 T=C !=500 ;=(A>4)+(A<0) "?"/ ] 9430 ;=T<>"C" #=9450 9432 T=A*2 A=W(T) ;=A W(T)=0 ;=A:0)=$F7 A:0)=W(T+1) ] 9434 "?"/ ] 9450 ;=T<>"S" "?"/ ] 9452 T=A*2 9454 ;=W(T) "?"/ ] 9456 "adr" ?=T/2 ;=P<Q !=500 ":" ??=A #=9458 9457 Z=":" !=200 !=500 ;=F=0 #=9460 9458 / ;=A:0)<>$F7 W(T)=A W(T+1)=A:0) A:0)=$F7 ] 9460 "?"/ 9470 ] 10000'** main 10010 !=100 10020 @ 10030 Z="]" !=200 !=300 / 10040 ;=C="H" !=7000 #=11000 10050 ;=C="R" >=$20C / #=11000 10060 ;=C="W" !=8000 #=11000 10070 ;=C="=" !=9000 #=11000 10080 ;=C="?" !=9100 #=11000 10090 ;=C="!" !=9200 #=11000 10100 ;=C="G" !=4000 #=11000 10110 ;=C="D" !=1000 #=11000 10120 ;=C="S" !=2000 #=11000 10130 ;=C="F" !=3000 #=11000 10140 ;=C="I" !=5000 #=11000 10150 ;=C="O" !=6000 #=11000 10160 ;=C="X" !=9300 #=11000 10170 ;=C="B" !=9400 #=11000 10180 ;=(C<>"Q")*(C>0) $=C " ??"/ 11000 ;=C=":" !=300 #=10100 11010 @=(C="Q")

★変更 2011/11/19
 Ver0.03a "Gxxxx"と"G"の混在使用を可能にしました。


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

nice!(0)  コメント(0) 

Z80GALの構想(その8)Blocking/Deblockingの実装 [Z80]

 前回の「Z80GALの構想(その7)hexローダーでのCP/M-80ブート」の記事の最後の方で「Blocking/Deblocking処理を追加してSDアクセスを高速化したい」と書いたように、今回はBlocking/Deblocking処理の実装について書いてみたいと思います。

 その前に、前回記事に追記しましたが、"dir *.z"のようにファイルが一致しないdirコマンドで固まる現象が発生することが判明しました。割込み処理で引き継いだスタック上へのpushを無くし、スタックを切りなおすことでこの問題が解消しました。

 この為、割込み処理の先頭と末尾にSPストア/リストア等が追加になったことから実行速度が少し遅くなりました。
 下表が今回の割込み処理の変更とBlocking/Deblocking処理を追加前後において、いくつかのパターンの処理時間を測定した結果です。
 純粋にメモリ上のみの処理のASCIIART.BASの実装速度を16MHzで動作するPic24CPMと比較すると、従来はほぼ同じ速度だったのが、速度が約1割減となり14.5MHz(=16x75/83)相当の速度ということになりました。
 SDカードのアクセスはPic24CPMよりも早くなり、全体的にはPic24CPM相当のパフォーマンスと言えるのではないでしょうか?(シリアル通信の速度がPic24CPMは38400bpsでZ80GALは9600bpsなので画面表示はZ80GALの方が遅い)

 SPIインターフェースやシリアル通信をソフトで実装した割にはまずまずの結果だと思います。

Pic24CPM
(16MHz)
Z80GAL
(20MHz)
beforeafterimprove
ASCIIART実行時間1:15(75sec)1:15(75sec)1:23(83sec)1:15(75sec)
BIOSアセンブル時間※125sec29sec19sec19sec
コンパイル時間※228sec48sec21sec21sec
※1 今回のbiosソースをM80でリストファイル出力付きでアセンブルした際の時間(m80 =bios/l)
※2 hello.cをHiTech Cでコンパイルした場合の時間(c -v hello.c)

★追記 2011/11/29
 CP/M側のスタック制限により10%弱遅くなるのはもったいないのでCP/M側のCCP用スタックを拡張してBIOS内の割込み処理ではStackを再設定しないように改善してみた結果を追記しました(improveカラム)。処理速度は16MHz相当に戻りました。


 話をBlocking/Deblocking処理に戻すと、処理自体は「3チップ構成68Kマイコンの構想(その12)Blocking/Deblocking」の記事に書いたものとほぼ同じロジックです。この時はsector read時に無条件にバッファをFlushしていましたが今回はバッファ内にリード対象のデータがある場合にはFlushしないようにしました(この変更による速度的な効果は未確認でほとんど効果は無いと思いますが少しでも速くなるように変更した)

 上記の記事の時と同様にSDカードのブロックサイズは512byteでCP/Mのブロックサイズが2Kbyteです。CP/Mのセクタサイズは128byteに固定なのでSDカードの1ブロックリード/ライトで4セクタ分のリード/ライトができるようになります。また、CP/Mの新ブロックへの書込み時にはSDブロックの先読みが4回分省略できます。

 動作を確認するために、連続的なリード処理とライト処理のテストプラグラムを作成し、ロジアナで確認しました(ファイルアクセスの直前でポーズ(キー入力)することでディレクトリ処理が観測されないようにしています)。
 また、ライト処理は新規に作成したファイルに対しての連続書込みになるようにしています。

 今回の変更(Blocking/Deblocking処理の実装と割込み処理の変更)前の確認結果が下図になります。

ファイルの連続リード処理(変更前)


ファイルの連続ライト処理(変更前)


 変更後のロジアナでの確認結果が下図になります。
 上図での4回分のリード/ライトがBlocking/Deblocking処理により1回に集約されています。また、新ブロックへの書込みでは想定通り、先読み無しでSDカードへのライト処理のみになっています。
 ライト時のデータ送信完了からSDからの応答までの時間が上図よりも短いのはSDカードの応答速度の違いによるものです。

★追記 2020/11/15 {
 本確認作業で使用したSDカードは下記になります(いずれもmicroSDです)
・変更前:ShanDian micro SD HC1 8GB
・変更後:SAMSUNG EVO Plus 32GB
}
ファイルの連続リード処理(変更後)


ファイルの連続ライト処理(変更後)


  twitterにポストしたBDS Cでのコンパイル動画付きコメントを貼っておきます。



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

nice!(0)  コメント(1) 

Z80GALの構想(その7)hexローダーでのCP/M-80ブート [Z80]

 前々回の記事でタイマ割込みによるシリアル通信を実装し、前回の記事でSDカードのアクセスに必要なSPIインターフェースを実装したので今回はいよいよCP/M-80の移植です。

 いままで検討してきたZ80GAL基板は想定通り、Z80:Z84C0020PEG(40PIN)、RAM:UM61512-15(32PIN)、EEPROM:W24C512-45Z(28PIN)、GAL:GAL22V10D(24PIN)、クロック&タイマ:PIC12F683(8PIN)の5チップ構成で仕上がりそうです。

 CP/M-80の起動画面が下図です。
 hexローダーでCP/M本体とBIOSをロード後、BIOSの先頭(boot)を実行することでCP/Mが起動しています。(^^)/

Z80GALでのCP/M-80起動画面


 久々にHI-TECH Cでコンパイルした際の画面です。「-v」オプションで詳細表示指定しています。

HI-TECH Cでのコンパイル


 しかし、今回のCP/Mの移植はいつもより、少し手間取りました・・ ^^;
 他の人の参考になるかもしれないので発生した主要な問題を二つ書いておきます。

  1. M80/L80で作成したCOMファイルの問題
     アセンブラ/リンカはMACRO-80(M80/L80)を使っていて、DOS窓でCPMエミュレータで動かしています。生成したcomファイルは開始アドレスを指定してhexファイルに変換後、モニタでZ80GAL内のメモリにダウンロードしています。
     開始アドレスを指定できるHEXファイル変換ツールが見当たらなかったので簡易的なツールを自作しました(「Z80GALの構想(その4)簡易モニタの製作」の記事の末尾に追記して公開しました)

     今回は割込み処理、SPIインターフェース及びSDカード初期化等もあるので64KBのCP/MではBIOS領域が足りず、CP/Mを62KBサイズにしているのでBIOSは0F200Hから始まります。
     COMファイルを生成するために先頭で「ORG 0100H」で宣言後、.phaseステートメントを使って実アドレスを指定しているのですが、.dephaseして、再度.phaseした際に生成されるcomファイルが16バイト欠落する問題が発生しました。

     アドレス再指定部分のソースは下記のとおりです。

      0020               RBUFSZ	EQU	32
      FFE0               RBUFMSK	EQU	NOT (RBUFSZ - 1)
    
      F6C0               RBufAd	EQU	($ + RBUFSZ -1) AND RBUFMSK
                         	.DEPHASE
                         	.PHASE	RBuFAd
    
      F6C0               RBuf:	DS	RBUFSZ
    
                         ;	;Drive	 A    B    C    D  ..     K L
      F6E0               drvtbl: dtbl	<dpha,dphb,dphc,dphd,dphe,,,,,,,>
      F6E0  F74B   +     	 dw	dpha
      F6E2  F75B   +     	 dw	dphb
     

     下図が生成されたcomファイルをバイナリエディタで確認した結果です。
     comファイル内コードの開始アドレスは0F200Hなので「drvtbl」が04E0H(=0F6E0H-0F200H)に配置されるはずなのに下図のように04D0Hに配置されてしまっています。orz

    04C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    04D0 4B F7 5B F7 6B F7 7B F7 8B F7 00 00 00 00 00 00
    04E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     

     この問題は下記のようにして回避できました(その他の変更箇所もあるので上記のものとアドレスは異なっています)。

      0020               RBUFSZ	EQU	32
      FFE0               RBUFMSK	EQU	NOT (RBUFSZ - 1)
    
      F6E0               RBufAd	EQU	($ + RBUFSZ - 1) AND RBUFMSK
                         ;	.DEPHASE
                         ;	.PHASE	RBuFAd
    
      F6D2               DS	RBufAd - $
    
      F6E0               RBuf:	DS	RBUFSZ
     

     今回のディバッグスタイルはブレークなどは使わず、机上ディバッグがメインなのでこの手の問題が発生すると手こずります^^;


  2. CP/M-80の割込み用スタックの制限
     Z80GAL用のBIOSを書き上げてCP/Mを起動するとdirコマンド終了後にwboot内のシステムを読込み処理が走った後ディレクトリセクタの読込みが走る(固まる場合もある)現象が発生しました。
     他の動作は問題無いように見え、"dir"だけがおかしな状態です。BIOSのソースをいくら確認してもおかしな箇所は見当たらないので、コンソール入出力を初期に作った割込みを使わないものに変えてみたところ"dir"が正常に動作しました。

     割込み処理によるスタックの問題と思いdirコマンド実施時に実行されるであろうBIOS内の「READ」や「CONOUT」等の処理を確認し、「READ」処理内のPUSH BC,DE,HLを削除しても状況は変わりませんでした(CONOUTではPUSHは1つだけ)
     タイマ割込み処理ではPUSHは2個(AFとHL)だけでしたが少し遅くりますがHLをメモリ変数に退避したところ、問題が解消しました。

     CCP内でのdirコマンド処理からのBIOSコール時ではなく、dirコマンド処理内自体でスタックが溢れていたようです。

     ネット情報をざっと調べましたが、CP/M-80実装時における割込み処理のスタック数についての情報は見当たりませんでした(どこかにあるのかも)。
     割込み処理内では"PUSH AF"以外はレジスタをメモリ上の変数に保存するか、スタックを切りなおしてPUSH/POPした方がいいようです

    ★追記 2020/11/15 {
     "dir *.z"等のようにどのファイルにもヒットしないワイルドカードでのdirコマンドで固まる現象が発生しました。
     割込み処理内で引き継いだスタックへの"PUSH"を無くし、スタックを切り直してから"PUSH"することで問題が解消しました。

     上の記載では割込み処理内で1段だけはスタックを使用しても問題ないようなことを書きましたが割込み処理内ではスタックを切りなおしてPUSH/POPした方がいいようです("CALL"でスタックを使用する場合も同様)
    }

     因みにCCPがコマンド内容を処理する際のスタック領域は下記のように8段しか確保されていないようです。

     E386 CD66E0    GETBACK CALL  RESETDR ;reset previous drive.
     E389 CD5EDE    GETBACK1:CALL CONVFST ;convert first name in (FCB).
     E38C 3ACEE3            LDA   FCB+1   ;if this was just a drive change request,
     E38F D620              SUI   ' '     ;make sure it was valid.
     E391 21F0E3            LXI   H,CHGDRV
     E394 B6                ORA   M
     E395 C209DE            JNZ   SYNERR
     E398 C382DF            JMP   CMMND1  ;ok, return to command level.
                    ;
                    ;   ccp stack area.
                    ;
     E39B 0000000000        DB    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
     E3AB =         CCPSTACK:EQU  $       ;end of ccp stack area.
     



 以上が、Z80GALへCP/M-80を移植した際に発生した問題の簡単なメモになります。
 今回のソフトによるSPIインターフェスでのSDカードのアクセスはできるだけ高速化したつもりですが、Pic24CPMと比較して体感上はSDアクセスが倍以上遅く感じます(^Cでのシステム+ディレクトリ読込みで2秒程度時間が掛かる)。

 Pic24CPMでは例えばSDリード時はPICでアクセスしたデータをZ80に引き渡し、更にDMA領域に転送していたので(Z80GALでは直にDMA領域への読込み)もう少し速くなると期待していたのですが・・

 CP/Mのメモリサイズも62KBになったのでBlocking/Deblocking処理を追加してSDアクセスを高速化したいと思います。

 因みにタイマ割込み処理は34.7us間隔で割込み処理時間は約7us程度(シリアル通信状態にも依存)なので

   20MHz x (35 - 7) / 35) = 16MHz

のクロック相当の処理速度です。Pic24CPMのクロックが丁度16MHzなのでASCIIART.BASを並べて実行してみたところ、見た目上はまったく同じ速度でした。



[TOP] [ 前へ ] 連載記事 [ 次へ ]
nice!(0)  コメント(0)