目次

WonderKit_Z80_board2

 部屋を掃除していると、TMPZ84C011を利用したWonderKit
 の基板が出てきました。



 TMPZ84C011は、I/OにCTCと独自仕様のパラレルポートを
 内蔵したZ80シリーズですが、シリアルポートを持たない
 ので、使いやすくなっています。

 シリアルインタフェースを持たないと不便ですが
 データ交換をSPI準拠にすると、Arm、Arduino利用
 で、データ交換できます。Arm、Arduinoはシリアル
 処理とSPI処理を担当させます。



 パラレルポートは、AからEの5チャネルあり
 入出力指定とポートの2種のレジスタで制御
 できます。

 I/Oアドレスは、以下となっています。

 入出力指定

   アドレスは、以下。
    PACON EQU 54h
    PBCON EQU 55h
    PCCON EQU 56h
    PDCON EQU 34h
    PECON EQU 44h

   レジスタの該当ビットに1をライトすると出力
   0をライトすると入力になります。

   ポートのビット毎に入出力を指定できるので
   Z80PIOより使いやすいですが、割込みに対応
   していないので、入力変化はタイマー割込み
   で検出すればよいでしょう。

 ポート

   アドレスは、以下。
    PADAT EQU 50h
    PBDAT EQU 51h
    PCDAT EQU 53h
    PDDAT EQU 30h
    PEDAT EQU 40h

 基板の配線を追って、ROMとRAMの配置がどうなっている
 のか調べるのが面倒なのでファームウエアを8kバイト
 のROMに焼いてプログラムで調べることにしました。

 SRAMとして8kバイトが載せられているので
 64kバイトを8分割すると、SRAMの先頭は
 2000h、4000h、6000h、8000h、A000h、C000h
 のどれかです。スタックポインタを、SRAMの
 最終アドレスしておいて、ファームウエアが
 動けばビンゴになります。

 SRAMの先頭アドレスとスタックの関係は、以下。

    entry  stack
  2000h 4000h
  4000h 6000h
  6000h 8000h
  8000h A000h
  A000h C000h
  C000h 0000h

 組み合わせは、6種類だけなのでプログラムで
 スタックは、次のように記述してコメント記号
 の追加、削除で対応できます。

    SENT EQU 2000h
    ;SENT EQU 4000h
    ;SENT EQU 6000h
    ;SENT EQU 8000h
    ;SENT EQU 0A000h
    ;SENT EQU 0C000h
    STKT EQU SENT+2000h
    ;STKT EQU 0h

 スタックポインタの設定は、以下のようにします。

;SENT EQU 2000h
;SENT EQU 4000h
;SENT EQU 6000h
SENT EQU 8000h
;SENT EQU 0A000h
;SENT EQU 0C000h
STKT EQU SENT+2000h
;STKT EQU 0h

    org 0h
    jp  start

    org 100h
start:
    ld    sp,STKT
    call  INIT_PIO
    call  INIT_other
main:
    call  PIO_PROC
    call  CNT_OUT
    jp	  main

 パラレルポートの入出力を指定するサブルーチンを
 定義します。ポートB、Cを出力に、他を入力に設定
 すると、以下。

INIT_PIO:
    push  af
    push  bc
    ;
    ld    a,PAINIT
    ld    c,PACON
    out   (c),a
    ;
    ld    a,PBINIT
    ld    c,PBCON
    out   (c),a
    ;
    ld    a,PCINIT
    ld    c,PCCON
    out   (c),a
    ;
    ld    a,PDINIT
    ld    c,PDCON
    out   (c),a
    ;
    ld    a,PEINIT
    ld    c,PECON
    out   (c),a
    ;
    pop   bc
    pop   af
    ret

 ポートCからは、0から255の値を出力します。

CNT_OUT:
    push  af
    push  bc
    ; set PORT number
    ld    c,PCDAT
    ; get counter
    ld    a,l
    ; inverse
    cpl
    ; output
    out   (c),a
    ; counter increment
    inc   hl
    ;
    pop   bc
    pop   af
    ret

 ポートCへの出力は、カウンタを用意して初期化
 しておきます。HLレジスタペアを、カウンタに
 使います。

