Meet the Altair · Nested calls
A routine
inside a routine.
The printing routine already knows how to return to its caller. Now it asks another routine to send each character. Can the CPU remember both ways back?
Begin here: Step seven instructions. PC is 001B and SP is 01FC. The stack holds two addresses: 0016 for the character routine to return to, and 0006 for the printing routine. A holds 72, the H, but nothing has been printed yet.
Loaded nested-call 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 printing routine, saving 0006 and 0009 respectively. 0009: HLT finishes.
000A: LXI H,0100H resets the message pointer. 000D: MOV A,M, 000E: CPI 0, and 0010: JZ 001AH check for NUL. 0013: CALL 001BH calls the character routine, saving 0016. 0016: INX H advances HL; 0017: JMP 000DH repeats. 001A: RET returns to the main program.
001B: OUT 01H sends A. 001D: RET returns to the printing loop.
The lesson checks all thirty program bytes. Message and stack data can be edited while stopped. Each RET reads the address at SP in RAM; it has no fixed 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 smaller routine one job.
Our main program still calls the printing routine twice, then halts. The printing loop still reads the zero-terminated message at 0100. This time, instead of executing OUT itself, it calls a character routine at 001B.
That small routine contains just two instructions: OUT 01H sends the byte in A, then RET returns. The printing loop supplies the character in A and advances HL after the character routine finishes.
A routine calling another routine is a nested call. “Inside” describes the calls: the character routine is still ordinary code at its own addresses in RAM.
Keep the first address. Add another.
The first CALL, at 0003, saves 0006 in 01FE–01FF and enters the printing routine at 000A. SP is now 01FE, just as in the previous lesson.
After loading H into A and checking that it is not NUL, the printing loop reaches CALL at 0013. Its destination is 001B, but the instruction after this call is at 0016. That is the second return address.
CALL decreases SP and writes the high byte 00 at 01FD. It decreases SP again and writes the low byte 16 at 01FC. The first saved address is untouched. With SP at 01FC, both ways back are waiting in RAM.
The four-row window lists addresses in ascending order. Its “inner” and “outer” labels describe how this program uses those locations; the CPU sees only bytes and SP.
Return to the printing loop first.
From that paused point, step once: OUT prints H and PC advances to 001D. Step again: RET reads 16 and 00 from the pair beginning at SP. PC becomes 0016 and SP becomes 01FE.
We are back in the printing loop, ready for INX H. We have not returned to the main program. The outer return address, 0006, is still waiting at the new SP.
This is last in, first out: the most recently saved address is the first one used. RET does not know the names of the routines. It simply reads the address at SP and moves SP past it.
Neither return address is erased. The bytes 16 and 00 remain after the inner return, but SP has moved past them. Visible bytes alone do not tell us how many calls are still waiting to return.
Reuse the space, then finish the larger job.
The loop advances HL and reads E. Its next CALL again saves 0016 at 01FC–01FD. Each character uses the same small routine and the same stack space: one inner call finishes before the next begins.
When the loop finds NUL, it skips the character routine and jumps to its own RET at 001A. SP is 01FE, so this RET reads the outer address 0006. SP returns to 0200 and the main program continues.
The second outer CALL saves 0009. Printing repeats with the same inner return address, 0016. After the second outer return, HLT finishes the program: 110 instructions and twelve output writes. SP is 0200; all four saved bytes remain in RAM.
Change one way back.
The two saved addresses are independent pieces of data. Changing the outer one need not change where the character routine returns.
Skip the second HELLO while both calls are active
Start again and step seven instructions. EXAMINE 01FE, set the switches to 0009, and DEPOSIT. You have changed the outer address from 0006 to 0009; the inner address is still 0016. EXAMINE 001B to restore PC. Step twice: H prints, then the inner RET still returns to 0016. RUN finishes the first HELLO line, but the outer RET now selects HLT at 0009. The second line is skipped.
The last-instruction record preserves what happened when CALL executed, even after you edit RAM. The live table shows the bytes RET will actually read. Start again to restore the ordinary path.
The character routine happens to preserve A, HL, and flags: OUT and RET leave them alone. CALL itself does not promise that a routine will preserve registers. The next lesson shows how to save values across a call.