z8086 is a FPGA-targeted implementation of the Intel 8086/8088 microprocessor. It executes the original 8086 microcode and provides a simplified memory/IO interface for FPGA integration.
The z8086 core is a faithful implementation of the 8086 microprocessor architecture, using the original Intel microcode to ensure instruction-level compatibility. The core is written in SystemVerilog and designed to be vendor-agnostic (no FPGA-specific primitives).
- Microcode-driven: Executes the original 8086 microcode (21-bit words) loaded from
ucode.hex - Simplified bus interface:
rd/wr/io/wordwithreadyhandshake; unaligned word accesses are automatically split internally - Prefetch queue: 3-word (6-byte) prefetch queue with suspend/flush/correct-IP support
- Bit-slice ALU: Explicit 16×1-bit slice design with accurate flags (DAA/DAS/AAA/AAS, shifts/rotates, etc.)
- FPGA-friendly: Single clock, synchronous design, no vendor primitives
This project is still experimental. The core passes all single-instruction tests, achieving a 100% pass rate (16,150 out of 16,150 vectors) with full 8086 instruction set coverage—including arithmetic, logic, shifts, control flow, string operations, multiplication/division, interrupts, I/O, and BCD/ASCII adjust instructions. FPGA synthesis has been validated on Gowin GW5A, Xilinx Artix-7 and Altera Cyclone-V platforms. Note that current limitations include non-cycle-accurate timing, no LOCK pin support, and no coprocessor interface.
Resource usage: ~1800 LUTs on Xilinx Artix7, ~1200 ALMs on Altera Cyclone V, ~2500 LUTs on Gowin GW5A. One BRAM block is used for the microcode. The code is about 1800 lines of SystemVerilog.
The z8086 maintains the logical distinction between BIU (Bus Interface Unit) and EU (Execution Unit) but implements them in a unified datapath. The core includes:
- Microcode Engine: Fetches micro-instructions from the 512-word ROM (with address compression) and orchestrates all key CPU operations
- Loader State Machine: Manages instruction fetch timing (FC/SC signals)
- Prefetch Queue: 6-byte circular buffer for instruction prefetching
- ALU: 16-bit arithmetic/logic unit with comprehensive flag support
- Memory Interface: Unified bus FSM that arbitrates EU data operations vs instruction fetch
The core implementation is located in src/:
src/z8086/z8086.sv: Main CPU coresrc/z8086/alu.sv: 16-bit ALU and flag computationsrc/z8086/ucode.hex: Microcode ROM image (512 × 21-bit words)src/soc/z8086_top.sv: Example SoC top-level with memory interfacesrc/soc/spram.sv: Simple single-port RAM for memory subsystem
To run simulations, build the Verilator testbench and use the provided Python runners in tests/. Prerequisites: Verilator 4.0+, C++ compiler, make, Python 3.
Build simulator and prepare test data:
cd 04.z8086/tests
make -s build
wget https://github.com/nand2mario/z8086/releases/download/v0.1/8088.tar.gz
tar xzf 8088.tar.gzTest an instruction:
python test8088.py -f 8088/10.json -v # single opcode/file-f <file>: test file (JSON)-v: verbose-i <case>: specific test case
Test all instructions:
python test8088.pyRun small program tests:
python test186.pyRun interrupt test:
make sim-int(Exercises the INTA cycle and vector fetch.)
FPGA board projects are in the boards/ directory:
- Tang Console 60K (Gowin GW5AT-60B):
z8086_console60k.gprj(Gowin IDE). Drives LED pmod or Muse Labs LCD pmod. Press S1 button to start. - MicroPhase XC7A35T (Artix-7):
z8086.xpr(Vivado 2021.1). Drives onboard LED. For LCD, see photo linked below for connections. Press K1 button to start. - DE10-Nano (Cyclone V):
z8086.qpf(Quartus Prime). Drives onboard LED. For LCD, see photo linked below for connections. Press KEY0 to start.
See LCD demo screenshots: DE10-Nano, Tang Console 60K, MicroPhase Artix7, DE10-Nano.
The src/soc/z8086_top.sv module shows how to integrate the z8086 core:
z8086 cpu (
.clk(clk50),
.reset_n(cpu_reset),
.addr(addr),
.din(din),
.dout(dout),
.wr(wr),
.rd(rd),
.io(io),
.word(word),
.ready(ready),
.intr(1'b0),
.nmi(1'b0),
.inta()
);
// Memory subsystem
spram #(.AWIDTH(16)) ram (
.clk(clk50),
.addr(addr[15:0]),
.din(dout),
.dout(din),
.we(wr & ~io),
.oe(rd & ~io)
);Here are the memory interface:
Signals:
addr[19:0]: 20-bit physical addressdin[15:0]: Data input from memory/IOdout[15:0]: Data output to memory/IOrd: Read strobe (active high)wr: Write strobe (active high)io: 1=I/O access, 0=memory accessword: 1=16-bit access, 0=8-bit accessready: Memory/IO ready (handshake)
Bus Protocol:
- The z8086 core asserts
rd(read) orwr(write) along with validaddr,word, andiosignals. - The external memory or I/O subsystem initiates and completes the requested operation.
- When the operation is finished, the external logic sets
ready=1. For reads, data must be present ondin. - The core automatically splits unaligned word accesses into two sequential byte operations. External systems will never see unaligned word accesses (
wordandaddr[0]will not both be 1). - Interrupt Handling: When INTR is raised, the core triggers two INTA (interrupt acknowledge) bus cycles in accordance with the 8086 protocol. For each INTA cycle, the CPU asserts INTA. After the second INTA, external logic should place the interrupt vector number on
dinand assertready=1to complete the transaction.
The example SoC in z8086_top.sv and the programs/ directory demonstrate one way of programming the core, backed by FPGA BRAM.
- Write your firmware: Create your program in 8086 assembly language. You can find example programs in the
programs/directory. - Build the firmware:
- Use the Makefile in
programs/to automate the build process. The Makefile invokes NASM to assemble.asmsources into.binbinaries. - These binaries are then converted to
.hexformat for FPGA loading.
- Use the Makefile in
- Understanding the memory map and program loading:
- The system is configured with 128KB of main memory. Even-numbered segments map to the lower 64KB ("data segment"), while odd-numbered segments map to the upper 64KB ("program segment").
- When building, the
program.hexoutput places an empty 64KB in the first half, and the assembled program in the second 64KB. - The SoC module
z8086_top.svuses$readmemhto load the.hexdirectly into FPGA RAM. - A reset vector trampoline (far jump to F000:0000) is at the end of the second page (offset FFF0).
- Boot procedure: Upon reset, the CPU sets CS:IP to FFFF:0000 and executes that far jump. Execution then continues from F000:0000, the start of the user program, which is correctly mapped to the program segment.
Several example firmware files are included in the programs/ directory:
blinky.asm: Minimal example toggling LEDs and counting up.lcd_bars.asm: Demonstrates basic LCD display functionality.lcd_shapes.asm: Repeated square drawing.
Refer to these examples as starting points for your own programs, and consult z8086_top.sv for detailed integration.
This is a larger demo: a complete 8086-compatible SoC featuring HDMI video output, UART communication, and C firmware (as opposed to the assembly-only firmware used in previous examples). It currently targets the Tang Console 60K. The Gowin project file is located at boards/Tang Console 60K.soc_hdmi, with source files in src/soc_hdmi, and firmware in programs/soc_hdmi.c.
┌───────────────────────────────────────────────────────┐
│ z8086 soc_hdmi │
│ ┌──────────┐ ┌──────────┐ ┌─────────────────┐ │
│ │ z8086 │◄──►│ RAM │ │ z8086hdmi │ │
│ │ CPU │ │ 128KB │ │ Video Engine │-─┼──► HDMI
│ └──────────┘ └──────────┘ │ - Font ROM │ │
│ │ │ - VRAM (4KB) │ │
│ │ ┌──────────┐ │ - Color Palette│ │
│ └─────────►│ uart_ │ └─────────────────┘ │
│ │ simple │────────────────────────-┼──► UART
│ └──────────┘ │
│ │
│ Clocks: 50MHz (logic) → 74.25MHz (pixel) → 371.25MHz │
└───────────────────────────────────────────────────────┘
The firmware accepts simple commands such as: led (controlling on-board LED), text (print colorful text), blink (enable blinking text attributes). And of course, don’t miss Snake 😁 ��� written in C and compiled to real 8086 code.
z8086 would not have been possible without the outstanding reverse engineering work of Ken Shirriff and Andrew Jenner.
@misc{z8086,
title = {z8086: FPGA 8086 Core Powered by Original Microcode},
author = {nand2mario},
year = {2025},
url = {https://github.com/nand2mario/z8086}
}z8086 was developed primarily as an educational and learning tool. For broader or commercial use, the main limitation is the copyright status of the original 8086 microcode. Ideally, Intel would formally release the rights to this significant historical code.
As for z8086 itself, I have chosen to release it under the permissive Apache 2.0 license.
