目次
前
次
ハードウエアテスト
今回利用するUEC、AKI80にはZ84C15が使われています。
Z84C15には、次のペリフェラルが内蔵されています。
他に、基板上に8080ファミリの8255があります。
これらのペリフェラルをテストします。
手元に、8ビットのスイッチボードとLEDボードが
あるので、これを利用し、なるべく簡単にテスト
します。
このスイッチLEDボードとZ80ボードとは、10ピン
ケーブルで接続します。
Z80ボードに用意したコネクタは、次のように
ピン配置を決めてあります。
- +5V
- PD7
- PD6
- PD5
- PD4
- PD3
- PD2
- PD1
- PD0
- 0V
コネクタを介して、電源を他のハードウエアに
供給できるようにしてあります。
8ビットの入出力ピンを、接続ハードウエアに
合わせて使えるようにします。
Z80PIO関係テスト
Z80PIOには、A、Bの2チャネルがあるので
ともに出力にし、LEDボードの8個のLEDの
点滅で、PIOをテストします。
テストプログラムは、次の仕様とします。
PIOをモード0で利用し、0〜255の数値を
8ビットで出力します。
アセンブリ言語で短く記述します。
アセンブラは、システムロードのXA80を使います。
0番地に100h番地に分岐するコードをおき
100h番地から動かします。
CP/M80のメモリの使い方と同じにし、テスト
しやすくします。
初期化をINIT_PIO、LEDの点滅をLED_FLASHという
サブルーチンで実現するとして、TOPレベルコード
を記述します。
STACKB EQU 0
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_PIO
main:
call LED_FLASH
jp main
Z84C15に内蔵されているZ80PIOのデータ、制御
レジスタのアドレスを、ラベル定義します。
PIO_AD EQU 1CH
PIO_AC EQU 1DH
PIO_BD EQU 1EH
PIO_BC EQU 1FH
Z80PIOを初期化します。
2つのチャネルを、モード0で利用するため
制御レジスタに、制御ワード0fをライトします。
さらに、割込みを使わないように、制御ワード
03をライトします。
DEレジスタペアに、030fを格納して、一時メモリ
として利用します。
INIT_PIO:
push de
push bc
push af
; set control code
ld de,030fh
; PIO A control
ld c,PIO_AC
; set mode 0 (channel A)
ld a,e
out (c),a
; no interrupt (channel A)
ld a,d
out (c),a
; PIO B control
ld c,PIO_BC
; set mode 0 (channel B)
ld a,e
out (c),a
; no interrupt (channel B)
ld a,d
out (c),a
;
pop af
pop bc
pop de
ret
LEDに、8ビットのパターンを出力します。
Bレジスタをカウンタで利用し255〜0まで
変化させます。Bレジスタの値を、Aレジスタ
に転送し、Aレジスタの値をPIOの2チャネルに
出力します。
Bレジスタをカウンタと割当てると、DJNZ命令を
使えます。
LED_FLASH:
push bc
push af
; set counter
ld b,BMAX
LED_FLASH_L:
; set channel A port
ld c,PIO_AD
ld a,b
out (c),a
; wait
call SWAIT
call SWAIT
call SWAIT
call SWAIT
call SWAIT
; set channel B port
ld c,PIO_BD
ld a,b
out (c),a
; wait
call SWAIT
call SWAIT
call SWAIT
call SWAIT
call SWAIT
;
djnz LED_FLASH_L
;
pop af
pop bc
ret
マイコンは、高速で動作するため、点滅が確認
できるように、空ループで無駄時間を作ります。
無駄時間は、SWAITサブルーチンで生成します。
SWAIT:
push bc
push af
;
ld b,BMAX
SWAITL:
nop
djnz SWAITL
;
pop af
pop bc
ret
まとめます。
PIO_AD EQU 1CH
PIO_AC EQU 1DH
PIO_BD EQU 1EH
PIO_BC EQU 1FH
STACKB EQU 0
BMAX EQU 255
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_PIO
main:
call LED_FLASH
jp main
INIT_PIO:
push de
push bc
push af
; set control code
ld de,030fh
; PIO A control
ld c,PIO_AC
; set mode 0 (channel A)
ld a,e
out (c),a
; no interrupt (channel A)
ld a,d
out (c),a
; PIO B control
ld c,PIO_BC
; set mode 0 (channel B)
ld a,e
out (c),a
; no interrupt (channel B)
ld a,d
out (c),a
;
pop af
pop bc
pop de
ret
LED_FLASH:
push bc
push af
; set counter
ld b,BMAX
LED_FLASH_L:
; set channel A port
ld c,PIO_AD
ld a,b
out (c),a
; wait
call SWAIT
call SWAIT
call SWAIT
call SWAIT
call SWAIT
; set channel B port
ld c,PIO_BD
ld a,b
out (c),a
; wait
call SWAIT
call SWAIT
call SWAIT
call SWAIT
call SWAIT
;
djnz LED_FLASH_L
;
pop af
pop bc
ret
SWAIT:
push bc
push af
;
ld b,BMAX
SWAITL:
nop
djnz SWAITL
;
pop af
pop bc
ret
end
このテストで、コネクタとZ84C15の接続が
正しいかを確認できます。
また、I/Oアドレスを正しく利用しているかを
確認できます。
出力を確認できたので、入出力をテストします。
Aチャネルを出力、Bチャネルを入力にします。
Aチャネルには、8ビット分のLEDを接続し
Bチャネルには、8ビット分のプッシュスイッチ
を接続して、入出力をテストします。
Bチャネルを入力に設定する制御ワードを
考えます。
入力はモード1になるので、2進数01??1111を
制御レジスタに設定します。
??は、0でも1でも構わないので01001111と
します。
A、Bチャネルの制御ワードを設定するサブルーチン
は、次のようになります。
INIT_PIO:
push de
push bc
push af
; set control code
ld de,030fh
; PIO A control
ld c,PIO_AC
; set mode 0 (channel A)
ld a,e
out (c),a
; no interrupt (channel A)
ld a,d
out (c),a
; set control code
ld de,034fh
; PIO B control
ld c,PIO_BC
; set mode 1 (channel B)
ld a,e
out (c),a
; no interrupt (channel B)
ld a,d
out (c),a
;
pop af
pop bc
pop de
ret
Bチャネルから入力した情報を、そのまま
Aチャネルに転送する処理を、無限ループ
で繰返します。
main:
; get data from PIO_B
ld c,PIO_BD
in a,(c)
; wait
call SWAIT
; put data to PIO_A
ld c,PIO_AD
out (c),a
; wait
call SWAIT
;
jp main
まとめます。
PIO_AD EQU 1CH
PIO_AC EQU 1DH
PIO_BD EQU 1EH
PIO_BC EQU 1FH
BMAX EQU 255
STACKB EQU 0
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_PIO
main:
; get data from PIO_B
ld c,PIO_BD
in a,(c)
; wait
call SWAIT
; put data to PIO_A
ld c,PIO_AD
out (c),a
; wait
call SWAIT
;
jp main
INIT_PIO:
push de
push bc
push af
; set control code
ld de,030fh
; PIO A control
ld c,PIO_AC
; set mode 0 (channel A)
ld a,e
out (c),a
; no interrupt (channel A)
ld a,d
out (c),a
; set control code
ld de,034fh
; PIO B control
ld c,PIO_BC
; set mode 1 (channel B)
ld a,e
out (c),a
; no interrupt (channel B)
ld a,d
out (c),a
;
pop af
pop bc
pop de
ret
SWAIT:
push bc
push af
;
ld b,BMAX
SWAITL:
nop
djnz SWAITL
;
pop af
pop bc
ret
AチャネルとBチャネルの役割を入れ替えて
テストします。
PIO_AD EQU 1CH
PIO_AC EQU 1DH
PIO_BD EQU 1EH
PIO_BC EQU 1FH
STACKB EQU 0
BMAX EQU 255
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_PIO
main:
; get data from PIO_A
ld c,PIO_AD
in a,(c)
; wait
call SWAIT
; put data to PIO_B
ld c,PIO_BD
out (c),a
; wait
call SWAIT
;
jp main
INIT_PIO:
push de
push bc
push af
; set control code
ld de,034fh
; PIO A control
ld c,PIO_AC
; set mode 1 (channel A)
ld a,e
out (c),a
; no interrupt (channel A)
ld a,d
out (c),a
; set control code
ld de,030fh
; PIO B control
ld c,PIO_BC
; set mode 0 (channel B)
ld a,e
out (c),a
; no interrupt (channel B)
ld a,d
out (c),a
;
pop af
pop bc
pop de
ret
SWAIT:
push bc
push af
;
ld b,BMAX
SWAITL:
nop
djnz SWAITL
;
pop af
pop bc
ret
end
8255テスト
Z84C15には、8255を接続したので、3ポートを
出力に設定し、LEDボードの8個のLEDを点滅させ
テストします。
テストプログラムは、次の仕様とします。
8255の全ポートをモード0の出力で利用し
0〜255の数値を8ビットで出力します。
0番地に100h番地に分岐するコードをおき
100h番地から動かします。
CP/M80のメモリの使い方と同じにし、テスト
しやすくします。
初期化をINIT_8255、LEDの点滅をLED_FLASHという
サブルーチンで実現するとして、TOPレベルコード
を記述します。
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_8255
main:
call LED_FLASH
jp main
8255のデータ、制御レジスタのアドレスを
ラベル定義します。
P55A EQU 00H
P55B EQU 01H
P55C EQU 02H
P55CTL EQU 03H
8255を初期化します。
8255では、入出力方向とモード指定は
制御レジスタの制御ワードだけでよい
ので、次のように書けます。
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,80h
out (c),a
;
pop af
pop bc
ret
LEDに、8ビットのパターンを出力します。
Bレジスタをカウンタで利用し255〜0まで
変化させます。Bレジスタの値を、Aレジスタ
に転送し、Aレジスタの値を8255の3ポートに
出力します。
Bレジスタをカウンタと割当てると、DJNZ命令を
使えます。
LED_FLASH:
push bc
push af
; set counter
ld b,BMAX
LED_FLASH_L:
; load value
ld a,b
; set port A address
ld c,P55A
out (c),a
call SWAIT
call SWAIT
; set port B address
ld c,P55B
out (c),a
call SWAIT
call SWAIT
; set port C address
ld c,P55C
out (c),a
call SWAIT
call SWAIT
;
djnz LED_FLASH_L
;
pop af
pop bc
ret
SWAITを無駄時間として、まとめます。
P55A EQU 00H
P55B EQU 01H
P55C EQU 02H
P55CTL EQU 03H
STACKB EQU 0
BMAX EQU 255
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_8255
main:
call LED_FLASH
jp main
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,80h
out (c),a
;
pop af
pop bc
ret
LED_FLASH:
push bc
push af
; set counter
ld b,BMAX
LED_FLASH_L:
; load value
ld a,b
; set port A address
ld c,P55A
out (c),a
call SWAIT
call SWAIT
; set port B address
ld c,P55B
out (c),a
call SWAIT
call SWAIT
; set port C address
ld c,P55C
out (c),a
call SWAIT
call SWAIT
;
djnz LED_FLASH_L
;
pop af
pop bc
ret
SWAIT:
push bc
push af
;
ld b,BMAX
SWAITL:
nop
djnz SWAITL
;
pop af
pop bc
ret
end
8255には、3ポートあるので入出力をテストしておきます。
1ポートを入力にし、他のポートは出力に設定します。
2つの出力ポートには、1ポートには、入力値そのものを
残りのポートには、入力値を反転した値を出力します。
- PORT A : input
- PORT B : output
- PORT C : output
の場合、初期化は、次のようになります。
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,90h
out (c),a
;
pop af
pop bc
ret
入力値を出力する処理は、以下です。
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55A
ld b,P55B
ld e,P55C
; get switch state from port A
in a,(c)
; put contents to port B
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port C
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
まとめます。
P55A EQU 00H
P55B EQU 01H
P55C EQU 02H
P55CTL EQU 03H
org 0h
jp start
org 100h
start:
ld a,(hl)
call INIT_8255
main:
call SW_PROC
jp main
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,90h
out (c),a
;
pop af
pop bc
ret
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55A
ld b,P55B
ld e,P55C
; get switch state from port A
in a,(c)
; put contents to port B
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port C
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
end
- PORT A : output
- PORT B : input
- PORT C : output
の場合、初期化は、次のようになります。
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,82h
out (c),a
;
pop af
pop bc
ret
入力値を出力する処理は、以下です。
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55B
ld b,P55A
ld e,P55C
; get switch state from port B
in a,(c)
; put contents to port A
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port C
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
まとめます。
P55A EQU 00H
P55B EQU 01H
P55C EQU 02H
P55CTL EQU 03H
org 0h
jp start
org 100h
start:
ld a,(hl)
call INIT_8255
main:
call SW_PROC
jp main
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,82h
out (c),a
;
pop af
pop bc
ret
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55B
ld b,P55A
ld e,P55C
; get switch state from port B
in a,(c)
; put contents to port A
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port C
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
end
- PORT A : output
- PORT B : output
- PORT C : input
の場合、初期化は、次のようになります。
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,89h
out (c),a
;
pop af
pop bc
ret
入力値を出力する処理は、以下です。
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55C
ld b,P55A
ld e,P55B
; get switch state from port C
in a,(c)
; put contents to port A
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port B
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
まとめます。
P55A EQU 00H
P55B EQU 01H
P55C EQU 02H
P55CTL EQU 03H
org 0h
jp start
org 100h
start:
ld a,(hl)
call INIT_8255
main:
call SW_PROC
jp main
INIT_8255:
push bc
push af
; set control address
ld c,P55CTL
; set control word
ld a,89h
out (c),a
;
pop af
pop bc
ret
SW_PROC:
push de
push bc
push af
; set port A address
ld c,P55C
ld b,P55A
ld e,P55B
; get switch state from port C
in a,(c)
; put contents to port A
ld c,b
out (c),a
; inverse
xor 0ffh
; put contents to port B
ld c,e
out (c),a
;
pop af
pop bc
pop de
ret
end
Z80CTC関係テスト
Z80CTCには、4チャネルがあるので、タイマー
モードで、クロックを出力してテストします。
各チャネルのクロックを、周波数カウンタで
測定して、分周されていることを確認します。
全チャネルをタイマーモードにし、プリスケーラ
でソースクロックを1/16とした後、各カウンタに
分周比を設定します。
各チャネルの分周比を次のように設定します。
- チャネル0 128
- チャネル1 64
- チャネル2 32
- チャネル3 16
初期化をINIT_CTCとして、無限ループで
CPUをダイナミックストップします。
STACKB EQU 0
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_CTC
main:
nop
jr main
Z80CTCの各チャネルのアドレスを
ラベル定義します。
CTC_C0 EQU 10H
CTC_C1 EQU 11H
CTC_C2 EQU 12H
CTC_C3 EQU 13H
Z80CTCを初期化します。
制御ワード、分周比の順でレジスタに
データを格納します。
INIT_CTC:
push bc
push af
; set default control word
ld b,27h
; set channel 0 I/O address
ld c,CTC_C0
; set control word
ld a,b
out (c),a
; set divide count
ld a,80h
out (c),a
; set channel 1 I/O address
ld c,CTC_C1
; set control word
ld a,b
out (c),a
; set divide count
ld a,40h
out (c),a
; set channel 2 I/O address
ld c,CTC_C2
; set control word
ld a,b
out (c),a
; set divide count
ld a,20h
out (c),a
; set channel 0 I/O address
ld c,CTC_C3
; set control word
ld a,b
out (c),a
; set divide count
ld a,10h
out (c),a
;
pop af
pop bc
ret
まとめます。
CTC_C0 EQU 10H
CTC_C1 EQU 11H
CTC_C2 EQU 12H
CTC_C3 EQU 13H
STACKB EQU 0
org 0h
jp start
org 100h
start:
ld sp,STACKB
call INIT_CTC
main:
nop
jr main
INIT_CTC:
push bc
push af
; set default control word
ld b,27h
; set channel 0 I/O address
ld c,CTC_C0
; set control word
ld a,b
out (c),a
; set divide count
ld a,80h
out (c),a
; set channel 1 I/O address
ld c,CTC_C1
; set control word
ld a,b
out (c),a
; set divide count
ld a,40h
out (c),a
; set channel 2 I/O address
ld c,CTC_C2
; set control word
ld a,b
out (c),a
; set divide count
ld a,20h
out (c),a
; set channel 0 I/O address
ld c,CTC_C3
; set control word
ld a,b
out (c),a
; set divide count
ld a,10h
out (c),a
;
pop af
pop bc
ret
end
Z80SIO関係テスト
Z80SIOは、タイマー/カウンタ、割込み等が複雑に
絡み合って、はじめて動作するので、CTC、SIOへの
設定と割込み処理プログラムを記述してしまいます。
この後に、個々の処理が正しいことを確認します。
CTC0 EQU 10h
CTC1 EQU 11h
CTC2 EQU 12h
CTC3 EQU 13h
SIOAD EQU 18H
SIOAC EQU 19H
SIOBD EQU 1aH
SIOBC EQU 1bH
;INT_SIO EQU 7f00h
org 0h
jp start
org 100h
start:
call INIT_CTC_01
call INIT_SIO
main:
nop
jr main
INIT_SIO:
push bc
push af
;
di ; disable interrupt
im 2 ; select mode 2 interrupt
ld hl,INT_SIO
ld a,h
ld i,a ; set upper 8 bit address to I register
; set control address
ld c,SIOBC ;
ld a,18h ; reset channel B
out (c),a
ld a,02h ; WR2
out (c),a
ld hl,INT_SIO ; get interrupt vector
ld a,l
out (SIOBC),a ; set interrupt vector
ld b,SIOBE-SIOB ; set number of data
ld hl,SIOB ; set destination address
otir
; set control address
ld c,SIOBC ;
ld a,18h ; reset channel A
out (c),a
ld b,SIOAE-SIOA ; set number of data
ld hl,SIOA ; set destination address
otir
xor a ; A = 0
ld (_rx_putpa),a ; channel A initialize GET_POINTER
ld (_rx_getpa),a ; channel A initialize PUT_POINTER
ld (_rx_putpb),a ; channel B initialize GET_POINTER
ld (_rx_getpb),a ; channel B initialize PUT_POINTER
;
pop af
pop bc
ret
INIT_CTC_01:
push bc
push af
;
; D7 0 disable interrupt
; D6 0 set timer mode
; D5 0 set presclaer (1/16)
; D4 0 inhibit timer wakeup with CLK/TG
; D3 0 start timer
; D2 1 set time constant
; D1 1 stop channel
; D0 1 channel control word
; Φ = (16MHz/16)/8 = 125kHz
ld c,CTC3
ld a,07h
out (c),a
;
; 1 / 13
; 125kHz
; SIOA,SIOB : (1/32)
ld a,13
out (c),a
;
pop af
pop bc
ret
;#################################################
; store SIOA data to ring buffer
; if No more space then Nothing do
;#################################################
_rs_rx_a:
di
push af
push bc
push hl
;
ld a,(_rx_putpa); get PUT_POINTER
ld c,a
inc c ; set next PUT_POINTER
ld a,(_rx_getpb); get GET_POINTER
cp c ; if next PUT_POINTER = GET_POINTER
in a,(SIOAD) ; get data
jr z,ignore_data; no space
; and 7fH ; for 7 bit character
ld b,0
dec c ; set current PUT_POINTER
ld hl,_rx_buf_a ; get ring buffer A entry point
add hl,bc ; calculate rest capacity
ld (hl),a ; store data to ring buffer A
ld hl,_rx_putpa ; update PUT_POINTER
inc (hl)
jr ignore_data
;#################################################
; store SIOB data to ring buffer
; if No more space then Nothing do
;#################################################
_rs_rx_b:
di
push af
push bc
push hl
;
ld a,(_rx_putpb); get PUT_POINTER
ld c,a
inc c ; set next PUT_POINTER
ld a,(_rx_getpb); get GET_POINTER取得
cp c ; if next PUT_POINTER = GET_POINTER
in a,(SIOBD) ; get data
jr z,ignore_data; no capacity
; and 7fH ; for 7 bit character
ld b,0
dec c ; set current PUT_POINTER
ld hl,_rx_buf_b ; get ring buffer B entry point
add hl,bc ; calculate rest capacity
ld (hl),a ; store data to ring buffer B
ld hl,_rx_putpb ; update PUT_POINTER
inc (hl)
;
ignore_data:
pop hl
pop bc
pop af
ei
reti
;###################################
; C function handling
; get data from ring buffer A
;###################################
_sioa_get:
ld a,(_rx_getpa); get GET_POINTER
ld c,a
ld a,(_rx_putpa); get PUT_POINTER
cp c ; if data exist
jr z,_a_get0 ; return -1
ld b,0 ; calculate data entry point
ld hl,_rx_buf_a
add hl,bc
ld a,(hl) ; a = data
ld hl,_rx_getpa ;
inc (hl) ; update GET_POINTER
ld l,a ; l = data
ld h,b ; h = 0
jr _a_get_e
_a_get0:
ld hl,-1 ; HL (return value in int type)
_a_get_e:
ret
;###################################
; C function handling
; get data from ring buffer B
;###################################
_siob_get:
ld a,(_rx_getpb); get GET_POINTER
ld c,a
ld a,(_rx_putpb); get PUT_POINTER
cp c ; if data exist
jr z,_b_get0 ; return -1
ld b,0 ; calculate data entry point
ld hl,_rx_buf_b
add hl,bc
ld a,(hl) ; a = data
ld hl,_rx_getpb ;
inc (hl) ; update GET_POINTER
ld l,a ; l = data
ld h,b ; h = 0
jr _b_get_e
_b_get0:
ld hl,-1 ; HL (return value in int type)
_b_get_e:
ret
;###################################
; reset channel A error
;###################################
_er_reset_a:
di
push af
push bc
;
ld c,SIOAC
ld a,00110000B ; clear error
out (c),a
;
pop bc
pop af
ei
reti
;###################################
; reset channel B error
;###################################
_er_reset_b:
di
push af
push bc
;
ld c,SIOBC
ld a,00110000B ; clear error
out (c),a
;
pop bc
pop af
ei
reti
;###################################
; stop SIO
;###################################
_sioret:
jp _sioret ; STOP
reti
;###################################################
; channel B
; WR4
; receive clock x32 / 1 stop bit / non parity
; WR3
; receive 8 bits / no CTS & DCD control / enable receive
; WR5
; DTR_on / transmit 8 bits / enable transmission / RST_on
; WR1
; receive interrupt / enable affect vector
;###################################################
org 06000h
SIOB:
DB 14H ; WR4 & Reset
DB 10000100B ; x32,S1,PN
DB 03H ; WR3
DB 11000001B ; B8,Rx Enable
DB 05H ; WR5
DB 11101010B ; DTR on,B8,Tx Enable,RTS on
DB 01H ; WR1
DB 00010100B ; All Char Enable,Save Affect Vector
SIOBE:
;############################################################
; channel A
; WR4
; receive clock x32 / 1 stop bit / non parity
; WR3
; receive 8 bits / no CTS & DCD control / enable receive
; WR5
; DTR_on / transmit 8 bits / enable transmission / RST_on
; WR1
; receive interrupt
;############################################################
SIOA:
DB 14H ; WR4 & Reset
DB 10000100B ; x32,S1,PN
DB 03H ; WR3
DB 11000001B ; B8,Rx Enable
DB 05H ; WR5
DB 11101010B ; DTR on,B8,Tx Enable,RTS on
DB 01H ; WR1
DB 00010000B ; All Char Enable
SIOAE:
INT_SIO: ; SIO interrupt vector
DEFW _sioret ; channel B send
DEFW _sioret ; RETINT
DEFW _rs_rx_b ; channel B receive interrupt
DEFW 0000h ; channel B error interrupt
;DEFW _er_reset_b
; channel B error interrupt
DEFW _sioret ; channel A send
DEFW _sioret ; RETINT
DEFW _rs_rx_a ; channel A receive interrupt
DEFW _er_reset_a ; channel A error interrupt
org 8000h
_rx_putpa: DEFS 1 ; Receive Put Data Pointer
_rx_getpa: DEFS 1 ; Receive Get Data Pointer
_rx_buf_a: DEFS 256 ; Receive Data Buffer
_rx_putpb: DEFS 1 ; Receive Put Data Pointer
_rx_getpb: DEFS 1 ; Receive Get Data Pointer
_rx_buf_b: DEFS 256 ; Receive Data Buffer
end
このコードは、Cで記述するモニタプログラムと
接続して利用します。
Intel系ICテスト
Z84C15基板の外部に、8251、8255、8254を接続して使います。
各ICの動作テストをしました。
8251
8251は、シリアルインタフェースICですが、コンピュータ
と通信しながらテストするために使われました。
次の条件で利用します。
データ転送速度 9600bps
データ長 8ビット
ストップビット 1ビット
パリティ なし
フロー制御 なし
データ転送速度は、8251のTXC、RXCピンに与えるクロックを
決めます。4MHzを分周してデータ転送速度の16倍、64倍での
クロックを与えます。計算して、次のようになりました。
4800bps 64x => 307.2kHz 13
9600bps 16x => 153.6kHz 26
19200bps 16x => 307.2kHz 13
モード設定するときには、ビットの組合せを考えます。
設定項目は、以下です。
- 同期、非同期指定
- ストップビット (非同期)
- データ長
- パリティ
- データ転送速度レート (非同期)
- 外部同期、内部同期の指定 (同期)
- キャラクタ同期時の同期キャラクタ数 (同期)
非同期で動かすことにします。
ビットの組み合わせは、以下。
S2 S1 EP PEN L2 L1 B2 B1
(S2,S1)は、ストップビット数になるので(0,1)を指定。
(EP,PEN)は、パリティ関係で使わないので(0,0)を指定。
(L2,L1)は、データ長をつかうので8ビットとして(1,1)を指定。
(B2,B1)は、データ転送速度の16倍、64倍を決めるので16倍として(1,0)を指定。
8ビットにまとめると、01001110(4Fh)になります。
コマンド設定にも、ビットの組合せを考えます。
ビットの組み合わせは、以下。
EH IR RTS ER SBRK RXE DTR TXEN
EHは、Huntモードを使うときに1にします。(EH = 0)
IRは、Internal Resetをしたいときに1にします。(IR = 0)
RTSは、RTSピンの論理値を指定します。(RTS = 1)
ERは、Reset Error flagを使うときに1にします。(ER = 0)
SBRKは、Break Charactorを使うときに1にします。(SBRK = 0)
RXEは、受信処理する場合に1にします。(RXE = 1)
DTRは、DTRピンの論理値を指定します。(DTR = 1)
TXENは、送信処理する場合に1にします。(TXEN = 1)
8ビットにまとめると、00100111(27h)になります。
初期化処理は、次のコードになります。
URTDA EQU 80h ;8251 UART Data Port
URTCNT EQU 81h ;8251 UART Control Port
XOR A
OUT (URTCNT),A
OUT (URTCNT),A
OUT (URTCNT),A
LD A,40h
OUT (URTCNT),A
LD A,4Fh
OUT (URTCNT),A
LD A,27h
OUT (URTCNT),A
8251は、初期化時にはレジスタをクリアします。
データ入力、出力は、次のようになります。
; data input
DAIN:
IN A,(URTDA)
RET
; data output
DAOUT:
PUSH AF
;
OUT (URTDA),A
;
POP AF
;
RET
データ入力の場合は、ステータスを確認します。
ビットの組み合わせは、以下。
DSR SYNDET/BD FE OE PE TXEMPTY RXRDY TXRDY
DSRは、DSRピンの状態を反映しています。1でDSR=0。
SYNDET/BDは、同期の場合に使います。1にするとSYNDET/BD=1。
FEは、Framingエラーのとき1になります。
Framingエラーは、データ長が設定値と一致しないとき発生。
OEは、Overflowエラーのとき1になります。
Overflowエラーは、ストップビットを検出できないとき発生。
PEは、Parityエラーのとき1になります。
Parityを使わない場合は、無視します。
TXEMPTYは、送信バッファ(シフトレジスタ)が空のとき1になります。
RXRDYは、受信可能のときに1になります。(DTR = 1)
TXRDYは、送信可能のときに1になります。(TXEN = 1)
ステータスを使い、データ入力、出力は、次のようにします。
; data input
DAIN:
IN A,(URTCNT) ; Get status byte
BIT 1,A ; Check buffer full
JR Z,DAIN ; no data
IN A,(URTDA)
RET
; data output
DAOUT:
PUSH AF
PUSH BC
;
DAOUT1:
IN A,(URTCNT) ; Get status byte
BIT 0,A ; Check buffer full
JR Z,DAOUT1 ; not empty
;
LD A,B
OUT (URTDA),A
;
POP BC
POP AF
;
RET
8255
8255は、パラレルインタフェースICです。
Z80ファミリICでパラレルインタフェースと
なるとZ80PIOがありますが、1ポートを8
ビットとして2ポート。
8255は8ビットを1ポートとするポート数が
3と多かったので、単純なパラレル入出力に
利用されていました。
ポートA、B、Cを単純な入出力で利用する
他に、グループA、Bとして使うことも可能。
ポートA、ポートCの下4ビットをグループA
として、ポートB、ポートCの上4ビットを
グループBとします。
グループA、Bとして使うのが基本のようで
制御レジスタの設定は、グループ単位となって
いました。
AVR、PICのようなワンチップマイコンなら当然
のように持っている、内蔵プルアップ抵抗もなく
外部でプルアップ、プルダウンする仕様でした。
制御、ポートのレジスタの配置は以下。
- 00 PORT A
- 01 PORT B
- 10 PORT C
- 11 CONTROL
ポートの入出力には、AからCを利用しますが
グループA、Bの扱いはCONTROLレジスタでの
設定に依存します。
CONTROLレジスタのビットの組み合わせは、次のように
決められています。
D7 1:モード設定 0:ビットハンドリング
D6 groupA mode (M1) [M1,M0] = [1,1] or [1,0] mode_2
D5 groupA mode (M0) [M1,M0] = [0,1] mode_1 [M1,M0] = [0,0] mode_0
D4 PORT_A 1:inputs 0:outputs
D3 PORT_C upper nibble 1:inputs 0:outputs
D2 groupB mode 1:mode_1 0:mode_2
D1 PORT_B 1:inputs 0:outputs
D0 PORT_C lower nibble 1:inputs 0:outputs
グループA、Bともに、12ビットで動作を指定すると
考えれば、わかりやすくなります。
グループA、Bのモードと入出力設定用制御ワードは、スクリプトで
簡単に生成すればよいでしょう。
テキストファイルには、次のように記述します。
2 IN OUT 1 OUT OUT
単純な組み合わせなので、AWKでは次のように記述します。
{
# groupA mode
result = $1 * 4
# groupA PORT_A in or out
if ( tolower($2) == "in" ) {
result = result + 2
}
# groupB PORT_C upper nibble in or out
if ( tolower($3) == "in" ) {
result = result + 1
}
# shift
result *= 8
# groupB mode
result += $4 * 4
# groupB PORT_B in or out
if ( tolower($5) == "in" ) {
result = result + 2
}
# groupB PORT_C upper nibble in or out
if ( tolower($6) == "in" ) {
result = result + 1
}
# add MSB
result = result + 128
# show
printf("%s => 0%02Xh\n",$0,result)
}
テキストファイルに、モードと入出力を定義して
実際の制御ワードがどうなるのかを見ると以下。
ビットハンドリングは、ポートCの8ビットの出力論理値を
設定します。ビットの組み合わせは、以下。
D7 0
D6 * Don't care
D5 * Don't care
D4 * Don't care
D3 bit address 2
D2 bit address 1
D1 bit address 0
D0 1:set 0:reset
ビットハンドリングの制御ワード(1バイト)を生成するのは
次のようなスクリプトを使うと簡単です。(AWKの場合)
{
result = $1 * 2 + $0
printf("%s => %02Xh\n",$0,result)
}
MSBからLSBにかけて、セット、リセットのビット位置と値を
定義したファイルを作成し、I/Oリダイレクトでコードを生成
すると、以下。
利用したテキストファイルの内容は、次のようにしました。
(ビット位置、値の並びです。)
7 0
7 1
6 0
6 1
5 0
5 1
4 0
4 1
3 0
3 1
2 0
2 1
1 0
1 1
0 0
0 1
ビットハンドリングの制御バイトは、アセンブリ言語
でも生成できます。
; set bit location
LD A,04h
SLA A
; set or reset
SET 0,A
; store
OUT (CT8255),A
グループA、Bともに、モード0(単純入出力)に設定し
グループAを入力、グループBを出力に設定します。
グループAには12ビット分のスイッチ、グループBには
12ビット分のLEDがあるとして、動作テストさせる場合
次のコードになります。
A8255 EQU 80h
B8255 EQU 81h
C8255 EQU 82h
CT8255 EQU 83h
INIT_8255:
;
PUSH AF
; control word
LD A,98h
; store
OUT (CT8255),A
;
POP AF
;
RET
P8255:
;
PUSH AF
PUSH DE
; get from group A
IN A,(C8255)
AND 0F0h
SRL A
SRL A
SRL A
SRL A
LD E,A
IN A,(A8255)
; inverse
CPL
LD D,A
LD A,E
CPL
; put to group B
OUT (C8255),A
LD A,D
OUT (B8255),A
;
POP DE
POP AF
;
RET
8254
8254は、8253とピン互換、機能互換であるタイマーICです。
Z80CTCは、プリスケーラと8ビットカウンタが4チャネル
ありますが、8254は16ビットカウンタが3チャネルです。
16ビットカウンタが3チャネルで、1チャネルあたり入出力
はCLK、GATE、OUTの3ビットあります。
CLKは、16ビットカウンタへのクロック入力。
GATEは、カウンタのデクリメントを制御します。
OUTは、カウンタがゼロになったときに1か0を出力。
カウント処理は、すべてダウンカウントになります。
モードが次の6種類あります。
- mode0 interrupt on terminal count
- mode1 programmable one shot
- mode2 rate generator
- mode3 rectangle rate generator
- mode4 software trigger strobe
- mode5 hardware trigger strobe
これらすべてのモードを利用することがないので
応用例を考えて、使うモードを限定します。
制御ワードは、次のビット構成になってます。
D7 SCI1 select channel
D6 SCI0
D5 RL1 read / load
D4 RL0
D3 M2 select mode
D2 M1
D1 M0
D0 BCD 1:BCD count 0:binary count
ビットの組合せは、以下。
(SCI1,SCI0)=(0,0) channel_0
(0,1) channel_1
(1,0) channel_2
(1,1) invalid
(RL1,RL0)=(0,0) counter latch operation
(0,1) read or load lower byte
(1,0) read or load upper byte
(1,1) read or load both byte
(M2,M1,M0)=(0,0,0) mode_0
(0,0,1) mode_1
(0,1,0) mode_2
(0,1,1) mode_3
(1,0,0) mode_4
(1,0,1) mode_5
(1,1,0) invalid
(1,1,1) invalid
通常カウントダウンするためのカウント値は16ビット
で指定するので、RLは固定値とし他をファイルに記述
して制御バイトを生成するスクリプトを定義します。
{
result = 3 * 16
result = result + $1 * 64
result = result + $2 * 2
result = result + $3
printf("%s -> 0%02Xh\n",$0,result)
}
ファイルには、チャネル、モード、BCDかどうかを
数値で記述します。
2 4 0
0 2 1
スクリプトを動かすと、次のようになります。
目次
前
次