MC6802シングルボードコンピューター その5 ~2系統シリアル化~

MC6802

MC6802シングルボードコンピューターにUSBキーボードを装備したので、こんどは2系統シリアル化を実施してUniversal Monitorのシリアル入力を切り替えて別のシリアルからプログラムなどのIntel HexやS形式ダンプをロードできるようにしたいと思います。

プログラム流し込みのために別シリアルが欲しい

最初の概要のところにも書きましたが、もともとはMC6802シングルボードコンピューターとはTeraTermでつないでいたので入出力ともにTeraTermの機能が使えましたので、電大版TinyBASICのS形式ダンプをメモリーにロードしたりするのはUniversal MonitorのLコマンドに対してパソコンからTeraTermを使って流し込んだりできていたのです。

前回のキーボードI/FによってMC6802シングルボードコンピューターのシリアル端子をUSBキーボード専用ということにしちゃったので、プログラム流し込みのためにもう一つシリアル端子が欲しくなりました。接続イメージは以下のような感じです。

SBC-IOの組付け

以前も作ったことがあるTomi9さん作のSBC-IOを使ってもう一つのシリアル端子を作ってみたいと思います。SBC-IO基板はオレンジピコさんとこで1枚単位で買えるので助かります。

SBC-IO Rev02
オレンジピコさんにて販売をしていただきましたのでお知らせします。商品ページはこちらです。 SBC6800/SB…
SBC-IO Rev02 専用基板 : オレンジピコショップ - 通販 - Yahoo!ショッピング
SBC-IO Rev02 基板です。SBC6800/SBC6809ルーズキットAdapter拡張版用のIOボードです。SBC6303ルーズキットで使用される場合はSBC6303ルーズキットに2本のジャンパー配線が必要になります。SBC680...

以前つくったときはMC6821パラレルI/Oを使ったり一部のICだけ使ってましたが今回はMC6802シングルボードコンピューターと競合するSRAMメモリーとメモリーのアドレスデコード用74LS00以外は搭載してみたいと思います。
※タイマーICもふくめて全部のICを使いこなすのはまだまだ先かもですが

ICソケットを組み付けて ICを装着した写真がこちらです。
左上部のLEDは$8000番地にマッピングされてまして、$8000番地の下4bitがLEDで点灯するという仕掛けです。今回は別シリアルとしてSBC-IOに搭載のMC6850シリアルICを使いたいと思ってます。

SBC-IOのMC6850シリアルICですが、シリアルクロック153.6kHzをSBC6800と同じPIC12F1822で作成しています。しばらくぶりにSBC6800 Datapackの osc1536.X を焼かねばということで、環境準備から実施しました。

シリアルクロックのプログラムをPICヘ書き込み

PIC12F1822ヘ SBC6800 Datapackの osc1536.X を焼くためにパソコンに書き込み用のMPLAB X IPEの環境を整える必要があります。私はPICKit3というちょっと古めの書き込み装置を持ってまして、そちらで書き込めると便利ですので、MPLAB X IPEのちょっと古いバージョンがあると便利。

以下のページで、MPLAB X IPEのちょっと古いバージョンのセットアップと、PICへの書き込みの流れが図解されていまして、書き込み手順の思い出しにたいへん助かりましたのでご紹介します。

Web Nucky
PICマイコンを用いたDCCポイントデコーダの自作について紹介します。

最初 MPLAB X IPEでPickit3がなかなか認識しなかったんですが、「Piclit3本体のボタンを押しながらパソコンのUSB端子へ接続する」という技で無事認識しました。
認識しない場合はお試しください。

上記のページの手順で、SBC6800 Datapackに入っている osc1536.X をPIC12F1822へ流し込んでやりますと153.6kHzの発振器に生まれ変わります。

ちなみにSBCルーズキットシリーズの各種ファイルですが、以下のgithubのページにまとまっていますので、こちらから手元に git cloneするといっぺんに技術資料やDatapackが取得できるので、たいへん便利です。
https://github.com/vintagechips/SBC_loosekit_files

Universal Monitorの2シリアル入出力対応

2つ目のシリアル端子のハードウェアはできましたので、1文字入力、1文字出力を行っているUniversal Monitorのドライバを改造して2シリアルポート対応できるようにしたいと思います。

いったんMC6802シングルボードコンピューターとSBC-IOのシリアル端子へアクセスする際のアドレス仕様を整理すると下表のようになります。

