Subroutines

Subroutines & the Stack

Writing reusable functions with proper register saving.

Callee-Save Convention

When writing a subroutine, you should save any registers you'll modify (except the return value) and restore them before returning. This way the caller's data is preserved.

The standard approach uses the stack — a region of memory managed with R6 as the stack pointer.

The Stack

The LC-3 stack grows downward (toward lower addresses). R6 is the stack pointer by convention.

Push: Decrement R6, then store Pop: Load, then increment R6
; Push R0 onto the stack
ADD R6, R6, #-1    ; Make room
STR R0, R6, #0     ; Store R0 at top of stack

; Pop from stack into R0
LDR R0, R6, #0     ; Load top of stack
ADD R6, R6, #1     ; Shrink stack

Complete Subroutine Example

Here's a subroutine that multiplies R0 by R1 using repeated addition:

.ORIG x3000
; Initialize stack pointer
LD R6, STACK

; Set up arguments
AND R0, R0, #0
ADD R0, R0, #6    ; R0 = 6
AND R1, R1, #0
ADD R1, R1, #7    ; R1 = 7

; Call MULTIPLY
JSR MULTIPLY

; R0 now contains 42
HALT

;---------------------------
; MULTIPLY: R0 = R0 * R1
; Uses R2 as counter, R3 as accumulator
;---------------------------
MULTIPLY
  ; Save registers
  ADD R6, R6, #-1
  STR R7, R6, #0    ; Save R7 (return address)
  ADD R6, R6, #-1
  STR R2, R6, #0    ; Save R2
  ADD R6, R6, #-1
  STR R3, R6, #0    ; Save R3

  ; R3 = 0 (accumulator), R2 = R1 (counter)
  AND R3, R3, #0
  ADD R2, R1, #0

  MUL_LOOP
    ADD R2, R2, #0
    BRz MUL_DONE
    ADD R3, R3, R0   ; accumulator += R0
    ADD R2, R2, #-1  ; counter--
    BR MUL_LOOP

  MUL_DONE
  ADD R0, R3, #0     ; Result in R0

  ; Restore registers
  LDR R3, R6, #0
  ADD R6, R6, #1
  LDR R2, R6, #0
  ADD R6, R6, #1
  LDR R7, R6, #0
  ADD R6, R6, #1

  RET

STACK .FILL xFE00
.END

Always save and restore R7 in subroutines that call other subroutines (JSR overwrites R7). The stack makes this clean and systematic.

Exercise

Write a subroutine called ABS that computes the absolute value of R0. If R0 is negative, negate it. If positive or zero, leave it unchanged. The main program should test with R0 = -3 and print the result.

1
R0
x00000
R1
x00000
R2
x00000
R3
x00000
R4
x00000
R5
x00000
R6
x00000
R7
x00000
PCx3000
CC
NZP