💻 Paper 1 · 1.4 Assembly Language Programming
1.4.1 Subroutines, Stacks and CALL/RET
Cambridge 9618 · International A Level Computer Science · ~14 min read
Notes
Video
Slides
Quiz
Worksheet

Subroutines in Assembly Language

A subroutine (also called a procedure or function) is a named block of code that can be called from multiple points in a program. Using subroutines avoids code duplication and makes programs easier to read and maintain.

In Cambridge 9618 assembly language, subroutines are implemented using:

  • CALL label — call the subroutine at the given label; the return address is saved automatically
  • RET — return from the subroutine back to the instruction after the CALL

How CALL Works

When CALL SUBR is executed:

  • The current value of PC (the return address — address of the next instruction after CALL) is automatically pushed onto the stack
  • The PC is set to the address of SUBR — execution jumps to the subroutine

When RET is executed:

  • The return address is popped from the stack into the PC
  • Execution continues from where the CALL was made

The Stack

The stack is a region of memory that operates on a Last In, First Out (LIFO) basis. The Stack Pointer (SP) register keeps track of the top of the stack.

  • PUSH: decrements SP, then writes data to the location pointed to by SP
  • POP: reads data from the location pointed to by SP, then increments SP
Stack during subroutine call (stack grows downward):
addr 500:[ free ]
addr 499:return address (e.g. 0120)← SP (after CALL)
addr 498:[ lower addresses ]

Nested Subroutines

Subroutines can call other subroutines. The stack handles multiple return addresses automatically — each CALL pushes a new return address, and each RET pops the most recent one (LIFO). This is why a stack is the ideal data structure for managing subroutine calls.

Complete Example: Subroutine Call

; MainLDM#5; ACC = 5
CALLDOUBLE; Call subroutine — pushes return addr to stack, jumps to DOUBLE
STORESULT; Store doubled value (returned in ACC)
END; Halt
; Subroutine
DOUBLE:ADDACC; Wait — wrong approach; we use the result in the register
LSL#1; Shift ACC left by 1 = multiply by 2 (doubling ACC)
RET; Return — pops return address from stack into PC
RESULT:0; Data location

Using PUSH and POP to Preserve Registers

If a subroutine modifies the ACC or other registers, the original values in those registers (needed by the calling program) are lost. To preserve register values across a subroutine call:

  • PUSH ACC at the start of the subroutine to save ACC to the stack
  • Use ACC for subroutine operations
  • POP ACC at the end of the subroutine to restore the original value before returning

This is known as a callee-saved (subroutine saves and restores) convention.

Parameter Passing via the Stack

Parameters can be passed to subroutines by pushing them onto the stack before the CALL. The subroutine then pops them off the stack to read the values. Return values can also be passed back via the stack (push before RET, pop after CALL) or returned in the ACC.

Summary Table

InstructionEffect
CALL labelPush PC (return address) to stack; jump to label
RETPop return address from stack into PC; resume after CALL
PUSH ACCDecrement SP; write ACC to Mem[SP]
POP ACCRead Mem[SP] into ACC; increment SP
Exam tip: Cambridge questions often ask you to describe what happens to the stack during CALL and RET. Key points: CALL pushes the return address (the address of the instruction AFTER the CALL) onto the stack; RET pops it off. The stack uses LIFO — this allows nested subroutine calls to work correctly because each CALL pushes a new return address on top.
⚠️ Common Mistakes
  • Saying CALL saves the address of the CALL instruction itself — CALL saves the address of the NEXT instruction after CALL (so execution resumes there after RET)
  • Confusing the stack pointer direction — conventionally the stack grows downward (SP decrements on PUSH, increments on POP)
  • Not understanding that RET must match CALL — every CALL needs a RET at the end of the subroutine
  • Forgetting to POP in reverse order — if you push ACC then IX, you must pop IX then ACC (LIFO)
✅ Notes completed!
Video coming soon
Click slide or press arrow keys to navigate

Worksheet — 1.4.1 Subroutines and the Stack

8 questions · instantly marked · Cambridge 9618 standard

Q1Explain what a subroutine is and give two benefits of using subroutines in assembly programs.[3]
✅ Mark scheme
Mark scheme
A subroutine is a named block of code that can be called from multiple points in a program [1]; benefits: avoids code duplication — write once, call many times [1]; makes programs easier to read, maintain and debug [1].
Q2Describe exactly what happens to the stack and the Program Counter when CALL SUBR is executed.[3]
✅ Mark scheme
Mark scheme
The address of the NEXT instruction after CALL (the return address) is pushed onto the stack [1]; the SP is decremented [1]; the PC is set to the address of SUBR — execution jumps to the subroutine [1].
Q3Describe what happens when RET is executed.[2]
✅ Mark scheme
Mark scheme
The return address is popped from the top of the stack [1]; the SP is incremented; this value is loaded into the PC so execution continues with the instruction that follows the CALL in the main program [1].
Q4Why is the stack (LIFO structure) ideal for managing subroutine return addresses, especially when subroutines call other subroutines?[3]
✅ Mark scheme
Mark scheme
Each CALL pushes a new return address on top of the previous one [1]; when RET is executed, it pops the most recently pushed address — returning to the most recently called subroutine's caller [1]; this LIFO order correctly mirrors the nesting of subroutine calls so the program always returns to the right place [1].
Q5Explain why a subroutine might need to PUSH ACC at the start and POP ACC at the end. What problem does this solve?[3]
✅ Mark scheme
Mark scheme
If the subroutine uses ACC for its own operations, the original value in ACC (from the calling program) is overwritten [1]; PUSHing ACC at the start saves the original value to the stack; POPping it at the end restores it before returning [1]; this ensures the calling program's register state is preserved — it sees the same register values before and after the CALL [1].
Q6State the effect of PUSH and POP on the Stack Pointer (SP).[2]
✅ Mark scheme
Mark scheme
PUSH: SP is decremented (stack grows downward); data is written to the new top of stack [1]; POP: data is read from the current top of stack; SP is incremented [1].
Q7A subroutine PUSHes both ACC and IX at the start. In what order must they be POPped at the end?[2]
✅ Mark scheme
Mark scheme
They must be POPped in reverse order to how they were pushed [1]; if ACC was pushed first then IX, then POP IX first, then POP ACC — because the stack is LIFO: last pushed (IX) must come off first [1].
Q8Write a short assembly subroutine called HALVE that divides the value in ACC by 2 using LSR, then returns.[3]
✅ Mark scheme
Mark scheme
HALVE: LSR #1 [1] (shift right 1 bit = divide by 2); RET [1]; label HALVE at start [1]. Accept with PUSH/POP if subroutine preserves registers.
Topic Quiz
Question 1 of 15
You scored
out of 15
Card 1 of 8
Click to reveal definition
🎉
All cards reviewed!
TermDefinition
🎯

Mini Test — 1.4.1 Subroutines & Stack

10 questions · 10 marks · 10 minutes

← 1.3.6 Virtual Memory
24 of 82 · Cambridge 9618
1.4.2 Addressing Modes →