レジスタ種類MC6802マイコンSBC-IO
コントロールレジスタ$8018$8094
データレジスタ$8019$8095

もともとのUniversal Monitorの1文字入力(CONIN)、1文字出力(CONOUT)ルーチンではMC6850シリアルICのコントロールレジスタとかデータレジスタのアドレスをリテラル値(即値)指定でコードしてましたが切り替えできるようにするためにメモリーにアドレスを持ってMC6802のインデックスレジスタでポイントして入出力する仕組みに改造が必要そうです。

confg.incで定数定義と unimon_6800.asmに変数・定数定義

まずUniversal monitorの config.incでは、以下のようにSBC6800(MC6802マイコンと同じ)や、SBC-IO、将来的なことを考えてMIKBUG2.0仕様のシリアルICのアドレスを定数定義しました。

;;;
;;; Motorola MC6850 & VRAM
;;;

USE_DEV_6850_VRAM = 1
 	IF USE_DEV_6850_VRAM
 	;; ACIA
ACIAC_SBC6800:	equ	$8018		; Control / Status Register
ACIAD_SBC6800:	equ	$8019		; Data Register
ACIAC_SBCIO:	equ	$8094		; Control / Status Register
ACIAD_SBCIO:	equ	$8095		; Data Register
ACIAC_MIKBUG2_0:	equ	$8008		; Control / Status Register
ACIAD_MIKBUG2_0:	equ	$8009		; Data Register
ACCR_V:	EQU	$15		; Control: x16, 8-bit, N, 1
;
VRAM_TOP        EQU     $A000
VRAM_END        EQU     $A200
 	ENDIF

unimon_6800.asmORG WORK_B以降に「現在のシリアルポートNo(WK_SERNO)」と、「現在のシリアルポートアドレス(WK_SERADD)」を保存するワークエリアを3バイト確保します。ここへシリアル切り替えコマンドによってそれぞれのシリアルポートアドレスを入れると切り替えできるようにしたいと考えました。

	;;
	;; Work Area
	;;

	ORG	WORK_B
      :
      :
      :
	IF USE_DEV_6850_VRAM
WK_VRAM DS     2
WK_X    DS     2
WK_X_DISP		DS 2

WK_SERNO    DS     1
WK_SERADD    DS     2
	ENDIF

ちなみに「現在のシリアルポートアドレス(WK_SERADD)」が1つしかないのは、「コントロールポートアドレス」だけ定義しておけば「データポートアドレス」は「コントロールポートアドレス」+1でアクセスできるという割り切りです。

unimon_6800.asmの後半の出力メッセージなどの定数定義しているところに、シリアル状況表示ルーチンで使用するメッセージとか、シリアルポートのアドレステーブルを定義しました。

	IF USE_DEV_6850_VRAM
SER_MSG:
	DC	"SER#:",$00
SER_ADDR_MSG:
	DC	"($",$00
SER_ADDR_MSG_END:
	DC ")",$00

MC6850_AD_CNT: DC (MC6850_AD_TBL_END-MC6850_AD_TBL)/4
MC6850_AD_TBL:
	DC.W ACIAC_SBC6800,PN_SBC6800
	DC.W ACIAC_SBCIO,PN_SBCIO
	DC.W ACIAC_MIKBUG2_0,PN_MIKBUG2_0
MC6850_AD_TBL_END: EQU *
;
PN_SBC6800:		DC "SBC6800  ",$00
PN_SBCIO:		DC "SBC-IO   ",$00
PN_MIKBUG2_0:	DC "MIKBUG2.0",$00
	ENDIF

コンソールドライバの「初期化処理」改良

UniversalMonitor コンソールドライバの初期化処理で「現在のシリアルポートNo(WK_SERNO)」には#0を入れ、「現在のシリアルポートアドレス(WK_SERADD)」にはSBC6800(MC6802コンピュータと同じ)で MC6850_AD_TBLの先頭に定義されている #$8018を入れます。

その後 MC6850シリアルICへ初期化データを送信しますが、その際にはWK_SERADDに入っているアドレスへインデックスレジスタ経由でアクセスする仕組みとしました。

;;;
;;;	MC6850 (ACIA) & K68-VDG Console Driver
;;; 

INIT:
	LDAA    #$FF
INIT_LOOP1:
	LDAB    #$FF
