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枚単位で買えるので助かります。

以前つくったときは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への書き込みの流れが図解されていまして、書き込み手順の思い出しにたいへん助かりましたのでご紹介します。
最初 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.asm
の ORG 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ホストをつなげば、スタンドアロンパソコン(別シリアルつき)が動くようになりそうです!! 徐々に進化していってます。
コメント