INIT_other:
    push  af
    push  bc
    ;
    xor   a
    ld    h,a
    ld    l,a
    ;
    pop   bc
    pop   af
    ret

 ポートAの入力値を反転し、ポートBへ出力する
 処理で、2ポートのテストをします。

PIO_PROC:
    push  af
    push  bc
    ;
    ld    c,PADAT
    ld    b,PBDAT
    ; get data
    in    a,(c)
    cpl
    ; put data
    ld    c,b
    out   (c),a
    ;
    pop   bc
    pop   af
    ret

 まとめます。

SENT EQU 8000h
;SENT EQU 4000h
;SENT EQU 6000h
;SENT EQU 8000h
;SENT EQU 0A000h
;SENT EQU 0C000h
STKT EQU SENT+2000h

PACON EQU 54h
PBCON EQU 55h
PCCON EQU 56h
PDCON EQU 34h
PECON EQU 44h

PADAT EQU 50h
PBDAT EQU 51h
PCDAT EQU 53h
PDDAT EQU 30h
PEDAT EQU 40h

PAINIT EQU 00h  ; inputs
PBINIT EQU 0ffh ; outputs
PCINIT EQU 0ffh ; outputs
PDINIT EQU 00h  ; inputs
PEINIT EQU 00h  ; inputs

    org 0h
    jp  start

    org 100h
start:
    ld    sp,STKT
    call  INIT_PIO
    call  INIT_other
main:
    call  PIO_PROC
    call  CNT_OUT
    call  SWAIT
    jp	  main

;*****************************
INIT_PIO:
    push  af
    push  bc
    ;
    ld    a,PAINIT
    ld    c,PACON
    out   (c),a
    ;
    ld    a,PBINIT
    ld    c,PBCON
    out   (c),a
    ;
    ld    a,PCINIT
    ld    c,PCCON
    out   (c),a
    ;
    ld    a,PDINIT
    ld    c,PDCON
    out   (c),a
    ;
    ld    a,PEINIT
    ld    c,PECON
    out   (c),a
    ;
    pop   bc
    pop   af
    ret

INIT_other:
    push  af
    push  bc
    ;
    xor   a
    ld    h,a
    ld    l,a
    ;
    pop   bc
    pop   af
    ret

PIO_PROC:
    push  af
    push  bc
    ;
    ld    c,PADAT
    ld    b,PBDAT
    ; get data
    in    a,(c)
    cpl
    ; put data
    ld    c,b
    out   (c),a
    ;
    pop   bc
    pop   af
    ret

CNT_OUT:
    push  af
    push  bc
    ; set PORT number
    ld    c,PCDAT
    ; get counter
    ld    a,l
    ; inverse
    cpl
    ; output
    out   (c),a
    ; counter increment
    inc   hl
    ;
    pop   bc
    pop   af
    ret

SWAIT:
    push  af
    push  bc
    ;
    ld    c,255
SWAITL:
    ; set counter
    ld    b,255
SWAITLP:
    nop
    ; b <= b - 1
    djnz  SWAITLP
    ;
    dec   c
    ld    a,c
    ;
    cp    0
    jr    nz,SWAITL
    ;
    pop   bc
    pop   af
    ret

    end

 基本となる調査用プログラムを作成したなら
 I/Oのピンアサインを導通チェッカーで調べ
 ました。

 50ピンのコネクタのピンアサインは、以下。

 1 GND       2 CLK/TRG3
 3 CLK/TRG2  4 CLK/TRG1
 5 CLK/TRG0  6 ZC/TO0
 7 ZC/TO1    8 ZC/TO2 
 9 PD0      10 PD1
11 PD2      12 PD3
13 PD4      14 PD5
15 PD6      16 PD7
17 PE0      18 PE1
19 PE2      20 PE3
21 PE4      22 PE5
23 PE6      24 PE7
25 PB0      26 PB1
27 PB2      28 PB3
29 PB4      30 PB5
31 PB6      32 PB7
33 PA0      34 PA1
35 PA2      36 PA3
37 PA4      38 PA5
39 PA6      40 PA7
41 PC0      42 PC1
43 PC2      44 PC3
45 PC4      46 PC5
47 PC6      48 PC7
49 Vcc      50 GND

 コネクタには、CTCとPIOの信号線が接続されています。
 この信号線配置は、チップの信号ピンの並びで決められ
 ているようです。



 10ピンケーブルワイヤーを使えば、PIOCを利用する
 プログラムのテストができます。

 電源ピンは、次のようになっていました。

