Lecture 06
6502 Assembly on the Apple II
BASIC is a comfortable living room; assembly is the engine bay. This lecture gets your hands dirty: the built-in Monitor, entering machine code byte by byte, calling ROM routines, and writing programs that run hundreds of times faster than BASIC.
1. Meet the Monitor
Every Apple II ships with the Monitor — a tiny debugger in ROM. From BASIC, enter it with:
]CALL -151
*█
The * prompt means you're talking to the Monitor. Its language is terse but powerful:
| You type | It does |
|---|---|
300 | Show the byte at $0300 |
300.30F | Show memory from $0300 to $030F |
300: A9 C1 | Write bytes $A9 $C1 starting at $0300 |
300G | Go: execute the code at $0300 |
300L | List: disassemble 20 instructions from $0300 |
F800.F8FF | Read the ROM itself — nothing is secret |
| Ctrl+C then Return | Back to BASIC (or 3D0G with DOS) |
2. Your first machine-language program
We'll print a letter using a ROM routine. The Monitor ROM contains COUT at
$FDED: "output the character in A". Character codes want the high bit set,
so 'A' is $C1. The program:
0300: A9 C1 LDA #$C1 ; A = 'A' (high-bit set)
0302: 20 ED FD JSR $FDED ; call COUT — print it
0305: 60 RTS ; return to caller
Enter and run it from the Monitor:
*300: A9 C1 20 ED FD 60
*300G
A
*█
Six bytes. You just wrote machine code the way people did in 1978 — and called a subroutine
Woz's team wrote into the ROM. From BASIC, the same program runs with CALL 768
(768 = $0300).
RTS at the end is what makes it a well-behaved subroutine:
it returns to whoever called it — the Monitor, or BASIC's CALL. Forget it and the CPU marches on
into whatever garbage follows your code.3. The ROM is your standard library
These entry points have been stable since the 1970s — every Apple II programmer knew them by heart:
| Address | Name | What it does |
|---|---|---|
$FDED | COUT | Print character in A (screen code, high bit set) |
$FD0C | RDKEY | Wait for a keypress, return it in A |
$FC58 | HOME | Clear the text screen, cursor to top-left |
$FDDA | PRBYTE | Print A as two hex digits |
$FCA8 | WAIT | Delay for a time set by A (bigger = longer) |
$FBDD | BELL1 | Beep the speaker |
$F864 | SETCOL | Set lo-res color from A (low nibble) |
$F800 | PLOT | Lo-res plot: A = row, Y = column |
4. A real example: HELLO with a loop
Printing a whole string introduces loops, labels, indexed addressing, and flags working together:
ORG $300 ; assemble at $0300
COUT EQU $FDED
START LDX #$00 ; X = string index
LOOP LDA MSG,X ; fetch MSG[X]
BEQ DONE ; byte $00 marks the end → quit
JSR COUT ; print it
INX ; X = X + 1
BNE LOOP ; loop (always taken here)
DONE RTS
MSG HEX C8C5CCCCCF ; "HELLO" with high bits set
HEX 8D00 ; carriage return, terminator
Hand-assembled, ready to paste into the Monitor:
*300: A2 00 BD 0E 03 F0 06 20 ED FD E8 D0 F5 60
*30E: C8 C5 CC CC CF 8D 00
*300G
HELLO
*█
Walk through what the CPU does: LDA MSG,X is the absolute,X addressing
mode from Lecture 02 — the table walk. BEQ watches the Z flag, which
LDA sets whenever it loads a zero. This load–test–branch pattern is the assembly
equivalent of a while loop, and you will write it a thousand times.
5. Why bother? Speed.
Filling the whole text screen with a character:
BASIC: 10 FOR I = 1024 TO 2039 : POKE I, 42 : NEXT
(~4 seconds — you can watch it paint)
ASM: LDA #$AA ; screen code for '*'
LDX #$00
FILL STA $0400,X ; four STA,X instructions cover
STA $0500,X ; the whole $0400-$07FF page
STA $0600,X
STA $0700,X
INX
BNE FILL ; 256 iterations
RTS
(~9 milliseconds — appears instantly)
That's a factor of several hundred. Every arcade-style Apple II game, every smooth animation, every music routine is assembly for exactly this reason. (This fill writes 1024 bytes, which includes the 64 screen-hole bytes — harmless for a screen fill, and cheaper than avoiding them.)
6. From hand-assembly to real tools
Nobody ships software by typing hex. The upgrade path:
- The Monitor's mini-assembler (present on the IIe/IIc, and on the original II's Integer ROM): type
!then mnemonics like300:LDA #$C1, and it assembles in place. Good for experiments. - Native assemblers of the era: Merlin, LISA, the DOS Toolkit assembler — full editors running on the Apple II itself.
- Cross-development (the modern way): write source on your PC/Mac in an editor, assemble with
ca65orMerlin32, copy onto a disk image, run in an emulator. Fast, comfortable, version-controllable. Getting Started walks you through this exact setup.
7. Mixing BASIC and assembly
The classic architecture of a polished Apple II program: BASIC for the top level (menus, logic, file handling), assembly for the hot paths (drawing, sound). The glue:
10 REM POKE THE ROUTINE INTO $300 FROM DATA
20 FOR I = 0 TO 13 : READ B : POKE 768 + I, B : NEXT
30 DATA 162,0,189,14,3,240,6,32,237,253,232,208,245,96
40 REM ... POKE THE STRING AT $30E TOO, THEN:
50 CALL 768
READ/DATA + POKE + CALL: machine code delivered inside a BASIC listing — exactly how magazines distributed hybrid programs in the 1980s.
Check your understanding
Q1. In the Monitor, what does 300: A9 C1 do?
Q2. Why does the HELLO loop end when LDA MSG,X reads a $00 byte?
Q3. How does BASIC hand control to a machine-language routine at $0300?