Z80 PIOで割り込みの実験

KZ80-ZilogI/O

だいぶ昔に作ったKZ80_ZailogIO(Z80 PIO/SIOボード)ですが、シリアルLSIのSIOは割り込み駆動で動作させたことがありましたが、パラレルLSI PIOでは割り込み駆動を試したことがありませんでした。
Twitter(現 X)でZ80 PIOの割り込みを試されている方がいらっしゃったので、私もやってみました。

まずは、動作環境をつくる..

まずはZ80 PIOが動く動作環境をつくります。
以前構築した Z80マイコン基板の組み合わせは、もうバラしてしまったので必要な基板を部品箱から発掘するところからです。

Z80 PIOの割り込みを確認するために準備した基板

Z80 PIOの割り込みを確認するために準備した基板は、KZ80_ZailogIO(Z80 PIO/SIOボード)を始めとして以下の通りです。

基板名説明
KZ80-CPUB REV3CPUボードです。REV3が必須です。(Z80専用信号線ポートが必要なため)
KZ80-ZilogIOZ80 PIO/SIOが載ったI/Oボードです。
KZ80-1MSRAMZ80マイコン用メモリーボード

これの基板をフラットケーブルや、Tomi9さんのSBC-BUSなどで組み合わせて使用します。

40Pフラットケーブルで基板同士をつなぐのもよいのですが、SBC-BUSを使うと気軽に実験できて便利です。

SBC-BUS Rev02
オレンジピコさんにて販売をしていただきましたのでお知らせします。商品ページはこちらです。 SBCルーズキットを…

機械語モニターはUniversal Monitor

プログラムをロードして動作確認したり、メモリーダンプ、I/Oポートの操作などが便利に行える機械語モニターをROMに焼く必要があります。
今回も、いつもお世話になってます Universal Monitorを使用しました。

Universal Monitor | Electrelic
Universal Monitor はさまざまなプロセッサで同一操作のモニタを使いたいということから生まれました。 元になっているのは以前 Z80 用に書いたモニタで、次々に移植して現在のところ MC6800, MC6809, 8080, Z8, Z80, 6502, SC/MP, TLCS-90, MN1610, T...

Z80をはじめとして、いろいろなプロセッサに対応している素敵な機械語モニターです。
Z80 SIOのデバイス対応ソースもついてますので、ビルドしてROMへ焼きます。

config.incの変更(Z80 SIOソースの有効化とI/Oアドレス変更)

Universal Monitorのダウンロードページからソースコードを入手して展開し、Z80のフォルダにある config.inc にある以下の”USE_DEV_Z80SIO”を”1″に設定してZ80 SIO対応のソースが展開されるようにします。また、SIOのI/OアドレスをKZ80-ZilogIOに合わせて以下のように変更します。

;;;
;;; Zilog Z80 SIO
;;;
	
USE_DEV_Z80SIO = 1
	IF USE_DEV_Z80SIO
SIOAD:	equ	00H		; 
SIOAC:	equ	02H		;
SIOBD:	equ	01H		; (Ch.B not supported)
SIOBC:	equ	03H		; (Ch.B not supported)

別のシリアルLSIの設定は全部 “0” にして展開されないようにしてください。

Universal Monitorのビルド

