如何在不使用 C 的情况下使用 aarch64 打印数字

How can I print Numbers with aarch64, without using C

如问题所述,我想在 aarch64 中打印多位数字。

通常在解决问题时我 python 首先看它们是否有效,特别是因为我对任何组装都很陌生。我的 pythonic 解决方案是

number = 1234

while number != 0:
   digit = 1234 % 10         # always gives me the last digit
   print(digit, end='')      # to print the digit and not the next line
   number = number - digit   # to remove the digit I just got so the last digit is 0
   number = number / 10      # to remove said last digit and shorten number by 1

我试图在 AARCH64 中实现这一点的方式:

printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x17, x16, x12        /* thanks to:  */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        mov  x0,  #1                   /* the print part */
        mov  x1,  x13                  /* the other part where I suspect a mistake */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

代码状态:它编译和运行没有问题,虽然它没有打印任何东西,但我无法调试,因为我用实际的 aarch64 设备编程并且没有使用模拟器(虽然我确信有一种方法我不知道)

我希望清楚我正在尝试做什么,我知道一些问题,比如我应该为其中的一些事情使用堆栈,但我希望我可以让它工作。我也希望可以看到我为此付出了一些努力并试图寻找这样的解决方案,但找不到这样做的人,或任何其他打印数字的非 c-lib 方式(请注意我不需要数字作为 str,我真的只想打印它)

正如 Jester 指出的那样,您无法打印的原因是 write 系统调用需要 指向要写入 [=13] 的数据的指针 =],而不是数据本身。您需要将您的角色存储到内存中的某个适当地址 (strb) 并将该地址传递给 x1.

一种方法是使用堆栈;只需从堆栈指针中减去(16的倍数,对齐)即可为自己分配一些内存,并记得在完成后将其放回原处。这是一个应该起作用的例子; XXX 是我添加或更改的行。

printNumberEntry:                      /* XXX */
        sub  sp, sp, #16               /* XXX allocate 16 bytes of stack space */
printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x14, x16, x12        /* XXX fix unrelated bug */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        strb w13, [sp]                 /* XXX Store the low byte of x13/w13 in memory at address sp */

        mov  x0,  #1                   /* the print part */
        mov  x1,  sp                   /* XXX x1 points to the byte to be written */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

exit:                                  /* XXX */
        add  sp, sp, #16               /* XXX restore stack before returning */
        ret                            /* XXX */

我还修复了一个不相关的错误:在您的 msub 中,x17 应该是 x14,因为那是商所在的位置。

另一种方法是在静态内存中保留一个字节:

        .bss
dataToWrite:
        .resb 1
        .text
printNumberEntry:                      /* XXX */
        adr  x1, dataToWrite           /* XXX keep address in x1 throughout */
printNumber:
        mov  x16, #10                  /* apparently need this for udiv/msub */
        udiv x14, x12, x16             /* x12 is the number I defined above, bc idk what registers are save to use (e.g. when syscall 64, print, happens, 0-8 are used) */
        msub x13, x14, x16, x12        /* XXX fix unrelated bug */
        sub  x12, x12, x13             /* x13 is what above is digit, x12 is number */
        udiv x12, x12, x16
        add  x13, x13, #48             /* digit to string, possible error source 1 */

        strb w13, [x1]                 /* XXX Store the low byte of x13/w13 at dataToWrite */

        mov  x0,  #1                   /* the print part */
        /* x1 already contains the proper address */
        mov  x2,  #1
        mov  w8,  #64
        svc  #0

        cmp  x12, #0                   /* the loop part */
        beq  exit                      /* genereric exit method I left out */
        b    printNumber

exit:                                  /* XXX */
        ret                            /* XXX */
        /* your code */

缺点是这会使您的函数无法用于信号处理程序、多线程等,因为它们都将尝试使用相同的字节。

其他说明:

  • 数字当然是倒序打印的。这是我猜你以后会做的事情。

  • sub 和第二个 udiv 是不必要的,因为第一个 udiv 已经产生了四舍五入的商(像 Python 的x14 = x12 // 10)。所以你可以用 mov x12, x14.

    替换这两条指令
  • 您的几个寄存器在整个函数 (x16, x1, x2, x8) 中保持恒定值,因此您可以在循环外初始化它们,而不是在每次迭代时重复重复。 (注意 x0 被系统调用的 return 值覆盖,所以你每次都需要重新初始化它。)

  • 真的想找到一种能够使用调试器的方法。否则,请准备好应对更多情况,在这些情况下,您会花半小时写一个 Whosebug 问题,然后等待更多小时才能得到答案,因为您可能会在三分钟内使用调试器找到自己的错误。如果你不能在你的 Android 设备上安装 gdb,那么考虑设置另一个 Linux AArch64 盒子(比如 Raspberry Pi 4,或者云服务器,甚至是qemu 模拟器),你可以。