1 Vcc
2 <74HC14 1pin>
3 GND
4 Vcc
5 GND

 電源は5ピンですが、2ピンはシュミットインバータ
 74HC14の1ピンに接続されています。同時に、正電圧
 に抵抗を介して接続されています。

 2ピンをVccに接続すると、基板上にあるアドレスデコーダが
 イネーブルになります。GNDに接続するとアドレスでコーダを
 ディセーブルにするため、基板上のメモリを使うか、外付け
 のメモリを利用するかを指定できます。

 外部にメモリを接続することを考えているのか
 30ピンコネクタがあります。
 導通チェッカーで確認できた信号は、以下です。

 1 GND      2 GND
 3 Vcc      4 nRESET
 5 nIORQ    6 nMREQ
 7 nWR      8 nRD
 9 A7      10 A6
11 A5      12 A4
13 A3      14 A2
15 A1      16 A0
17 D7      18 D6
19 D5      20 D4
21 D3      22 D2
23 D1      24 D0
25 nINT    26 E1
27 nBUSREQ 28
29         30 nRFSH

 リフレッシュ信号があり、アドレスが8ピンしか
 出ていないので、外部にはDRAMを接続することを
 考えていたのかも知れません。

 タイマー、PIO以外のI/Oであるシリアルインタフェース
 を拡張することを考えたピンアサインの様に見えます。

 後で、26ピンのフラットケーブルを利用し、CTC、PIOでは
 ないI/Oを、拡張基板に半田付けしてみます。

 I/O部分がわかったので、64kバイトのメモリ空間をどう
 扱っているのか、アドレスデコーダを回路図にしました。



 アドレスデコーダから、メモリは0hからROM、8000hからRAM
 が配置されていることがわかります。

 8kバイトのSRAMが実装されていたので、スタックポインタは
 次のように設定すればよいと理解できます。

SENT EQU 8000h
STKT EQU SENT+2000h

    org 0h
    jp  start

    org 100h
start:
    ld    sp,STKT

 メモリ配置がわかったので、CTCのテストをします。

 CTCは、ZC/TO0、ZC/TO1、ZC/TO2の3出力に異なる
 周波数のクロックを出力してチェックします。

 CPUが利用しているクロックが4MHzなので、分周して
 次のクロックを出力します。

 ZC/TO0 prescaler:1/16  time constant:256 (976Hz)
 ZC/TO1 prescaler:1/256 time constant:128 (122Hz)
 ZC/TO2 prescaler:1/256 time constant:256 (61Hz)

 CTCはカウンタモード、タイマーモードが選べますが
 タイマーモードを利用します。

 CTCの制御レジスタには、次の項目を指定します。

 CTCの各チャネルの初期化は、割込みを利用しないときには
 次の手順になっています。

 制御ワードライト
 時間定数ライト

 この動作をチャネル回数だけ繰り返せばよいので、制御ワードと
 時間定数を定義して、OUT命令で書き出します。

 CTCのI/Oアドレスは、Z84C015と同じなので、次のように定義。

  CTC_C0 EQU 10H
  CTC_C1 EQU 11H
  CTC_C2 EQU 12H
  CTC_C3 EQU 13H

 チャネル0から2までの制御ワードと時間定数を定義します。

  C0INI  EQU 15H ; 00010101
  C0TCNT EQU 00H ; 00000000(256)
  C1INI  EQU 35H ; 00110101
  C1TCNT EQU 80H ; 10000000(128)
  C2INI  EQU 35H ; 00110101
  C2TCNT EQU 00H ; 00000000(256)

 初期化したなら自動でタイマーが動くので、ダイナミック
 ストップで動作を確認できるようにします。

SENT EQU 8000h
STKT EQU SENT+2000h

; CTC I/O address
CTC_C0 EQU 10H
CTC_C1 EQU 11H
CTC_C2 EQU 12H
CTC_C3 EQU 13H

