6502CPUマイコン用I/Oボード(2) W65C51の罠

6502

前回作った6502マイコン用I/Oボードの、シリアルLSI W65C51を動かそうと奮闘した話をまとめます。

W65C51 : pico-i-126 : オレンジピコショップ - 通販 - Yahoo!ショッピング
W65C51N6TPG-14、DIP-28Pです。MOUSERからの取寄品です。保管状態はとても良好です。

シリアルICの回路おさらい

シリアルIC W65C51はI/Oボード上で以下の回路となってます。

  • CPUからのデータバスとW65C51 ACIAのレジスタ選択用にA0,A1、R/W(読み書き)の信号線、リセット(RES)を接続。
  • チップセレクトとしては$8000番地でデコードしたものと、アドレスバスA5を接続。これにより$8020〜$8023番地でW65C51 ACIAのレジスタが選択できます。
  • CPUのクロック(PHY-2)は、今回の私のマイコンボードでは1.8432MHz(7.3728MHzの1/4)となっていますので、ACIAのPHY-2端子とシリアルクロック用のXTL-IN端子両方につなぎます。
    • CPUクロックが1MHz等の場合は別に1.8432MHzを生成する必要があります

Universal Monitorの6551用モジュールを使用〜応答なし

Universal Monitorの6502版のソースにモステック6551用のドライバーが含まれています。

ちなみに、W65C51のデータシートをみると、各種制御レジスタは以下のようになっています。私のI/Oボードでいうと$8023番地にコントロールレジスタ、$8022番地にコマンドレジスタがあります。以下はデータシートからの引用です。

w65c51 datasheet

コントロールレジスタはビット数、ストップビット有無、パリティ等とボーレートを設定します。またコマンドレジスタはパリティ使用可否、ローカルエコー、割り込み設定などを設定します。

今回は、ノンパリティ、8ビット、ストップビット1 & 9600bpsへ設定したいので、Universal Monitor付属のソースの設定のままとしました。ACIAのレジスタアドレスや初期値設定の値は config.incにかかれています。

USE_DEV_6551 = 1
	IF USE_DEV_6551
	;;
ACIADR:	EQU	$8020		; Data Register
ACIASR:	EQU	$8021		; Status Register
ACIACM:	EQU	$8022		; Command Register
ACIACR:	EQU	$8023		; Control Register
ACCR_V:	EQU	$1E		; Control: 8bit, 9600bps
ACCM_V:	EQU	$0B		; Command: No-parity
ENDIF

残念ながら、この設定で Universal Monitorの dev/dev_6551.asm を使ってROMを焼いてみましたが無応答でした。….orz

Universal Monitor 6551ドライバーの仕組み

Universal Monitor のモステック6551用ドライバーの仕組みは以下の通りで、モトローラMC6850のコントロールの場合と似てますが、ステータスレジスタのフラグを見ながらポーリング受信/ポーリング送信を実施しています。割り込みは使用していません。

ちなみに、W65C51のステータスレジスタを2007年版のデータシートから引用すると以下のとおりです。

w65c51 datasheet (2007)

シリアル受信ルーチン

ACIAのステータスレジスタ(今回は$8021番地)を読んで $08(受信バッファFull)まで待ちます。受信バッファに1文字入ったらデータレジスタ($8020)からアキュームレータにロードしてリターンします。

シリアル送信ルーチン

ACIAのステータスレジスタ($8021)を読んで$10(送信バッファ空)を確認した後、アキュームレータの文字コードをデータレジスタ($8020)へセットします。

データシート(2007年版)を見る限り、上記の動作に問題はないみたいです。

いろいろと試してみているうちにUniversalMonitorの起動時にメッセージや入力促進プロンプト “]”は表示されないものの、入力した文字のエコーバックが返ってくることがわかりました。マイコンボード←→パソコン間のシリアル通信としてはうまくいっている模様です。以下のTweetではデータ送信後にNOPを入れたりして苦しんでますが、データ送信後にNOPを入れなくてもエコーバックは返ってきてました。

ROMで起動したときの初期化タイミングの問題かもと思い、6502CPUマイコン上のMC6850で通信しながら、RAMの番地($200)へリロケートしたUniversal Monitorをつくって流し込んで試してみたりしましたが、入力した文字のエコーバックしか返ってこない事象は同じでした。