INIT_LOOP2:
	DECB
	BNE INIT_LOOP2
	DECA
	BNE INIT_LOOP1

	LDAA #0
	STAA WK_SERNO
	LDAA MC6850_AD_TBL
	LDAB MC6850_AD_TBL+1
	STAA WK_SERADD
	STAB WK_SERADD+1

	STX     WK_X
	LDX     WK_SERADD
	LDAA	#$03		; Master reset
	STAA	,X
	NOP
	NOP
	LDAA	#ACCR_V
	STAA	,X
	LDX     WK_X


;
; VRAM関係初期化
;

コンソールドライバの「1文字入力」「ステータスチェック」「1文字出力」改良

コンソールドライバの「1文字入力」「ステータスチェック」「1文字出力」は以下のように改良しました。

CONIN:
	STX     WK_X
	LDX     WK_SERADD
CONIN_L:
	LDAA	,X
	ANDA	#$01
	BEQ	CONIN_L
	LDAA	1,X
	LDX     WK_X
	RTS

「1文字入力」では、まずインデックスレジスタを WK_Xへ保管して、次に WK_SERADDにあるシリアルコントロールアドレスをインデックスレジスタへロード。
インデックスレジスタが指しているコントロールレジスタからデータを取得してステータスをチェックしデータが来たら1バイト先のシリアルデータレジスタからデータをロードします。

ここのロジックを修正するときに、コントロールレジスタの状態チェックしてループしている部分がCONINに戻るロジックのままになっていて、せっかく保存したWK_Xを壊してしまい「うまく動かない!!!」と数日悩んだのは内緒です…w

CONST:
	STX     WK_X
	LDX     WK_SERADD
	LDAA	,X
	LDX     WK_X
	ANDA	#$01
	RTS

シリアルの「ステータスチェック」ロジックも同じような仕組みで、インデックスレジスタを WK_Xへ保管して、シリアルステータスレジスタからシリアルコントロールアドレスをインデックスレジスタへロード。インデックスレジスタが指しているコントロールレジスタからデータを取得して状況を返します。

ここで若干ハマったのは、Z80 CPUだとレジスタへのデータロード命令でフラグ変化は起きないのですが、MC6800系CPUはデータロードしただけでゼロフラグ等のフラグ変化が起きるのです。よって、 WK_Xからインデックスレジスタを復元するロード命令より後にアキュームレーターAの#$01チェックのAND命令を入れてからリターンするようにしました。

MC6800系CPUはデータロードしただけでゼロフラグ等のフラグ変化が起きるということは、アキュームレーターのゼロチェックとかをいちいちCMP命令で実施しなくて良いということなんですね…今後活用したいと思います。

CONOUT:
	PSHA
	STX     WK_X
;
	LDX     WK_SERADD
CO0:
	LDAA	,X
	ANDA	#$02
	BEQ	CO0
;
	PULA
	STAA	1,X

       :
       :
 <<これ以下はVRAM出力ルーチン>>
       :

「1文字出力」でも仕組みは一緒で、インデックスレジスタを WK_Xへ保管して、次に WK_SERADDにあるシリアルコントロールアドレスをインデックスレジスタへロード。インデックスレジスタが指しているコントロールレジスタからデータを取得してステータスをチェックし OKだったらデータを1バイト先のシリアルデータレジスタへストアします。

Universal Monitor本体にシリアル切り替え K コマンド装備

上記までのコンソールドライバの改良で、シリアルアドレスをワークエリアを参照しながら切り替えできる仕組みができましたので、次はシリアルアドレスを切り替えたり、現在のシリアルポート情報を表示したりできるようにします。

       :
       :
M04:
	IF USE_REGCMD
	CMPA	#'R'
	BNE	M05
	JMP	REG
	ENDIF

M05:
 	IF USE_DEV_6850_VRAM
	CMPA	#'K'
	BNE	M06
	JMP	SERIAL_CMD
M06:
	ENDIF
ERR:
	LDX	#ERRMSG
	JSR	STROUT
	BRA	WSTART
       :
       :

Universal Monitorのコマンドチェックルーチンの最後にKが押されたら…のチェックを追加してSERIAL_CMDルーチンへジャンプするように仕込みました。

コマンドとしては以下のような入力を想定しています。

コマンドコマンドの機能
Kシリアルポート状況を表示
(使用中のシリアルポートには * を表示)
K[0|1|2]シリアルポートを0、1、2に切り替え