; CTC control word and time constant
C0INI  EQU 17H ; 00010111
C0TCNT EQU 00H ; 00000000(256)
C1INI  EQU 37H ; 00110111
C1TCNT EQU 80H ; 10000000(128)
C2INI  EQU 37H ; 00110111
C2TCNT EQU 00H ; 00000000(256)

    org 0h
    ld  sp,STKT
    jp  start

    org 100h
start:
    call  INIT_CTC

main:
    ;
    nop
    ;
    jp	  main

;*****************************
INIT_CTC:
    push  af
    push  bc
    ; CTC channel 0
    ld    a,C0INI
    ld    c,CTC_C0
    out   (c),a
    ld    a,C0TCNT
    out   (c),a
    ; CTC channel 1
    ld    a,C1INI
    ld    c,CTC_C1
    out   (c),a
    ld    a,C1TCNT
    out   (c),a
    ; CTC channel 2
    ld    a,C2INI
    ld    c,CTC_C2
    out   (c),a
    ld    a,C2TCNT
    out   (c),a
    ;
    pop   bc
    pop   af
    ret

    end

 Z80CTC、Z80PIOのテスト用プログラムを作成したので
 信号を引き出すための基板を半田付けしました。



 50ピンフラットケーブルを利用してZ80基板と接続します。

 Z80基板側



 コネクタ基板側



 Z80CTCのコネクタは、次のように信号を配置。

 1 Vcc
 2 (no signal)
 3 CLK/TRG0
 4 ZC/TO0
 5 CLK/TRG1
 6 ZC/TO1
 7 CLK/TRG2
 8 ZC/TO2
 9 CLK/TRG3
10 GND

 チャネル0から2は、利用する信号がピンの
 上下に並ぶ配置にしています。

 PIOの配置は、以下。

 1 Vcc  1 Vcc  1 Vcc  1 Vcc  1 Vcc
 2 PA7  2 PB7  2 PC7  2 PD7  2 PE7
 3 PA6  3 PB6  3 PC6  3 PD6  3 PE6
 4 PA5  4 PB5  4 PC5  4 PD5  4 PE5
 5 PA4  5 PB4  5 PC4  5 PD4  5 PE4
 6 PA3  6 PB3  6 PC3  6 PD3  6 PE3
 7 PA2  7 PB2  7 PC2  7 PD2  7 PE2
 8 PA1  8 PB1  8 PC1  8 PD1  8 PE1
 9 PA0  9 PB0  9 PC0  9 PD0  9 PE0
10 GND 10 GND 10 GND 10 GND 10 GND

 Z80CTCの動作テスト用に、27C256にファームウエアを
 書き込んで、接続します。



 ゼロプレッシャソケットは、何度もROMを交換するので
 ROMの足を痛めないために使っています。ROMは、27C256
 を利用しました。

 ファームウエアは、分周したクロックを出力する仕様
 なので、マルチメータについている周波数カウンタで
 周波数を測定します。



 周波数カウンタの表示をみる限りでは、プログラム通り
 に、動作しているとわかります。端数があるはずですが
 1Hzの精度が出ていれば、充分でしょう。

 周波数カウンタの他に、圧電スピーカを利用しても
 Z80CTCの動作を確認できます。



 写真のスピーカは、¥100ショップで入手してきた
 スピーカです。インピーダンスが4Ωから32Ωくらい
 ですから、アンプをつけないと音が小さいです。

 1kHz前後なら、クリップワイヤーでZ80CTC出力を直接
 接続して、微かに聞こえます。

 今度は、PIOの動作テストをしてみます。
 スイッチとLEDを載せた基板を利用します。



 CPLD/FPGAで利用しているI/O基板ですが
 コネクタ仕様をあわせているので、流用
 できます。

 PIOのテストは、ポートAを入力、他のポートを出力にし
 ポートAの状態をポートBに反映させます。他のポート
 は、内部カウンタの値を編集して出力します。

SENT EQU 8000h
STKT EQU SENT+2000h

PACON EQU 54h
PBCON EQU 55h
PCCON EQU 56h
PDCON EQU 34h
PECON EQU 44h

PADAT EQU 50h
PBDAT EQU 51h
PCDAT EQU 53h
PDDAT EQU 30h
PEDAT EQU 40h

    org 0h
    ; set stack pointer
    ld  sp,STKT
    jp  start

    org 100h
start:
    ; initialize PIO
    call  INIT_PIO
    ; clear counter
    ld    b,0
    ; endless loop
