Skip to content

goldfishrock/Z80Emu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

16 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Z80Emu

Being a professional developer and being fascinated with all things retro tech & Z80, I have flirted with the idea of writing a Z80 emulator for a long time. It has been 47 years since I was first exposed to my first Z80 computer and I know itโ€™s already been done a thousand times over however, for me itโ€™s about the challenge of learning and understanding Z80 architecture to a highly granular level and writing it in a language I havenโ€™t used in โ€˜angerโ€™ for almost 30 years, C++. Needless to say, things have changed a little and the days of Borland Turbo C++ are long gone. So anyway, Iโ€™ve started this project and I plan on documenting progress as I go; both the progress of the emulator and my โ€˜rediscoveryโ€™ of C++!


A from-scratch Z80 CPU emulator written in modern C++20.

This project is a structured learning exercise in emulator development, focusing first on correctness, determinism, and architectural clarity before performance or hardware accuracy.

The goal is not to rush to a full machine emulator, but to build a solid, extensible CPU core with proper timing (T-state based), clean separation of concerns, and strong debugging visibility.


What is Z80Emu?

A slightly obsessive, thoroughly geeky, modern C++20 Z80 emulator.

This isnโ€™t a โ€œletโ€™s throw something togetherโ€ emulator.
Itโ€™s a learn it properly, build it cleanly, test it properly, understand every opcode kind of emulator.

And yesโ€ฆ itโ€™s being built because the Z80 is awesome.


๐ŸŽฏ What This Project Is (And Isnโ€™t)

This project is:

  • A ground-up Z80 emulator written in modern C++20
  • Built with CMake
  • Test-driven using Catch2
  • Structured to be clean, readable, and extendable
  • A learning vehicle as much as a functional emulator

This project is not:

  • A copy-paste from an existing emulator
  • A โ€œgood enoughโ€ hack
  • A speed-optimised black box

The goal here is understanding. Every instruction. Every flag. Every tick.


๐Ÿ— Architecture Overview

The emulator is deliberately split into simple, clear layers.

CPU Core

The CPU class handles:

  • 8-bit and 16-bit register pairs (AF, BC, DE, HL)
  • Flag manipulation via helper functions
  • Instruction decoding via switch dispatch
  • Opcode family grouping (e.g. LD r,r, INC r, DEC r)
  • Clean helper functions for arithmetic (inc8, dec8, etc.)

The aim is clarity over cleverness.
If something looks โ€œtoo magicโ€, it probably gets rewritten.


Bus Layer

The bus abstracts memory access.

The CPU connects to the bus and performs:

  • Instruction fetch
  • Memory reads
  • Memory writes

This separation makes unit testing clean and predictable.


Execution Model

Each step:

  1. Fetch opcode from memory (PC)
  2. Decode via switch statement
  3. Execute instruction
  4. Update PC
  5. Update flags as required

No macro trickery. No hidden state mutation.


โœ… Currently Implemented Instructions

๐Ÿ“ฅ 16-bit Load Instructions

  • LD BC,nn (0x01)
  • LD DE,nn (0x11)
  • LD HL,nn (0x21)
  • LD SP,nn (0x31)

๐Ÿ“ฅ 8-bit Load Instructions

  • LD r,n
    • A, B, C, D, E, H, L
  • LD r,r

๐Ÿ” 8-bit Increment / Decrement

  • INC r (including (HL))
  • DEC r (including (HL))

๐Ÿ” 16-bit Increment / Decrement

  • INC BC (0x03)

  • INC DE (0x13)

  • INC HL (0x23)

  • INC SP (0x33)

  • DEC BC (0x0B)

  • DEC DE (0x1B)

  • DEC HL (0x2B)

  • DEC SP (0x3B)


๐Ÿงฎ 8-bit Arithmetic

  • ADD A,r (0x80โ€“0x87)
  • ADD A,n (0xC6)
  • ADC A,r (0x88โ€“0x8F)
  • SUB r (0x90โ€“0x97)
  • SBC A,r (0x98โ€“0x9F)

๐Ÿง  8-bit Logical Operations

  • AND n (0xE6)
  • OR n (0xF6)
  • XOR n (0xEE)
  • CP n (0xFE)

๐Ÿ”ข 16-bit Arithmetic

  • ADD HL,BC (0x09)
  • ADD HL,DE (0x19)
  • ADD HL,HL (0x29)
  • ADD HL,SP (0x39)

๐Ÿ“ฆ Stack Operations

  • PUSH BC (0xC5)

  • PUSH DE (0xD5)

  • PUSH HL (0xE5)

  • POP BC (0xC1)

  • POP DE (0xD1)

  • POP HL (0xE1)


๐Ÿงญ Control Flow (Phase 9)

Absolute Jumps

  • JP nn (0xC3)
  • JP (HL) (0xE9)

Relative Jumps

  • JR e (0x18)
  • JR NZ,e (0x20)
  • JR Z,e (0x28)
  • JR NC,e (0x30)
  • JR C,e (0x38)

Subroutine Control

  • CALL nn (0xCD)
  • RET (0xC9)

All control-flow instructions:

  • Correct little-endian handling
  • Proper signed displacement behaviour
  • Accurate stack interaction
  • No unintended flag side-effects

