How to print a number in ARM assembly?
The syscall write takes on the second argument (r1) as a pointer to the string you want to print. You are passing it a pointer to an integer, which is why it's not printing anything, because there are no ASCII characters on the memory region you are passing to it.
Below you'll find a "Hello World" program using the syscall write.
.text
.global main
main:
push {r7, lr}
mov r0, #1
ldr r1, =string
mov r2, #12
mov r7, #4
svc #0
pop {r7, pc}
.data
string: .asciz "Hello World\n"
If you want to print a number you can use the printf function from the C library. Like this:
.text
.global main
.extern printf
main:
push {ip, lr}
ldr r0, =string
mov r1, #1024
bl printf
pop {ip, pc}
.data
string: .asciz "The number is: %d\n"
Finally, if you want to print the number with the syscall write you can also implement a itoa function (one that converts an integer to a string).
Add and print two numbers in ARM assembly
Your fix works because all architectures follow a certain calling convention, where registers are mapped to function parameters and return value. In this case, scanf
takes two parameters so according to the ARM EABI calling convention the first parameter would be in r0 and the second in r1.
How can I print Numbers with aarch64, without using C
As Jester points out, the reason you can't print is that the write
system call expects a pointer to the data to be written in x1
, not the data itself. You need to store your character to some appropriate address in memory (strb
) and pass that address in x1
.
One approach would be to use the stack; just subtract from the stack pointer (in multiples of 16, for alignment) to allocate some memory for yourself, and remember to put it back when you're done. Here's an example that should work; XXX
are the lines I added or changed.
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 */
I also fixed an unrelated bug: in your msub
, x17
should be x14
, since that's where the quotient is.
Another approach would be to reserve a byte in static memory:
.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 */
The downside is this will make your function unusable for signal handlers, multiple threads, etc, as they will all try to use the same byte.
Other notes:
The digits print in reverse order, of course. That's something I guess you will work on later.
The
sub
and secondudiv
are unnecessary, because the firstudiv
already produces the rounded-down quotient (like Python'sx14 = x12 // 10
). So you could replace those two instructions with justmov x12, x14
.Several of your registers hold constant values throughout the whole function (
x16, x1, x2, x8
), so you could initialize them outside the loop instead of redundantly redoing it on every iteration. (Notex0
is overwritten with the system call's return value, so you do need to reinitialize it each time.)You really want to find a way to be able to use a debugger. Otherwise, get ready for a lot more instances where you spend half an hour writing a StackOverflow question and waiting many more hours for an answer, for a bug that you could have found yourself in three minutes with a debugger. If you can't install
gdb
on your Android device, then consider setting up another Linux AArch64 box (say a Raspberry Pi 4, or a cloud server, or even a qemu emulator) where you can.
ARM printing hexadecimal number from register (not using C)
I suspect that SWI_WriteC
takes the ASCII code of a character to print and then prints it to the console. So, if r0
is 2 on entry, it will print the character with code 2.
However, the character with ASCII code 2 is not the digit 2. It is a non-printing character. The ASCII code for the digit 2 is 50, or 0x32. In fact, the codes for digits 0 to 9 are 48 to 57, or 0x30 to 0x39, with the letters A to F at 65 to 70 (0x41 to 0x46) and lower-case letters a to f at 97 to 102 (0x61 to 0x66).
So, what you want is a routine that takes a number in the range 0 to 15, converts it into the ASCII code for the relevant digit or letter and then passes that ASCII code to SWI_WriteC
.
That will work, assuming you don't have any more than 15 vowels. If you have 16 or more, your hex number will have two or more digits, and this is where things get a little trickier.
Related Topics
How to Join Multiple PDF Pages to a Single Page
What's the Max File Mapping Size in 64Bits MAChine
How Can a Process Try to Access Other Process's Memory in Linux Virtual Memory System
How to Create a File with Any Given Size in Linux
Linux Kernel Headers' Organization
Fatal: Git Was Built Without Support for Git-Add--Interactive (No_Perl=1)
Kubernetes Can't Start Due to Too Many Open Files in System
How to Determine the Precise Set of Environment Variables a Systemd Environmentfile Would Set
Suppress or Prevent Duplicate Inotifywait Events
Installing Multiple Versions of R
Shell Command to Update Pom File from a Variable
How to Get Only Filenames Without Path by Using Grep
How to Monitor Newly Created File in a Directory with Bash