基本に返って、エコーバックプログラムや文字列出力

まずは、データ送信、受信ルーチンが正しいかしらべて見ようと思い、Universal Monitorの部品を使ってシリアルから1文字受信したら1文字送信するプログラムを書いてみました。

	CPU	6502

TARGET:	EQU	"6502"

ACIADR:	EQU	$8020		; Data Register
ACIASR:	EQU	$8021		; Status Register
ACIACM:	EQU	$8022		; Command Register
ACIACR:	EQU	$8023		; Control Register
ACCR_V:	EQU	$1E		; Control: 8bit, 9600bps
ACCM_V:	EQU	$0B		; Command: No-parity

	ORG	$0200

START:
	JSR	INIT
LOOP:
	JSR	CONIN
	CMP	#'#'
	BEQ	QUIT
	JSR	CONOUT
	JMP	LOOP
QUIT:
	BRK


INIT:
	LDA	#ACCR_V
	STA	ACIACR
	LDA	#ACCM_V
	STA	ACIACM
	RTS
	
CONIN:
	LDA	ACIASR
	AND	#$08
	BEQ	CONIN
	LDA	ACIADR
	RTS
	
CONST:
	LDA	ACIASR
	AND	#$08
	LSR	A
	LSR	A
	LSR	A
	RTS
	
CONOUT:
	PHA
CO0:
	LDA	ACIASR
	AND	#$10
	BEQ	CO0
	PLA
	STA	ACIADR
	RTS

END

このプログラムは無事稼働しまして、W65C51につないだシリアルUSB変換経由でパソコンのTeraTERMから1文字送ると1文字エコーバックされる動作が確認できました。ルーチンは正しそう。

つづいて、まとまった文字列をダダダっとACIAへ送信するプログラムを作ってみました。

	CPU	6502

TARGET:	EQU	"6502"

;;; Functions
low	function	x,(x & 255)
high	function	x,(x >> 8)

ACIADR:	EQU	$8020		; Data Register
ACIASR:	EQU	$8021		; Status Register
ACIACM:	EQU	$8022		; Command Register
ACIACR:	EQU	$8023		; Control Register
ACCR_V:	EQU	$1E		; Control: 8bit, 9600bps
ACCM_V:	EQU	$0B		; Command: No-parity

	ORG	$0200

START:
	JSR	INIT
	LDA	#low(OPNMSG)
	STA	PT0
	LDA	#high(OPNMSG)
	STA	PT0+1
	JSR	STROUT
	BRK

STROUT:
	LDY	#0
STRO0:
	LDA	(PT0),Y
	BEQ	STROE
	JSR	CONOUT
	INY
	JMP	STRO0
STROE:
	RTS

;;;
;;;	MCS6551 Console Driver
;;;

INIT:
	LDA	#ACCR_V
	STA	ACIACR
	LDA	#ACCM_V
	STA	ACIACM
	RTS
	
CONIN:
	LDA	ACIASR
	AND	#$08
	BEQ	CONIN
	LDA	ACIADR
	RTS
	
CONST:
	LDA	ACIASR
	AND	#$08
	LSR	A
	LSR	A
	LSR	A
	RTS
	
CONOUT:
	PHA
CO0:
	LDA	ACIASR
	AND	#$10
	BEQ	CO0
	PLA
	STA	ACIADR
	RTS

OPNMSG:
	FCB	"KUNI-NET 6551 PRINT Test JA!!",$00

	ORG	$30
PT0:	RMB	2
	END

案の定、このプログラムではなにもシリアル端末(TeraTERM)に文字は出ません。エコーバックでは出るけど、まとめて送信すると駄目。怪しい動きです。

実はW65C51 ACIAの送信バッファ空きステータスは立ちっぱなし

海外サイトにW65C02のバグ?情報が

私がつないだ回路がおかしいのかと思い、いろいろな先達の方々のページを見ていましたら、以下の方のページに重要な情報が!!