๐Ÿณ Flag Control

  • SCF (0x37)

๐Ÿงช Test Coverage

  • 76+ passing tests
  • ALU flag behaviour verified
  • 16-bit half-carry verified
  • Stack byte order verified
  • Stack pointer movement verified
  • Control flow and signed displacement verified

The emulator now has:

  • Core 8-bit ALU functionality
  • 16-bit arithmetic
  • 16-bit register increment/decrement
  • Functional stack primitives
  • Verified flag behaviour for implemented instructions

๐Ÿ–ฅ Build Instructions (Windows / MSYS2 UCRT64)

cmake -S . -B out/build
cmake --build out/build
ctest --test-dir out/build

If all goes well, youโ€™ll see a nice wall of passing tests.
If notโ€ฆ thatโ€™s why we have tests.


๐Ÿ—บ Roadmap

The foundation is now solid.
What remains is depth, completeness and hardware accuracy.


๐Ÿงญ Remaining Control Flow

๐Ÿ“ž Conditional Subroutines

  • RET NZ, RET Z, RET NC, RET C
  • CALL NZ,nn, CALL Z,nn, CALL NC,nn, CALL C,nn

Completes flag-driven subroutine branching.


๐Ÿ” Loop Control

  • DJNZ e

Classic Z80 tight-loop instruction (decrement B and branch).


๐Ÿ“ Absolute Conditional Jumps

  • JP NZ,nn
  • JP Z,nn
  • JP NC,nn
  • JP C,nn
  • JP PO,nn
  • JP PE,nn
  • JP P,nn
  • JP M,nn

Extends conditional branching beyond relative forms.


๐Ÿงท Flag & Accumulator Instructions

  • CCF (Complement Carry Flag)
  • CPL (Complement Accumulator)
  • DAA (Decimal Adjust Accumulator โ€” BCD correctness)

These tighten full flag compliance.


๐Ÿ”„ Rotate & Shift Instructions (Non-Prefixed)

  • RLCA
  • RRCA
  • RLA
  • RRA

Flag-sensitive and timing-sensitive.


๐Ÿง  CB Prefix Group (Bit / Rotate / Shift)

  • RLC, RRC, RL, RR
  • SLA, SRA, SRL
  • BIT
  • SET
  • RES

Large instruction family.
Major milestone once complete.


๐Ÿงจ ED Prefix Group (Extended Instructions)

  • Block transfer (LDI, LDIR, LDD, LDDR)
  • Block compare (CPI, CPIR, etc.)
  • Extended arithmetic
  • I/O instructions (IN, OUT)
  • Interrupt mode control (IM 0/1/2)
  • RETI, RETN

Brings the CPU closer to real-world software compatibility.


๐Ÿ“ Index Registers (DD / FD Prefix)

  • IX / IY register support
  • Indexed addressing with displacement
  • CB-prefixed indexed bit operations

This significantly increases decoder complexity.


๐Ÿ”Œ I/O System

  • IN A,(n)
  • OUT (n),A
  • Register-based port I/O
  • Block I/O (ED-prefixed)

Required for real hardware emulation.


โšก Interrupt System

  • DI, EI
  • Interrupt modes 0 / 1 / 2
  • HALT
  • I and R registers
  • Interrupt acknowledge cycle modelling

Essential for CP/M and full system behaviour.


โฑ Timing & Accuracy

  • Per-instruction T-state modelling
  • Taken vs not-taken branch timing differences
  • Prefix timing penalties
  • Optional memory contention modelling
  • Accurate HALT cycle behaviour

Moves from logical correctness to hardware realism.


๐Ÿงฑ Memory System Evolution

  • ROM/RAM region separation
  • Write-protected ROM
  • Configurable memory map
  • ROM image loading
  • Bank switching (future)
  • Integration test harness for real programs

๐Ÿ–ฅ System-Level Goals

  • Execute small hand-written assembly programs
  • Run test ROM suites
  • Eventually boot CP/M
  • Add minimal peripheral layer
  • Possibly build a simple Z80-based virtual machine

Structural / Accuracy Work

  • Proper T-state timing model
  • Interrupt handling
  • Stack operations
  • Jump / Call / Return instructions

Longer Term

  • Real program execution
  • CP/M experiments
  • Possibly memory-mapped peripherals
  • Something mildly ridiculous

๐Ÿ““ Development Philosophy

This emulator grows incrementally.

Each session:

  • One instruction group
  • Full flag correctness
  • Tests first
  • Everything green before moving on

No rushed megacomits. No โ€œweโ€™ll fix flags laterโ€.


๐Ÿš€ Why?

Because writing an emulator is one of the best ways to truly understand:

  • CPU architecture
  • Binary execution
  • Instruction decoding
  • Timing behaviour
  • Clean software structure

And because the Z80 deserves it.

......Itโ€™s 50 years old.
......Itโ€™s elegant.
......Itโ€™s still everywhere. ......And itโ€™s a joy to implement properly.


If you're building your own emulator โ€” welcome.
If you love the Z80 โ€” even better.

About

Building a Z80 Emulator (Platform Independant) in C++20, using CMake and test-driven developed using Catch2.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors