QDECCH: MOV #H'30,R13 CMP/HS R13,R0 ; '0' より小さければ BF CLRCY ; 数字ではない MOV #H'3A,R13 CMP/HS R13,R0 ; '9' より大きければ BT CLRCY ; 数字ではない ADD #-H'30,R0 ; アスキーコードをバイナリに変換 RTS ; 数字だったら SETT ; Tビットを立てる(遅延スロット) CLRCY: RTS ; 数字でなければ CLRT ; Tビットをクリヤ(遅延スロット) |
次は複数桁の10進文字列をバイナリに変換するルーチン ATOI を見ていきます。名前の由来は、C言語の atoi() そのままです。
ATOI: STS.L PR,@-R15 MOV #0,R2 |
ATOIL: MOV.B @R3+,R0 ; メモリから1文字取り出す BSR QDECCH ; 数字か? EXTU.B R0,R0 ; 符号なし化(遅延スロット) BF ATOIE ; 数字でなければ終了 MOV R2,R1 ; R2を10倍する処理 SHLL2 R2 ; (左2ビットシフトで4倍) ADD R1,R2 ; (元の数を足して5倍) SHLL R2 ; (さらに左1ビットシフトして10倍) BRA ATOIL ; ループ先頭に戻る ADD R0,R2 ; 獲得した一桁分のバイナリをR2に加算(遅延スロット) |
CMBは全体的にオーバーフローをチェックしない方針なので、ここでもオーバーフローのチェックはしていません。なぜオーバーフローのチェックをしないかと言うと、CMBは数値を符号付きとみなすか符号無しとみなすかをプログラマーに任せているからです(ようするにアセンブラと同じ)。H'7FFFFFFF + 1 は符号付き数値であればオーバーフローとみなすべきですが、符号無し数値だとするとなんら問題の無い演算でであり、これをオーバーフローとみなされてエラー停止されるようではかえって困ります。 |
ATOIE: LDS.L @R15+,PR RTS ADD #-1,R3 |
10進文字列をバイナリに変換するルーチンとしては ATOI のもう1レベル上位に GETLIT というルーチンがあります。 ATOI は数字でない文字を見つけた時点で終了しますので、必ず QDECCH によってTビットがクリヤされた状態でリターンしてきます。これでは、「正しく変換が行われた後に数字ではない文字を見つけて正常終了した」のか、あるいは「はじめっから数字などなく全く変換できなかった」か、の区別ができません。それを区別できるようにしたのが GETLIT です。get literal の意味です。
GETLIT: MOV.B @R3,R0 ; R3が指す文字を読む MOV #H'30,R13 ; 数字かどうか判定(QDECCH とほぼ同じ処理) EXTU.B R0,R0 CMP/HS R13,R0 BF CLRCY MOV #H'3A,R13 CMP/HS R13,R0 ; 数字でなければ BT CLRCY ; Tビットをクリヤしてリターン STS.L PR,@-R15 ; 数字なら BSR ATOI ; ATOI を呼び出し NOP LDS.L @R15+,PR RTS ; Tビットをセットしてリターン SETT ; (遅延スロット) |
SGNLIT: CMP/EQ #H'2D,R0 ; '-' か? BF GETLIT ; '-' でなければそのまま GETLIT にジャンプ STS.L PR,@-R15 ; '-' なら BSR GETLIT ; GETLIT を呼び出し ADD #1,R3 ; '-' のぶんR3を進める(遅延スロット) LDS.L @R15+,PR RTS NEG R2,R2 ; GETLIT の結果を符号反転(遅延スロット) |
今度はバイナリを10新文字列にする ITOA を見ていきましょう。これもC言語の itoa() が名前の由来です(ただし、C言語の itoa() とは違って右詰出力です)。やはり最初には符号無し数値を扱う UITOA から見ていきましょう。
UITOA: BRA ITOA11 MOV.L R3,@-R15 ; R3を保存(遅延スロット) |
ITOA11: STS.L PR,@-R15 ; PRを保存 MOV.L ITOAE9,R1 ; 1000000000 BSR ITOASB ; R2を 1000000000 で割って10の9乗の桁を求める MOV #H'30,R0 ; 初期値、'0' のアスキーコード(遅延スロット) MOV.L ITOAE8,R1 ; 100000000 BSR ITOASB ; R2を 100000000 で割って10の8乗の桁を求める MOV #H'30,R0 ; 初期値、'0' のアスキーコード(遅延スロット) (中略) MOV.W ITOAE2,R1 ; 100 BSR ITOASB ; R2を 100 で割って10の2乗の桁を求める MOV #H'30,R0 ; 初期値、'0' のアスキーコード(遅延スロット) MOV #H'0A,R1 ; 10 BSR ITOASB ; R2を 10 で割って10の1乗の桁を求める MOV #H'30,R0 ; 初期値、'0' のアスキーコード(遅延スロット) LDS.L @R15+,PR ; PR復帰 |
なお、 ITOASB 呼びだしのたびに遅延スロットでいちいち MOV #H'30,R0 を実行しています。「共通の処理なんだからサブルーチン側に入れればいいじゃないか」という声が聞こえてきそうですが、この遅延スロットに MOV #H'30,R0 を入れないとすると NOP を入れるしかなくなります。 |
MOV R2,R0 ; R2には1の桁が残っている ADD #H'30,R0 ; 1の桁をアスキーコードに変換 MOV.B R0,@R3 ; メモリに書きこむ ADD #1,R3 ; R3を1文字進める MOV #0,R0 ; 文字列の終わり '\0' を MOV.B R0,@R3 ; メモリに書きこむ |
MOV.L @R15+,R3 ; R3復帰 MOV #H'20,R1 ; 空白の文字コード MOV #9,R4 ; ループ回数(桁数) ITOA2: MOV.B @R3,R0 ; R3が指している文字は CMP/EQ #H'30,R0 ; '0' でないか? BF ITOAE ; '0' でなければループ脱出 MOV.B R1,@R3 ; '0' を空白で置きかえる DT R4 BF/S ITOA2 ; 規定回数繰り返し ADD #1,R3 ; R3を1バイト進める(遅延スロット) ITOAE: RTS ; 終了 NOP |
次は符号付きの ITOA を見ていきます。
ITOA: CMP/PZ R2 ; 正の数か? BT/S ITOA1 ; 正の数ならR0に空白を入れて ITOA1 へ MOV #H'20,R0 ; 空白のアスキーコード(遅延スロット) ; 負の数なら NEG R2,R2 ; R2を符号反転し MOV #H'2D,R0 ; R0に '-' のアスキーコードを入れる ITOA1: MOV.B R0,@R3 ; R0をメモリに書き込み ADD #1,R3 ; 書き込んだ符号文字ぶんR3を進める MOV.L R3,@-R15 ; R3を保存 |
最後にITOA専用割り算ルーチン ITOASB を見ていきます。
ITOASB: CMP/HS R1,R2 ; R2からR1を引くことができるか? BF ITOASE ; できなければ終了 SUB R1,R2 ; R2からR1を引く BRA ITOASB ; ループ先頭に戻る ADD #1,R0 ; R0(引けた回数)をインクリメント(遅延スロット) ITOASE: MOV.B R0,@R3 ; 結果のアスキーコードをメモリに書き込む RTS ; R3をインクリメントしてリターン ADD #1,R3 ; (遅延スロット) |