前回の記事「
レトロマイコンZ80ボードの構想(その11)CP/M80 BIOS検討3」でBUSACK/信号のチェックは省略可能なことが判り、Z80と接続する信号はほぼ決まったのでPICとZ80とのソフトインターフェースについて動作検証してみました。
PIC側で使用するI/O数を削減するため、Z80からのサービスコール時のHALTの脱出は NMI(またはINT)ではなく、RESETを使用することにします。
NMIを使う場合は66HのコードをRETNコードに書き換えてからHALTします。RESETの場合は0000Hから実行が開始するので0000HのコードをJP XXXXに書き換えてもいいのですが、処理簡略化(かつ高速化)のためにJP (HL):0E9H に書き換えます。
NMI方式と比較し、スタック操作がなく、書き換えが1バイト(NMI方式は2バイト)なので若干ですが処理が簡易かつ高速になります(NMIのための制御出力が不要であることが最大のメリット)。
ディメリットとしてはRESETにより割込み用FFと割り込みモードがクリアされるという点がありますが、今回のように割込みを使わない場合は問題ありません。
Z80がPIC側のサービスを受ける際の手順の概要としては次のようになります(下記のサンプルソースでは「PIC_SRV」部分)
- ワークエリアにサービスIDとサービス処理に必要なパラメータを設定
- 0000Hのコード(多くの場合、0C3H(=JP XXXX)))を保存
- HLレジスタにHALTの次のアドレスを設定
- 0000Hに0E9H(=JP (HL))を設定
- HALT実行
- 0000Hを元のコードに戻す
- ワークエリアからPICが設定した処理結果を参照
PIC側の処理手順は次のようになります(下記のサンプルソースでは「 PicSrv()」部分)
- Z80がHALT状態になるまで待つ
- BUSREQ/をアクティブ(アサート)にしBUSの制御権を得る
- サービスIDとパラメータに従い処理し、結果をワークエリアに保存する
- RESETをアクティブにする
- BUSREQ/をインアクティブ(ディアサート)にする
- RESETをインアクティブにする
動作検証としてCP/MのBIOSにもあるコンソール入出力関連の処理を実験した際の信号サンプルが下のキャプチャです。
キーセンスの繰り返し処理の部分を1周期分抜き出したものです。
Z80側はキー入力状態をチェックする短いループ処理であるということもありますが、PIC側の処理時間(BUSREQ/がLOWの期間)がZ80の3倍弱程度(PIC側の7割程度が外部メモリアクセスの処理)になっています。
Z80は16MHz、PICは32MHzで動作しています。PIC側はPMPを使っているものの内部のメモリに展開しながらの外部メモリアクセスなので時間がかかっていますが、PIC側もアセンブラで書けば外部メモリアクセス部を数倍程度高速化できると思います。
|
Z80からPICへのサービス要求処理時の信号サンプル
|
|
|
Z80側のソースは次のとおりです。
「Start」と表示後、キー入力状態をチェックし、キー入力があった場合には入力データを読込み16進で表示します。
RESETによるBIOS処理実験(Z80:アセンブラ)
; PIC service call test for CP/M BIOS
; Ver 0.01 2018/03/10 by skyriver
000D CR EQU 13
000A LF EQU 10
FF00 PICSRV EQU 0FF00h ; AP area
FFF0 PICWRK EQU 0FFF0H ; PIC service work area
0080 SECSIZE EQU 128 ; sector size
0000 FC_SYSLOAD EQU 0 ; EXIT CP/M
0001 FC_CONST EQU 1 ; CONST
0002 FC_CONIN EQU 2 ; CONIN
0003 FC_CONOUT EQU 3 ; CONOUT
0004 FC_READ EQU 4 ; READ SD Card
0005 FC_WRITE EQU 5 ; WRITE SD Card
0006 FC_PIC EQU 6 ; PIC control
; ++++ work area for PIC Interface
FFF0 FuncNo equ PICWRK ; ds 1 : function No
FFF1 RetVal EQU PICWRK + 1 ; ds 1 : return value
FFF2 FCData equ PICWRK + 2 ; ds 1 : conout data
FFF3 SelDk equ PICWRK + 3 ; ds 1
FFF4 Track equ PICWRK + 4 ; ds 2
FFF6 DmaAdr equ PICWRK + 6 ; ds 2
FFF8 Sector equ PICWRK + 8 ; ds 1
0000' ASEG
ORG 0100H
.PHASE PICSRV
.Z80
FF00 31 0100 TEST: LD SP,0100h
FF03 21 FF27 LD HL,MSG
FF06 CD FF1D CALL PRNMSG
FF09 CD FF54 LOOP: CALL CONST
FF0C B7 OR A
FF0D 28 FA JR Z,LOOP
FF0F 21 FF31 LD HL,MSGP
FF12 CD FF1D CALL PRNMSG
FF15 CD FF58 CALL CONIN
FF18 CD FF3A CALL PRNHEX
FF1B 18 EC JR LOOP
FF1D 7E PRNMSG: LD A,(HL)
FF1E B7 OR A
FF1F C8 RET Z
FF20 4F LD C,A
FF21 CD FF5C CALL CONOUT
FF24 23 INC HL
FF25 18 F6 JR PRNMSG
FF27 0D 0A 53 74 MSG: DB CR,LF,'Start',CR,LF,0
FF2B 61 72 74 0D
FF2F 0A 00
FF31 20 50 75 73 MSGP: DB ' Pushed:',0
FF35 68 65 64 3A
FF39 00
FF3A PRNHEX:
FF3A 47 LD B,A
FF3B CB 3F SRL A
FF3D CB 3F SRL A
FF3F CB 3F SRL A
FF41 CB 3F SRL A
FF43 CD FF47 CALL PRNHNIB
FF46 78 LD A,B
FF47 PRNHNIB:
FF47 E6 0F AND 00FH
FF49 C6 30 ADD A,'0'
FF4B FE 3A CP '9'+1
FF4D 38 02 JR C,PRNHNIB2
FF4F C6 07 ADD A,'A' - '0' - 10
FF51 PRNHNIB2:
FF51 4F LD C,A
FF52 18 08 JR CONOUT
;
; check console key data
; A -> 0ffh:data exist 00h:none
FF54 CONST:
FF54 3E 01 LD A,FC_CONST
FF56 18 0C JR PIC_SRV
;
; input console
; A -> key data
FF58 CONIN:
FF58 3E 02 LD A,FC_CONIN
FF5A 18 08 JR PIC_SRV
;
; out console
; C <- data
FF5C CONOUT:
FF5C 79 LD A,C
FF5D 32 FFF2 LD (FCData),A
FF60 3E 03 LD A,FC_CONOUT
FF62 18 00 JR PIC_SRV
;
; PIC Service Function
; A <- service no
; A -> return value
FF64 PIC_SRV:
FF64 C5 PUSH BC
FF65 E5 PUSH HL
FF66 32 FFF0 LD (FuncNo),A
FF69 3A 0000 LD A,(0000h)
FF6C 47 LD B,A
FF6D 21 FF76 LD HL,NEXT_HALT
FF70 3E E9 LD A,0E9H ; ope-code : JP (HL)
FF72 32 0000 LD (0000H),A
FF75 76 HALT
FF76 NEXT_HALT:
FF76 78 LD A,B
FF77 32 0000 LD (0000H),A
FF7A 3A FFF1 LD A,(RetVal)
FF7D E1 POP HL
FF7E C1 POP BC
FF7F C9 RET
END
|
PIC側では上記のZ80のアセンブル結果のバイナリファイルを MemWr() での書込み処理に変換するツールを作り、picleソース内に取り込んでいます。
PIC側でのリスト表示と実行結果を下記に示します。
末尾にある「pushed」の部分は '1'、'2'、'3'のキー入力をした結果です。
RESETによるBIOS処理実験(PIC側:picle言語)
:l
1:# Test Reset I/F to Z80 by skyriver 2018/03/10
2:
3:use LibCpm;
4:
5:var ProgStart,ProgWrk,_WrkVal;
6:
7:
8:proc InitProg() {
9: ProgStart = $ff00; # Prog start addr
10: ProgWrk = $fff0; # Prog work addr
11: _WrkVal = Array_(ArIdx);
12: ArIdx = ArIdx + 5;
13:
14: PmpOn();
15:
16: PmpSetAdr( 0 );
17: MemWr( $c3 );
18: MemWr( $00 );
19: MemWr( $ff );
20:
21: PmpSetAdr( ProgStart );
22:
23: MemWr( $31 ); MemWr( $00 ); MemWr( $01 ); MemWr( $21 ); MemWr( $27 );
24: MemWr( $ff ); MemWr( $cd ); MemWr( $1d ); MemWr( $ff ); MemWr( $cd );
25: MemWr( $54 ); MemWr( $ff ); MemWr( $b7 ); MemWr( $28 ); MemWr( $fa );
26: MemWr( $21 ); MemWr( $31 ); MemWr( $ff ); MemWr( $cd ); MemWr( $1d );
27: MemWr( $ff ); MemWr( $cd ); MemWr( $58 ); MemWr( $ff ); MemWr( $cd );
28: MemWr( $3a ); MemWr( $ff ); MemWr( $18 ); MemWr( $ec ); MemWr( $7e );
29: MemWr( $b7 ); MemWr( $c8 ); MemWr( $4f ); MemWr( $cd ); MemWr( $5c );
30: MemWr( $ff ); MemWr( $23 ); MemWr( $18 ); MemWr( $f6 ); MemWr( $0d );
31: MemWr( $0a ); MemWr( $53 ); MemWr( $74 ); MemWr( $61 ); MemWr( $72 );
32: MemWr( $74 ); MemWr( $0d ); MemWr( $0a ); MemWr( $00 ); MemWr( $20 );
33: MemWr( $50 ); MemWr( $75 ); MemWr( $73 ); MemWr( $68 ); MemWr( $65 );
34: MemWr( $64 ); MemWr( $3a ); MemWr( $00 ); MemWr( $47 ); MemWr( $cb );
35: MemWr( $3f ); MemWr( $cb ); MemWr( $3f ); MemWr( $cb ); MemWr( $3f );
36: MemWr( $cb ); MemWr( $3f ); MemWr( $cd ); MemWr( $47 ); MemWr( $ff );
37: MemWr( $78 ); MemWr( $e6 ); MemWr( $0f ); MemWr( $c6 ); MemWr( $30 );
38: MemWr( $fe ); MemWr( $3a ); MemWr( $38 ); MemWr( $02 ); MemWr( $c6 );
39: MemWr( $07 ); MemWr( $4f ); MemWr( $18 ); MemWr( $08 ); MemWr( $3e );
40: MemWr( $01 ); MemWr( $18 ); MemWr( $0c ); MemWr( $3e ); MemWr( $02 );
41: MemWr( $18 ); MemWr( $08 ); MemWr( $79 ); MemWr( $32 ); MemWr( $f2 );
42: MemWr( $ff ); MemWr( $3e ); MemWr( $03 ); MemWr( $18 ); MemWr( $00 );
43: MemWr( $c5 ); MemWr( $e5 ); MemWr( $32 ); MemWr( $f0 ); MemWr( $ff );
44: MemWr( $3a ); MemWr( $00 ); MemWr( $00 ); MemWr( $47 ); MemWr( $3e );
45: MemWr( $e9 ); MemWr( $32 ); MemWr( $00 ); MemWr( $00 ); MemWr( $21 );
46: MemWr( $76 ); MemWr( $ff ); MemWr( $76 ); MemWr( $78 ); MemWr( $32 );
47: MemWr( $00 ); MemWr( $00 ); MemWr( $3a ); MemWr( $f1 ); MemWr( $ff );
48: MemWr( $e1 ); MemWr( $c1 ); MemWr( $c9 );
49:}
50:
51:
52:proc PicSrv() {
53: var i,fno,rval;
54:
55: while (1) {
56: while ( LATB[-1] & 8 ) {} # wait halt
57: PmpOn();
58: PmpSetAdr( ProgWrk );
59: fno = MemRd(); # dummy
60: for ( i=0; i<9; i=i+1 ) {
61: _WrkVal[i] = MemRd();
62: }
63: fno = _WrkVal[0]; # function No
64: if ( fno = 1 ) { # CONST
65: if ( InpChk_() ) {
66: rval = $ff;
67: } else {
68: rval = 0;
69: }
70: }
71: else if ( fno = 2 ) { # CONIN
72: if ( InpChk_() ) {
73: rval = InpChar_();
74: if ( rval = $1b ) { # if ESC then end
75: exit();
76: }
77: } else {
78: PrnChar_('x'); # __ for debug
79: rval = InpChar_();
80: }
81: }
82: else if ( fno = 3 ) { # CONOUT
83: PrnChar_( _WrkVal[2] );
84: rval = 0;
85: } else {
86: PrnStr_( "\nFunc err" );
87: }
88: PmpSetAdr( ProgWrk + 1 );
89: MemWr( rval );
90:
91: PmpOff();
92: LATC[0] = $0000; # reset on
93: BusRelease();
94: LATC[0] = $0001; # reset off
95: }
96:}
97:
98:
99:proc main() {
100: init();
101: initPmp();
102: InitProg();
103:
104: dump( 0 );
105: dump( ProgStart ); # dump & PmpOff
106: LATC[0] = $0000; # reset on
107: BusRelease();
108: LATC[0] = $0001; # reset off
109:
110: PicSrv();
111:}
:run
0000 : C3 00 FF 43 00 43 00 43 00 43 00 43 00 43 00 43
0010 : 00 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
0020 : 00 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
0030 : 00 43 00 43 00 43 00 2D B0 00 64 3A 20 43 C6 2E
0040 : DB 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
0050 : 00 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
0060 : 00 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
0070 : 00 43 00 43 00 43 00 43 00 43 00 43 00 43 00 43
FF00 : 31 00 01 21 27 FF CD 1D FF CD 54 FF B7 28 FA 21
FF10 : 31 FF CD 1D FF CD 58 FF CD 3A FF 18 EC 7E B7 C8
FF20 : 4F CD 5C FF 23 18 F6 0D 0A 53 74 61 72 74 0D 0A
FF30 : 00 20 50 75 73 68 65 64 3A 00 47 CB 3F CB 3F CB
FF40 : 3F CB 3F CD 47 FF 78 E6 0F C6 30 FE 3A 38 02 C6
FF50 : 07 4F 18 08 3E 01 18 0C 3E 02 18 08 79 32 F2 FF
FF60 : 3E 03 18 00 C5 E5 32 F0 FF 3A 00 00 47 3E E9 32
FF70 : 00 00 21 76 FF 76 78 32 00 00 3A F1 FF E1 C1 C9
Start
Pushed:31 Pushed:32 Pushed:33
|
[TOP] [ 前へ ] 連載記事
[ 次へ ]
posted by skyriver at 01:57|
Comment(0)
|
Z80
|

|