前回作った6502マイコン用I/Oボードの、シリアルLSI W65C51を動かそうと奮闘した話をまとめます。
シリアル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の関連するフォーラムメッセージを読むと、新しいデータシートでは送信バッファ空きフラグの記述が修正されて、「常に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で公開しています。
コメント