2021-09-07 12:32 — asano
カテゴリー:
前々回や前回書いたような厄介な点はありましたが、CDP1802ボードでUniversal Monitorを動かすのは思っていたより簡単でD(ump), G(o), S(et), L(oad)の各コマンドは動作するようになりました。
そうなると今度はSBC1802でも動かしてみたくなります。
ところがこれにはUART相当のハードウェアは無く、CPU内蔵のQ, EF3をシリアルポートとして使うようになっています。UARTの機能をソフトウェアで実現しなくてはなりません。
これはシリアルクロックの話の送受信処理をソフトウェアで行なうということです。処理自体は難しくありませんが、1ビット・1.5ビットの時間は正確でなくてはなりません。
ボーレートはMCBASIC3が9600bpsで動作していることと最近作ったボードはどれも9600bpsを標準にしていることから9600bpsで検討することにします。
まず1ビットの時間は 1 / 9600 [s] ≒ 104.16 [μs] となります。
SBC1802にはタイマは無い(仮にあっても時間が短すぎて使えないでしょう)ので命令の実行時間を利用するしかありません。
COSMACの命令は一部の3バイト命令が3サイクルかかる以外は2サイクルです。1サイクルは8クロックです。
1サイクルの時間CPUクロックからは 1 / (1.79 [MHz]) × 8 ≒ 4.47 [μs] となります。
これらから1ビットは23サイクル、1.5ビットは35サイクルとなります。誤差は1.3%ほどなのでギリギリですね。
また約10命令で1ビットの処理をしなくてはならないのでこちらもギリギリです。パラメータを渡してボーレートを切り替える余地はなさそうです。
送信の方が簡単なのでまず送信から見ていきましょう。
(1) 62/ 736 : CONOUT: ; Cyc
(1) 63/ 736 : 7A REQ ; : TxD H=>L (Start bit)
(1) 64/ 737 :
(1) 65/ 737 : 88 GLO 8 ; 2
(1) 66/ 738 : B7 PHI 7 ; 2
(1) 67/ 739 : F8 08 LDI 8 ; 2 : 8bit
(1) 68/ 73B : A7 PLO 7 ; 2
(1) 69/ 73C :
(1) 70/ 73C : C8 07 3C NLBR $ ; 3
(1) 71/ 73F : 38 3F NBR $ ; 2
(1) 72/ 741 : CO0:
(1) 73/ 741 : 97 GHI 7 ; 2
(1) 74/ 742 : F6 SHR ; 2
(1) 75/ 743 : B7 PHI 7 ; 2
(1) 76/ 744 : 33 49 BDF CO1 ; 2
(1) 77/ 746 : 7A REQ ; 2
(1) 78/ 747 : 30 4C BR CO2 ; 2
(1) 79/ 749 : CO1:
(1) 80/ 749 : 7B SEQ
(1) 81/ 74A : 30 4C BR CO2
(1) 82/ 74C : CO2:
(1) 83/ 74C : 38 4C NBR $ ; 2
(1) 84/ 74E : C8 07 4E NLBR $ ; 3
(1) 85/ 751 :
(1) 86/ 751 : 27 DEC 7 ; 2
(1) 87/ 752 : 87 GLO 7 ; 2
(1) 88/ 753 : 3A 41 BNZ CO0 ; 2
(1) 89/ 755 :
(1) 90/ 755 : C8 07 55 NLBR $ ; 3
(1) 91/ 758 : C8 07 58 NLBR $ ; 3
(1) 92/ 75B : 7B SEQ ; 2 : TxD =>H (Stop bit)
(1) 93/ 75C :
(1) 94/ 75C : C8 07 5C NLBR $ ; 3
(1) 95/ 75F : C8 07 5F NLBR $ ; 3
(1) 96/ 762 :
(1) 97/ 762 : D5 SEP RETN
まず63行でQを下げます。これがスタートビットの開始です。本来は前のストップビットの開始から1ビット以上の時間が経過しているか確認しなくてはなりませんが、本ルーチンを呼び出すためのCALL, RETN処理だけで23サイクル以上消費するので連続して呼んでもストップビット長が不足する心配はありません。
73~75行で1ビット取り出して、77行か80行でQに出力します。リストの右に2とか3と書いてあるのが命令のサイクル数で、66行からここまで23サイクルになるように70,71行で調整しています。
NBR
命令がページ外でエラーになったのでこうしています。
81行も無駄に見えますが、0のときと1のときでサイクルが同じになるように調整しています。
72~88行を8回ループすることで8ビット分出力しますが、これも一周が23サイクルになるように83,84行で調整しています。
ループを抜けたあと92行でQを上げてストップビットとします。ここも77,88行からここまで23サイクルになるように90,91行を挿入して調整しています。
続いて受信の処理です。
(1) 23/ 70C : CONIN: ; Cyc
(1) 24/ 70C : 3E 0C BN3 CONIN ; : Wait falling edge of /EF3
(1) 25/ 70E :
(1) 26/ 70E : F8 03 LDI 3 ; 2
(1) 27/ 710 : A7 PLO 7 ; 2
(1) 28/ 711 : CI0:
(1) 29/ 711 : 27 DEC 7 ; 2
(1) 30/ 712 : 87 GLO 7 ; 2
(1) 31/ 713 : 3A 11 BNZ CI0 ; 2
(1) 32/ 715 : C8 07 15 NLBR $ ; 3
(1) 33/ 718 :
(1) 34/ 718 : F8 08 LDI 8 ; 2 : 8 bit
(1) 35/ 71A : A7 PLO 7 ; 2
(1) 36/ 71B : CI1:
(1) 37/ 71B : 88 GLO 8 ; 2
(1) 38/ 71C : F6 SHR ; 2
(1) 39/ 71D : 36 23 B3 CI2 ; 2
(1) 40/ 71F : F9 80 ORI 80H ; 2
(1) 41/ 721 : 30 27 BR CI3 ; 2
(1) 42/ 723 : CI2:
(1) 43/ 723 : 38 23 NBR $ ; 2
(1) 44/ 725 : 30 27 BR CI3 ; 2
(1) 45/ 727 : CI3:
(1) 46/ 727 : A8 PLO 8 ; 2
(1) 47/ 728 :
(1) 48/ 728 : 38 28 NBR $ ; 2
(1) 49/ 72A : C8 07 2A NLBR $ ; 3
(1) 50/ 72D :
(1) 51/ 72D : 27 DEC 7 ; 2
(1) 52/ 72E : 87 GLO 7 ; 2
(1) 53/ 72F : 3A 1B BNZ CI1 ; 2
(1) 54/ 731 :
(1) 55/ 731 : D5 SEP RETN
まず24行でEF3が下がるのを待ちます。これがスタートビットの始まりです。
35サイクル後に39行で最初のビットを判定してR(8)
の上位から入れていきます。これは右にシフトされていくので8ビット読み終わった時には最下位のビット0になります。
これを8回繰り返して1キャラクタ8ビットの受信が完了します。このループも周期が23サイクルになるよう調整しています。
ストップビットのチェックはしていません。どうせフレーミングエラーになっても無視するだけですから。むしろストップビットの確認をすると次のスタートビットまでの余裕が不足してきます。
実はソフトウェアUARTを書くのは久しぶりなのですが、最初送信処理のサイクル数を数え間違えていて文字化けしオシロスコープのお世話になった以外はすんなり動作しました。
最後に制約というか注意点を一つ。
バックグラウンドでの受信はできないのでCONST
ルーチンは使えず、メモリダンプを途中で中断することはできません。またHEXファイルの送信などを行なう場合はキャラクタごとに少し間隔をあけるなどしないと取りこぼす可能性があるので要注意です。
コメントを追加