浮動小数点文字列変換()

これから 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

サンプルプログラム

ラベル (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 <mizutani.jun@nifty.ne.jp>
// 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

続く...



このページの目次