Universal Monitorのビルドは、The Macroassembler AS(http://john.ccac.rwth-aachen.de:8000/as/)を使います。以下のビルドのページを参考にアセンブラとかmakeを準備してください。Windows環境でもビルドできますがWSL2とかMacとかUNIXな環境がお勧めです。わたしはWSL2のubuntuでビルドしました。

ビルド | Electrelic
目的のシステムに合わせたUniversal Monitorのビルド方法です。 はじめにビルドに必要な環境・ツールについてです。 アセンブラ さまざまなプロセッサに対応しているという理由でThe Macroassembler AS()を使用しています。 あまり古いものですとMN1610に非対応だったり、前方参照の不具合報...
$ make
Makefile:23: warning: ignoring prerequisites on suffix rule definition
Makefile:23: warning: ignoring prerequisites on suffix rule definition
asl -L unimon_z80.asm
Assembling unimon_z80.asm
PASS 1
config.inc(137)
common.inc(19)
dev_z80sio.asm(72)
unimon_z80.asm(2058)
PASS 2
config.inc(137)
common.inc(19)
dev_z80sio.asm(72)
unimon_z80.asm(2058)

0.94 seconds assembly time

   2286 lines source file
      2 passes
      0 errors
      0 warnings
p2bin unimon_z80.p unimon_z80.bin
Deduced address range: 0x00000000-0x00000D0F
unimon_z80.p==>>unimon_z80.bin  (3122 Bytes)
rm unimon_z80.p
$

Z80のソースのMakefileがあるディレクトリで、”make”を実行すると上記のように “unimon_z80.bin”がビルドされます。これを TL-866IIなどの ROMライタで28C256型のROMに焼いてください。

Amazon.co.jp: ユニバーサル低消費電力USBプログラマー、USB AVRプログラマー、EEPROM向けUSBプログラマーTL866II Plus : パソコン・周辺機器
Amazon.co.jp: ユニバーサル低消費電力USBプログラマー、USB AVRプログラマー、EEPROM向けUSBプログラマーTL866II Plus : パソコン・周辺機器

TL866II ROMライターで焼く場合のロード方法は上記のような感じで、BINARY形式でアドレスは0000番地から素直にロードすればOKです。

Z80マイコン(Z80 SIO経由)の稼働確認

Universal MonitorのROMをKZ80-1MSRAM(メモリーボード)へ装着して、Z80 CPUボード(KZ80-CPUB)、I/Oボード(KZ80-ZilogIO)、メモリーボード(KZ80-1MSRAM)の3枚の基板を40PフラットケーブルまたはSBC-BUSで連結し、CPUボードとI/Oボードの間のZ80専用信号線を14Pフラットケーブルで接続します。
5VのACアダプタ電源をつなぎ、I/Oボードのシリアル端子にFTDI型 シリアルケーブルを接続します。シリアルケーブルの先はパソコンにつなぎTeraTERMを準備します。

TeraTermの速度設定

TeraTERMのシリアルの設定ですが、通信速度を76800bpsに設定してください。
KZ80-ZilogIOの設定にもよりますが、74HC4060でZ80 SIOのシリアルクロックを生成している場合1228.8kHzが入力されてまして、Universal MonitorのZ80 SIO初期設定で1/16に設定されてますので76.8kHzで通信することになります。

電源ON! と 起動メッセージ確認

TeraTermの通信速度設定ができたらZ80マイコンの電源をONにすると、以下のような起動メッセージが出るはずです。

これで、ユーザープログラムをロードしたり、任意のメモリーを参照更新したりする環境が整いました。

割り込み駆動テストのためのハードウェア

パラレルLSI(Z80 PIO)の割り込み駆動の確認のため、I/Oボード(KZ80-ZilogIO)の入出力ポートにつなぐ入力基板と出力確認基板を準備します。

出力確認基板(Aポートにつなぐ)

出力装置は、だいぶ以前から使っているLEDを8つ並べた装置を使います。KZ80-ZilogIOとかKZ80-IOBなどで動作確認用に使ってきた基板です。2×7 MILコネクタがついた14PフラットケーブルでI/OボードのAポートにつなぎます。

以前こちらで紹介しています。各データ信号にLEDと抵抗器がつながっていて、信号が L レベルの時にLEDが光ります。

入力確認基板(Bポートにつなぐ)

割り込み駆動確認のためには、入力確認するためのスイッチが必要です。ちょっと雑ですが、DIP SWをI/Oポートにつなぐため以下のような回路を考えました。

8本の信号線を抵抗器でプルアップして、DIP SWがONになるとGNDに落ちる仕組みです。見た目としてはこんな感じで、ピアノ型DIP SWを使用しました。地元の部品店がまだ在庫が豊富な頃に買ったやつだと思いますがOMRONって書いてますね。国産です。

ちなみに上記 回路図とか、これから述べるテストプログラム類はgithubの以下のURLをご覧ください。

KZ80_PIOSIO/PIO_TEST at master · kuninet/KZ80_PIOSIO
KZ80シリーズ PIO/SIO2/CTCボード. Contribute to kuninet/KZ80_PIOSIO development by creating an account on GitHub.

Universal MonitorのI/O命令で動作確認

割り込み駆動確認テストプログラムで確認する前に、LEDガちゃんと光るか、DIP SWの状態が読み取れるかを確認したいところです。
そんなときには、Universal MonitorのI/O命令(I命令、O命令)が便利です。I/Oポートを指定して16進数のデータを出力したり、特定のI/Oポートのデータを入力して表示したりできます。

Z80 PIOの初期化コマンドを以下のように打鍵することで、入出力装置の稼働確認ができますので、ぜひ活用してみてください。

割り込み駆動確認テストプログラム

プログラムの仕様について

割り込み駆動確認プログラムですが、以下のような仕様のものをつくりました。

  • I/OボードのBポートのDIS SWをONにすると、Z80 PIOの割り込みが発動。
  • 割り込み処理ではBポートの値を読み取ってAポートのLED列へ出力する。
  • BポートのDISP SWが全部OFF(16進数の値だと0FFH)だったらプログラム終了。

テストプログラムの所在

割り込み稼働確認テストプログラムは、以下のgithubにUPしてます。

KZ80_PIOSIO/PIO_TEST/src/int_test.asm at master · kuninet/KZ80_PIOSIO
KZ80シリーズ PIO/SIO2/CTCボード. Contribute to kuninet/KZ80_PIOSIO development by creating an account on GitHub.

続けて….プログラムでやってること記載していこうと思います。

Z80 PIOのI/Oアドレス

今回使用したI/Oボード(KZ80-ZilogIO)のZ80 PIOのI/Oアドレスですが、SIOと同様不連続な感じになってますので注意してください。

PIO_A_DATA  EQU 08H
PIO_B_DATA  EQU 09H
PIO_A_CTL   EQU 0AH
PIO_B_CTL   EQU 0BH

メインプログラムでやってること

メインプログラムでは、Z80のモード2 割り込みの設定を実施してます。Z80 モード2割り込み処理のアドレスが入っている割り込みベクタテーブルは 4100h番地に配置したので、I(割り込み)レジスタにベクタテーブルアドレスの上位バイト041hをセットします。

その後、PIOの初期設定を実施したのち、現在のポートBにある入力装置のbit状態を読みだして、ポートAのLEDをそれにあわせて初期状態として光らせます。

ポートBにある入力装置が 全部OFF(IN命令で読みだした値が0FFH)になったらプログラムを終了します。

MAIN:
;   04100H番地に割り込みベクタテーブルセット
    LD  A,041H
    LD  I,A
    IM 2    ; モード2割り込み

;   割り込み回数カウンタ初期化(デバグ用))
    LD  A,0
    LD  (INT_CNT),A

