PC-6001のRS-232C接続端子の調査

前置き

PC-6001シリーズは全ての機種でRS-232C(別売りオプション)を使えるようになっています。内蔵BASICでもサポートしています。

RS-232Cオプションは中古でも入手が難しいのですが、ハードウェアの構成はそれほど複雑ではなく、基本的には8251を使ったシリアルパラレル変換です。えすびさんの手によって互換ハードの回路図が作成&公開されています。

RS-232Cオプションは機種ごとに形状や固定方法が異なります。PC-6001mkII内部ではこちらの画像のような形状になっています。

画像中央の銀色の箱はRFモジュレータで、この上にRS-232Cオプションボードが載るようです。その下の2つのコネクタが接続端子になります。画像上側のPC-6001mkII背面にRS-232Cケーブル接続用のDSUBが出るようになります。私自身はPC-6001mkII用のRS-232Cオプションは所有していないので、実際にどのように配置&固定するのかはわかりません。

PC-6001初代機用のRS-232Cはこちらです。

2つのコネクタはP6内部とRS-232Cオプションとのやり取りを行うための電源やRS-232C制御信号などで構成されています。本来、これらの信号線はRS-232Cの制御に用いられるものですが、それ以外の用途にも使えるのではないかと思い、調査してみました。

これ以降、PC-6001内部のRS-232Cオプションを接続する2つの端子の事を「RS-232C接続端子」と表現します。(背面のDSUBではありません)

信号線

「RS-232C接続端子」は2つのコネクタがあり、10本 + 12本の端子で構成されています。基板のプリントによると、2つのコネクタはそれぞれCN10とCN11という名称のようです。CN10は10ピン、CN11は12ピンです。右側が1番ピンです。

信号線の本来の用途と、Z80からみた時の信号線の意味を表にまとめてみました。

CN10
PIN番号 信号名 Z80から見た時
1 CS 出力:チップセレクト(I/Oポート8xH)
2 A0 出力:アドレスバスA0
3 未接続 未接続
4 RxRDY 入力:INT割込み
5 CLK × 2  
6 CLK × 1/2  
7 GND  
8 +5V  
9 +12V  
10 -12V  
CN11
PIN番号 信号名 Z80から見た時
1 RESET 出力:RESET
2 D0 D0
3 D1 D1
4 D2 D2
5 D3 D3
6 D4 D4
7 D5 D5
8 D6 D6
9 D7 D7
10 IORD 出力:IO Read
11 IOWR 出力:IO Write
12 CD / RS-232C Carrier Detect信号 入力:IO Port C0HのBit0

2つの8bit入出力ポート

PC-6001のRS-232Cは2つのI/Oポートを利用出来ます。Z80から見た時のアドレスは80H,81Hで、本来は8251のデータ用と制御用に用いられます。この2つのアドレスは「RS-232C接続端子」のA0で判別できます。

「RS-232C接続端子」のCS信号は、Z80のIN/OUT命令のアドレスが80H,81Hの時にEnableとなります。

まとめると、CS、A0, IORD、IOWRの4つの信号で2つの8bit IOポートを扱うことが出来ます。

例えば、音源チップやビデオチップを搭載したボードを「RS-232C接続端子」に接続して、PC-6001の背面からオーディオ/映像端子を出すとか、FT245のようなUSBパラレル変換器を使って、外部からPC-6001に高速にデータを転送するといった活用が出来そうです。

少し残念なお知らせ

PC-6001は電源投入直後にBASICが起動しますが、その過程でRS-232Cの初期化(=8251の初期化)を行います。つまり、「RS-232C接続端子」側に初期化データの書き込みが行われるということですから、なんらかの自作機器を「RS-232C接続端子」に接続した場合は、その初期化データを読み捨てるか、PC-6001電源投入からの数秒間はデータを無視するような仕組みが必要になります。

参考までに、PC-6001初代機では以下のような初期化コードが実行されます。


	; Mode 92h = 1001_0010
	; STOP = 1.5bit
	; Parity ODD
	; Parity Enable
	; Character Length 5
	; Baud Rate Factor x16
	LD	A,092H
	OUT	(081H),A

	; Command 81h = 1000_0001
	OUT	(081H),A

	; Command 52h = 0101_0010
	; Reset?
	LD	A,052H
	OUT	(081H),A

	; ROM内部では3Fhのみ モードの再設定
	; 0011_1111
	; STOP = なし
	; Parity 偶数
	; 8bit
	; Factor x64
	LD	A,B
	OUT	(081H),A

	; Command 37h = 0011_0111
	; DTR ON/REC.TRNS ON
	LD	A,037H
	OUT	(081H),A

1bitの入力端子

CN11のPIN番号12番は、CD / RS-232C Carrier Detect信号として使われています。Carrier Detectは、通信相手が接続されているかどうかを確認するための信号です。この信号はTTLレベルで接続すればよく、PC-6001のZ80からはIOポートC0HのBit0で読み取ることが出来るようです(未確認)。

割込み

PC-6001の割込み制御は内部で完結しているので、外部機器からPC-6001のZ80に割込みをかけるのは難しくなっています。

そもそもPC-6001初代機ではINT信号がROMカートリッジ用の拡張コネクタが出ていません。なぜかNMIは出ているのですが、ジャンプ先のコードが意味を持っていません。さらに、PC-6001mkII以降ではNMIがEXINTに変更され、初代機との互換が取れなくなっています。

なかなか厳しいPC-6001の割込み事情なのですが、「RS-232C接続端子」のRxRDY信号は、PC-6001の正式な割込み端子として利用することが出来ます。

