A cycle-accurate Intel 8080 CPU emulator written in Go.
go8080 implements the full Intel 8080 instruction set, including all arithmetic, logic, branching, stack, I/O, and control instructions. It is designed as a reusable library — consumers provide their own memory and I/O implementations and wire them into the CPU. The emulator has been validated against the standard 8080 CPU test ROM suite.
go get github.com/khchehab/go8080
The library requires two interface implementations:
Memory — provides the CPU's 64 KB address space:
type Memory interface {
Read(address uint16) uint8
Write(address uint16, value uint8)
}IO — handles the 256-port I/O bus (used by IN/OUT instructions):
type IO interface {
Read(port uint8) uint8
Write(port, value uint8)
}Create a CPU with NewCPU, then drive it with Step or Run:
mem := NewMyMemory()
io := NewMyIO()
cpu, err := go8080.NewCPU(0x0100, 0xF000, mem, io)
if err != nil {
log.Fatal(err)
}
// Run until HLT or PC wraps to 0x0000
if err := cpu.Run(); err != nil {
log.Fatal(err)
}If your program does not use IN/OUT instructions, pass nil for the IO
argument.
Step executes one instruction and returns the number of cycles it consumed.
This is useful when you need to synchronise the CPU with external hardware,
such as a video chip that fires interrupts at fixed cycle intervals:
for !cpu.Halted() {
cycles, err := cpu.Step()
if err != nil {
log.Fatal(err)
}
totalCycles += uint64(cycles)
if totalCycles >= cyclesPerHalfFrame {
cpu.Interrupt(0xCF) // RST 1
totalCycles = 0
}
}Call Interrupt with a full RST opcode byte to request a hardware interrupt:
cpu.Interrupt(0xCF) // RST 1 — jumps to 0x0008
cpu.Interrupt(0xD7) // RST 2 — jumps to 0x0010The interrupt is held pending until the next Step call where interrupts are
enabled (EI) and the one-instruction EI delay has expired. This matches real
8080 hardware behaviour.
All registers and flags are exposed as read-only methods:
cpu.A() // accumulator
cpu.B(), cpu.C(), cpu.D(), cpu.E(), cpu.H(), cpu.L()
cpu.BC(), cpu.DE(), cpu.HL(), cpu.AF() // register pairs
cpu.PC() // program counter
cpu.SP() // stack pointer
cpu.PSW() // flags register
cpu.GetSign()
cpu.GetZero()
cpu.GetCarry()
cpu.GetAuxiliaryCarry()
cpu.GetParity()
cpu.Halted()
cpu.InterruptEnabled()
cpu.Cycles() // total clock cycles accumulatedDebug writes a single-line trace of the current CPU state to any io.Writer:
cpu.Debug(os.Stdout)
// PC: 0x0100, A: 0x00, F: 0x02, B: 0x00, ... 0x31 - LXI SP,d16The emulator passes all standard Intel 8080 CPU test ROMs:
| ROM | Result |
|---|---|
| TST8080.COM | Pass |
| 8080PRE.COM | Pass |
| CPUTEST.COM | Pass |
| 8080EXM.COM | Pass |
| 8080EXER.COM | Pass |
- Full 64 KB flat address space via the
Memoryinterface - 256-port I/O bus via the
IOinterface - Flags packed in a single byte (S, Z, AC, P, C) with fixed bits enforced on every write
-
Stepreturns the cycle count for the executed instruction;Cyclesreturns the running total - Conditional
CALLandRETreturn the correct cycle count for both the taken and not-taken paths - Interrupt handling with EI delay: an interrupt requested after
EIis not serviced until the instruction followingEIcompletes
- superzazu/8080 — provided the CP/M test ROMs used to validate the emulator, as well as a well-structured reference implementation that served as useful documentation during development.
- pastraiser.com Intel 8080 Opcodes — provided a clear and comprehensive opcode table covering instruction encoding, byte lengths, cycle counts, and flag effects, which was invaluable throughout the implementation.
MIT