WDC 65c51 bug report, Oct 2013: WDC obviously has a bug in that bit 4, “transmit data register empty,” is stuck in the on position, such that it always looks like it’s ready for another byte, so if you don’t have enough delay between giving it new bytes to transmit, you’ll get garbage out. Follow the forum discussion on it in this topic. They also have a bug in the even parity; but who uses parity? Elsewhere I mention something I consider a bug in the old NMOS 6551’s—either that or an idiotic part of the design; but it got corrected in the CMOS version. I have never had any trouble with Rockwell’s or CMD’s (formerly GTE’s) 65c51’s (ie, CMOS).

http://wilsonminesco.com/6502primer/IO_ICs.html

なんと、送信ルーチンで事前チェックしている bit 4, “transmit data register empty,” がスタックしていていつもONだと。次のバイトを送信するには「ちょっと待つ」のが良いとのことです。(゚∀゚)え゛…

ちなみに、6502.orgフォーラムへのリンクも貼ってらっしゃいまして、こちらに「ちょっと待つ」ルーチンが投稿されていました。なんと2013年。

6502.org • View topic - WDC 65C51 chips defective

新しいデータシートだと記述が修正されているらしい

6502.orgの関連するフォーラムメッセージを読むと、新しいデータシートでは送信バッファ空きフラグの記述が修正されて、「常にON」が仕様となっているらしいというのを見て2020年版を確認してみたら、まさにそのとおりでした….(私が見ていたのは2007年版….)

w65c51 Datasheet

W65C51用 UniversalMonitor ドライバー

W65C51の「バグではありません仕様です」を考慮したシリアルドライバールーチンを6502.orgのフォーラム投稿を参考に作ってみました。ちなみに、WAIT値はCPUクロック(PHY-2)によってかわります。1MHzあたり2ループ分待つのが良いらしいです。

PHY(Push Y)、PHX(Push X)命令は65c02にはあるみたいですが、6502のコードでアキュームレーター経由でPushするコードにしてみました。

;;;
;;;	W65C51 Console Driver
;;;

INIT:
	LDA	#ACCR_V
	STA	ACIACR
	LDA	#ACCM_V
	STA	ACIACM
	RTS
	
CONIN:
	LDA	ACIASR
	AND	#$08
	BEQ	CONIN
	LDA	ACIADR
	RTS
	
CONST:
	LDA	ACIASR
	AND	#$08
	LSR	A
	LSR	A
	LSR	A
	RTS
	
CONOUT:
;	PHA
;CO0:
;	LDA	ACIASR
;	AND	#$10
;	BEQ	CO0
;	PLA
	STA	ACIADR
	JSR     DELAY_6551
	RTS

DELAY_6551:
	TYA
	PHA
	TXA
	PHA
DELAY_LOOP   LDY   #4    ;Get delay value (clock rate in MHz 2 clock cycles)
;
MINIDLY   LDX   #$68      ;Seed X reg
DELAY_1   DEX         ;Decrement low index
         BNE   DELAY_1   ;Loop back until done
;
         DEY         ;Decrease by one
         BNE   MINIDLY   ;Loop until done
	PLA
	TAX
	PLA
	TAY
DELAY_DONE   RTS         ;Delay done, return

無事Universal Monitorが稼働!!

上記シリアルドライバーを組み込んだROMを作成して、無事6502CPU I/OボードのW65C51シリアルICでUniversal Monitorとうまく通信できるようになりました!!
ちょっと納得行かない対応ですけど。^^)

6502.orgフォーラムにも書かれていましたが、W65C51のシリアルクロックRxC端子から1.8432MHzが出ていますので、それをW65C22 VIA のタイマー機能へ投入。ある程度のカウント後にタイマー割り込みさせてそれをシリアルの送信割り込みとすれば、ソフトウェアループが排除できてCPUクロックの違いによる問題も解決、万事おっけーということもできる模様。
そこまで頑張るかというとアレなので、ひとまず動いたので満足している私です。

この6502CPUマイコン用I/Oボードのデータは、githubで公開しています。

GitHub - kuninet/K65-IOB: W65C22(VIA)、W65C51(ACIA)搭載の6502マイコン用I/Oボード
W65C22(VIA)、W65C51(ACIA)搭載の6502マイコン用I/Oボード. Contribute to kuninet/K65-IOB development by creating an account on GitHub.

コメント