RxRDYの制御はSUB CPUを介して行われます。単純に説明すると、RxRDY → SUBCPU → Z80割込みという流れです(正確には間に8255がいます)。SUBCPUは、RS-232Cだけではなくキーボード制御やCMT制御も行っていて、それらの割込みもまとめて面倒を見ています。

RxRDY割込みを試してみる

RxRDY端子をOn/Offして、どのようにZ80に割込みをかけるのかを確認してみました。

BASICプログラム (mode=5, page=4)


10 AD=&HF000
20 READ A$
30 IF A$="xx" THEN 200
40 D=VAL("&h"+A$)
50 POKE AD,D
60 AD=AD+1
70 GOTO 20
100 DATA f3,21,09,f0,22,04,fa,fb
110 DATA c9,f5,3a,00,c4,3c,32,00
120 DATA c4,db,80,32,01,c4,3e,0c
130 DATA cd,8f,0e,f1,fb,c9,xx
200 EXEC&HF000
300 CLS
310 LOCATE 0,2:PRINT PEEK(&HC400)
320 GOTO 310

上記マシン語部分


	org	0F000H

MAIN:
	di

	ld	hl,R0   
	; RS-232C INT ADDRESS
	ld	(0FA04H),hl

	ei
	ret

	R0:
	push	af

	ld	a,(0C400H)
	inc	a
	ld      (0C400H),a

	in	a,(080H)
	ld	(0C401H),a

	; Sub CPUに0CHを送る(RS-232C割込み受付On)
	ld	a,0CH
	call	0E8FH

	pop     af
	ei
	ret

RS-232C割込みが発生すると、C400Hの中身の値を+1します。BASICではC400Hの値を画面左上の2行目に表示し続けます。C400HはPAGE1の左上のアドレスなので、RxRDY信号からの割込みが発生するたびに画面上の文字が変化します。

RxRDY端子にはArduinoを接続してみました。Digital端子12を500msごとにHigh / Lowを切り替えているだけです。こんな単純なプログラムとジャンパケーブル1本でPC-6001に割込みをかけることができます。


	void setup()
	{
		pinMode( 13, OUTPUT );		// LED
		pinMode( 12, OUTPUT );
	}

	void loop()
	{
		digitalWrite( 13, LOW );	// LED
		digitalWrite( 12, LOW );
		delay(500);
		digitalWrite( 13, HIGH );	// LED
		digitalWrite( 12, HIGH );

		delay(500);
	}

BASICのプログラムを実行し、Arduinoを起動して、RxRDY端子とCS端子の状態をロジアナで確認すると以下のような波形になりました。

RxRDY信号がActive(Low)の時に、割込みが発生して、IOポートの読み取りをしていることがCS端子の変化によりわかります。

割込み周りの全体的な流れは以下の通りです。

  1. SUBCPUは、定期的に(ポーリング)RxRDYの状態を調べます。Active(Low)であり、また、SUBCPU内部のRS-232C割込みフラグが有効であれば、Z80に割込みをかけます。この時にSUB CPU内部の割込みフラグを無効にします。
  2. Z80は割込みを受け付けます。この時のベクタアドレスは04Hです。PC-6001 BASICの基本仕様ではIレジスタはFAHなので、FA04H,FA05Hに書かれているアドレスにジャンプします。
  3. Z80のRS-232C割込み処理ではSUB CPUに対してコマンド0CHを送っています。このコマンドによりSUB CPUはRS-232C割込みフラグを有効にします。コマンドを送らないと、RxRDY信号を変化させてもSUB CPUは割込みをかけないという仕組みです。

割込みを発生させる側が、RxRDY信号をどれくらいの期間だけActive(Low)にすればよいのかというと、Z80がデータをIOポートから読み取るまでということになります。つまり、CS,IORDがActiveになった時に割込みを発生させる側がRxRDY信号をInactive(High)にするということになります。このあたりは8251の仕様によるフロー制御方法です。

計測

割り込み間隔を調べてみました。先の500ms割込み発生期間の一部を拡大した画像です。

割込み間隔にばらつきがありますがBUSREQの影響です。BUSREQを無効にすることである程度の一定間隔にすることが出来ます。

それでもまだところどころで割込み間隔にズレ(抜け?)違いがありますが、Timer割込みの影響かもしれません(未調査)。また、キーボード操作を行うと、SUBCPUとZ80の両方でキー割込み処理が起きるので、割込みタイミングにズレが起きそうです。

割込み間隔に規則性はありますが、短い期間と長い期間の割込みが交互に発生していて、等間隔ではありません。これはSUB CPU内部のポーリングの実行タイミングの都合です。SUB CPUは、RS-232C、キーボード、CMTの判断処理を順に実行していますから、RxRDY信号を読み取るタイミングはSUB CPUの処理状態に依存します。つまり、RxRDYがActive(Low)になったからといって、すぐに割込みが発生するというわけではない、ということになります。最悪ケースでは、最も割込み間隔の広い箇所の時間だけ、割込みの発生が遅れるということです。

割り込み発生間隔を細かく調べてみました。

マーカーEを基準に、約391usと約1943usの間隔でした。SUB CPUの処理状況によっては約1600usの判定遅延が発生することになります(Eの次のマーカーの割込みの直後に割込みが起きても次の読み取りは更に次のタイミングになる)。かなり癖のある割込みですので、例えば1/60秒毎に綺麗に割込みをかけるといったことは出来なさそうです。

まとめ

色々と制約がありますが、PC-6001本体内部に機器を設置して背面からなんらかの端子を出せること、2個の8bit入出力 + 1bitの入力を使えること、割込みを使えるといったメリットがあるので、色々と活用できそうです。