ジョイスティックポートでRS-232C受信 見直し版

以前、PC-6001のジョイスティックポートでRS-232C通信を試してみる記事を公開しましたが、ハード工作の方法が分かりにくい箇所があったので、記事や回路を見直して、少しだけ実用的に使えるようにしてみました。

以下の説明では、WindowsとPC-6001をつなげる方法で説明をしていますが、原理的にはRS-232Cが使えれば、MacでもLinux機でもRaspberryPiでも動かすことが出来ます。

注意点1) PC-6001初代機とPC-6601で動作確認が取れています。SR系では動かないかもしれません。

注意点2) 異なる2つの環境で動作確認が取れていますが、接続機器やPC-6001の動作状態によっては安定した通信が出来ない場合があります。

ハード構成図

構成図

レベル変換回路(ケーブル)の作成

ジョイスティックポートでRS-232C通信を行うにあたり、変換ケーブルを作成する必要があります。

必要なものは以下の通りです。

RS-232C TTLコンバータ
PC-6001ジョイスティック端子用コネクタと9芯ケーブル
RS-232C USB変換器(既存品)
RS-232Cケーブル(無くてもOK)

以前の説明では、RS232Cのレベル変換用のパーツを集めてハンダ付けを行いましたが、今では変換回路一式が積まれている、コンパクトな変換基板があります。これがなんと、DSUBコネクタ内に収まってしまうのですね。この基板ですが、送受信を入れ替えているようです。なので、クロスケーブルではなくストレートケーブルで相互接続します。

PC-6001に挿し込むジョイスティック端子は、奥行きがあり、一般的なDSUB 9pinコネクタはつながりません。そこで、使えなくなったメガドライブ用コントローラのケーブルや、メガドライブ用の延長ケーブルを加工することで、PC-6001につなげることが出来ます。

ケーブル作成には、ケーブルの加工やハンダ付けが必要なので、それなりの手間はかかります。 ハンダ付けはちょっと・・・という方は、MSXで同じような仕組みを実現されている「ジョイジョイファイルシステム」のような方法でも可能かもしれません。ただし、以下で説明している内容では、RTS/CTS信号を使った制御を行っているので、MSX版と同じパーツを使うことは出来ません。

この記事では、PC-6001ジョイスティック端子と通信用の信号線の割り当ては以下のようにしてあります。

端子番号用途P6からみた入出力
1RxD入力
2CTS入力
3未使用
4未使用
5+5V出力
6未使用
7RTS出力
8TxD出力
9GND

ジョイスティック端子の番号は、PC-6001を横から見た時の並びです。(PC-6001のマニュアルに記載されている順)

挿し込む側からみると、このような並びになります。

ケーブルと端子の関係はテスターなどを使って通電確認してください。私の場合、たまたまDSUB9ピンの受け側パーツがあったので、それを使いました。

PC-6001側につなぐDSUB側のテーブルは適当な長さに切ります。適当な長さというのが具体的にどれくらいかというのは難しいのですが、とにかく短いほどいいです。ただ、短すぎるとハンダ付け作業が大変になりますし、PC(Windows)とPC-6001を繋いだ時に不便だったり、ケーブルを無理に引っ張った時に断線しやすくなるので、自分が扱うのにちょうどいい長さにしてください。

TTLコンバータの基板には、接続信号が書かれています。VCCは+5Vの事です。RxDとTxDは矢印の記号の向きに合わせてはんだ付けします。PC-6001から出ていく方がTxD(8番ピン)、PC-6001が受け取る側がRxD(1番ピン)になります。CTS(2番ピン)とRTS(7番ピン)は表記の通りに接続します。

DSUBのケーブルの被覆をはがして、ハンダ付けすれば終わりです。ケーブルの被覆は、ワイヤーストリッパーなどを使うと便利です。また、DSUB 9ピン端子のうち使うのは6ピンなので、残り3本の芯線が回路や他のケーブルに接触しないように注意してください。

完成。

RS-232C変換器の注意点

PC(Windows)に接続するUSBタイプのRS-232C変換器ですが、RTS/CTS信号に対応していないものがあるようです。私が使っているのは、CG-USBRS232Rという、かなり古い製品です。