main:
    ; get data
    in    a,(PADAT)
    ; inverse
    cpl
    ; put data
    out   (PBDAT),a
    ; counter handling
    ld    a,b
    out   (PCDAT),a
    xor   0ffh
    out   (PDDAT),a
    xor   0ffh
    out   (PDDAT),a
    ; counter increment
    inc   b
    ; delay
    call  SWAIT
    call  SWAIT
    call  SWAIT
    call  SWAIT
    call  SWAIT
    ;
    jr	  main

;*******************
INIT_PIO:
    ; PIOA (input)
    xor   a
    out   (PACON),a
    ; PIOB (output)
    or    0ffh
    out   (PBCON),a
    ; PIOC , PIOD , PIOE (output)
    out   (PCCON),a
    out   (PDCON),a
    out   (PECON),a
    ; initialize data
    xor   0fh
    out   (PCCON),a
    xor   0ffh
    out   (PDCON),a
    xor   0ffh
    out   (PECON),a
    ;
    ret

SWAIT:
    ; store
    push  bc
    ;
    ld    b,255
SWAITP:
    nop
    djnz  SWAITP
    ; resume
    pop   bc
    ret

    end

 CPUを時間待ちでループさせるのは無駄なので
 タイマー割込みを利用して、指定時間の経過
 後、イベントフラグで通知するのが定石。

 PIOに接続したLEDを、タイマー割込みにより
 点滅させてみます。

 Z80CTCは、モード2割込みに対応しているので
 このモードを使います。

 モード2割込みでは、割込み処理ルーチン(割込みハンドラ)の
 エントリーアドレスが入っているメモリブロックの上位8ビット
 をレジスタIに入れます。下位8ビットは、ペリフェラルが規定
 するので、ペリフェラルの初期化時に指定します。

 Z80CTCのチャネル1、3が割込みを発生させるとして
 割込みハンドラを定義します。

    org   1000h
THANDLE:
    ; store
    push  af
    ; load 
    ld    a,(XFLAGS)
    ; set 2^0 bit
    set   0,a
	; store
    ld    (XFLAGS),a
    ; resume
    pop   af
    ; enable interrupt
    ei
    ;
    reti

    org   1020h
THANDLE2:
    ; store
    push  af
    ; load 
    ld    a,(XFLAGS)
    ; set 2^1 bit
    set   1,a
	; store
    ld    (XFLAGS),a
    ; resume
    pop   af
    ; enable interrupt
    ei
    ;
    reti

 チャネル1、3の割込みハンドラのエントリーアドレスは
 orgで指定されたので、割込みベクターは、次のように確定
 します。

    dw  0000h  ; CTC_C0
    dw  1000h  ; CTC_C1
    dw  0000h  ; CTC_C2
    dw  1020h  ; CTC_C3

 これらの割込みベクターが、どこにあるのかZ80が把握して
 おかないと割込みが発生したとき、どこのアドレスからの
 コードを実行すればよいか戸惑うことになります。

 レジスタIで上位8ビットを指定し、下位8ビットを
 ペリフェラルに出力させます。

 今回は、0080hに割込みベクターをおくことにします。
 メモリの内容は、次のように確定します。

    org 80h
    dw  0000h  ; CTC_C0
    dw  1000h  ; CTC_C1
    dw  0000h  ; CTC_C2
    dw  1020h  ; CTC_C3

 レジスタIは上位8ビットが0なので、次のように指定。

    xor   a
    ld    i,a

 レジスタIに直接値を代入するのは、システム暴走を
 発生させることがあるので、レジスタAを経由します。

 Z80CTCは、割込みベクターをチャネル0から書込みます。

    ld    a,80h
    out   (CTC_C0),a

 割込み関連初期設定が終われば、タイマー割込みの周期を
 考えておきます。

 チャネル1 周期4Hzで割込み発生
 チャネル3 周期8Hzで割込み発生

 この条件で、Z80CTCの初期化は以下。

