Search code examples
assemblyarmraspberry-pi2

Issue with calling multiple labels/functions in assembly


I've been learning assembly programming recently and am trying to make a program for an assignment. I have a function/label that draws a line on the screen.

The problem is that after calling the function the first time it doesn't get called the second time. I'm using 'bl label' to call the function and 'bx lr' to return to the entry point.

The compiler I'm using is FASM v1.43, running on a Raspberry Pi 2

part of the main file responsible for calling drawline:

mov r4, #309 ;x
mov r5, #219 ;y

;draw veritcal line   
push{r11,r10,r5,r4}

;vertical or horizontal
mov r10,#1

;length
mov r11,$0100
orr r11,$0003

bl drawline
pop{r4,r5,r10,r11}

;draw second vertical line
push{r11,r10,r5,r4}
mov r10,#1
mov r4, #349 ;x
mov r11,$0100
orr r11,$0003

bl drawline
pop{r4,r5,r10,r11} 

code in drawline that gets called:

rect_vloop:
   push {r0-r3}
   mov r0,r7    ;screen address
   mov r1,r4 ;x
   mov r2,r5 ;y
   mov r3,r6 ;colour
     ;assume BITS_PER_PIXEL, SCREEN_X are shared constants
   bl drawpixel
   pop {r0-r3}

;increment and test
   add r5,#1
   mov r8,r11
   cmp r5,r8
bls rect_vloop

dl_end:
bx lr  

I didn't write drawpixel and I already know that it works.

The first line draws onto the screen like it should but the second doesn't draw, removing the first call makes the second draw, so I'd say I've messed up returning the function but I can't tell what i did wrong.

Thanks in advance.


Solution

  • Well, not all of your code is present in the question, but the main thing that stands out is that you are not preserving the correct registers within and between your functions.

    The ARM Application Binary Interface designates r0-r3 for passing parameters and return values, so preserving the values of r0-r3 in your function is not correct. Indeed the ABI states that

    A subroutine must preserve the contents of the registers r4-r8, r10, r11 and SP (and r9 in PCS variants that designate r9 as v6)

    and if you're not sure what the status of r9 is on your platform, play safe and preserve it too.

    So you need to preserve anything important in r0-r3 and r12 before calling a function, and preserve r4-r11 inside it. You will also need to ensure that the function's stack usage is balanced, hence preserving sp (r13).

    You must also preserve the link register (lr, r14) if your function calls any other functions, otherwise you lose your return address (this step is also missing in drawline as written).

    Of course if the only functions you ever call are your own, and your functions are only called by your own code, you can violate the ABI - but I don't know why you'd want to, and the preservation of lr will still be required.

    FYI the order of registers in your push and pop instructions (actually STMDB and LDMIA) doesn't matter; the instructions are encoded with bitfields describing the registers to be saved or loaded, and the order in which they are stored and retrieved is with ascending register numbers occupying ascending memory locations. Most ARM assemblers will warn you if you try to specify the register list in anything other than ascending order because they assume that you're trying to obtain some specific load or store ordering which you won't get.

    Finally note that you should make it a habit to push and pop even numbers of registers to maintain 8-byte stack alignment - perhaps you already know this, because you're doing it in the code you presented. This helps your code to be compatible with existing code that requires 8-byte stack alignment, of which there is plenty.