Amazonで取り扱いがある安価なものでは、こちらのケーブルがRTS/CTSにも対応しているそうです。

RS-232Cの規格では、最大15mまでの長さのケーブルを使うことが出来るので、PC(Windows)とPC-6001が離れている場合は、中間にRS-232Cケーブルを挟むとよいでしょう。ケーブルを買う時は、接続口のオス/メス形状に注意してください。また、先に説明した通り、RS-232C TTL変換基板は送受信の信号を入れ替えているようなので、ストレートケーブルで接続してください。

動作確認

ここではWindowsを使った説明をします。

  1. PC(Windows)とPC-6001のジョイスティック端子1を変換ケーブルで接続し、ソフトウェアの設定をします
  2. PC-6001からのRTS信号(データ受け取れますよ信号)を、PC側でCTSとして認識しているかを確認します
  3. PC(Windows)側から、00からFFまでの値を送り、それをPC-6001側で受信できるかどうかを確認します

RS-232C変換器をPC(Windows)に接続してドライバをインストールします。製品によっては、OSが自動認識&必要なドライバのインストールをしてくれます。

PC(Windows)側の通信ソフトですが、RTS/CTS信号の状態を表示してくれるAcknowrichが便利です。

Acknowrichを起動し、メニューのファイル→シリアルデバイスを開く...から、USB変換器に割り当てられているCOMポートを選択します。COMポートの確認は、コントロールパネルのデバイスマネージャから確認できます。

通信設定では、CTSとRTSにチェックが入っていることを確認してください。

通信ウィンドウでは、38400bps, Parity=NONE, StopBit=1.0, Length=8に切り替えてください。設定を変更するには、変更したい箇所でマウスの右ボタンを押すと選択項目が表示されます。COMポート番号が変更された時などに、ここの設定が違う値に切り替わってしまうことがあるようです。

PC-6001側の受信テストプログラムですが、以下のようなコードになります。


; 受信速度38400bps
; 00~FFの値が送られてくるのを受信する
; 順番通りに受け取れたらOK
;

	; BASIC ROM ROUTINE
PUTCHAR	EQU	1075H
CLS	EQU	1DFBH
LOCATE	EQU	116DH
PUTSTR	EQU	30CFH

	; マシン語開始番地
	; RAM 32K
	; P6ファイルの先頭部分に、10 EXEC&H8410のBASICプログラム付き
	ORG	08410H

	CALL	AYSET

	CALL	CLS
	XOR	A		; キークリック音をオフにする(CONSOLE命令第4パラメータ)
	LD	(0FA2DH),A

	DI
	CALL	CHECK255
	EI

	RET
	;----------------------------

	;----------------------------
CHECK255:
	LD	BC,0000H	; Breg Loop Counter, Creg Check Data

CHECK255_1:
	PUSH	BC

	; LOCATE 1,1:PRINT HEX$(Creg)
	LD	HL,0101H
	CALL	LOCATE
	CALL	OutHex8		; in:Creg

	LD	A,02H		; BUSREQ禁止
	OUT	(93H),A

	CALL	RECV		; 受信待ち~受信処理

	POP	BC
	CP	C

	JR	NZ,check_error

	LD	A,03H		; BUSREQ許可
	OUT	(93H),A

	INC	C
	DJNZ	CHECK255_1

	LD	HL,0102H
	CALL	LOCATE
	LD	HL,sucmsg
	CALL	PUTSTR
	RET

check_error:
	; Areg:間違っている受信データ
	PUSH	AF

	LD	A,03H		; BUSREQ許可
	OUT	(93H),A

	; LOCATE 1,1:PRINT HEX$(Creg)
	LD	HL,0102H
	CALL	LOCATE

	pop	AF
	LD	C,A
	CALL	OutHex8		; in:Creg

	LD	HL,0103H
	CALL	LOCATE
	LD	hl,errmsg
	CALL	PUTSTR
	RET

	;----------------------------
	; TEST結果用
crlf:
	DB	0DH,0AH,00H
errmsg:
	DB	"ERROR.",00H
sucmsg:
	DB	"SUCCESS.",00H

	;----------------------------
	; disp HL(HEX)
DispHLhex:
	LD 	C,H
	CALL 	OutHex8
	LD	C,L

	;----------------------------
	; disp C(HEX)
OutHex8:
	LD	A,C
	RRA
	RRA
	RRA
	RRA
	CALL	OutHex8_1
	LD	A,C
OutHex8_1:
	AND	0FH
	ADD	A,90H
	DAA
	ADC	A,40H
	DAA
	CALL	PUTCHAR
	RET
	;----------------------------

	;----------------------------
	; 1Byte受信処理
	; Aregにデータが入る
RECV:
	; 9bit(データ8bit + STOP1bit)受信するためループだけど、
	; STOP Bitは別に読まなくてもいいよね...
	LD	B,8

	; 後の処理の高速化用
	LD	C,01H
	LD	E,0EH

	; RTS信号をONにする
	CALL	RTSON		; RETの分だけ 10CLK [11CLK]

	; portAから入力する
	LD	A,E		; 4CLK [ 5CLK]
	OUT	(0A0H),A	;11CLK [13CLK]

WAIT_BIT:
	; ひたすらスタートビットを待つ
	; RxDに割り当てられているBit0を調べる
	IN	A,(0A2H)	; 11CLK [13CLK]
	AND	C		;  4CLK [ 5CLK]
	JR	NZ,WAIT_BIT	;  7CLK [ 8CLK]

	; RTSをOFFにする。データを受信後にRTS OFFを送っても間に合わず、次のデータが送られてしまうのでここで
	CALL	RTSOFF		; [51CLK]

	; portAから入力する
	LD	A,E		;  4CLK [ 5CLK]
	OUT	(0A0H),A	; 11CLK [13CLK]

	; この時点で、122CLK経過しているので、最初のデータ読み取りタイミングに突入している。
	; RxDに割り当てられているBit0からデータ読込み。
	; 受信データはCregに組み立てられる
LOOP1:
	IN	A,(0A2H)	; 11CLK [13CLK]
	RRA			;  4CLK [ 5CLK]
	RR	C		;  8CLK [10CLK]
	CALL	WAIT		; 17CLK [18CLK] / CALLしないで、ここにWAIT処理を書いてもいい
	DJNZ	LOOP1		; 13CLK [14CLK]

	; 受信データ確定
	LD	A,C

	RET

	;-------------------------------
	; WAIT ルーチン
	; CPU 3.9936MHzなので、3993600 / 38400 = 104clock
	; ここがCALLされた時点で59CLKを消費
	; PC-6001のM1ステートは常に+1Clockされます
	; []内の数字がP6でのクロック数です
WAIT:
	NEG			;  8CLK [10CLK]
	NOP			;  4CLK [ 5CLK]
	NOP			;  4CLK [ 5CLK]
	NOP			;  4CLK [ 5CLK]
	NOP			;  4CLK [ 5CLK]
	NOP			;  4CLK [ 5CLK]
	RET			; 10CLK [11CLK] 

	;-------------------------------
AYSET:
	; AY3-8910入出力設定
	; I/OportAを入力に
	; I/OportBを出力に
	LD	A,07H
	OUT	(0A0H),A	; REG7
	LD	A,080H
	OUT	(0A1H),A

	; 初期BIT状態(アイドル状態に->HIGH)
	LD	A,0FH
	OUT	(0A0H),A
	LD	A,7FH
	OUT	(0A1H),A
	RET

	;-------------------------------

	; RTS信号はBit3に割り当てられている。
	; ビット単位での状態変化は出来ないので、送信用TxDも変化してしまうことに注意
	; RTS ON, TxD 0 --> 0000_0000
	; RTS ON, TxD 1 --> 0111_0111
	; ここでTxDをLOWにしてしまうとSTART BITとみなされてしまい、向こう側の機器で
	; データの読み取りをしてしまうので、TxDは1にしておく事
RTSON:
	LD	A,0FH		; AY3-8910のREG15に77H(0111_0111)を出力
	OUT	(0A0H),A
	LD	A,77H
	OUT	(0A1H),A
	RET

	; RTS OFF, TxD 0 --> 0000_1000
	; RTS OFF, TxD 1 --> 0111_1111
	; AY3-8910のREG15に7FH(0111_1111)を出力
RTSOFF:
	LD	A,0FH		; [ 8CLK]
	OUT	(0A0H),A	; [13CLK]
	LD	A,7FH		; [ 8CLK]
	OUT	(0A1H),A	; [13CLK]
	RET			; [11CLK]

実行アドレスの都合で、拡張ROM&RAMカートリッジが必要ですが、アドレスを変更すれば、RAM 16KByteの初代機でも可能です。

プログラム中のクロック計算ですが、PC-6001ではメモリアクセスやAY-3-8910への入出力でWAITがかかるので、補正しています。[]の数値がPC-6001でのクロック数です。

このコードをBASICから実行できるようにして、CLOADで読み込めるようにしたファイルを用意しました。これをCLOADで読込んでからRUNすると、テストプログラムが実行されます。

rs232cRecv38400.P6.wav

RUNした後、Acknowrichの通信ウィンドウ左下にあるCTSが点灯していれば通信OK(PC-6001側のデータ受け入れ準備OK)な状態ということになります。

Acknowrichの編集メニューから、BINARYモードを選択に切り替え、通信ウィンドウに00を入力してEnterを押すと、00HがPC-6001に送られます。続けて、01, 02 ... FFと順に入力(&Enter)していきます。

最後にPC-6001側でSUCCESSと表示されればテスト完了です。

順に打ち込むのは大変だと思いますので、こちらの00からFFが並んでいるだけのファイルをAcknowrichのメニューから編集→ファイル転送で送信すると、まとめて確認が出来ます。

testdata_00-FF.dat

正しいデータが受信できなかった場合、一行目には期待する値、2行目には受け取った値を表示してERROR表示になります。(下記の例では、05が送られてきて欲しいのに55Hを受け取った)

プログラム解説

解説というより、上記プログラムの使い方です。

CALL AYSETをした後、割り込み禁止(DI)とBUSREQ OFF(LD A,02H / OUT (93H),A)を実行してから、CALL RECVを呼ぶと、1バイト受信されます。CALL RECV中、Z80は受信待機&受信データの構築に全力を出す必要があるため、割込み処理を受け付けることが出来ません。

ジョイスティック端子、AY-3-8910のポートA/B、PC-6001のIN命令で読み取るビットの位置は、上記のプログラムを参照してください。受信データ(RxD)は、PORT-Aのビット0、Z80からはI/OアドレスのA2HのBit0から読み取ることになります。

上記のプログラムは、8410Hから配置しています。PC-6001の32K RAMでは、BASICプログラムが8401Hから配置されていきます。また、これを実行するためのBASICプログラムが必要なので


10 EXEC &H8410

とだけ書かれたBASICプログラムの後に、上記のマシン語プログラムを8410Hに読み込まれるようにCLOAD出来るようにするため、簡単なバッチファイルを書きました。BASIC部分、マシン語部分、CLOAD終端検出用の00H9個というファイルを結合しています。 また、CLOAD可能なプログラムを、実機で読み込ませるためにはWAVファイル化する必要があるので、wavへの変換プログラムのp6towav.exeを使わせてもらっています。

rs232cRecv38400test00-FF.zip

さらなる高速化&簡易化

上記のプログラムでは、38400bpsで速度を調整していますが、WAIT周りを調整することで57600bpsでの受信が出来ることは実験済みです。ただ、データの一部が化けることがありました。

更に上の115200bpsを目指そうとすると、ほんの少しだけZ80側の処理が間に合わず、最初の1bitか最後の1bitのいずれかが受信に失敗してしまいます。これについては、最初のBitを受信する前のRTSをOFFにする処理をなんとかするしかなく、そもそもRTS/CTSによるフロー制御を行うのをやめる、というのが一つの解決方法です。

RTS/CTSにるフロー制御をせず、115200bpsを実現する方法については、えすびさんがジョイスティックポートでRS-232C通信という記事を書かれていますので、参考にしていただくのがよいと思います。その場合、ハードウェアもシンプルに「ジョイジョイファイルシステム」のようなUSB変換器を使うことが出来ます。