※シリアルポート0、1、2は定数領域に定義した MC6850_AD_TBLのエントリー0番目、1番目、2番目を指します。こちらの情報は「現在のシリアルポート(WK_SERNO)」に格納されます。

つづいて呼び出される SERIAL_CMDルーチンはこんな感じです。

※シリアルポートの状況表示結果を ちょっと見やすくしようと思ったところ、プログラムが長くなってしまいました。

 	IF USE_DEV_6850_VRAM
;;;;
;;;; SERIAL Command
;;;;
SERIAL_CMD:
	INX
	JSR	SKIPSP
	JSR	GETSER_NO		; get arg.
	TSTB
	BNE SC0
	;;No Arg
;;
;;シリアル状況表示ルーチン
SC_PR:
	LDAB #$00
	LDX #MC6850_AD_TBL
.LOOP
	PSHB
	BSR SC_1PR
	INX
	INX
	INX
	INX
	PULB
	INCB
	CMPB MC6850_AD_CNT
	BNE .LOOP
	JMP	WSTART
SC_1PR:
	LDAA WK_SERNO
	CBA
	BNE .SPACE_PRT
	LDAA #'*'
	BRA .PR1LINE
.SPACE_PRT
	LDAA #' '
.PR1LINE
	JSR CONOUT
;
	STX	WK_X_DISP
	LDX	#SER_MSG
	JSR	STROUT
	LDX WK_X_DISP
;
	TBA
	ADDA #'0'
	JSR CONOUT
	LDAA #' '
	JSR CONOUT
;
	STX	WK_X_DISP
	LDX	2,X
	JSR	STROUT
	LDX WK_X_DISP
;
	STX	WK_X_DISP
	LDX	#SER_ADDR_MSG
	JSR	STROUT
	LDX WK_X_DISP
;
	LDAA 0,X
	LDAB 1,X
	JSR	HEXOUT4
;
	STX	WK_X_DISP
	LDX	#SER_ADDR_MSG_END
	JSR	STROUT
	LDX	WK_X_DISP
	JSR  CRLF
	RTS
;
;シリアル切り替え
SC0:
	SUBA  #'0'
	CMPA  MC6850_AD_CNT
	BGE  GSE
SC1:
	STAA WK_SERNO
	LDX  MC6850_AD_TBL
	ROLA
	ROLA
GS1:
	TSTA
	BEQ GS2
	INX
	INX
	INX
	INX
	DECA
	BRA GS1
GS2:
	LDAA 0,X
	LDAB 1,X
	STAA WK_SERADD
	STAB WK_SERADD+1
	JMP  SC_PR
;
GETSER_NO:
	CLRB
	LDAA	,X
	CMPA	#'0'
	BCS GSE
	CMPA	#'9'+1
	BCC GSE
;
	LDAB  #1
	RTS
;

GSE:
	RTS

	ENDIF

処理の流れの概略はこんな感じ…

  • インデックスレジスタを+1して入力データをポイント
  • SKIPSPルーチンを呼び出してスペース文字を読み飛ばし
  • GETSET_NOルーチンで数字入力されているか判断してアキュームレータAへ入力文字を返す。アキュームレータBへは文字数を返すので 文字数がゼロだったら シリアル状況表示SC_PRへ。
  • 入力文字が数字だったらSC0ルーチンでシリアル切り替えを実施する。
  • SC0では入力文字から ‘0’の文字コードを引いて数値化してその数値をもとにMC6850_AD_TBLのどのエントリー(要素)を使うか特定してWK_SERADDにシリアルアドレスを格納する。その後 シリアル状況表示SC_PRへ。

動作確認! OK!

ここまでのUniversal Monitorの改造を加えたものをROMに焼いて、TeraTermを2つ立ち上げてテストを実施しました。

上記の動画を見ていただけると分かりますが、最初は上のTeraTerm(シリアルポート0)がActiveになっていて、K1コマンドで下のTeraTerm(シリアルポート1)の切り替えて……
最後にK0コマンドで元のTeraTerm(シリアルポート0)に戻るという動作がうまくいきました!!

これで、シリアルポート0側に前回書いた かんたんUSBホストをつなげば、スタンドアロンパソコン(別シリアルつき)が動くようになりそうです!! 徐々に進化していってます。

コメント