Meet the Altair · Saving registers

Keeping a value
across a call.

A routine can find its way back and still leave your registers changed. How can it borrow the message pointer, then give back the value its caller needs?

Begin here: Step four instructions. CALL has saved return address 0009, and PUSH H has saved the pointer 0100 in a second pair of stack bytes. HL is still 0100. Nothing has been printed yet.

Loaded register-saving program · reference card All addresses and bytes here are hexadecimal
These twenty-eight program bytes are loaded at 0000. HELLO, LF, and NUL begin at 0100; saved H/L and the return address use 01FC–01FF. RAM shows what is stored now.
AddressReferenceRAMCheck

0000: LXI SP,0200H chooses the stack starting point. 0003: LXI H,0100H chooses the message. 0006 and 0009: CALL 000DH call the printing routine, saving 0009 and 000C respectively. 000C: HLT finishes.

000D: PUSH H saves H and L. 000E: MOV A,M, 000F: CPI 0, and 0011: JZ 001AH check for NUL. 0014: OUT 01H sends A; 0016: INX H advances HL; 0017: JMP 000EH repeats.

001A: POP H restores L and H from RAM. 001B: RET reads the next stack pair into PC. Restoring HL lets the second call start at the same message without another LXI H.

The lesson checks all twenty-eight program bytes. Message and stack data can be edited while stopped. PUSH, POP, and RET act on real RAM; the viewer keeps no hidden copy of HL.

Operate the memory panel

The 8080 behind the panel

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

Saved values in RAM

Addresses and bytes in hexadecimal.
AddressByteRole
01FC 00Saved L
01FD 00Saved H
01FE 00Return · low
01FF 00Return · 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.

This program saves L at 01FC and H at 01FD, below the return address at 01FE–01FF. POP H reads the first pair into HL; RET then reads the next pair into PC. The labels describe this program's use of RAM. Reading the bytes does not erase them.

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.

    Borrow the pointer.

    In the previous lessons, the printing routine left HL pointing at NUL. RET brought execution back to the caller, but it left HL changed. The routine had to set HL to 0100 again each time it began.

    Here the caller chooses the message: its LXI H puts 0100 into HL once, before either call. The printing routine starts reading wherever HL points. As it prints, INX H still advances the pointer.

    We want the routine to preserve the caller's starting pointer, so both calls can print the same message. Two new instructions make that possible: PUSH H before the loop and POP H after it.

    Save two registers in two bytes.

    The CALL at 0006 saves 0009 at 01FE–01FF and leaves SP at 01FE. The routine's first instruction is PUSH H. In this instruction's name, H selects the H–L register pair: both bytes are saved.

    PUSH decreases SP and writes H, 01, at 01FD. It decreases SP again and writes L, 00, at 01FC. These bytes form the saved pointer 0100. The return address in the next pair is untouched.

    PUSH copies the value into RAM. It does not empty the registers or move the pointer: HL remains 0100, ready for the loop to use. PC advances to the next instruction at 000E.

    The CPU does not tag these bytes as “pointer” or “return address.” The table's labels explain how our program uses the locations. Which instruction reads them determines what happens next.

    Use the pointer, then restore it.

    The familiar loop prints HELLO and LF. HL advances to 0106, while its original value stays in the saved bytes. When MOV reads NUL and CPI sets Z, the conditional jump selects POP H at 001A.

    POP H reads the byte at SP into L, then the next byte into H. HL becomes 0100 again and SP advances to 01FE. The bytes remain in RAM.

    POP advances PC to the following instruction, RET at 001B. It has restored the pointer, but has not returned to the caller. RET then reads 0009 from the next stack pair into PC and advances SP to 0200.

    Make a promise the caller can use.

    The second call now starts with the same HL as the first, without another LXI H. It saves and restores that pointer in the same two locations. The complete run prints two HELLO lines in 89 instructions, leaving HL at 0100 and SP at 0200.

    Our routine's promise is specific: HL is preserved. A and the flags still hold the values left by reading and testing NUL. PUSH H and POP H do not save or restore them.

    This promise comes from the instructions we wrote. CALL and RET do not automatically preserve registers. A routine can preserve selected values itself, or its caller can save values it needs before calling.

    Change the saved pointer before it is restored

    Start again and step four instructions. EXAMINE 01FC, set the switches to 0001, and DEPOSIT. You have changed the saved L, making the stored pointer 0101. HL is still 0100. EXAMINE 000E to restore PC, then RUN. The first call prints HELLO as usual; POP H restores the edited pointer, so the second call prints ELLO. The captured PUSH explanation still reports the original 0100.

    Finish in reverse order.

    CALL saved a return address first; PUSH saved HL afterward. To finish correctly, POP must read the saved HL before RET reads the return address. This is the same last-in, first-out rule as nested calls.

    What happens if RET runs before POP?

    Start again and step until PC is 001A after the first HELLO line. EXAMINE 001B to skip POP, then step RET. SP still points at the saved pointer, so RET reads 0100 into PC. It leaves HL at 0106 and advances SP only to 01FE. The lesson pauses because 0100 is not an instruction start in its reference program. A real CPU does not check whether these bytes were intended as a return address.

    Start again to restore the normal path. We now have the pieces for a small interactive program: input, a buffer, decisions, and routines that return with the values their callers need.