1文字表示するルーチンです。これが無いと何も表示できないわけで、すべての表示ルーチンの根幹です。R0に表示したい文字のアスキーコードを入れて呼びだすと、それをSCIから送り出して端末の画面上に表示します。
PUTCH: STC.L GBR,@-R15 ; GBR保存 MOV.L SCIBASE2,R13 ; SCIの先頭番地 MOV.L R2,@-R15 ; レジスタ保存 MOV.L R1,@-R15 LDC R13,GBR ; GBRはSCI先頭番地を指す MOV.L R0,@-R15 ; R0保存(出力すべき文字) |
MOV.L LATKEY1,R2 ; 最後に入力された文字が格納されている番地 |
受信を割り込みで処理すれば文字落ちの心配なんかしなくてもいいのですが、BASICインタプリタが勝手に割り込み許可/不許可をしない、というのもある面ではメリットかと思い、CMBシリーズでは伝統的に割り込みを使っていません。たとえば「一切の割り込みを受け付けたくない、でも端末との文字のやりとりはしたい」というケースもあるかと思います。そういうときにBASICのPRINT文やINPUT文を使うとインタプリタが勝手に割り込み許可したりするようでは困ります。 |
PUTCHL: MOV.B @(SSR_3-SCIBASE,GBR),R0 ; SSR(シリアルステータスレジスタ)を読む TST #H'80,R0 ; 送信可か? BF PUTCHX ; 送信可なら送信実行 MOV.B @R2,R1 ; 最後に入力された文字(R2は LATKEY を指している) TST R1,R1 ; すでに最後に入力された文字があるなら BF PUTCHL ; それ以上入力できないので無視 TST #H'40,R0 ; 受信データあり? BF PUTCH1 ; あれば受信処理へ TST #H'38,R0 ; エラーあり? BT PUTCHL ; 無ければループ繰り返し AND #H'C4,R0 ; エラークリヤ BRA PUTCHL ; ループ繰り返し MOV.B R0,@(SSR_3-SCIBASE,GBR) ; エラークリヤした値をSSRに書く(遅延スロット) |
SHのマニュアル見れば書いてあることですが、参考までにSCIのSSRのビット構成は次のようになっています。
エラーをクリヤするには H'C4、エラーと受信フラグをクリヤするには H'84、送信可フラグをクリヤするには H'7F でAND すればよいことがわかります。(マルチプロセッサ関連は使っていないのでどうでもいいです) |
PUTCH1: MOV R0,R1 ; SSRの値をR1に保存 MOV.B @(RDR_3-SCIBASE,GBR),R0 ; RDR(受信データレジスタ)を読む MOV.B R0,@R2 ; 受信データを LATKEY に書きこむ MOV R1,R0 ; SSRの値 AND #H'84,R0 ; 受信フラグとエラーをクリヤ BRA PUTCHL ; ループ先頭へ MOV.B R0,@(SSR_3-SCIBASE,GBR) ; フラグクリヤした値をSSRに書きこむ(遅延スロット) |
PUTCHX: MOV R0,R1 ; SSRの値をR1に保存 MOV.L @R15+,R0 ; スタックからR0(送信すべき文字)を復帰 MOV.B R0,@(TDR_3-SCIBASE,GBR) ; をTDR(送信データレジスタ)に書きこむ MOV R1,R0 ; SSRの値 AND #H'7F,R0 ; 送信可フラグをクリヤ MOV.B R0,@(SSR_3-SCIBASE,GBR) ; フラグクリヤした値をSSRに書きこむ MOV.L @R15+,R1 ; R1復帰 MOV.L @R15+,R2 ; R2復帰 RTS ; リターン LDC.L @R15+,GBR ; GBR復帰(遅延スロット) |
1文字入力ルーチンです。端末から文字が送られてくるのを待ち、受け取った文字コードをR0に返すルーチンです。
GETCH: MOV.B @(LATKEY-WRKTOP,GBR),R0 ; LATKEY に何か文字が CMP/EQ #0,R0 ; 入っているなら BF GETCHE ; 終了(その文字を返す) |
MOV.L SCIBASE2,R13 ; SCIの先頭番地 STC.L GBR,@-R15 ; GBR保存 LDC R13,GBR ; GBRはSCI先頭番地を指す MOV.L R1,@-R15 ; レジスタ保存 MOV.L R2,@-R15 ; |
GETCHL: MOV.B @(SSR_3-SCIBASE,GBR),R0 ; SSRを読む TST #H'40,R0 ; 受信データあり? BF GETCHX ; あれば受信処理へ TST #H'38,R0 ; エラーあり? BT GETCHL ; 無ければループ繰り返し AND #H'C4,R0 ; エラークリヤ BRA GETCHL ; ループ繰り返し MOV R0,@(SSR_3-SCIBASE,GBR) ; エラークリヤした値をSSRに書きこむ(遅延スロット) |
GETCHX: AND #H'84,R0 ; 受信フラグとエラーをクリヤ MOV R0,R1 ; SSRの値をR1に保存 MOV.B @(RDR_3-SCIBASE,GBR),R0 ; RDRを読む MOV R0,R2 ; 受信した文字をR2に保存 MOV R1,R0 ; フラグクリヤされたSSRの値 MOV.B R0,@(SSR_3-SCIBASE,GBR) ; SSRに書きこむ EXTU.B R2,R0 ; 受信した文字を符号無し化してR0に移す MOV.L @R15+,R2 ; レジスタ復帰 MOV.L @R15+,R1 ; RTS ; リターン LDC.L @R15+,GBR ; GBR復帰(遅延スロット) |
GETCHE: SHLL8 R0 ; R0の下位8ビットを保存し同時にクリヤ MOV.B R0,@(LATKEY-WRKTOP,GBR) ; LATKEY をクリヤ MOV.B R0,@(LATKTM-WRKTOP,GBR) ; LATKTM をクリヤ SHLR8 R0 ; 保存した8ビットを復帰 RTS ; リターン EXTU.B R0,R0 ; R0を符号無し化(遅延スロット) |
ということで思いついたのがこのコードです。SHLL8 を実行すればR0の下位8ビットを、H8で言うところの「R0H」(SHでは絶対こういう呼び方はしませんが)に退避すると同時に「R0L」を0にすることができます。0を使う用事がすんだら SHLR8 で元に戻せます。
最後にその値を符号無し化してリターンします。
今回の最後に、もうひとつSCIを直接いじっているルーチン CHKBRK を見ていきます。これは、プログラムの実行中やプログラムリスト表示中に、CTRL−Cキーが押されたら中断するためのルーチンです。RUNコマンドやLISTコマンドは処理の区切れごとにこれを呼びだし、CTRL−Cキーをチェックします。
このルーチンが呼ばれたとき、CTRL−Cキーが押されていなかったら何事も無かったかのようにリターンします。押されていたらブレーク処理にジャンプし、呼びだし元には戻りません。
CHKBRK: MOV.B @(LATKEY-WRKTOP,GBR),R0 ; LATKEY を読む MOV.L SCIBASE3,R13 ; SCIの先頭番地 CMP/EQ #0,R0 ; LATKEY が0でなければ(なにか入力があるなら) BF CHKBR1 ; CHKBR1 へ飛ぶ STC.L GBR,@-R15 ; GBR保存 LDC.L R13,GBR ; SCIの先頭番地をGBRにロード |
MOV.B @(SSR_3-SCIBASE,GBR),R0 ; SSRを読む TST #H'38,R0 ; エラーあり? BF CHKBR2 ; あれば CHKBR2 へ TST #H'40,R0 ; 受信文字あり? BT CHKBR3 ; あれば CHKBR3 へ |
MOV R0,R1 ; SSRの値をR1に保存 MOV.B @(RDR_3-SCIBASE,GBR),R0 ; RDRを読む MOV R0,R2 ; RDRの値をR2に保存 MOV R1,R0 ; SSRの値 AND #H'84,R0 ; 受信フラグとエラーをクリヤ MOV.B R0,@(SSR_3-SCIBASE,GBR) ; クリヤ後の値をSSRに書き戻す MOV R2,R0 ; RDRの値 CMP/EQ #H'03,R0 ; CTRL−Cか? BT JBREAK1 ; |
LDC.L @R15+,GBR ; GBR復帰 MOV.B R0,@(LATKEY-WRKTOP,GBR) ; 受信した文字を LATKEY に入れる MOV #30,R0 ; 適当な定数 RTS ; リターン MOV.B R0,@(LATKTM-WRKTOP,GBR) ; 適当な定数を LATKTM に入れる(遅延スロット) |
;(エラー検出した場合) CHKBR2: AND #H'C4,R0 ; エラーをクリヤ MOV.B R0,@(SSR_3-SCIBASE,GBR) ; SSRに書き戻す ;(受信文字が無かった場合) CHKBR3: MOV.B @(LATKTM-WRKTOP,GBR),R0 ; LATKTM を読む LDC.L @R15+,GBR ; GBR復帰 CMP/EQ #0,R0 ; LATKTM は0か? BT CHKBR4 ; 0なら CHKBR4 へ ADD #-1,R0 ; LATKTM をデクリメント RTS ; リターン MOV.B R0,@(LATKTM-WRKTOP,GBR) ; LATKTM を書き戻す(遅延スロット) |
CHKBR4: MOV #0,R0 ; 0を RTS ; リターン MOV.B R0,@(LATKEY-WRKTOP,GBR) ; LATKEY に書きこむ(遅延スロット) |
;(最初に LAYKEY に何か入っていた場合) CHKBR1: CMP/EQ #H'03,R0 ; CTRL−Cか? BF CHKBR3 ; CTRL−Cでなければ CHKBR3 へ BRA JBREAK1 ; CTRL−Cならブレーク処理へ NOP ; (遅延スロット) |
CHKBRK: MOV.B @(LATKEY-WRKTOP,GBR),R0 ; LATKEY を読む STC.L GBR,@-R15 ; GBR保存 MOV.L SCIBASE3,R13 ; SCIの先頭番地 CMP/EQ #0,R0 ; LATKEY の値をチェック LDC.L R13,GBR ; SCIの先頭番地をGBRにロード BF CHKBR1 ; LATKEY の値により分岐 |
CHKBR3: LDC.L @R15+,GBR ; GBR復帰 MOV.B @(LATKTM-WRKTOP,GBR),R0 ; LATKTM を読む CMP/EQ #0,R0 ; 0かどうかチェック |