それぞれ改行/空白を表示するルーチンです。
CRLF: STS.L PR,@-R15 ; PR保存 BSR PUTCH ; ↓を1文字表示 MOV #H'0D,R0 ; CRコード(遅延スロット) LDS.L @R15+,PR ; PR復帰 BRA PUTCH ; ↓を1文字表示 MOV #H'0A,R0 ; LFコード(遅延スロット) SPACE: BRA PUTCH ; ↓を1文字表示 MOV #H'20,R0 ; 空白(遅延スロット) |
C言語で言うところの PUTS() に該当するルーチンです。0で終わる、C言語風文字列を表示します。文字列の先頭番地をR2に入れて呼びだします。
(ちなみに先頭に長さデータが付いているのが PASCAL風文字列です。なんだかんだ言って、C言語風文字列の方が扱いは楽なんですよね。PASCAL風だと、こういうとき長さをカウントするためにもう一つ汎用レジスタが必要になってしまいます。)
PUTSTR: MOV.B @R2+,R0 ; 最初の文字を読む STS.L PR,@-R15 ; PR保存 PUTSTRL:CMP/EQ #0,R0 ; 0(文字列の終わり)か? BT PUTSTE ; 終わりなら終了処理へ BSR PUTCH ; その文字を出力 NOP ; (遅延スロット) BRA PUTSTRL ; ループ繰り返し MOV.B @R2+,R0 ; 次の文字を読む(遅延スロット) PUTSTE: LDS.L @R15+,PR ; PR復帰 RTS ; リターン NOP ; (遅延スロット) |
符号付き/符号無し10進左詰表示ルーチンです。R2の値を表示します。名前の由来ですが、ずっと昔に作ったMSX用FORTHのルーチン名を引きずっています。FORTHでは数値表示するワードは "." です。だから「ドット」なんですね。それがMSX用超ミニBASICを作ったときにそのサブルーチンが使いまわしされ、他のCPUに移植した際にもルーチン名だけはそのまま残ってます。
DOT: MOV.L R3,@-R15 ; R3保存 MOV.L ITOABF1,R3 ; 変換用バッファの番地 STS.L PR,@-R15 ; PR保存 BSR ITOA ; ITOA 呼びだし NOP ; (遅延スロット) |
MOV.L ITOABF1,R2 ; バッファの番地(変換済みデータが格納されている) BSR PUTCH ; 最初の1文字(符号)を表示 MOV.B @R2+,R0 ; 変換後の最初の1文字を読む(遅延スロット) BRA UDOT1 ; UDOT1 へ飛ぶ NOP ; (遅延スロット) |
UDOT: MOV.L R3,@-R15 ; R3保存 MOV.L ITOABF1,R3 ; 変換用バッファの番地 STS.L PR,@-R15 ; PR保存 BSR UITOA ; UITOA 呼びだし NOP ; (遅延スロット) MOV.L ITOABF1,R2 ; バッファの番地(変換済みデータが格納されている) |
UDOT1: MOV.B @R2+,R0 ; 1文字読む CMP/EQ #0,R0 ; 文字列の終わりか? BT UDOTE ; 文字列の終わりなら終了 CMP/EQ #H'20,R0 ; 空白か? BT UDOT1 ; 空白ならループ繰り返し BSR PUTSTR ; PUTSTR 呼びだし ADD #-1,R2 ; 1文字進みすぎているので戻す(遅延スロット) |
UDOTE: LDS.L @R15+,PR ; PR復帰 RTS ; リターン MOV.L @R15+,R3 ; R3復帰(遅延スロット) |
R2の値を符号無し右詰め表示するルーチンです。LISTコマンドの行番号表示で使われます。
UDOTR: MOV.L R3,@-R15 ; R3保存 MOV.L ITOABF1,R3 ; 変換用バッファの番地 STS.L PR,@-R15 ; PR保存 BSR UITOA ; UITOA 呼びだし NOP ; (遅延スロット) MOV.L ITOABF1,R2 ; 変換後の文字列を BSR PUTSTR ; そのまま PUTSTR で出力 NOP ; (遅延スロット) LDS.L @R15+,PR ; PR復帰 RTS ; リターン MOV.L @R15+,R3 ; R3復帰(遅延スロット) |
行入力ルーチンです。コマンド、プログラムの入力や、INPUT文で使われます。一行ぶんの入力を行い、ワークエリア LINBUF にその内容を返します。ENTERキーが押されて正常終了した場合はTビット=1でリターンします。CTRL−Cで入力が中断された場合はTビット=0でリターンします。
INPUT文がダイレクトモードで使えない仕様になっているのは、ここに理由があります。ダイレクトモードでは LINBUF に入力された文字列を解釈・実行しているわけですが、 LINBUF は1つしかないので、INPUT文の入力で LINBUF が上書きされると、今まさに実行している文字列が消えてしまいます。わざわざINPUT文のダイレクト実行のためだけに256バイト近いサイズのバッファをもう1つ確保するのもバカバカしいので(そもそも変数に数値を入れたいなら代入すればよい)、ダイレクトモードではINPUT文は使えない仕様にしました。 |
GETLN: MOV.L R4,@-R15 ; R4保存 MOV.L R5,@-R15 ; R5保存 STS.L PR,@-R15 ; PR保存 MOV #0,R3 ; 長さの初期値=0 MOV R3,R0 ; ついでにその0で MOV.B R0,@(LATKEY-WRKTOP,GBR) ; LATKEYもクリヤする MOV.L LINBUF1,R4 ; 入力用バッファの先頭番地 MOV #MAXLL,R5 ; 最大入力可能文字数=240 |
GETLNL: BSR GETCH ; 1文字入力する NOP ; (遅延スロット) CMP/EQ #H'03,R0 ; CTRL−Cなら BT GETLNN ; 行入力キャンセル CMP/EQ #H'0D,R0 ; ENTERキーなら BT GETLNE ; 行入力正常終了 CMP/EQ #H'08,R0 ; BSキーなら BT GETLBS ; BS処理へ CMP/EQ #H'09,R0 ; TABキー以外の文字なら BF GETLN1 ; GETLN1 へ MOV #H'20,R0 ; TABは空白に置きかえる GETLN1: MOV #H'20,R13 ; 文字コードが CMP/HS R13,R0 ; H'20未満なら BF GETLNL ; その文字は無視 CMP/HS R5,R3 ; 最大入力可能文字数に達していたら BT GETLNL ; それ以上入力しない BSR PUTCH ; 入力された文字を表示 MOV.B R0,@R4 ; その文字をバッファに置く(遅延スロット) ADD #1,R3 ; 文字数をインクリメント BRA GETLNL ; ループ繰り返し ADD #1,R4 ; バッファの書きこみポインタをインクリメント |
まあ、不幸中の幸いというか、LINBUF はRAM領域のほぼ末尾に位置しており、その後ろには有効なワークエリアはありません。どんどん書き進んでいってもアドレスが0になってROM領域に突入すると、もう当分は何も書きこめません。もしこのバッファオーバーフロー脆弱性を利用して悪さをするつもりならI/O領域やRAM領域にたどりつくまで約4Gバイトも文字を送り続ける必要があります |
;(正常終了処理) GETLNE: BSR CRLF ; 改行する NOP ; (遅延スロット) BRA GETLNEE ; 共通終了処理へ CLRT ; Tビットをセット(遅延スロット) ;(キャンセル終了処理) GETLNN: BSR CRLF ; 改行する NOP ; (遅延スロット) SETT ; Tビットをクリヤ GETLNEE:MOV #0,R0 ; 行末に0を置く MOV.B R0,@R4 ; MOV.B R0,@(LATKEY-WRKTOP,GBR) ; ついでにLATKEY もクリヤ LDS.L @R15+,PR ; PR復帰 MOV.L @R15+,R5 ; R5復帰 MOV.L @R15+,R4 ; R4復帰 RTS ; リターン MOV.B R0,@(LATKTM-WRKTOP,GBR) ; LATKTM もクリヤ(遅延スロット) |
;(BSキー処理) GETLBS: TST R3,R3 ; 現在入力されている文字が無ければ BT GETLNL ; ループに戻る BSR PUTCH ; BSを出力してカーソルを戻す MOV #H'08,R0 ; BSコード(遅延スロット) BSR PUTCH ; 空白を出力して前の1文字を消す MOV #H'20,R0 ; 空白のコード(遅延スロット) BSR PUTCH ; もう一度BSを出力してカーソルを戻す MOV #H'08,R0 ; BSコード(遅延スロット) ADD #-1,R3 ; 文字数を1減らす BRA GETLNL ; ループに戻る ADD #-1,R4 ; 書きこみポインタを1つ戻す(遅延スロット) |
16進表示するルーチン群です。これらはCCMBには含まれていない、CMBのルーチンなのですが、「表示ルーチンのソースを載せるならこれを載せないでどうする」という重要なルーチンです。実際オールアセンブラのプログラムをデバッグする際には10進より16進数の表示のほうが圧倒的にニーズが高いです。さほど難しくは無いので詳細な説明はしませんが、各自読解してみてください。
;(R2を16進8桁出力) PUTX8: STS.L PR,@-R15 ; PR保存 MOV.L R2,@-R15 ; R2保存 BSR PUTX4 ; ↓で移動した上位16ビットを出力 SHLR16 R2 ; 上位16ビットを下位へ移動(遅延スロット) BSR PUTX4 ; ↓で復帰したR2の下位16ビットを出力 MOV.L @R15+,R2 ; R2復帰(遅延スロット) LDS.L @R15+,PR ; PR復帰 RTS ; リターン NOP ; (遅延スロット) ;(R2の下位16ビットを16進4桁出力) PUTX4: STS.L PR,@-R15 ; PR復帰 MOV R2,R0 ; R2をR0にコピー BSR PUTX2 ; ↓で移動したビット15〜8を出力 SHLR8 R0 ; ビット15〜8をビット7〜0に移動(遅延スロット) BSR PUTX2 ; ↓でコピーしたビット7〜0を出力 MOV R2,R0 ; もう一度R2をR0にコピー(遅延スロット) LDS.L @R15+,PR ; 以下、お約束 RTS NOP ;(R0の下位8ビットを16進2桁出力) PUTX2: STS.L PR,@-R15 ; PR保存 MOV.L R0,@-R15 ; R0保存 SHLR2 R0 ; ビット7〜4をビット5〜2に移動 BSR PUTX1 ; ↓で移動した最初のビット7〜4を表示 SHLR2 R0 ; 最初のビット7〜4をビット3〜0に移動(遅延スロット) BSR PUTX1 ; ↓で復帰したR0のビット3〜0を表示 MOV.L @R15+,R0 ; R0復帰(遅延スロット) LDS.L @R15+,PR ; 以下お約束 RTS NOP ;(R0のビット3〜0を16進1桁出力) PUTX1: AND #H'0F,R0 ; ビット3〜0のみ取りだす MOV #H'0A,R13 ; 10より CMP/HS R13,R0 ; 小さく BF PUTX11 ; なければ ADD #H'07,R0 ; 7を足す PUTX11: BRA PUTCH ; 1文字出力(BRAなのでPR保存不用) ADD #H'30,R0 ; H'30を足す(遅延スロット) |
ソースの見た目のインパクトはSH版より上です。PRを保存しなくてよいH8は、SHよりトリッキーなコードが書けます。
;(8桁出力) PUTX8 PUSH.W R2 ; ER2の下位16ビット保存 MOV.W E2,R2 ; 上位16ビットを下位へ移動 BSR PUTX4:8 ; 移動した上位16ビットを出力 POP.W R2 ; 下位16ビットを復帰し、 ; そのままPUTX4 になだれこんで下位16ビットを表示 ;(4桁出力) PUTX4 MOV.B R2H,R0L ; R2のビット15〜8をR0Lに移動 BSR PUTX2:8 ; 移動したビット15〜8を出力 MOV.B R2L,R0L ; R2のビット7〜0をR0Lに移動し、 ; そのままPUTX2 になだれこんでビット7〜0を表示 ;(2桁出力) PUTX2 PUSH.W R0 ; R0保存 SHLR.B R0L ; この4行でビット7〜4をビット3〜0に移動 SHLR.B R0L ; 〃 SHLR.B R0L ; 〃 SHLR.B R0L ; 〃 BSR PUTX1:8 ; 移動したビット7〜4を表示 POP.W R0 ; R0復帰し、 ; そのままPUTX1 になだれこんでビット3〜0を表示 ;(1桁出力) PUTX1 AND.B #H'0F,R0L ; 下位4ビットのみ取りだす CMP.B #H'0A,R0L ; 10より BCS PUTX11:8 ; 小さくなければ ADD.B #H'07,R0L ; 7を足す PUTX11 ADD.B #H'30,R0L ; H'30 を足す BRA PUTCH:8 ; 1文字出力 |