だいぶ間があいちゃいましたが、8259割り込みコントローラーと一緒に8254タイマーICを搭載できるKZ80-IOB(I/Oボード)の新型を試作しまして、その記事を書いてみようと思います。
まんまと設計バグが見つかっている基板ですので、チャレンジャー以外には現時点ではお勧めできないわけですがw
8254プログラマブルタイマー
かつて8ビットパソコンとかPC-9801などに8253プログラマブルタイマーが搭載されておりました。またPC-8001の場合はかの有名なPCGというキャラジェネのRAM化装置に音源として8253が搭載されておりました。8254はその8253のピンコンパチでちょっとだけ改良された版のICです。なにやらカウンター読み出しコマンドがついたのと、クロックが上がっても対応できるようになったものらしいです。このICの特徴は以下の通りです。
- 3つの16ビットタイマー装備
- タイマーごとにバイナリ/BCDカウンターを設定可能
- 6種類の動作モードがある
今回は8254でないとできない!ってことは目指してないので実は8253でもOkなお題だったりしますが、KZ80シリーズに以下の機能を搭載したいというのが目標です。
- タイマー割り込みで定期的にZ80に割り込みをかけたい
- PC-8001のPCGみたいにスピーカーから音楽を奏でてみたい
KZ80-IOB 改良版の回路
KZ80-IOB の改良版で8254の回路は以下のようになっています。
- 8254プログラマブルタイマーのクロックは19.6608MHzの水晶振動子を74HC4060で1/16した1.2288MHzを使用します。
- 74HC4060の分周した出力をジャンパピンで選択して8251シリアルにも入れているのでシリアルスピードの増速(最大 76800bps)&可変対応も実施しています。
- 各チャンネルのゲート端子はプルアップしちゃってます。
- Z80 CPUのタイマー割り込みにはチャンネル#1/2でモード0を、チャンネル#3はモード3に設定してブザー的に使いたいと思いますのでゲート端子は使いません。
- チップ選択信号は74HC139を使って細かいアドレスを指定できるようにしました。いままでだとちょっと大雑把だったので….
- ここでちょっと失敗が….NOT回路を追加したくなくて、KZ80-YM2151に習ってトランジスタ(2SC1855)でNOT回路を構成したんですが、シリコントランジスタでは反応がいまいちでした…orz I/Oアドレス$80以上に設定する場合にちょっとこの回路では追従できません。トランジスタのベースのところに抵抗と並列にスピードアップコンデンサが必要でした。実験してみたところでは22pFもあれば大丈夫です。(ものの本では2200pFとか….)
- ちなみにI/Oアドレスが$7F以下の場合はスピードアップコンデンサが無くても大丈夫です。
上の写真が試作した基板と部品実装した状態です。Arduino端子をあきらめたにもかかわらずDIP部品でギュウギュウです。
タイマー割り込みの制御(イメージ)
まずはタイマー#0/1の割り込みを試してみたいと思います。そのための8254の初期化と割り込み制御プログラムを機械語モニターに仕込もうと思います。
ちなみに、タイマー割り込みはユーザープログラムでも使いたいことがあるだろうと思い割り込みフックをRAM上に持つ形にしてみました。イメージは以下になります。
ちなみにこの記事には直接関係ないですけど上図の作図はdraw.ioを使いました。無料でほぼVisioと同じ操作性、素晴らしいです。
さて….今回 通常はROM領域に配置するRST命令の飛び先のアドレスにJP命令を仕込んで一度 RAM領域($4000〜)にすっ飛ばしてから対象のROM処理ルーチンへ再度ジャンプするという仕込みを機械語モニターに実施しました。このかたちにすることで例えば8254タイマー0割り込みを横取りしてユーザーサブルーチンへジャンプするというようなことができると思ったからです。
Z80用機械語モニターの作成
上記の仕組みを仕込むZ80用機械語モニターですが、自由にソースを改造したりできる機械語モニターがほしくて自分で作り始めました。といってもシリアル入出力の部分はVintageChips氏のSBC8080データパックのソースを参考にさせていただいたので、それ以外のところを自分のほしいコマンド分作った感じです。
Githubにて公開しています。以下のURLをご覧ください。
機械語モニターの詳しい内容は別記事で紹介したいと思いますが、Dコマンドでメモリーダンプ、Mコマンドでメモリー変更、Gコマンドでプログラム実行、Iコマンド・OコマンドでIN/OUT命令を出すということで自分がほしいコマンドは一通り装備しています。
8254タイマーの初期化
さて、8254のタイマー#0/1ですがモード0(ゼロ)へと設定します。カウント設定したのち規定カウントへ到達するとOUTがLからHへと変化しますので、これを8259割り込みコントローラーへ接続することで割り込み信号として使用します。
初期化は以下のような感じです。
;
PIT_TIMER_INIT EQU 30000
;
; 8254 INIT
LD HL,PIT_TIMER_INIT
LD (TIMER_COUNT),HL
CALL PIT_INIT
:
:
:
;
;------------------------------------------------------------------------------
; PIT 8254 初期化
;------------------------------------------------------------------------------
PIT_INIT:
; COUNTER INIT
CALL PIT_C0_INIT
CALL PIT_C1_INIT
RET
;
; PIT TIMER SET
;
PIT_C0_INIT:
LD HL,(TIMER_COUNT)
LD A,00110000b
OUT (PIT_CWR),A
LD A,H
OUT (PIT_C0),A
LD A,L
OUT (PIT_C0),A
RET
;
PIT_C1_INIT:
LD HL,(TIMER_COUNT)
LD A,01110000b
OUT (PIT_CWR),A
LD A,H
OUT (PIT_C1),A
LD A,L
OUT (PIT_C1),A
RET
- コントロールワードでタイマー0/1を指定してモード0へ設定
- 続いてHLレジスタへロードしたカウンタ値をLSB、MSBの順で1バイトづつ出力する
タイマー割り込み処理ルーチン
カウントがスタートした後、カウント値に達するとタイマー割り込みが発生します。タイマー割り込みされたときのルーチンは以下のような感じで 次回の割り込みに向けて タイマー値を再設定します。
;------------------------------------------------------------------------------
; PIT 8254 割り込み処理
;------------------------------------------------------------------------------
; PIT1
INT_VECTOR4_RTN:
PUSH AF
PUSH HL
CALL PIT_C1_INIT
JR PIT_INT_EXIT
; PIT0
INT_VECTOR5_RTN:
PUSH AF
PUSH HL
CALL PIT_C0_INIT
;
PIT_INT_EXIT:
LD A,00100000b
OUT (PICRC),A
POP HL
POP AF
EI
RET
- タイマーは割り込みをかけたところで止まっているため、割り込みをかけてきたタイマーの再セットを実施
- 8259割り込みコントローラーにEOI(End of Intrrupt)を指示して割り込み処理終了し次の割り込み待ちへ
これでフックアドレス経由でタイマー割り込みを処理する環境ができました。定期的に実行したいプログラムをこのフックアドレスを捻じ曲げて挿入すると色々と楽しいプログラムが作れそうです。
次回は3つある8254タイマーのうちのチャンネル#2を使って音楽を奏でてみたいと思います。
コメント
[…] 前回の回路を見ていただけるとわかりますが、i8254プログラマブルタイマーに接続しているクロックは1228.8kHzであります。この周波数が最高値で16ビットカウンターでありますから2を指定すると1/2、3を指定すると1/3の周波数が出てきます。これを元に近い音階に周波数をテーブル化することとしました。Z80アセンブラで作成した音階テーブルは以下になります。 […]