Jumping into ARM assembly


Ok, so doing that manually still gives me something in r0, but I don't know what to do with it :
r0 0xfffffff2 4294967282
Here's what the syscall function does with that:
Code:
cmn      r0, #4096
it       cc
RETINSTR(cc, lr)
b        PLTJMP(syscall_error)

Which if I understand correctly means you got an error code from the syscall. :)
 
Ok, so I did this :

sub sp, sp, #16
mov r0, sp
mov r2, #0
mov r1, #16
mov r7, #384
swi 0
ldr r5, [sp]

Then I use the display routine.
Sometimes I got random numbers, sometimes things like lonely random chars.
 
You should also check r0 after "swi 0". The value should be the same as the input buffer size (16 in your case).
 
You should also check r0 after "swi 0". The value should be the same as the input buffer size (16 in your case).
Yes it's put to 16 right after the swi.

What's that mean exactly? Are you increasing sp again at some point?
No, here's the code :
Code:
.text
.align 2
.include "macro.mac"

.global _start

_start:

        sub sp, sp, #16
        mov r0, sp
        mov    r2, #0
        mov    r1, #16
        mov     r7, #384
        swi 0
        mov    r0, #0 @have to do that otherwise the display part doesn't work
        ldr     r5, [sp]

  mov r2, #1                    @ buffer length, to write one character, or a newline
  mov r11, #0                   @ set the digit counter

_loop:
  add r11, #1                   @ increment the digit counter
  cmp r5, #10                   @ compare the number with 10
  blt _end                      @ branch if < 10

  mov r8, r5
  ldr r10, =div_10_reciprocal   @ load the magic number for /10
  ldr r10, [r10]
  umull r3, r4, r5, r10         @ r4 = r5 / 10, r3 is overwritten
  mov r5, r4                    @ save the divided value
  mov r10, #10
  mul r9, r4, r10               @ multiply the divided value by 10
  sub r8, r9                    @ substract the original value to get the remainder
  push {r8}                     @ push the remainder on the stack
  b _loop                       @ back to loop

_end:
  mov r8, r5                    @ push the last digit, with the highest weight
  push {r8}

  ldr r1, =asciicounter         @ point on =asciicounter
  add r6, r11, #48              @ convert the digit counter to ascii
  str r6, [r1]                  @ store it to be displayed

  syscall sys_write
  ldr r1, =asciinewline         @ point on the buffer to write, \n, a newline
  syscall sys_write

_reversed:
  pop {r8}                      @ display the number, beginning with the highest weight
  ldr r1, =asciicounter         @ point on =asciicounter
  mov r6, r8
  add r6, #48                   @ convert the number to ascii
  str r6, [r1]                  @ store it to be displayed
  syscall sys_write
  subs r11, #1                  @ the digit counter is now used for the number display loop
  bne _reversed

  ldr r1, =asciinewline         @ point on the buffer to write, \n, a newline
  syscall sys_write
 
  syscall sys_exit


.data
  asciicounter:      .word 0                 @ must be initialized
  asciinewline:      .ascii "\n"             @ \n is a newline
  div_10_reciprocal: .word 429496730         @ ceil((2^32)/10)

Macro :
Code:
sys_exit        =1
sys_write       =4
sys_getrandom   =384
 stdout =1
 
.macro syscall iint
 mov r7,#\iint
 swi 0
.endm
 
I just don't really understand what you're saying happens when it goes wrong.

There's probably easier ways to produce the output for debugging purposes to see if it's the print routine that's causing problems with some numbers. Like displaying it as a fixed width hex string, piping the raw output to another program, or linking with a libc and using printf.
 
Thanks for the tip, i tried with the hexa version, looks ok.
It's the decimal display routine which goes nuts with certain numbers (e.g. 3920113095). I'll try to understand why.

--edit: Oh, 3221225472 is reported to be <10 by the cmp r5, #10 !!!
 
Last edited:
--edit: Oh, 3221225472 is reported to be <10 by the cmp r5, #10 !!!
3221225472 is C0000000 in hex which interpreted as a signed integer is actually -1073741824 which is <10 (negative because highest bit is set).
 