INIT_CTC:
    ; store
    push  af
    push  bc
    ; set vector address
    ld    a,80h
    out   (CTC_C0),a
    ; channel 0 (timer mode)
    ld    a,17h
    out   (CTC_C0),a
    ; channel 0 (time constant)
    ; generate 1kHz
    ld    a,250
    out   (CTC_C0),a
    ; channel 1 (counter mode)
    ld    a,0d7h
    out   (CTC_C1),a
    ; channel 1 (time constant)
    ; generate 4Hz 
    ; since connected between ZC/TO0 and CLK/TRG1
    ld    a,250
    out   (CTC_C1),a
    ; channel 2 (timer mode)
    ld    a,17h
    out   (CTC_C2),a
    ; channel 2 (time constant)
    ; generate 2kHz
    ld    a,125
    ; channel 3 (counter mode)
    ld    a,0d7h
    out   (CTC_C3),a
    ; channel 3 (time constant)
    ; generate 8Hz
    ; since connected between ZC/TO2 and CLK/TRG3
    ld    a,250
    out   (CTC_C3),a
    ; resume
    pop   bc
    pop   af
    ret

 4MHzをタイマーモードのプリスケーラで1/16と250kHzを生成。

 分周器で250分周で1kHzのクロックを生成。このクロックは
 チャネル0、2で生成します。各ZC/TOをチャネル1、3の
 CLK/TRGに接続します。
 チャネル1、3をカウンタモードで動かし、1kHzを250、125
 分周して4Hz、8Hz周期のタイマー割込みを発生させます。



 割込み発生をイベントフラグで通知するように割込みハンドラを定義済み。

 イベントフラグを、割込みハンドラと通知を受ける
 ルーチンのどちらからでもアクセスできるように
 グローバル変数としておきます。

 フラグは、論理値をセット、クリアするのでSRAM領域に
 配置します。

    org 8000h
XFLAGS	DS 1
TSTAT	DS 1
VSTAT	DS 1

 タイマー割込みは、2種あるので4HzはXFLAGSの0ビット目
 8HzはXFLAGSの1ビット目を使います。

 タイマー割込みハンドラでは、フラグのセットだけ
 通知を受けるルーチンでは、リセットだけと規約を
 決めて使います。

 イベントフラグの通知がきたとき、どうするかを定義。

main:
    ; load flags
    ld    a,(XFLAGS)
    ; judge timer interrupt
    bit   0,a
    jr    z,main1
    ; clear 2^0 bit
    res   0,a
    ld    (XFLAGS),a
    ; impress
    call  IPROC
main1:
    ; load flags
    ld    a,(XFLAGS)
    ; judge timer interrupt
    bit   1,a
    jr    z,main2
    ; clear 2^1 bit
    res   1,a
    ld    (XFLAGS),a
    ; impress
    call  IPROCX
main2:
    ;
    jr	  main

 イベントフラグがセットされていたなら、リセットして
 対応するサブルーチンに処理を一任。

 タイマー割込み発生で仕事をするサブルーチンは、以下。

IPROC:
	; store
    push  af
    ; get now 
    ld    a,(VSTAT)
    ; inverse
    xor   a
    ; store
    ld    (VSTAT),a
    ; impress
    out  (PCDAT),a
    ; resume
    pop   af
	;
    ret

IPROCX:
	; store
    push  af
    ; get now 
    ld    a,(TSTAT)
	; inverse
    xor   a
    ; store
    ld    (TSTAT),a
    ; impress
    out   (PEDAT),a
    ; resume
    pop   af
	;
    ret

 4Hzのタイマー割込みでは、レジスタの値を反転するので
 8個のLEDが同時に点灯、消灯になります。



 8Hzのタイマー割込みでも、8個のLEDが同時に点灯、消灯と
 なります。タイマーの割込み周期の違いで、ブリンク周期が
 異なります。



 レジスタの初期化とPIOの設定などを含めて、まとめると
 アセンブリ言語のソースコードは以下となります。

SENT EQU 8000h
STKT EQU SENT+2000h

; CTC I/O address
CTC_C0 EQU 10H
CTC_C1 EQU 11H
CTC_C2 EQU 12H
CTC_C3 EQU 13H

; PIO I/O address
PACON EQU 54h
PBCON EQU 55h
PCCON EQU 56h
PDCON EQU 34h
PECON EQU 44h

PADAT EQU 50h
PBDAT EQU 51h
PCDAT EQU 53h
PDDAT EQU 30h
PEDAT EQU 40h

