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
These thirty program bytes are loaded at 0000. HELLO, LF, and NUL begin at 0100; the two levels of calls use RAM at 01FC–01FF. RAM shows what is stored now.
AddressReferenceRAMCheck

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.

Operate the memory panel

The 8080 behind the panel

A decimal
0
PC hex
0000
HL address · hex
0000
SP stack address · hex
0000

Return addresses in RAM

Addresses and bytes in hexadecimal.
AddressByteRole
01FC 00Inner · low
01FD 00Inner · high
01FE 00Outer · low
01FF 00Outer · high

Enable JavaScript to follow SP.

The marker follows SP. Looking at these bytes changes no CPU or memory state.

Message in RAM

Addresses and bytes in hexadecimal; characters interpreted as ASCII.
AddressByteCharacter
0100 48H
0101 45E
0102 4CL
0103 4CL
0104 4FO
0105 0ALF
0106 00NUL

Enable JavaScript to follow HL.

The marker follows HL. Looking at these bytes changes no CPU or memory state.

In this program, the inner return address uses 01FC–01FD; the outer one uses 01FE–01FF. RET reads the pair beginning at SP. The role labels stay in place after return, just as the bytes stay in RAM. These four rows are a window, not a CPU-enforced stack limit.

Zero flag Z: 0 · the most recent comparison’s answer

CPU halted: no

The program is loaded. Enable JavaScript to run or step it.

Last CPU instruction

No instruction has run.

Received text Output port 1


    Waiting for output
  

No byte sent yet.

0 writes received.

Only OUT adds text. LF starts a new line; other non-printing bytes appear as labels in angle brackets.

Look inside the last instruction
Step an instruction to see its fetched bytes, memory accesses, and any port transfers.
Recent instructions

0 instructions executed.

    RUN and STOP are teaching controls at instruction boundaries. Pace is for reading, not a simulation of the Altair’s clock. This page stops running when hidden. Looking at these readouts changes no state; EXAMINE changes PC.

    EXAMINE changes PC without running a program instruction. Stepping advances PC, and the panel lights follow it. This added control executes a whole instruction; the original SINGLE STEP switch advanced one machine cycle.

    ALTAIR 8800

    Memory controls · learning view

    Address lights The selected location

    0 decimal · 0000 hex

    Data lights The byte at that location

    0 decimal · 00 hex

    A lit lamp is 1; a dark lamp is 0. The digits help you read the lights.

    Data / address switches Up = 1 · Down = 0

    15–8 · Address only

    7–0 · Address or data

    All switches as an address
    0 decimal · 0000 hex
    Lower eight as a byte
    0 decimal · 00 hex

    Setting the switches prepares a number. It does not read or write memory.

    Lower all switches and press EXAMINE to select 0000.

    Select the address in the switches and read its byte.

    Move to the next address and read its byte.

    Write the lower eight switches to the selected address.

    Move to the next address, then write the lower eight switches.

    EXAMINE would select address 0. DEPOSIT would write 0 to the selected address, 0.

    Last memory operation

    Enable JavaScript to operate the panel.

    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.