Lesson learned !
I found this code for absolute value :
cmp r0, #0
rsblt r0, r0, #0
 
Lesson learned !
I found this code for absolute value :
cmp r0, #0
rsblt r0, r0, #0

For the purposes of what you're doing it'd be just as well to just clear the sign bit, but you really don't need to do this, just use unsigned conditions. Instead of this:

Code:
cmp r5, #10                   @ compare the number with 10
  blt _end                      @ branch if < 10

Do this:

Code:
cmp r5, #10                   @ compare the number with 10
  blo _end                      @ branch if < 10
 
I'm macroing the getrandom, so I'll stay with the abs for now, maybe I'll try to isolate a digit to have 0..9 . Of course I also now have another problem, some numbers end with / ^^.
Anyway, this blo instruction is nice to know, thx !
 
Hmm, from what I can see the 'lo' conditional is equivalent ot 'cc' i.e. carry clear (unset). I don't have in my headspace how cmp sets the flags but assuming it sets carry clear when the left hand is less than the right hand, I don't see what is gained from this change. What am I missing?

Also, in @LinuxSWAT's code, I'm not a fan of creating a 16 byte space on the stack to begin with. You've already got a data section, if you need some space, put it in there and leave the stack alone! Especially because I can't see where you're setting the stack back, which is likely to upset anything that's calling your code (such as your OS) unless it backs up the stack in case anyone does something daft with it like this.
 
I think it's ok now in my new macro, I push and pop the sp.
Find it more convenient with the sp, not to have offsetting.
 
Hmm, from what I can see the 'lo' conditional is equivalent ot 'cc' i.e. carry clear (unset). I don't have in my headspace how cmp sets the flags but assuming it sets carry clear when the left hand is less than the right hand, I don't see what is gained from this change. What am I missing?

blt is branch if signed less than, blo is branch if unsigned less than (branch if "lower"). The former won't work if you interpret the numbers as unsigned, which is necessary for the print algorithm to work when printing large unsigned numbers.

cmp is a subtraction that doesn't store the results. Z and N are set if the result is zero or negative respectively. C is cleared (not set) if there's an unsigned underflow, meaning that the result of subtracting two unsigned numbers can't be represented as an unsigned number (in other words, would be negative). V is set if there's a signed underflow or overflow, which means that the result can't be represented as a signed 32-bit number, either because it's smaller than the minimum possible number or larger than the maximum.

If you're dealing with unsigned numbers checking the C flag is sufficient, hence why the "lo" condition is the same as the "cc" condition.

With signed numbers it isn't enough to check the V flag. If you look at the four possible permutations of the N and V flags after (A - B):

N V
0 0 Result is positive (or zero) without overflow. Therefore, A >= B.
0 1 Result is positive (or zero) with overflow, meaning the true result would have been negative. Therefore, A < B.
1 0 Result is negative without overflow. Therefore A < B.
1 1 Result is negative with with overflow, meaning the true result would have been positive (or zero). Therefore, A >= B.

So the two middle results where ((N == 0) & (V == 1)) or ((N == 1) & (V == 0)). Another way to evaluate this is (N != V)

Also, in @LinuxSWAT's code, I'm not a fan of creating a 16 byte space on the stack to begin with. You've already got a data section, if you need some space, put it in there and leave the stack alone! Especially because I can't see where you're setting the stack back, which is likely to upset anything that's calling your code (such as your OS) unless it backs up the stack in case anyone does something daft with it like this.

Stack is really the preferable method for storing temporary data, that's what it's there for.

By storing things like this at fixed global locations you make the code non-reentrant (not callable by multiple threads simultaneously or by nested function calls), much more of a headache to deal with for position independence, possibly slightly less cache friendly, and needing more overhead to access.

The stack isn't some kind of system-wide entity, the OS doesn't expect it or other items like heap allocations or file handles to be freed before application exit. Otherwise the OS wouldn't be able to clean up a program very well if it crashes.
 
Ah right, yeah thinking through how you do 64-bit subtraction with an SUBS and an SBC explains why it would clear carry if there's an unsigned underflow, and if CMP is basically a SUBS that doesn't set the result then that ties together.

