Meet the Altair · Calling a subroutine
Remembering
where to return.
A program can ask another piece of code to do a job, then continue where it left off. Our familiar printing loop will do the same job twice, returning to a different place each time.
Begin here: Step once to set SP to 0200. Step again to execute CALL. PC becomes 000A, SP becomes 01FE, and the two stack bytes hold 06 and 00. No character has been read or printed yet. The CPU has saved where to come back.
Loaded subroutine program · reference card All addresses and bytes here are hexadecimal
| Address | Reference | RAM | Check |
|---|
0000: LXI SP,0200H chooses a stack starting point. 0003 and 0006: CALL 000AH call the same routine, saving 0006 and 0009 respectively. 0009: HLT finishes after the second return.
000A: LXI H,0100H resets the message pointer. 000D: MOV A,M, 000E: CPI 0, and 0010: JZ 0019H check for NUL. 0013: OUT 01H, 0015: INX H, and 0016: JMP 000DH print each nonzero byte. 0019: RET resumes at the address read from the stack.
The lesson checks all twenty-six program bytes. Message and stack data can be edited while stopped. The CPU reads the saved address from RAM; the viewer does not choose the return destination.
This lesson connects an 8080, 65,536 bytes of RAM, and a teaching output device at port 1. It calls one printing routine twice, then halts. Start again restores program and message bytes, clears the stack RAM, registers, flags, halted state, output, counts, and history, and stops at PC 0000. It is not the hardware RESET switch.
Give a piece of code a job.
A subroutine is a piece of a program that can be called to do a job and then return. Here, its job is to print the zero-terminated message at 0100.
The routine begins at 000A. It sets HL to 0100, reads through HL, and sends each nonzero byte with OUT. When it reaches NUL, it executes RET. The surrounding code—the caller—calls it twice, then halts.
The printing instructions occur once in RAM. Both calls execute those same instructions and read the same message. Each call resets HL, so the second one starts at H again.
Remember the instruction after the call.
You have used JMP to change PC. CALL also saves a return address before changing PC. The saved address is where execution should continue after the routine finishes.
The CALL at 0003 occupies three bytes: CD 0A 00. Its destination is 000A, but the next instruction is at 0006. The CPU saves 0006, then sets PC to 000A. The destination and the return address have different jobs.
CALL does not execute the whole routine as one step. After it finishes, PC points at the routine's first instruction. Its loads, comparisons, branches, and outputs still run one instruction at a time.
Keep the address in RAM.
The 8080 keeps saved return addresses in ordinary RAM. SP, the stack pointer, tells it where to put and find them. The first instruction, LXI SP,0200H, chooses a starting point. Setting SP does not itself write any memory.
CALL decreases SP to 01FF and writes the high byte of the return address. It decreases SP again to 01FE and writes the low byte. The stack grows toward lower addresses; SP now points at the low byte.
The table lists RAM in ascending address order, so it shows the low byte first. For address 0006, that is 06 at 01FE and 00 at 01FF. These are pieces of an address, not characters to print.
We use only these two stack locations because the first call returns before the second begins. The CPU does not know that the picture has two rows, and it does not check whether a chosen stack area overlaps other data or code.
Read the way back.
When the print loop finds NUL, its conditional jump selects RET at 0019. RET reads the low byte at SP and the high byte at the following address, advances SP past both, and puts the combined address into PC.
For the first return, that makes PC 0006 and SP 0200. The caller is ready to continue. A, HL, and flags keep the values left by the routine; RET does not restore their earlier values.
Reading does not erase RAM. The saved 06 and 00 remain visible after SP moves above them. They are no longer an outstanding return address. Another call can reuse those locations.
The same routine, a different continuation.
The next instruction is the CALL at 0006. It has the same destination, 000A, but its following instruction is at 0009. This call saves 0009 in the reused stack locations, replacing the low byte 06 with 09.
RET itself is still the same single byte, C9, at 0019. It has no fixed destination written after it. Its destination comes from the bytes in RAM at SP.
After the second return, PC is 0009 and SP is 0200. The caller's HLT then stops the CPU. PC advances to 000A, but the CPU is halted: that address alone does not mean the routine will run a third time.
A return address is data too.
Because RET reads RAM, changing the saved bytes changes where it goes. There is no hidden list in the viewer that sends the CPU back to its caller.
Skip the second call by changing the saved address
Start again and step twice to execute the first CALL. EXAMINE 01FE, set the switches to 0009, and DEPOSIT. The last-instruction explanation still reports the 0006 that CALL originally saved. Then EXAMINE 000A to restore PC and RUN. The routine prints one HELLO line, but RET now returns directly to HLT at 0009, skipping the second call.
This experiment deliberately changes the program's continuation. The lesson guards instruction bytes and pauses at unknown instruction addresses; a real 8080 does not check that a return lands where the programmer intended. Start again to restore the ordinary two-call path.