;  PIO-Aを出力、PIO-Bをモード3(ビットモード)&割り込み設定

    CALL PIO_INIT

;   ポートBのスイッチの状態を、ポートAのLEDヘ
    IN A,(PIO_B_DATA)
    OUT (PIO_A_DATA),A
    EI                  ;割り込み許可

;
.loop
    CALL WAIT
;
    IN A,(PIO_B_DATA)
    CP 0FFH
    JP  NZ,MAIN.loop
    RET
;
; 割り込みベクタテーブル
;
    org 04100H
INT_VECT_TB:
PIO_B_VECT  DW  PBINT

Z80 PIOの初期化処理

Z80 PIOの初期化処理は以下のとおりで、PIOのAポート、Bポートそれぞれの初期化コマンドを定数として持っておいて、まとめてZ80のOTIR命令で連続出力しています。

;
; PIO初期化
;
PIO_INIT:
    LD  HL,PIO_A_CMD
    LD  B,PIO_A_CMD_LEN
    LD  C,PIO_A_CTL
    OTIR
;
    LD  HL,PIO_B_CMD
    LD  B,PIO_B_CMD_LEN
    LD  C,PIO_B_CTL
    OTIR
;
    RET

;
; PIO-A初期化コマンド
;
PIO_A_CMD:
    DB  00FH    ; モード0
    DB  007H    ; 割り込みコントロール(割り込みなし)
PIO_A_CMD_LEN   EQU $-PIO_A_CMD

;
; PIO-B初期化コマンド
;
PIO_B_CMD:
    DB  0CFH    ; モード3
    DB  0FFH    ; データディレクション(すべて入力)
    DB  097H    ; 割り込みコントロール
    DB  000H    ; 割り込みマスク
    DB  PIO_B_VECT-INT_VECT_TB ; 割り込みベクタ位置
PIO_B_CMD_LEN   EQU $-PIO_B_CMD

ポートAはLEDをつないでるだけなので、出力モード(モード0)にして 割り込み設定は無し。ポートBはビット制御モード(モード3)に設定し 全ビット入力に設定、割り込みがLレベルの際にどのビットが変化しても割り込みが来るようにして割り込みマスクも全ビットの変化をキャッチするようにします。

割り込み処理ルーチン

割り込みがかかった時のルーチンは以下のようになってます。

;
; Port-B割り込みルーチン
;
PBINT:
    DI
;
    LD  A,(INT_CNT)
    INC A
    LD  (INT_CNT),A
;
    CALL WAIT
    IN A,(PIO_B_DATA)
    OUT (PIO_A_DATA),A
    EI                  ;再割り込み許可
    RETI

ポートBの入力装置のどこかのスイッチがONとなり、対象ビットがLレベルに落ちて割り込みが発動したら、割り込み禁止命令を出して、メモリーに持っている “割り込み回数” を+1し(これはあとで何回割り込みがかかったかデバグ的に見たかったもの)、その後 WAITルーチンで「ちょっとだけ待って」からポートBのDIP SWの状態を読み取って、ポートAのLEDヘ出力します。その後、再割り込み可にします。

WAITルーチンで「ちょっとだけ待って」いるのはチャタリング対策で、DIPスイッチの状態が安定してからZ80 PIOの入力ポートから読み出すという苦肉の策だったりします。もうちょっとロジックICをはさんだりして工夫した入力装置にすれば、もっとシンプルな割り込みルーチンにできそうです。

動かしてみた! & 今後について

上記のテストプログラムを動かしてみました。無事動いた….と言いたいところですが、電源ON直後しか動かなかったり、若干不安定です。うまく稼働すると、Universal Monitorのコマンド・プロンプト待ちの状態でも、入力装置のDIP SWをパチパチと動かすとLEDが連動して変化するので楽しいです。割り込み駆動なのでマルチタスク!!!

Z80 PIOをお持ちの方は、この基板じゃなくても試せると思いますので是非 試してみてください。楽しいです。

今後ですが、雑に作った入力装置をロジックIC経由にするなどチャタリング対策して安定して割り込みがかかるような実験をしたみたいなという気持ちもあったりします。
改良版ができたら、またご報告の予定です。

コメント