Good points about the stack. I guess with multitasking systems, the system has to give each process its own stack to work with, otherwise context switches would screw up everyone's stacks. I'd still be tempted to use OS calls to allocate space on the heap, if reentrancy was demanded, but I guess I'm showing my background working with single tasking embedded systems that didn't have a lot of stack space configured. It would certainly be a lot more work than taking a constant number off sp and adding it back on at the end (assuming a descending stack) though, and I guess for a mere 16 bytes it would probably even have been okay on the systems I used to work on.
 
Latest version of decimal displayer, fixing big signed numbers, and number of digits display :
Code:
.text
.align 2
.include "macro.mac"

.global _start

_start:
  mov r2, #1                    @ buffer length, to write one character, or a newline
  mov r5, #4096                 @ set the number
  mov r11, #0                   @ set the digit counter

_loop:
  add r11, #1                   @ increment the digit counter
  cmp r5, #10                   @ compare the number with 10
  blo _end                      @ branch if unsigned < 10

  mov r8, r5
  ldr r10, =div_10_reciprocal   @ load the magic number for /10
  ldr r10, [r10]
  umull r3, r4, r5, r10         @ r4 = r5 / 10, r3 is overwritten
  mov r5, r4                    @ save the divided value
  mov r10, #10
  mul r9, r4, r10               @ multiply the divided value by 10
  sub r8, r9                    @ substract the original value to get the remainder
  push {r8}                     @ push the remainder on the stack
  b _loop                       @ back to loop

_end:
  mov r8, r5                    @ push the last digit, with the highest weight
  push {r8}
  ldr r1, =asciicounter         @ point on =asciicounter

  cmp r11, #10                  @ simple way to display the number of digits
  blo _onedigit                 @ with a length of 10 as a particular case

  mov r6, #49                   @ procedure to display 10
  str r6, [r1]                  @ store 1 to be displayed
  syscall sys_write
  mov r6, #48
  str r6, [r1]                  @ store 0 to be displayed
  b _endigit

_onedigit:
  add r6, r11, #48              @ convert the digit counter to ascii
  str r6, [r1]                  @ store it to be displayed

_endigit:
  syscall sys_write
  ldr r1, =asciinewline         @ point on the buffer to write, \n, a newline
  syscall sys_write

_reversed:
  pop {r8}                      @ display the number, beginning with the highest weight
  ldr r1, =asciicounter         @ point on =asciicounter
  mov r6, r8
  add r6, #48                   @ convert the number to ascii
  str r6, [r1]                  @ store it to be displayed
  syscall sys_write
  subs r11, #1                  @ the digit counter is now used for the number display loop
  bne _reversed

  ldr r1, =asciinewline         @ point on the buffer to write, \n, a newline
  syscall sys_write
 
  syscall sys_exit

.data
  asciicounter:      .word 0                 @ must be initialized
  asciinewline:      .ascii "\n"             @ \n is a newline
  div_10_reciprocal: .word 429496730         @ ceil((2^32)/10)

macro.mac :
Code:
sys_exit   =1
sys_write   =4
 stdout   =1
 
.macro syscall iint
 mov r7,#\iint
 swi 0
.endm
 
The div_10_reciprocal:

.word 429496730 @ ceil((2^32)/10)
ldr r10, =div_10_reciprocal @ load the magic number for /10
ldr r10, [r10]
umull r3, r4, r5, r10 @ r4 = r5 / 10, r3 is overwritten

technique doesn't work for r5=0x80000000 .
I got 214748365 instead of 214748364 !!!

Is there any reliable method out there ?
 
That'll be a rounding error, introduced because 2**32 doesn't divide precisely into 10 in integer paths (and unfortunately, the number ends in a 6 so you're 0.4 off the floating point answer). Because in that umull you're discarding the bottom 32 bits of the multiplication, that discrepancy mostly doesn't matter until you're trying to multiply a pretty big number.

Have you investigated how the discrepancy results through the entire number range? I've written a three line python script and it looks to me the most you're over out is by +1, but problems start a fair bit before 0x80000000 - they're quite common even above 0x40000000. I'm still trying to see where it first fails.

Edit: 0x3fffffb5 seems to be the first failure, though I've not checked 0x2fffffff and below.
 
Last edited:
Back
Top