Introduction
Welcome to the SLP-Z80 emulator, a faithful implementation of the legendary Zilog Z80 microprocessor for the Emulator.ca modem network. The Z80 stands as one of the most influential processors in computing history, and this manual will guide you through its architecture, instruction set, and the unique features of this implementation.
The Z80 Legacy
In 1976, Federico Faggin founded Zilog and released the Z80---a processor that would shape the personal computing revolution. The Z80 was software-compatible with the Intel 8080 but added numerous enhancements: additional registers, more powerful instructions, a simpler clock design, and a built-in DRAM refresh circuit.
The Z80 powered an extraordinary range of systems:
- Sinclair ZX80, ZX81, ZX Spectrum - Brought computing to millions in the UK
- TRS-80 - Radio Shack's entry into personal computing
- Osborne 1 - The first commercially successful portable computer
- MSX computers - The unified Japanese home computer standard
- CP/M systems - The dominant business operating system before DOS
- Arcade games - Pac-Man, Galaga, Donkey Kong, and countless others
- Embedded systems - Still in production today for industrial applications
The Z80's elegant design and powerful instruction set made it the processor of choice for an entire generation of programmers. When you program the Z80, you're connecting with a heritage that spans gaming, business computing, scientific applications, and industrial control.
This Z80 emulator implements the complete documented instruction set plus many undocumented instructions. It provides 64KB of memory, configurable ROM protection, UART I/O, and timer peripherals with interrupt support.
About This Implementation
The SLP-Z80 provides:
- Complete Z80 register set including alternate registers
- All documented Z80 instructions
- Many undocumented instructions (SLL, IX/IY half-register operations, DDCB/FDCB autocopy)
- Three interrupt modes (IM 0, IM 1, IM 2) plus NMI
- 64KB memory space with configurable ROM protection
- UART peripheral for serial I/O
- Programmable timer with interrupt capability
- Accurate flag behavior including undocumented X and Y flags
Whether you're learning assembly language, exploring vintage computing, or developing embedded software, this emulator provides an authentic Z80 experience through your modem connection.
Getting Connected
To access the SLP-Z80 system, configure your modem and dial:
ATDT555-4355
Upon successful connection, you'll see the system banner:
CODE_FENCE_0
This simple demonstration loads a pre-compiled "Hello World" program, executes it on the Z80 emulator, and displays the output via the emulated UART. The program then halts, demonstrating basic BIOS functionality.
Terminal Settings
Configure your terminal software as follows:
- Terminal emulation: VT100 or ANSI
- Data bits: 8
- Parity: None
- Stop bits: 1
- Flow control: Hardware (RTS/CTS)
Modem Signals
The Z80 emulator participates in RS-232 handshaking:
| Signal | Direction | Function |
|---|---|---|
| DTR | DTE->DCE | Asserted when emulator ready; dropped to disconnect |
| RTS | DTE->DCE | Asserted when emulator can accept data |
| CTS | DCE->DTE | When low, output is paused (flow control) |
| DCD | DCE->DTE | Carrier detect - connection active |
This service runs a pre-compiled hello world program to demonstrate Z80 emulation. The program executes automatically upon connection and the line disconnects after completion. For interactive Z80 programming, see the Z80 Snake games (555-7635 and 555-9807).
Architecture Overview
The Z80 is an 8-bit microprocessor with a 16-bit address bus, providing access to 64 kilobytes of memory. It features a rich register set, powerful addressing modes, and a comprehensive instruction set that made it the most popular 8-bit processor of its era.
Memory Map
+------------------+ 0xFFFF (65535)
| |
| RAM |
| (56 KB) |
| |
+------------------+ 0x2000 (8192)
| |
| ROM |
| (8 KB) |
| |
+------------------+ 0x0000 (0)
The default configuration provides:
- ROM: 0x0000-0x1FFF (8KB) - Write-protected system area
- RAM: 0x2000-0xFFFF (56KB) - Read/write memory
ROM protection prevents accidental overwrites of system code. The ROM size is configurable.
I/O Space
The Z80 has a separate 256-port I/O address space. This implementation provides:
| Port | Function |
|---|---|
| 0x00 | UART Data (read: receive, write: transmit) |
| 0x01 | UART Status (bit 0: RX ready, bit 1: TX ready) |
| 0x10 | Timer Control |
| 0x11 | Timer Reload Low |
| 0x12 | Timer Reload High |
Block Diagram
+---------------------------+
| Z80 CPU |
| |
+----------+ | +-----+ +-------+ |
| |=========]| | ALU |=====]| Regs | |
| Memory | 16-bit | +-----+ +-------+ |
| 64 KB | Address | |
| |=========]| +-----+ +-------+ |
+----------+ 8-bit | | IR | | Flags | |
Data | +-----+ +-------+ |
| |
+----------+ | +-----+ +-------+ |
| I/O |=========]| | PC | | SP | |
| Devices | 8-bit | +-----+ +-------+ |
+----------+ I/O | |
+---------------------------+
The Register Set
The Z80's register set is one of its most distinctive features. In addition to the basic 8080-compatible registers, it provides alternate registers, index registers, and specialized registers for interrupt handling.
Main Registers
The primary working registers are organised as 8-bit registers that can be paired into 16-bit register pairs:
8-bit 16-bit pair
+---+---+
| A | F | AF - Accumulator and Flags
+---+---+
| B | C | BC - General purpose / Counter
+---+---+
| D | E | DE - General purpose / Destination
+---+---+
| H | L | HL - General purpose / Memory pointer
+---+---+
A (Accumulator): The primary register for arithmetic and logical operations. Most ALU operations use A as one operand and store the result back in A.
F (Flags): Contains the condition flags that reflect the results of operations. See the Flag Register section for details.
BC, DE, HL: General-purpose register pairs. HL is particularly important as it serves as the primary memory pointer---many instructions use (HL) to address memory.
Alternate Registers
The Z80 provides a complete set of alternate registers:
Main Set Alternate Set
+---+---+ +---+---+
| A | F | | A'| F'|
+---+---+ +---+---+
| B | C | | B'| C'|
+---+---+ +---+---+
| D | E | | D'| E'|
+---+---+ +---+---+
| H | L | | H'| L'|
+---+---+ +---+---+
The EX AF,AF' instruction exchanges AF with AF'. The EXX instruction exchanges BC, DE, and HL with their alternates simultaneously. This allows fast context switching---an interrupt handler can preserve the main registers with a single instruction.
Index Registers
+-------+
| IX | Index Register X (16-bit)
+-------+
| IY | Index Register Y (16-bit)
+-------+
IX and IY provide indexed addressing: you can access memory at IX+d or IY+d where d is a signed 8-bit displacement (-128 to +127). This is invaluable for accessing data structures, stack frames, and arrays.
Special Registers
+-------+
| SP | Stack Pointer (16-bit)
+-------+
| PC | Program Counter (16-bit)
+-------+
+---+
| I | Interrupt Vector Register (8-bit)
+---+
| R | Memory Refresh Register (8-bit)
+---+
SP (Stack Pointer): Points to the top of the stack. The stack grows downward (toward lower addresses). PUSH decrements SP by 2 then writes; POP reads then increments SP by 2.
PC (Program Counter): Contains the address of the next instruction to execute. Modified by jumps, calls, and returns.
I (Interrupt Vector): In Interrupt Mode 2, I provides the high byte of the interrupt vector table address. Combined with a byte from the interrupting device, it forms the address of the interrupt service routine.
R (Refresh Register): Automatically incremented after each instruction fetch. Originally designed to refresh dynamic RAM, it's useful for pseudo-random number generation.
Use HL for memory pointers (many instructions implicitly use it). Use BC as a counter in loops. Use DE as a secondary pointer or for block operations. Reserve IX and IY for complex data structures where indexed addressing is beneficial.
The Flag Register
The F register contains six status flags that reflect the results of operations. Two additional bits (X and Y) are undocumented but are set by the hardware.
Bit: 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| S | Z | Y | H | X |P/V| N | C |
+---+---+---+---+---+---+---+---+
Flag Descriptions
| Flag | Bit | Name | Description |
|---|---|---|---|
| S | 7 | Sign | Set if result is negative (bit 7 = 1) |
| Z | 6 | Zero | Set if result is zero |
| Y | 5 | (Undocumented) | Copy of bit 5 of result |
| H | 4 | Half Carry | Carry from bit 3 to bit 4 (BCD operations) |
| X | 3 | (Undocumented) | Copy of bit 3 of result |
| P/V | 2 | Parity/Overflow | Parity (logical ops) or Overflow (arithmetic) |
| N | 1 | Add/Subtract | Set if last operation was subtraction |
| C | 0 | Carry | Carry out of bit 7 (or borrow) |
Sign Flag (S)
The Sign flag reflects bit 7 of the result. In signed arithmetic, this indicates whether the result is negative:
LD A, 50 ; A = 50, S = 0 (positive)
ADD A, 100 ; A = 150, S = 1 (negative in signed)
For signed bytes: 0-127 are positive, 128-255 represent -128 to -1.
Zero Flag (Z)
Set when the result of an operation is zero:
LD A, 5
DEC A ; A = 4, Z = 0
DEC A ; A = 3, Z = 0
...
DEC A ; A = 0, Z = 1
Essential for loops and comparisons:
CP 10 ; Compare A with 10
JR Z, equal ; Jump if A equals 10
Half Carry Flag (H)
Set when there's a carry from bit 3 to bit 4. This is essential for BCD (Binary-Coded Decimal) arithmetic:
LD A, 0x09 ; A = 9 (BCD)
ADD A, 0x01 ; A = 0x0A, H = 0
; (no carry from bit 3)
LD A, 0x0F ; A = 15
ADD A, 0x01 ; A = 0x10, H = 1
; (carry from bit 3 to bit 4)
The DAA instruction uses H to correct BCD results.
Parity/Overflow Flag (P/V)
This flag serves two purposes depending on the operation:
Parity (logical operations): Set if the result has an even number of 1 bits:
LD A, 0b00001111 ; 4 ones = even parity
OR A ; P = 1
LD A, 0b00000111 ; 3 ones = odd parity
OR A ; P = 0
Overflow (arithmetic operations): Set if signed overflow occurred:
LD A, 127 ; Maximum positive signed byte
ADD A, 1 ; Result = 128 = -128 signed
; V = 1 (overflow!)
Add/Subtract Flag (N)
Set after subtraction operations, cleared after additions. Used by DAA to determine the correct BCD adjustment:
ADD A, B ; N = 0
SUB C ; N = 1
DAA ; Uses N to adjust correctly
Carry Flag (C)
Set when an operation produces a carry out of bit 7 (addition) or a borrow (subtraction):
LD A, 200
ADD A, 100 ; 300 > 255, C = 1, A = 44
LD A, 50
SUB 100 ; 50 - 100 = -50, C = 1 (borrow)
Used for multi-byte arithmetic with ADC and SBC:
; Add 16-bit numbers: HL = HL + DE
ADD HL, DE ; Adds with carry into H
Undocumented Flags (X and Y)
Bits 3 and 5 of the F register are copies of bits 3 and 5 of the result (or operand, depending on the instruction). While not officially documented, this emulator faithfully replicates this behavior for compatibility with software that relies on it.
Addressing Modes
The Z80 supports multiple addressing modes, providing flexibility in how instructions access data.
Immediate
The operand is contained in the instruction itself:
LD A, 42 ; Load 42 into A
LD BC, 0x1234 ; Load 0x1234 into BC
ADD A, 5 ; Add 5 to A
Register
The operand is in a register:
LD A, B ; Copy B to A
ADD A, C ; Add C to A
INC D ; Increment D
Register Indirect
The register contains an address; the operand is in memory at that address:
LD A, (HL) ; Load byte at address HL into A
LD (BC), A ; Store A at address BC
INC (HL) ; Increment byte at address HL
Extended (Direct)
The instruction contains a 16-bit address:
LD A, (0x4000) ; Load byte at 0x4000 into A
LD (0x5000), A ; Store A at 0x5000
LD HL, (0x6000) ; Load 16-bit value at 0x6000 into HL
JP 0x8000 ; Jump to 0x8000
Indexed
The address is formed by adding a signed displacement to IX or IY:
LD A, (IX+5) ; Load byte at IX+5 into A
LD (IY-3), B ; Store B at IY-3
INC (IX+0) ; Increment byte at address IX
The displacement ranges from -128 to +127, making indexed addressing ideal for accessing structure fields and array elements.
Relative
Used only by JR (Jump Relative) and DJNZ. The target address is PC plus a signed 8-bit displacement:
JR loop ; Jump relative to 'loop'
DJNZ back ; Decrement B, jump if not zero
Relative jumps save memory (2 bytes vs 3) and produce position-independent code.
Bit Addressing
BIT, SET, and RES instructions address individual bits within a byte:
BIT 3, A ; Test bit 3 of A
SET 7, (HL) ; Set bit 7 of byte at (HL)
RES 0, B ; Clear bit 0 of B
Implicit
Some instructions have implicit operands:
DAA ; Decimal adjust A (A is implicit)
CPL ; Complement A
RLA ; Rotate A left through carry
Instruction Set Reference
The Z80 instruction set comprises over 700 opcodes (including duplicates and undocumented variations). This section organises them by function.
Data Transfer Instructions
8-Bit Loads
| Instruction | Operation | Flags |
|---|---|---|
| LD r, r' | r ← r' | - |
| LD r, n | r ← n | - |
| LD r, (HL) | r ← (HL) | - |
| LD (HL), r | (HL) ← r | - |
| LD (HL), n | (HL) ← n | - |
| LD A, (BC) | A ← (BC) | - |
| LD A, (DE) | A ← (DE) | - |
| LD A, (nn) | A ← (nn) | - |
| LD (BC), A | (BC) ← A | - |
| LD (DE), A | (DE) ← A | - |
| LD (nn), A | (nn) ← A | - |
| LD A, I | A ← I | S Z - H:0 - P:IFF2 N:0 - |
| LD A, R | A ← R | S Z - H:0 - P:IFF2 N:0 - |
| LD I, A | I ← A | - |
| LD R, A | R ← A | - |
16-Bit Loads
| Instruction | Operation | Flags |
|---|---|---|
| LD dd, nn | dd ← nn | - |
| LD HL, (nn) | HL ← (nn) | - |
| LD dd, (nn) | dd ← (nn) | - |
| LD (nn), HL | (nn) ← HL | - |
| LD (nn), dd | (nn) ← dd | - |
| LD SP, HL | SP ← HL | - |
| LD SP, IX | SP ← IX | - |
| LD SP, IY | SP ← IY | - |
Stack Operations
| Instruction | Operation | Flags |
|---|---|---|
| PUSH qq | (SP-1) ← qqH, (SP-2) ← qqL, SP ← SP-2 | - |
| POP qq | qqL ← (SP), qqH ← (SP+1), SP ← SP+2 | AF: all flags |
Exchange Instructions
| Instruction | Operation | Flags |
|---|---|---|
| EX DE, HL | DE ↔ HL | - |
| EX AF, AF' | AF ↔ AF' | All flags from AF' |
| EXX | BC↔BC', DE↔DE', HL↔HL' | - |
| EX (SP), HL | (SP) ↔ L, (SP+1) ↔ H | - |
| EX (SP), IX | (SP) ↔ IXL, (SP+1) ↔ IXH | - |
| EX (SP), IY | (SP) ↔ IYL, (SP+1) ↔ IYH | - |
Arithmetic Instructions
8-Bit Addition
| Instruction | Operation | Flags |
|---|---|---|
| ADD A, r | A ← A + r | S Z - H - V N:0 C |
| ADD A, n | A ← A + n | S Z - H - V N:0 C |
| ADD A, (HL) | A ← A + (HL) | S Z - H - V N:0 C |
| ADD A, (IX+d) | A ← A + (IX+d) | S Z - H - V N:0 C |
| ADD A, (IY+d) | A ← A + (IY+d) | S Z - H - V N:0 C |
| ADC A, s | A ← A + s + C | S Z - H - V N:0 C |
8-Bit Subtraction
| Instruction | Operation | Flags |
|---|---|---|
| SUB r | A ← A - r | S Z - H - V N:1 C |
| SUB n | A ← A - n | S Z - H - V N:1 C |
| SUB (HL) | A ← A - (HL) | S Z - H - V N:1 C |
| SBC A, s | A ← A - s - C | S Z - H - V N:1 C |
| CP r | A - r (flags only) | S Z - H - V N:1 C |
| CP n | A - n (flags only) | S Z - H - V N:1 C |
Increment and Decrement
| Instruction | Operation | Flags |
|---|---|---|
| INC r | r ← r + 1 | S Z - H - V N:0 - |
| INC (HL) | (HL) ← (HL) + 1 | S Z - H - V N:0 - |
| DEC r | r ← r - 1 | S Z - H - V N:1 - |
| DEC (HL) | (HL) ← (HL) - 1 | S Z - H - V N:1 - |
16-Bit Arithmetic
| Instruction | Operation | Flags |
|---|---|---|
| ADD HL, ss | HL ← HL + ss | - - - H - - N:0 C |
| ADC HL, ss | HL ← HL + ss + C | S Z - H - V N:0 C |
| SBC HL, ss | HL ← HL - ss - C | S Z - H - V N:1 C |
| ADD IX, pp | IX ← IX + pp | - - - H - - N:0 C |
| ADD IY, rr | IY ← IY + rr | - - - H - - N:0 C |
| INC ss | ss ← ss + 1 | - |
| DEC ss | ss ← ss - 1 | - |
Special Arithmetic
| Instruction | Operation | Flags |
|---|---|---|
| DAA | Decimal adjust A | S Z - H - P N C |
| CPL | A ← NOT A | - - - H:1 - - N:1 - |
| NEG | A ← 0 - A | S Z - H - V N:1 C |
Logical Instructions
| Instruction | Operation | Flags |
|---|---|---|
| AND r | A ← A AND r | S Z - H:1 - P N:0 C:0 |
| AND n | A ← A AND n | S Z - H:1 - P N:0 C:0 |
| AND (HL) | A ← A AND (HL) | S Z - H:1 - P N:0 C:0 |
| OR r | A ← A OR r | S Z - H:0 - P N:0 C:0 |
| OR n | A ← A OR n | S Z - H:0 - P N:0 C:0 |
| OR (HL) | A ← A OR (HL) | S Z - H:0 - P N:0 C:0 |
| XOR r | A ← A XOR r | S Z - H:0 - P N:0 C:0 |
| XOR n | A ← A XOR n | S Z - H:0 - P N:0 C:0 |
| XOR (HL) | A ← A XOR (HL) | S Z - H:0 - P N:0 C:0 |
Bit Manipulation Instructions
Bit Test, Set, and Reset
| Instruction | Operation | Flags |
|---|---|---|
| BIT b, r | Test bit b of r | - Z - H:1 - P:~Z N:0 - |
| BIT b, (HL) | Test bit b of (HL) | - Z - H:1 - P:~Z N:0 - |
| SET b, r | Set bit b of r | - |
| SET b, (HL) | Set bit b of (HL) | - |
| RES b, r | Reset bit b of r | - |
| RES b, (HL) | Reset bit b of (HL) | - |
Where b = 0-7 specifies the bit position.
Rotate and Shift Instructions
Rotate Accumulator
| Instruction | Operation | Flags |
|---|---|---|
| RLCA | Rotate A left circular | - - - H:0 - - N:0 C |
| RLA | Rotate A left through carry | - - - H:0 - - N:0 C |
| RRCA | Rotate A right circular | - - - H:0 - - N:0 C |
| RRA | Rotate A right through carry | - - - H:0 - - N:0 C |
Rotate Register/Memory
| Instruction | Operation | Flags |
|---|---|---|
| RLC r | Rotate left circular | S Z - H:0 - P N:0 C |
| RL r | Rotate left through carry | S Z - H:0 - P N:0 C |
| RRC r | Rotate right circular | S Z - H:0 - P N:0 C |
| RR r | Rotate right through carry | S Z - H:0 - P N:0 C |
Shift Instructions
| Instruction | Operation | Flags |
|---|---|---|
| SLA r | Shift left arithmetic | S Z - H:0 - P N:0 C |
| SRA r | Shift right arithmetic | S Z - H:0 - P N:0 C |
| SRL r | Shift right logical | S Z - H:0 - P N:0 C |
Rotate and Shift Operations:
RLCA/RLC [C] ← [7 ← 0] ←+
+----------+
RLA/RL [C] ← [7 ← 0] ← [C]
RRCA/RRC +-> [7 -> 0] -> [C]
+----------+
RRA/RR [C] -> [7 -> 0] -> [C]
SLA [C] ← [7 ← 0] ← 0
SRA [7] -> [7 -> 0] -> [C]
(sign preserved)
SRL 0 -> [7 -> 0] -> [C]
Rotate Digit Instructions
| Instruction | Operation | Flags |
|---|---|---|
| RLD | Rotate left digit (A and (HL)) | S Z - H:0 - P N:0 - |
| RRD | Rotate right digit (A and (HL)) | S Z - H:0 - P N:0 - |
These unusual instructions rotate the lower nibble of A with both nibbles of (HL), useful for BCD manipulation.
Branch Instructions
Unconditional Jumps
| Instruction | Operation | Bytes | Cycles |
|---|---|---|---|
| JP nn | PC ← nn | 3 | 10 |
| JP (HL) | PC ← HL | 1 | 4 |
| JP (IX) | PC ← IX | 2 | 8 |
| JP (IY) | PC ← IY | 2 | 8 |
| JR e | PC ← PC + e | 2 | 12 |
Conditional Jumps
| Instruction | Condition | Branch if |
|---|---|---|
| JP NZ, nn | Z = 0 | Not zero |
| JP Z, nn | Z = 1 | Zero |
| JP NC, nn | C = 0 | No carry |
| JP C, nn | C = 1 | Carry |
| JP PO, nn | P/V = 0 | Parity odd / No overflow |
| JP PE, nn | P/V = 1 | Parity even / Overflow |
| JP P, nn | S = 0 | Positive |
| JP M, nn | S = 1 | Minus (negative) |
The same conditions apply to JR, except JR only supports NZ, Z, NC, and C.
Subroutine Calls and Returns
| Instruction | Operation |
|---|---|
| CALL nn | (SP-1) ← PCH, (SP-2) ← PCL, SP ← SP-2, PC ← nn |
| CALL cc, nn | If condition cc true, CALL nn |
| RET | PCL ← (SP), PCH ← (SP+1), SP ← SP+2 |
| RET cc | If condition cc true, RET |
| RETI | Return from interrupt |
| RETN | Return from NMI |
Special Branches
| Instruction | Operation |
|---|---|
| DJNZ e | B ← B - 1; if B != 0, PC ← PC + e |
| RST p | CALL to address p (p = 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38) |
Block Instructions
The Z80's block instructions are among its most powerful features, allowing memory-to-memory and I/O operations with automatic incrementing or decrementing and repeat capability.
Block Transfer
| Instruction | Operation |
|---|---|
| LDI | (DE) ← (HL), DE++, HL++, BC-- |
| LDIR | LDI repeated until BC = 0 |
| LDD | (DE) ← (HL), DE--, HL--, BC-- |
| LDDR | LDD repeated until BC = 0 |
Block Compare
| Instruction | Operation |
|---|---|
| CPI | A - (HL), HL++, BC-- |
| CPIR | CPI repeated until BC = 0 or A = (HL) |
| CPD | A - (HL), HL--, BC-- |
| CPDR | CPD repeated until BC = 0 or A = (HL) |
Block I/O
| Instruction | Operation |
|---|---|
| INI | (HL) ← IN(C), HL++, B-- |
| INIR | INI repeated until B = 0 |
| IND | (HL) ← IN(C), HL--, B-- |
| INDR | IND repeated until B = 0 |
| OUTI | OUT(C) ← (HL), HL++, B-- |
| OTIR | OUTI repeated until B = 0 |
| OUTD | OUT(C) ← (HL), HL--, B-- |
| OTDR | OUTD repeated until B = 0 |
I/O Instructions
| Instruction | Operation | Flags |
|---|---|---|
| IN A, (n) | A ← port n | - |
| IN r, (C) | r ← port C | S Z - H:0 - P N:0 - |
| OUT (n), A | port n ← A | - |
| OUT (C), r | port C ← r | - |
Control Instructions
| Instruction | Operation |
|---|---|
| NOP | No operation |
| HALT | Halt until interrupt |
| DI | Disable interrupts (IFF1=IFF2=0) |
| EI | Enable interrupts (IFF1=IFF2=1) |
| IM 0 | Set interrupt mode 0 |
| IM 1 | Set interrupt mode 1 |
| IM 2 | Set interrupt mode 2 |
Undocumented Instructions
The Z80 contains several undocumented instructions that work reliably and are used by some software. This implementation supports them for compatibility.
SLL (Shift Left Logical)
Opcode: CB 30+r
Similar to SLA but shifts a 1 into bit 0 instead of 0:
SLL A ; Shift A left, bit 0 becomes 1
; Equivalent to: SLA A : OR 1
IX/IY Half-Register Operations
The DD and FD prefixes allow access to the high and low bytes of IX and IY:
| Register | Encoding |
|---|---|
| IXH | DD with H encoding |
| IXL | DD with L encoding |
| IYH | FD with H encoding |
| IYL | FD with L encoding |
Examples:
LD A, IXH ; DD 7C - Load high byte of IX into A
LD IXL, 0x42 ; DD 2E 42 - Load 0x42 into low byte of IX
INC IYH ; FD 24 - Increment high byte of IY
DDCB/FDCB Autocopy
When using indexed bit/rotate/shift instructions (DDCB or FDCB prefixed), the result is automatically copied to a register if the final byte encodes a register:
; Standard: RLC (IX+5)
; With autocopy: result also goes to specified register
; DD CB 05 00 - RLC (IX+5),B - rotate and copy to B
; DD CB 05 01 - RLC (IX+5),C - rotate and copy to C
; DD CB 05 06 - RLC (IX+5) - standard (no copy)
This behavior, while undocumented, is relied upon by some Z80 software.
Interrupts
The Z80 supports two types of interrupts: maskable (INT) and non-maskable (NMI).
Non-Maskable Interrupt (NMI)
The NMI is edge-triggered and cannot be disabled. When an NMI occurs:
- Current PC is pushed onto the stack
- IFF1 is copied to IFF2, then IFF1 is cleared
- PC is set to 0x0066
The interrupt handler should end with RETN, which restores IFF1 from IFF2.
Maskable Interrupt (INT)
The maskable interrupt can be enabled (EI) or disabled (DI). The response depends on the current interrupt mode.
Interrupt Mode 0
The interrupting device places an instruction (typically RST) on the data bus. The Z80 executes this instruction. This mode is 8080-compatible.
Interrupt Mode 1
The simplest mode. When an INT occurs with IFF1 set:
- IFF1 and IFF2 are cleared
- Current PC is pushed onto the stack
- PC is set to 0x0038
This is the most commonly used mode on many systems.
Interrupt Mode 2
The most powerful mode. When an INT occurs:
- IFF1 and IFF2 are cleared
- Current PC is pushed onto the stack
- The device provides an 8-bit vector
- This vector is combined with I to form: (I * 256 + vector)
- PC is loaded from the 16-bit value at that address
This allows up to 128 different interrupt vectors.
Interrupt State Registers
IFF1: Controls whether maskable interrupts are accepted. IFF2: Temporary storage of IFF1 during NMI processing.
; Typical interrupt handler structure
ORG 0x0038 ; IM 1 vector
PUSH AF
PUSH BC
PUSH DE
PUSH HL
; ... handle interrupt ...
POP HL
POP DE
POP BC
POP AF
EI
RETI
After EI, interrupts are not enabled until after the following instruction. This allows EI followed by RETI to complete atomically.
Programming Examples
Hello World
A simple program to output a string via UART:
ORG 0x2000
START:
LD HL, MESSAGE ; Point to message
LOOP:
LD A, (HL) ; Get character
OR A ; Check for null terminator
JR Z, DONE ; Exit if zero
CALL PUTCHAR ; Output character
INC HL ; Next character
JR LOOP
DONE:
HALT
PUTCHAR:
PUSH AF
WAIT:
IN A, (0x01) ; Check UART status
BIT 1, A ; TX ready?
JR Z, WAIT ; Wait if not
POP AF
OUT (0x00), A ; Send character
RET
MESSAGE:
DB "Hello, World!", 0x0D, 0x0A, 0
16-Bit Addition
Add two 16-bit numbers:
; HL = HL + DE
ADD HL, DE ; Simple case
; For numbers in memory:
; Add (NUM1) to (NUM2), store in (RESULT)
LD HL, (NUM1)
LD DE, (NUM2)
ADD HL, DE
LD (RESULT), HL
Memory Fill
Fill a block of memory with a value:
; Fill 256 bytes at 0x4000 with 0xFF
LD HL, 0x4000 ; Start address
LD BC, 0x0100 ; Count (256 bytes)
LD A, 0xFF ; Fill value
FILL:
LD (HL), A
INC HL
DEC BC
LD A, B
OR C
JR NZ, FILL
Or using block instructions:
LD HL, 0x4000 ; Start address
LD DE, 0x4001 ; Destination (start + 1)
LD BC, 0x00FF ; Count - 1
LD (HL), 0xFF ; Set first byte
LDIR ; Copy to fill
Delay Loop
Create a delay using register counting:
; Delay approximately (BC * 13) T-states
DELAY:
LD BC, 0xFFFF ; Maximum count
DELAY_LOOP:
DEC BC ; 6 T-states
LD A, B ; 4 T-states
OR C ; 4 T-states
JR NZ, DELAY_LOOP ; 12/7 T-states
RET
Simple UART Echo
Echo received characters back to the sender:
ORG 0x2000
ECHO:
IN A, (0x01) ; Check UART status
BIT 0, A ; RX data available?
JR Z, ECHO ; Keep waiting
IN A, (0x00) ; Read received byte
WAIT_TX:
PUSH AF
IN A, (0x01) ; Check TX status
BIT 1, A ; TX ready?
JR Z, WAIT_TX ; Keep waiting
POP AF
OUT (0x00), A ; Echo the byte
JR ECHO ; Continue
Quick Reference Tables
Condition Codes
| Code | Flag Test | True When |
|---|---|---|
| NZ | Z = 0 | Not zero |
| Z | Z = 1 | Zero |
| NC | C = 0 | No carry |
| C | C = 1 | Carry |
| PO | P/V = 0 | Parity odd / No overflow |
| PE | P/V = 1 | Parity even / Overflow |
| P | S = 0 | Positive |
| M | S = 1 | Minus (negative) |
Register Codes
8-Bit Registers (r)
| Code | Register |
|---|---|
| 000 | B |
| 001 | C |
| 010 | D |
| 011 | E |
| 100 | H |
| 101 | L |
| 110 | (HL) |
| 111 | A |
16-Bit Register Pairs
| dd | ss | pp | |
|---|---|---|---|
| BC | BC | BC | BC |
| DE | DE | DE | DE |
| HL | HL | HL | IX/IY |
| SP | SP | AF | SP |
Common Opcode Prefixes
| Prefix | Effect |
|---|---|
| CB | Bit/Rotate/Shift instructions |
| DD | IX register instead of HL |
| ED | Extended instructions |
| FD | IY register instead of HL |
Instruction Timing (Selected)
| Instruction | T-States |
|---|---|
| NOP | 4 |
| LD r, r' | 4 |
| LD r, n | 7 |
| LD r, (HL) | 7 |
| LD (HL), r | 7 |
| LD r, (IX+d) | 19 |
| ADD A, r | 4 |
| ADD HL, ss | 11 |
| INC r | 4 |
| JP nn | 10 |
| JP cc, nn | 10 |
| JR e | 12 |
| JR cc, e | 12/7 |
| CALL nn | 17 |
| RET | 10 |
| PUSH qq | 11 |
| POP qq | 10 |
| LDIR | 21/16 |
Troubleshooting
Common Problems
Program Won't Run
Symptom: Code loads but doesn't execute as expected.
Possible Causes:
- Code loaded into ROM area (write-protected)
- Stack pointer not initialized
- Interrupts disabled when interrupt-driven
Solutions:
- Load code at 0x2000 or higher (RAM area)
- Initialize SP early:
LD SP, 0xFFFF - Enable interrupts if needed:
EI
Garbled Output
Symptom: UART output appears corrupted.
Possible Causes:
- Not waiting for TX ready before sending
- Baud rate mismatch
- Flow control issues
Solutions:
- Always check UART status before transmitting
- Verify terminal settings match system configuration
Infinite Loop
Symptom: Program appears stuck.
Possible Causes:
- Condition never met for loop exit
- Missing or incorrect jump target
- Interrupt handler not returning
Solutions:
- Press ESC to return to monitor
- Check loop termination conditions
- Verify interrupt handlers end with RETI/RETN
Stack Overflow/Underflow
Symptom: Erratic behavior, wrong return addresses.
Possible Causes:
- SP initialized too low
- Unbalanced PUSH/POP
- Deep recursion
Solutions:
- Initialize SP high (e.g., 0xFFFF)
- Ensure every PUSH has matching POP
- Limit recursion depth
See Also
- SLP-8008 (555-8008) - Intel 8008 emulator, the Z80's ancestor
- SLP-8088 (555-8088) - Intel 8088 emulator for x86 architecture
- SLP-6502 (555-6502) - MOS 6502/C64 graphics demonstration
- SLP-BASIC (555-0300) - High-level BASIC programming
- SLP-FORTH (555-0400) - Stack-based Forth language
- Z80 Snake (555-7635) - Snake game written in C, compiled for Z80
- Z80 Snake ASM (555-9807) - Snake game in Z80 assembly language