いよいよ完成したSBC6303を使っていきます。
SBC6303ルーズキットにはモニタプログラムLILBUGやサンプルプログラムが同梱されていますが、当時を追体験したいと考えているので、あえてフルスクラッチで作りたいと思います。
クロスアセンブラはSB-Assemblerを使うことにしました。Python3で動作するアブソリュートアセンブラです。
Pythonで動作しますので、macOSでも問題なく動かせます。
シリアル端末はCoolTermを使用します。端末エミュレータではないのでエスケープシーケンスに対応していないのが残念ですが、当面は問題ないと思います。
ROMプログラマはTL866xxを使用します。オープンソースソフトウェアのminiproで制御できます。
初めてのプログラムは当然「hollo world」です。
プログラムの構成
Hello worldを実行するには次のルーチンが必要です。
- チップの初期化
- SCIへ一文字送信
- SCIへ文字列送信
上記ルーチンが完成したら、文字列をサブルーチンに渡すメインルーチンを書くだけです。
SB-Assembler特有の設定
SB-AssemblerはCPUの種類や出力ファイル名などをソースコードに記述します。
; * Hello world for SBC6303
; * SB-Assembler
.cr 6301 ; Cross Overlay HD6301を指定
.tf helloworld.s19,s19 ; Target File Name
.lf helloworld ; List File Name
.sf helloworld ; Symbol File Name
定数の定義
プログラム中で使用する定数を定義しておきます。
; HD6301 コントロールレジスタ
DDR1 .eq $00 ; Port1 Data Direction Register
PORT1 .eq $02 ; Port1 Data Register
RMCR .eq $10 ; Rate and Mode Control Register
TRCSR .eq $11 ; Transmit/Receive Control and Status Register
RDR .eq $12 ; Receive Data Register
TDR .eq $13 ; Transmit Data Register
; Rate and Mode Control Register
E128 .eq %00000001 ; E/128
NRZIN .eq %00000100 ; Format:NRZ Sorce:Internal Port2:Not use
; Transmit/Receive Control and Status Register
TE .eq %00000010 ; bit 1 :Transmit Enable
RE .eq %00001000 ; bit 3 :Recevie Enable
TDRE .eq %00100000 ; bit 5 :Transmit Data Register Empty
ORFE .eq %01000000 ; bit 6 :Over Run Framing Error
RDRF .eq %10000000 ; bit 7 :Receive Data Register Full
チップの初期化(init_sbc6303)
起動したら最初に
- スタックポインタの設定
- ポートの入出力方向の決定
- SCIの設定
を行ないます。
init_sbc6303:
.top sei ; 割り込み禁止
lds #$1fff ; スタックポインタ設定
;-- Port 1 設定 --
ldab #%11111111
stab <DDR1 ; Port 1 全ポート -> 出力
;-- SCI設定 --
ldab #E128|NRZIN ; 9,600bps、内部クロック使用、ポート2ビットは不使用
stab <RMCR
ldab #TE|RE ; SCI有効化(TEとREをセット)
stab <TRCSR
ldab <RDR ; 空読み
cli ; 割り込み許可
まず割り込みを禁止し、スタックポインタを設定します。RAMの最終番地にしました。
リセット後のパラレルI/Oは入力になっています。データシートによるとHi-Zになっているのでそのままでも問題なさそうですが、念のため出力にしておきます。
SCIはRMCRとTRCSRを操作して設定します。
このSBC6303は4.9152MHzの水晶振動子を付けていますのでE÷128の9,600bpsで設定したいと思います。
SBC6303はパワーオンリセットするとゴミデータが入っていることがあり、12行目で空読みをしています。
最後に割り込みを許可して終了します。
SCIへ一文字送信(write_char)
Bレジスタの内容をSCIに送信します。
AレジスタではなくBレジスタを使っているのは、Dレジスタの下位8ビットがBレジスタになるからです。
2バイトのデータを受け取るときに、Aレジスタで受け取ってしまうと、その値を何処かに退避しておかないといけません。Bレジスタで受け取れば、tba
命令でAレジスタに転送してそのまま2バイト目を受け取ればいいわけです。
write_char:
.top tim #TDRE,<TRCSR
beq :top ; TDRE=0だったら出力を待つ
stab <TDR ; [TDR] <- B
rts
tim op1,op2
はop2
のメモリ内容とop1
の論理積を取り、フラグを変化させます。レジスタもメモリも内容は変化しませんのでレジスタ数の少ないHD6301にはとても有益な命令です。
ここではTDREビットが立っているかどうか調べるのに使っています。
.top
というラベルはローカルラベルです。beq write_char
でもいいのですが、ローカルラベルはbeq :top
のようにラベルの前にコロンを付けて指定することもできます。ルーチン内の分岐なのかルーチン外への分岐なのか分かりやすくなるので、こうしています。
SCIへ文字列送信(write_line)
インデックスレジスタで指定されたアドレスから始まるデータをSCIに出力します。終端文字はいくつか候補がありますが、転送命令でフラグが変化する68系の特性を考えて$00としました。
write_line:
.top ldab 0,x ; B <- [X]
beq :end ; $00だったら終了
bsr write_char
inx ; X += 1
bra :top
.end rts
2行目でBレジスタにロードした時、内容が$00だとゼロフラグが立ちます。そうすると比較命令を実行することなく、すぐに条件分岐することができるわけです。
メインルーチン
メインルーチンはインデックスレジスタに文字列の先頭アドレスを転送し、write_line
ルーチンを呼び出します。
最後は無限ループに入るようにしました。
hello_world:
ldx #MSG_HELLO
jsr write_line
.end bra :end
MSG_HELLO: .az "Hello world from SBC6303!",#$0d,#$0a
ディレクティブ.az
はASCII文字列の最後に$00を追加してくれます。
割り込みベクタの設定
割り込みベクタは9種類ありますが、とりあえずRESETベクタのみ設定しておきます。
.or $fffe
.dw init_sbc6303 ; Reset Vector
完成したプログラム
忘れずにプログラムの先頭アドレスを記載しておきます。ROMの先頭アドレスにしました。
; * Hello world for SBC6303
; * SB-Assembler
.cr 6301 ; Cross Overlay HD6301を指定
.tf helloworld.s19,s19 ; Target File Name
.lf helloworld ; List File Name
.sf helloworld ; Symbol File Name
; *** 定数 ***
; HD6301 コントロールレジスタ
DDR1 .eq $00 ; Port1 Data Direction Register
PORT1 .eq $02 ; Port1 Data Register
RMCR .eq $10 ; Rate and Mode Control Register
TRCSR .eq $11 ; Transmit/Receive Control and Status Register
RDR .eq $12 ; Receive Data Register
TDR .eq $13 ; Transmit Data Register
; Rate and Mode Control Register
E128 .eq %00000001 ; E/128
NRZIN .eq %00000100 ; Format:NRZ Sorce:Internal Port2:Not use
; Transmit/Receive Control and Status Register
TE .eq %00000010 ; bit 1 :Transmit Enable
RE .eq %00001000 ; bit 3 :Recevie Enable
TDRE .eq %00100000 ; bit 5 :Transmit Data Register Empty
ORFE .eq %01000000 ; bit 6 :Over Run Framing Error
RDRF .eq %10000000 ; bit 7 :Receive Data Register Full
.or $e000
; *** 初期化 ***
init_sbc6303:
.top sei ; 割り込み禁止
lds #$1fff ; スタックポインタ設定
;-- Port 1 設定 --
ldab #%11111111
stab <DDR1 ; Port 1 全ポート -> 出力
;-- SCI設定 --
ldab #E128|NRZIN ; 9,600bps、内部クロック使用、ポート2ビットは不使用
stab <RMCR
ldab #TE|RE ; SCI有効化(TEとREをセット)
stab <TRCSR
ldab <RDR ; 空読み
cli ; 割り込み許可
; *** メインルーチン ***
hello_world:
ldx #MSG_HELLO
jsr write_line
.end bra :end
MSG_HELLO: .az "Hello world from SBC6303!",#$0d,#$0a
; *** 一文字出力 ***
write_char:
.top tim #TDRE,<TRCSR
beq :top ; TDRE=0だったら出力を待つ
stab <TDR ; [TDR] <- B
rts
; *** 一行出力 ***
write_line:
.top ldab 0,x ; B <- [X]
beq :end ; $00だったら終了
bsr write_char
inx ; X += 1
bra :top
.end rts
; *** Interrupt Vectors ***
.or $fffe
.dw init_sbc6303 ; Reset Vector
アセンブルします
SB-Asssemblerをダウンロードしたら好みのディレクトリで解凍します。今回はホームディレクトリに/bin
を作成しました。
パスを通したら正常に動作するかチェックします。
% sbasm -h
SB-Cross Assembler version 3.03.06
Please visit www.sbprojects.net for a complete description.
Sorry, help is not implemented yet.
まだヘルプが実装されていないようですが動作するようです。
早速アセンブルしましょう。
% sbasm helloworld.asm
SB-Cross Assembler version 3.03.06
Please visit www.sbprojects.net for a complete description.
Assembling....
Pass one
Loaded 6301 overlay version 3.01.01
Pass two
0000- 1 ; * Hello world for SBC6303
0000- 2 ; * SB-Assembler
0000- 3
0000- 4 .cr 6301 ; Cross Overlay HD6301を指定
0000- 5 .tf helloworld.s19,s19 ; Target File Name
0 Errors found during assembly.
0 Warnings found during assembly.
同じ階層にhelloworld.s19
、helloworld.lst
、helloworld.sym
の3ファイルができました。
ROMに書き込みます
TL866xxの制御ソフトウェアのminiproはHomebrewでインストールするのが簡単です。
関連ソフトウェアのsrecordも一緒にインストールされます。
miniproはs19ファイルを直接ROMに書き込むこともできるのですが、開始アドレスが$e000になっているためうまくいきません。
まずはsrecordでs19ファイルを$0000から始まるバイナリファイルに変換します。
% srec_cat helloworld.s19 -Motorola -offset -0xe000 -o helloworld.bin --Binary
srec_cat: helloworld.s19: 1: warning: no header record
SB-AssemblerはS0レコードを生成しないので警告がでるものの、変換は問題なくされますので大丈夫です。
同じ階層にhelloworld.bin
ができました。
いよいよTL866xxでEEPROMに書き込みます。EEPROMは日立のHN58V65Aを使用します。
HN58V65Aは対応リストにないのですが、代わりにAT28C64BやuPD28C64として書き込み可能です。
TL866xxをつないで、下記のコマンドを入力します。
% minipro -p at28c64b -w helloworld.bin
Found TL866CS 03.2.86 (0x256)
Erasing... 0.02Sec OK
Protect off...OK
Writing Code... 1.75Sec OK
Reading Code... 0.16Sec OK
Verification OK
Protect on...OK
うまく書き込めました。EEPROMをSBC6303に取り付けます。
CoolTermの設定
USB-シリアル変換ケーブルをMacに接続し、CoolTermを起動したら「Options」の「serial Port」を下記のように設定します。
- Port: お使いのUSB-シリアル変換ケーブル
- Bandrate: 9600
- Data Bits: 8
- Parity: None
- Stop Bits: 1
- Flow Control CTS: チェックなし
- Flow Control DTR: チェックなし
- Flow Control XON: チェックなし
- Software Supported Flow Control: どちらでもOK
- Block Keystrokes while flow is halted: チェックあり
- DTR: On
- RTS: On
ほぼデフォルトのままで大丈夫なはずです。
いよいよ実行!
CoolTermツールバーの「Connect」をクリックし、SBC6303の電源を入れてください。
うまく表示されました!
パワーオンリセットが不安定なので、SBC6303のリセットボタンを押さないとうまくいかないかもしれません。
文字列ひとつ表示するだけでも苦労しますが、その分うまくいったときは嬉しいですね!
コメント