The CPUlator: https://cpulator.01xz.net/?sys=arm-de1soc.
ARM allows the creation of named subroutines, chunks of code that are the rough equivalent of functions in higher-level languages.
The following subroutine swaps the values in two locations in memory:
        
//-------------------------------------------------------
swap:
/*
-------------------------------------------------------
Swaps location of two values in memory.
Equivalent of: swap(*x, *y)
-------------------------------------------------------
Parameters:
  r0 - address of x
  r1 - address of y
Uses:
  r2 - value of x
  r3 - value of y
-------------------------------------------------------
*/
stmfd   sp!, {r0-r3}    // preserve other registers
ldr     r2, [r0]        // get value at x
ldr     r3, [r1]        // get value at y
str     r2, [r1]        // store value of x in y
str     r3, [r0]        // store value of y in x
ldmfd   sp!, {r0-r3}    // pop preserved registers
bx      lr              // return from subroutine
      
      and to call this subroutine we issue the instructions:
        
ldr    r0, =x   // store location of x
ldr    r1, =y   // store location of y
bl     swap     // call the swap subroutine
      
      The data is stored as:
        
.data
.align
x:
.word    9
y:
.word    4
      
      
        Download the full program: swap.s.
      
        Note that our subroutines are placed after the _stop B _stop
        instruction, but before the .data section. This guarantees
        that subroutines will be not executed by mistake.
      
        Loading values into r0 and r1 is the
        equivalent of passing a parameter - the subroutine documentation tells
        you that r0 must contain the address of x and
        r1 contains the address of y. These registers
        are arbitrary, and any could be used for this. There are more
        sophisticated methods for passing parameters to subroutines that are
        covered in later labs.
      
        The subroutine call is executed with the bl (Branch
          Link) instruction. Immediately before any line is executed, register pc
        (Program Counter), contains the address of the line. In this
        case, the pc contains the address 1008, the
        address of the bl instruction:
      
 
      
        and the bl instruction contains the address 1010,
        the address of the swap subroutine.
      
        When the bl instruction is executed, control of the program
        passes to the line labeled swap. The pc now
        contains the address of this line, in this case, 1010.
        However, the program needs to be able to return to the line following
        the bl instruction once the subroutine has finished. The L
        (Link) part of the bl instruction instructs the processor
        to copy the address of the instruction following the bl
        into Register lr (Link Register). In this case the
        address 100c is stored in lr:
      
 
      It is very important that the subroutine do two things:
Registers are preserved by pushing their values onto the system stack. The stack is an area of memory set aside for program use. A typical use is to push values onto it at the start of a subroutine and pop them off when finished. ARM provides instructions for pushing and popping multiple register values at once, which is far simpler than having to deal with the values on the stack one at a time.
        The stack pointer is store in the register sp (Stack
          Pointer). The instructions to push / pop the stack are:
      
stmfd (STore Memory Full Descending stack)
        ldmfd (LoaD Memory Full Descending stack)
        
        If multiple registers are adjacent, they can be listed in the push and
        pop with a dash (-) representing the registers in between.
        For example:
      
        
stmfd sp!, {r0, r3, r6}  // Pushes registers r0, r3, and r6
…
stmfd sp!, {r0-r4, lr}       // Pushes registers r0, r1, r2, r3, r4, and lr
      
      Subroutines should thus be written in such a way that any registers that are not actually being used to return values should be preserved. This includes the stack pointer and the stack itself, which must be returned to their original states at the end of the subroutine.
        The swap subroutine stores the values of r2,
        and r3 on the stack with the line:
      
        
stmfd  sp!, {r0-r3}
      
      and the stack contains:
| Address | Value | Description | 
|---|---|---|
| Call from main | ||
| fffffff0 | 1038 | contents of r0 | 
| fffffff4 | 103c | contents of r1 | 
| fffffff8 | 0 | contents of r2 | 
| fffffffc | 0 | contents of r3 | 
| 00000000 | 0 | bottom of stack | 
        When finished, r0 through r3 need to be set
        back to their original values by being popped off the stack. The
        following line does this:
      
        
// recover temporary registers
ldmfd sp!, {r0-r3}
      
      
        Note that if a register is being updated, or used to return a value
        (typically r0 is used for this purpose), then that register
        is not saved on the stack.
      
        The pc register needs to contain the address of the that
        followed the bl subroutine call so that the program knows
        where to continue execution. The following line does this:
      
        
bx    lr                 // return from subroutine
      
      
        The bx lr (Branch and eXchange) instruction copies
        the contents of lr to the pc register and
        continues program execution from that line.
      
        Program execution then continues from the address in the pc.
      
        This subroutine call process is not automated as it is in a high-level
        language like Python. It is up to you as the programmer to make sure the
        subroutine starts with the proper stmfd and ends with the
        proper ldmfd and that the pc register is
        properly reset with the bx instruction.
      
Forgetting to preserve a register may cause a Function clobbered register(s): error. (Details are found by clicking on the Details link that appears in the simulator Messages dialog box.) Although this warning can be turned off, for this lab at least make sure that you fix the error, rather than ignore it!
        For all subroutines fill in the registers used in the Parameters
        and Uses sections as appropriate.
      
            Complete the list_total subroutine in l05_t01.s. (Hint: you covered
            working with lists in Lab 4.) (Second Hint: the total is returned in
            r0 - do not preserve it.)
          
            Complete the list_stats subroutine in l05_t02.s.
          
            This program uses the same data as in the previous task. Note that
            the registers used for the list addresses are different. In
            CPULator, registers r0 through r3 can be
            used to return data from a subroutine. If the contents of registers
            r4 and are changed within a subroutine body and are not
            preserved, they will cause a Function clobbered
              register(s): error. Try it with this program - change the contents
            of r4 within the subroutine without preserving the
            register and see what happens.
          
Zip your files together in zip file named login_l05.zip (using your Laurier login, of course) and submit that zip file to the MLS dropbox.