浮動小数点文字列変換()
これから Arm64 の浮動小数点型命令の解説を順次進めていく予定ですが、 浮動小数点命令のメモリやレジスタに格納された浮動小数点数を確認したくなります。 整数を10進数の文字列に変換するのに比べて、2進数で表現された浮動小数点数を 10進数を使った浮動小数点数の文字列表現に変換するのは大変です。
そこで、今回は Arm64 の浮動小数点型命令の解説ではなく、Arm64 の浮動小数点型命令を使って 倍精度浮動小数点数を文字列に変換して、倍精度浮動小数点数を表示するツールを アセンブラで作ります。今回のリストは arm64asm0.tar.gz (12KB) です。 「tar zxf arm64asm0.tar.gz」として展開してできる arm64asm0 ディレクトリ内で、 例えば 「./d2str」 のように実行できます。
$ tar zxvf arm64asm0.tar.gz arm64asm0/ arm64asm0/multestb arm64asm0/mul4x4b.s arm64asm0/debug.s arm64asm0/multestb.s arm64asm0/d2str arm64asm0/stdio.s arm64asm0/d2str.s arm64asm0/syscalls.s arm64asm0/double2string.s
倍精度浮動小数点数の内部表現
倍精度浮動小数点数は64ビット(8バイト)で表現します。 先頭の1ビットが符号フラグで0が正、1が負となります。 続く11ビット(0..2047)は2のべき乗の指数部となり、中央となる 0x3FF をゼロとして、 0x001 が最小値の -1022、0x7FE が最大の 1023 を示します。 指数部が 0x000 の場合、仮数部もすべて 0 ならゼロ、仮数部が0でなければ非正規化数を示します。 指数部が 0x7FF の場合は、仮数部が 0 なら無限大、0でなければ非数値 (NaN) を表します。 残りの 52 ビットで仮数 (小数点以下の数値) を表現します。 ほぼ16桁の有効数字となります。仮数は必ず正規化 (最上位のビットが 1 となるように指数部の値を決める) すると決めることで最上位の 1 ビットを省略する事ができます。 その結果、実質53ビットの精度となります。
次のリストは、倍精度浮動小数点数の10進表現と内部表現の例です。 16進表現の12ビット目の仮数部との境にハイフンを挿入しています。
1.2345678987654321 3ff-3c0ca44de3938 -1.2345678987654321 bff-3c0ca44de3938 -9.87654E33 c6f-e6f375e71abf3 9.87654E33 46f-e6f375e71abf3 5.87654E33 46f-21bc5cd4e4725 -1.87654E-33 b92-37cb2f215b389 1.2345678987654321E299 7e0-798ba335bafbd -1.2345678987654321E-299 81e-0891ed3843971 123.8987654E1 409-35bf35b91f70e 1.2345678987653 3ff-3c0ca44de36e5 0.12345678987652 3fb-f9add3afd21d2 -123456789876543 c2d-c122186c3cfc0 -123456789876542 c2d-c122186c3cf80 -123456789876541 c2d-c122186c3cf40 1.000 3ff-0000000000000 0.1 3fb-999999999999a 0 000-0000000000000 (特殊)
Double2String
倍精度浮動小数点数を文字列に変換するサブルーチンです。 Double2String を呼び出すと d0 レジスタの倍精度浮動小数点数を 有効数字 16 桁の浮動小数点表記の文字列に変換して、 その文字列の先頭アドレスを x0 レジスタに設定します。
//------------------------------------------------------------------------- // file : double2string.s 16digit version // 2017/01/27 Jun Mizutani (https://www.mztn.org/) //------------------------------------------------------------------------- .ifndef __STDIO .include "stdio.s" .endif .ifndef __D2STR __D2STR = 1 .text //------------------------------------------------------------------------- // float(d0) to asciiz string // x0 returns string address //------------------------------------------------------------------------- Double2String: stp x1, x30, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x6, x7, [sp, #-16]! stp q0, q1, [sp, #-32]! // save v0.2d, v1.2d stp q2, q3, [sp, #-32]! // save v2.2d, v3.2d stp q4, q5, [sp, #-32]! // save v4.2d, v5.2d mov x4, #0 // plus fcmp d0, #0.0 // check 0 b.eq 8f // d0 = 0.0 --> zero b.gt 1f fabs d0, d0 // if negative, d0 = abs(d0); x4=1 mov x4, #1 1: adr x7, num_buffer mov x0, #' ' cbz x4, 2f // if negative, print '-'s mov x0, #'-' 2: strb w0, [x7], #2 // additional space for decimal point // n * 2^m = u * 10^v mov x6, #0 // exponent (v) mov x3, #TblSize mov x2, #512 // w = 512, v <= 1023 ldr d3, DOne // 1.00 --> d3 ldr d4, DTen // 10.0 --> d4 fcmp d0, d3 b.ge 4f // goto positive, if d0 >= 1.0 3: // negative exponent (f<1) sub x3, x3, #8 asr x2, x2, #1 // start from w=256 cbz x2, 6f // range01_1 adr x5, NegTable ldr d5, [x5, x3] fcmp d0, d5 // u > 10^-w b.ge 3b sub x6, x6, x2 // v = v - w fdiv d0, d0, d5 // u = u / 10^-w = u * 10^w b 3b 4: // positive exponent (f>=1) sub x3, x3, #8 asr x2, x2, #1 // w cbz x2, 5f // div 10 adr x5, PosTable ldr d5, [x5, x3] fcmp d0, d5 // u <= 10^n b.lt 4b add x6, x6, x2 // v = v + w fdiv d0, d0, d5 // u = u / 10^w b 4b 5: // div10 fdiv d0, d0, d4 // u = u / 10 add x6, x6, #1 // w = w + 1 //------------------------------ 6: // mantissa should be 0.1 <= u < 1 ldr d2, Exp16 fmul d0, d0, d2 fcvtzu x4, d0 mov x0, x4 bl _put_uint // sub x0, x6, #1 // x0 = x6-1 cbz x0, 7f mov w1, #'E' strb w1, [x7], #1 bl _put_int // exponent 7: adr x0, num_buffer // shift left 1 digit for decimal point ldrb w1, [x0, #2] strb w1, [x0, #1] mov w1, #'.' strb w1, [x0, #2] ldp q4, q5, [sp], #32 // restore v4.2d, v5.2d ldp q2, q3, [sp], #32 // restore v2.2d, v3.2d ldp q0, q1, [sp], #32 // restore v0.2d, v1.2d ldp x6, x7, [sp], #16 ldp x4, x5, [sp], #16 ldp x2, x3, [sp], #16 ldp x1, x30, [sp], #16 ret 8: // zero adr x7, num_buffer mov w0, #' ' strb w0, [x7], #2 // additional space for decimal point mov x0, xzr bl _put_uint b 7b //------------------------------------ // Output 16 digit unsigned number to buffer // x0 : number // x7 : buffer _put_uint: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! mov x2, xzr // counter 1: mov x1, #10 // x1 = 10 udiv x3, x0, x1 // division by 10 mul x4, x3, x1 sub x1, x0, x4 // x1 = x0 - x4*10 mov x0, x3 add x2, x2, #1 // counter++ str x1, [sp, #-16]! // least digit (reminder) cmp x2, #16 bne 1b // done ? b 2f //------------------------------------ // Output signed number to buffer // x0 : number // x7 : buffer _put_int: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! mov x2, xzr // counter cmp x0, xzr b.ge 1f sub x0, x2, x0 // x0 = 0 - x0 mov w1, #'-' strb w1, [x7], #1 1: mov x1, #10 // x1 = 10 udiv x3, x0, x1 // division by 10 msub x1, x3, x1, x0 // x1 = x0 - x3*10 mov x0, x3 add x2, x2, #1 // counter++ str x1, [sp, #-16]! // least digit (reminder) cbnz x0, 1b // done ? 2: ldr x0, [sp], #16 // most digit add x0, x0, #'0' // ASCII strb w0, [x7], #1 // store a char into buffer subs x2, x2, #1 // counter-- bne 2b strb w2, [x7] // store 0 into buffer ldp x3, x4, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //============================================================== .data .align 3 DOne: .double 1.0 DTen: .double 10.000000000 Exp16: .double 1.0e16 NegTable: .double 1.0e-1 .double 1.0e-2 .double 1.0e-4 .double 1.0e-8 .double 1.0e-16 .double 1.0e-32 .double 1.0e-64 .double 1.0e-128 .double 1.0e-256 // 2^1023 < 10^308 PosTable: .double 1.0e+1 .double 1.0e+2 .double 1.0e+4 .double 1.0e+8 .double 1.0e+16 .double 1.0e+32 .double 1.0e+64 .double 1.0e+128 .double 1.0e+256 TblSize = . - PosTable .bss .align 2 num_buffer: .skip 32 .endif
浮動小数点文字列変換ルーチンの解説
2進の倍精度浮動小数点数を10進表現の倍精度浮動小数点数に変換する方法の解説です。 今回は、倍精度浮動小数点数の内部表現から文字列に変換するのではなく、 乗算すると数値が 1.0 未満、0.1 以上となるような 10 のべき乗の数値 (v) を求めて 10進表現の指数部とします。次に、1.0 未満、0.1 以上となった数値に10の16乗を乗算して 整数化することで有効数字部分 (u) の値を求めます。
f = n * 2 ^ m = u * 10 ^ v
- 変換する浮動小数点数 f と1.0を比較して、指数部 v が負 (fが1より小さい) 場合と、正 (1以上) の場合に分けます。
- 10のべき乗の浮動小数点数と大小比較しながら、10のべき乗の浮動小点数を乗算 (fが1より小さい)、 または除算して、 0.1 <= u < 1 となるまで続けます。この時乗除算した v の値を記録します。
- 最後に u 対して 10^16 を乗算して16桁の整数に変換して、文字列に変換します。
サンプルプログラム
ラベル (D001) 以降の倍精度浮動小数点数を順次文字列に変換して標準出力に表示します。 定数に 0.0 が出現したら終了します。
//------------------------------------------------------------------------- // file : d2str.s // 2016/01/27 Jun Mizutani (https://www.mztn.org/) //------------------------------------------------------------------------- .global _start .include "double2string.s" .text _start: adr x1, D001 1: ldr d0, [x1] // mem --> d0 add x1, x1, #8 bl Double2String bl OutAsciiZ bl NewLine fcmp d0, #0.0 // exit, if d0==0.0 bne 1b // next bl Exit //------------------------------------ D001: .double 1.2345678987654321 .double -1.2345678987654321 .double 9.87654E33 .double -9.87654E33 .double 9.87654E-33 .double 1.2345678987654321E299 .double -1.2345678987654321E-323 .double 1.2345678987653 .double -123456789876543 .double -123456789876542 .double 1.000 .double 0.1 .double 0
実行
Orange Pi PC2 上でアセンブル、リンクして、実行しました。 上のコード中の定数値と有効数字の最終桁の ±1 以内で一致していることが確認できます。jun@Orangepi:~/arm64asm0$ as -o d2str.o d2str.s jun@Orangepi:~/arm64asm0$ ld -o d2str d2str.o jun@Orangepi:~/arm64asm0$ ./d2str 1.234567898765432 -1.234567898765432 9.876540000000000E33 -9.876540000000000E33 9.876540000000000E-33 1.234567898765431E299 -9.881312916824932E-324 1.234567898765300 -1.234567898765430E14 -1.234567898765420E14 1.000000000000000 1.000000000000000E-1 0.000000000000000
標準入出力用のサブルーチン
アセンブリ言語でプログラムを作成する場合に、文字の出力の度に write システムコールを使っていては、毎回長いコードが必要で面倒です。 ライブラリとして利用できるように、基本的な入出力用のサブルーチンを作成しました。
文字列出力、数値出力などの基本的なサブルーチンを集めた stdio.s を 「.include "stdio.s"」としてアセンブリプログラムに取り込めば簡単に プログラムが作成できるようになります。
標準入出力サブルーチンの使い方
それぞれのサブルーチンの使い方です。 基本的に x0 に値を設定して BL 命令で「bl ラベル」という形式で実行します。
Exit
呼び出すと 0 (正常終了) を親プロセスに返してプログラムを終了します。普通はプログラムの最後で 「bl Exit」を実行します。
ExitN
呼び出すと x0 の値を親プロセスに返してプログラムを終了します。
OutString
x0 に文字列が格納されている先頭アドレス、x1 に文字列のバイト数を渡してコールします。
StrLen
x0 が示すアドレスに格納されている0で終わる文字列の長さを x1 に返します。
OutAsciiZ
x0 が示すアドレスに格納されている 0 で終わる文字列を出力します。
OutPString
パスカルタイプの文字列を表示するサブルーチンです。 x0 が示すアドレスに格納されている文字列の先頭に 1 バイトを文字数として持ち、 その後ろに文字列が続きます。 文字数を 1 バイトで持つため最大 255 文字に制限されます。
OutChar
1文字(1バイト)を標準出力に書き出すサブルーチンです。1文字表示は文字列表示の 特殊なパターンです。write システムコールには文字列の格納されたバッファアドレス を渡す必要がありますが、1文字表示するためにわざわざメモリ上にバッファを用意する もの面倒ですから、実行時にスタック上にバッファを確保(4バイト)しています。 実行前後でレジスタは保存されます。
OutChar4
x0 の下位 4 バイトを上位から文字として表示するサブルーチンです。 メモリダンプに便利なように表示可能文字以外はピリオドを表示します。
OutChar8
x0 の内容の8バイトを上位から文字として表示するサブルーチンです。 メモリダンプに便利なように表示可能文字以外はピリオドを表示します。
NewLine
改行コード (0x0A) を出力します。 レジスタの値は変化しません。
BackSpace
BackSpace は1文字左に移動してスペースを書き出し、1文字左に移動します。 レジスタの値は変化しません。
PrintBinary
PrintBinary は x0 の内容を2進数として表示するルーチンです。 x1 に表示するビット数(下位優先)を設定して呼びます。
PrintOctal
PrintOctal は x0 の内容をを8進数の数値として表示するルーチンです。 x1 に表示する桁数(下位優先)を設定して呼びます。
PrintHex2
PrintHex2 は x0 の内容の下位を 2 桁の16進数で標準出力に書き出します。 表示桁数をx1に設定するため、x1 レジスタの値は保存されません。
PrintHex4
PrintHex4 は x0 の内容の下位を 4 桁の16進数で標準出力に書き出します。 表示桁数をx1に設定するため、x1 レジスタの値は保存されません。
PrintHex8
PrintHex8 は x0 の内容の下位を 8 桁の16進数で標準出力に書き出します。 表示桁数をx1に設定するため、x1 レジスタの値は保存されません。
PrintHex16
PrintHex16 は x0 の内容を 16桁の16進数で標準出力に書き出します。 表示桁数をx1に設定するため、x1 レジスタの値は保存されません。
PrintHex
PrintHex は x0 の内容の下位を x1に指定した桁数の16進数で標準出力に 書き出します。
PrintLeftU
PrintLeftU は x0 の内容を符号なし10進数値として左詰めで出力します。
PrintLeft:
PrintLeftは x0 の内容を符号付10進数値として左詰めで標準出力に書き出します。
PrintRight0
PrintRight0 は x0 の内容を符号なし10進数値として前に 0 を補って右詰めで 標準出力に書き出します。x1 に表示桁数を指定します。
PrintRightU
PrintRightU は x0 の内容を符号なし10進数値として空白を補って右詰めで 標準出力に書き出します。x1 に表示桁数を指定します。
PrintRight
PrintRight は x0 の内容を符号付10進数値として空白を補って右詰めで標準出力に 書き出します。x1 に表示桁数を指定します。
InChar
1文字を標準入力から読みこみます。読んだ文字は x0 レジスタに格納されます。 入力バッファはスタック上に4バイト確保して, 結果を x0 に返します。x0 の上位 3バイトは0で返ります。
InputLine0
x0 にバッファのバイト数を、 x1 にバッファのアドレスを設定して 呼び出します。 標準入力から読み込んだバイト数を x0 に返します。 キーボードからの入力も標準入力となり、編集機能が全く無いと 実用的ではありませんが、ここでは1文字消去 (バックスペース) の機能のみを実装しています。
stdio.s のソースコード
// ------------------------------------------------------------------------ // Standard I/O Subroutine for ARM64 // 2017/01/28 arm64 system call // Copyright (C) 2015-2017 Jun Mizutani <[email protected]> // stdio.s may be copied under the terms of the GNU General Public License. // ------------------------------------------------------------------------ .ifndef __STDIO __STDIO = 1 .ifndef __SYSCALL .equ sys_exit, 93 .equ sys_read, 63 .equ sys_write, 64 .endif .text //------------------------------------ // exit with 0 Exit: mov x0, xzr mov x8, #sys_exit svc #0 ret // ret x30 //------------------------------------ // exit with x0 ExitN: mov x8, #sys_exit svc #0 ret //------------------------------------ // print string to stdout // x0 : address, x1 : length OutString: stp x8, x30, [sp, #-16]! stp x0, x1, [sp, #-16]! stp x2, x3, [sp, #-16]! // x3 : filler mov x2, x1 // a2 length mov x1, x0 // a1 string address mov x0, #1 // a0 stdout mov x8, #sys_write svc #0 ldp x2, x3, [sp], #16 ldp x0, x1, [sp], #16 ldp x8, x30, [sp], #16 ret //------------------------------------ // input x0 : address // output x1 : return length of strings StrLen: stp x2, x30, [sp, #-16]! stp x0, x3, [sp, #-16]! // x3 : filler mov x1, xzr // x1 : counter 1: ldrb w2, [x0], #1 // x2 = *pointer++ (1byte) cmp x2, #0 add x1, x1, #1 // counter++ bne 1b sub x1, x1, #1 // counter++ ldp x0, x3, [sp], #16 ldp x2, x30, [sp], #16 ret //------------------------------------ // print asciiz string // x0 : pointer to string OutAsciiZ: stp x1, x30, [sp, #-16]! bl StrLen bl OutString ldp x1, x30, [sp], #16 ret //------------------------------------ // print pascal string to stdout // x0 : top address OutPString: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! ldrb w1, [x0] add x0, x0, #1 bl OutString ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // print 1 character to stdout // x0 : put char OutChar: stp x8, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x1, sp // x1 address mov x0, #1 // x0 stdout mov x2, x0 // x2 length mov x8, #sys_write svc #0 ldp x0, x1, [sp], #16 ldp x1, x2, [sp], #16 ldp x8, x30, [sp], #16 ret //------------------------------------ // print 4 printable characters in x0 to stdout OutChar4: stp x2, x30, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x2, #4 b OutCharN //------------------------------------ // print 8 printable characters in x0 to stdout OutChar8: stp x2, x30, [sp, #-16]! stp x0, x1, [sp, #-16]! mov x2, #8 OutCharN: mov x1, x0 1: and x0, x1, #0x7F cmp x0, #0x20 b.ge 2f mov x0, #'.' 2: bl OutChar lsr x1, x1, #8 subs x2, x2, #1 b.ne 1b ldp x0, x1, [sp], #16 ldp x2, x30, [sp], #16 ret //------------------------------------ // new line NewLine: stp x0, x30, [sp, #-16]! mov x0, #10 bl OutChar ldp x0, x30, [sp], #16 ret //------------------------------------ // Backspace BackSpace: stp x0, x30, [sp, #-16]! mov x0, #8 bl OutChar mov x0, #' ' bl OutChar mov x0, #8 bl OutChar ldp x0, x30, [sp], #16 ret //------------------------------------ // print binary number // x0 : number // x1 : bit PrintBinary: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! cmp x1, #0 // x1 > 0 ? b.eq 4f // if x1=0 exit mov x2, #64 cmp x1, x2 b.le 1f mov x1, x2 // if x1>64 then x1=64 1: subs x2, x2, x1 lsl x2, x0, x2 // discard upper 64-x1 bit 2: mov x0, #'0' adds x2, xzr, x2 b.pl 3f add x0, x0, #1 3: bl OutChar lsl x2, x2, #1 subs x1, x1, #1 b.ne 2b 4: ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // print ecx digit octal number // x0 : number // x1 : columns PrintOctal: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x6, [sp, #-16]! mov x6, sp sub sp, sp, #32 // allocate buffer cmp x1, #0 // x1 > 0 ? beq 3f // if x1=0 exit mov x3, x1 // column 1: and x2, x0, #7 lsr x0, x0, #3 strb w2, [x6, #-1]! subs x3, x3, #1 bne 1b 2: ldrb w0, [x6], #1 // 上位桁から POP add x0, x0, #'0' // 文字コードに変更 bl OutChar // 出力 subs x1, x1, #1 // column-- bne 2b 3: add sp, sp, #32 ldp x3, x6, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // print 2 digit hex number (lower 8 bit of x0) // x0 : number PrintHex2: mov x1, #2 b PrintHex //------------------------------------ // print 4 digit hex number (lower 16 bit of x0) // x0 : number PrintHex4: mov x1, #4 b PrintHex //------------------------------------ // print 8 digit hex number (x0) // x0 : number PrintHex8: mov x1, #8 b PrintHex PrintHex16: mov x1, #16 //------------------------------------ // print hex number // x0 : number x1 : digit PrintHex: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x6, [sp, #-16]! // x6 : pointer for buffer mov x6, sp sub sp, sp, #16 // allocate buffer mov x3, x1 // column 1: and x2, x0, #0x0F // lsr x0, x0, #4 // orr x2, x2, #0x30 cmp x2, #0x39 b.le 3f add x2, x2, #0x41-0x3A // if (x2>'9') x2+='A'-'9' 3: strb w2, [x6, #-1]! // first in/last out subs x3, x3, #1 // column-- b.ne 1b mov x3, x1 // column 2: ldrb w0, [x6], #1 bl OutChar subs x3, x3, #1 // column-- b.ne 2b add sp, sp, #16 ldp x3, x6, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // Output Unsigned Number to stdout // x0 : number PrintLeftU: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x5, x6, [sp, #-16]! // x6 : fp mov x6, sp sub sp, sp, #32 // allocate buffer mov x2, #0 // counter mov x3, #0 // positive flag b 1f //------------------------------------ // Output Number to stdout // x0 : number PrintLeft: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x5, x6, [sp, #-16]! // x6 : fp mov x6, sp sub sp, sp, #32 // allocate buffer mov x2, #0 // counter mov x3, #0 // positive flag cmp x0, #0 b.ge 1f mov x3, #1 // set negative sub x0, x2, x0 // x0 = 0-x0 1: mov x1, #10 // x3 = 10 udiv x5, x0, x1 // division by 10 msub x1, x5, x1, x0 mov x0, x5 add x2, x2, #1 // counter++ strb w1, [x6, #-1]! // least digit (reminder) cmp x0, #0 bne 1b // done ? cmp x3, #0 b.eq 2f mov x0, #'-' // if (x0<0) putchar("-") bl OutChar // output '-' 2: ldrb w0, [x6], #1 // most digit add x0, x0, #'0' // ASCII bl OutChar // output a digit subs x2, x2, #1 // counter-- bne 2b add sp, sp, #32 ldp x5, x6, [sp], #16 ldp x3, x4, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // Output Number to stdout // x1:column // x0:number PrintRight0: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x5, x6, [sp, #-16]! stp x7, x8, [sp, #-16]! // x8 : fp mov x8, sp sub sp, sp, #32 // allocate buffer mov x4, #'0' b 0f //------------------------------------ // Output Unsigned Number to stdout // x1:column // x0:number PrintRightU: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x5, x6, [sp, #-16]! stp x7, x8, [sp, #-16]! // x8 : fp mov x8, sp sub sp, sp, #32 // allocate buffer mov x4, #' ' 0: mov x5, x1 mov x2, #0 // counter mov x3, #0 // positive flag b 1f // PrintRight.1 //------------------------------------ // Output Number to stdout // x1:column // x0:number PrintRight: stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x5, x6, [sp, #-16]! stp x7, x8, [sp, #-16]! // x8 : fp mov x8, sp sub sp, sp, #32 // allocate buffer mov x4, #' ' mov x5, x1 mov x2, xzr // counter=0 mov x3, xzr // positive flag cmp x0, xzr b.ge 1f mov x3, #1 // set negative sub x0, xzr, x0 // x0 = 0-x0 1: mov x1, #10 // x3 = 10 udiv x7, x0, x1 // division by 10 mul x6, x7, x1 sub x1, x0, x6 // x1:remainder mov x0, x7 // x0 : quotient7 add x2, x2, #1 // counter++ strb w1, [x8, #-1]! // least digit cmp x0, #0 bne 1b // done ? subs x5, x5, x2 // x5 = no. of space ble 3f // dont write space cmp x3, #0 b.eq 2f sub x5, x5, #1 // reserve spase for - 2: mov x0, x4 // output space or '0' bl OutChar subs x5, x5, #1 // nspace-- bgt 2b 3: cmp x3, #0 b.eq 4f mov x0, #'-' // if (x0<0) putchar("-") 3: cmp x3, #0 b.eq 4f mov x0, #'-' // if (x0<0) putchar("-") bl OutChar // output '-' 4: ldrb w0, [x8], #1 // most digit add x0, x0, #'0' // ASCII bl OutChar // output a digit subs x2, x2, #1 // counter-- b.ne 4b add sp, sp, #32 ldp x7, x8, [sp], #16 // x8 : filler ldp x5, x6, [sp], #16 ldp x3, x4, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // input 1 character from stdin // x0 : get char InChar: mov x0, xzr // clear upper bits stp x0, x30, [sp, #-16]! stp x1, x2, [sp, #-16]! stp x8, x3, [sp, #-16]! // x3 : filler add x1, sp, #32 // x1(stack) address mov x0, #0 // x0 stdin mov x2, #1 // x2 length mov x8, #sys_read svc #0 ldp x8, x3, [sp], #16 ldp x1, x2, [sp], #16 ldp x0, x30, [sp], #16 ret //------------------------------------ // Input Line // x0 : BufferSize // x1 : Buffer Address // return x0 : no. of char InputLine0: stp x1, x30, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x4, x5, [sp, #-16]! mov x4, x0 // BufferSize mov x5, x1 // Input Buffer mov x3, xzr // counter 1: bl InChar cmp x0, #0x08 // BS ? bne 2f cmp x3, #0 beq 2f bl BackSpace // backspace sub x3, x3, #1 b 1b 2: cmp x0, #0x0A // enter ? beq 4f // exit bl OutChar // printable: strb w0, [x5, x3] // store a char into buffer add x3, x3, #1 cmp x3, x4 bge 3f b 1b 3: sub x3, x3, #1 bl BackSpace b 1b 4: mov x0, #0 strb w0, [x5, x3] add x3, x3, #1 bl NewLine mov x0, x3 ldp x4, x5, [sp], #16 ldp x2, x3, [sp], #16 ldp x1, x30, [sp], #16 ret .endif