;***********************
; reset entry
;***********************
    org 0h
    ld  sp,STKT
    jp  start

;++++++++++++++++++
; interrupt vector
;++++++++++++++++++
    org 80h
    dw  0000h  ; CTC_C0
    dw  1000h  ; CTC_C1
    dw  0000h  ; CTC_C2
    dw  1020h  ; CTC_C3

;========================
    org 100h
start:
    ;
    call  INIT_CTC
    call  INIT_PIO
    ; set interrupt entry address
    xor   a
    ld    i,a
    ; set MODE 2 interrupt
    im    2
    ; clear flags
    xor   a
    ld    (XFLAGS),a
    ; enable interrupt
    ei
    ; endless loop
main:
    ; load flags
    ld    a,(XFLAGS)
    ; judge timer interrupt
    bit   0,a
    jr    z,main1
    ; clear 2^0 bit
    res   0,a
    ld    (XFLAGS),a
    ; impress
    call  IPROC
main1:
    ; load flags
    ld    a,(XFLAGS)
    ; judge timer interrupt
    bit   1,a
    jr    z,main2
    ; clear 2^1 bit
    res   1,a
    ld    (XFLAGS),a
    ; impress
    call  IPROCX
main2:
    ;
    jr	  main

;*******************
INIT_CTC:
    ; store
    push  af
    push  bc
    ; set vector address
    ld    a,80h
    out   (CTC_C0),a
    ; channel 0 (timer mode)
    ld    a,17h
    out   (CTC_C0),a
    ; channel 0 (time constant)
    ; generate 1kHz
    ld    a,250
    out   (CTC_C0),a
    ; channel 1 (counter mode)
    ld    a,0d7h
    out   (CTC_C1),a
    ; channel 1 (time constant)
    ; generate 4Hz 
    ; since connected between ZC/TO0 and CLK/TRG1
    ld    a,250
    out   (CTC_C1),a
    ; channel 2 (timer mode)
    ld    a,17h
    out   (CTC_C2),a
    ; channel 2 (time constant)
    ; generate 2kHz
    ld    a,125
    ; channel 3 (counter mode)
    ld    a,0d7h
    out   (CTC_C3),a
    ; channel 3 (time constant)
    ; generate 8Hz
    ; since connected between ZC/TO2 and CLK/TRG3
    ld    a,250
    out   (CTC_C3),a
    ; resume
    pop   bc
    pop   af
    ret

INIT_PIO:
    ; store
    push  af
    ; PIOA (input mode)
    xor   a
    out   (PACON),a
    ; PIOB (output mode)
    or    0ffh
    out   (PBCON),a
    ; PIOC , PIOD , PIOE (output mode)
    out   (PCCON),a
    out   (PDCON),a
    out   (PECON),a
    ; initialize data
    xor   00fh
    out   (PCCON),a
    xor   0ffh
    out   (PDCON),a
    xor   0ffh
    out   (PECON),a
    ; resume
    pop   af
    ret

IPROC:
	; store
	push  af
	; get now 
	ld    a,(VSTAT)
	; inverse
	xor   a
	; store
	ld    (VSTAT),a
	; impress
	out   (PCDAT),a
	; resume
	pop   af
	;
	ret

IPROCX:
	; store
	push  af
	; get now 
	ld    a,(TSTAT)
	; inverse
	xor    a
	; store
	ld    (TSTAT),a
	; impress
	out   (PEDAT),a
	; resume
	pop   af
	;
	ret

;********************
; interrupt handler
;********************
    org   1000h
THANDLE:
    ; store
    push  af
    ; load flag
    ld    a,(XFLAGS)
    ; set flag
    set   0,a
    ; store flag
    ld    (XFLAGS),a
    ; resume
    pop   af
    ; enable interrupt
    ei
    ;
    reti

    org   1020h
THANDLE2:
    ; store
    push  af
    ; load flag
    ld    a,(XFLAGS)
    ; set flag
    set   1,a
    ; store flag
    ld    (XFLAGS),a
    ; resume
    pop   af
    ; enable interrupt
    ei
    ;
    reti

;*******************
; data area in SRAM 
;*******************
    org 8000h
XFLAGS	DS	1
TSTAT	DS	1
VSTAT	DS	1

    end


目次

inserted by FC2 system