Intel 8080 CPU Reference

Overview

The Intel 8080 is an 8-bit microprocessor introduced in 1974. It is the foundation for the Z80 and Game Boy CPUs, and was used in early personal computers and arcade games. This implementation provides a reusable 8080 CPU core that can be used by any system (Space Invaders, CP/M systems, etc.) by implementing the Memory8080 trait.

Implementation: crates/core/src/cpu_8080.rs

Architecture

Registers

The 8080 has seven 8-bit registers that can be used as pairs:

  • A (Accumulator): Primary register for arithmetic and logic operations
  • B, C: 8-bit registers (can be paired as BC)
  • D, E: 8-bit registers (can be paired as DE)
  • H, L: 8-bit registers (can be paired as HL for memory addressing)
  • SP (Stack Pointer): 16-bit pointer to stack
  • PC (Program Counter): 16-bit pointer to current instruction
  • Flags: 8-bit status flags register

Register Pairs

Registers can be accessed as 16-bit pairs:

  • BC: B (high byte) and C (low byte)
  • DE: D (high byte) and E (low byte)
  • HL: H (high byte) and L (low byte)
  • PSW: A (high byte) and Flags (low byte)

Flags Register

SZ-A-P-C
││ │ │ └─ Carry flag
││ │ └─── Parity flag
││ └───── Auxiliary Carry flag (for BCD)
│└─────── Zero flag
└──────── Sign flag

Bits 1, 3, and 5 are always 0, 0, and 0 respectively.

Usage

Systems using the 8080 must implement the Memory8080 trait:

pub trait Memory8080 {
    fn read(&self, addr: u16) -> u8;
    fn write(&mut self, addr: u16, val: u8);
    fn port_in(&mut self, port: u8) -> u8;
    fn port_out(&mut self, port: u8, val: u8);
}

Example

use emu_core::cpu_8080::{Cpu8080, Memory8080};

struct MySystem {
    ram: [u8; 65536],
}

impl Memory8080 for MySystem {
    fn read(&self, addr: u16) -> u8 {
        self.ram[addr as usize]
    }
    
    fn write(&mut self, addr: u16, val: u8) {
        self.ram[addr as usize] = val;
    }
    
    fn port_in(&mut self, port: u8) -> u8 {
        0 // System-specific I/O handling
    }
    
    fn port_out(&mut self, port: u8, val: u8) {
        // System-specific I/O handling
    }
}

let system = MySystem { ram: [0; 65536] };
let mut cpu = Cpu8080::new(system);
cpu.reset();

Instruction Set

The 8080 has 244 opcodes organized into the following categories:

Data Transfer

  • MOV: Move register to register
  • MVI: Move immediate to register
  • LDA: Load accumulator direct
  • STA: Store accumulator direct
  • LHLD: Load HL direct
  • SHLD: Store HL direct
  • LXI: Load register pair immediate
  • LDAX: Load accumulator indirect (BC or DE)
  • STAX: Store accumulator indirect (BC or DE)
  • XCHG: Exchange DE and HL

Arithmetic

  • ADD: Add register to accumulator
  • ADI: Add immediate to accumulator
  • ADC: Add register to accumulator with carry
  • ACI: Add immediate to accumulator with carry
  • SUB: Subtract register from accumulator
  • SUI: Subtract immediate from accumulator
  • SBB: Subtract register from accumulator with borrow
  • SBI: Subtract immediate from accumulator with borrow
  • INR: Increment register
  • DCR: Decrement register
  • INX: Increment register pair
  • DCX: Decrement register pair
  • DAD: Add register pair to HL

Logical

  • ANA: AND register with accumulator
  • ANI: AND immediate with accumulator
  • ORA: OR register with accumulator
  • ORI: OR immediate with accumulator
  • XRA: XOR register with accumulator
  • XRI: XOR immediate with accumulator
  • CMP: Compare register with accumulator
  • CPI: Compare immediate with accumulator

Rotate

  • RLC: Rotate accumulator left
  • RRC: Rotate accumulator right
  • RAL: Rotate accumulator left through carry
  • RAR: Rotate accumulator right through carry

Branch

  • JMP: Jump unconditional
  • Jcc: Conditional jumps (JC, JNC, JZ, JNZ, JP, JM, JPE, JPO)
  • CALL: Call subroutine unconditional
  • Ccc: Conditional calls (CC, CNC, CZ, CNZ, CP, CM, CPE, CPO)
  • RET: Return from subroutine unconditional
  • Rcc: Conditional returns (RC, RNC, RZ, RNZ, RP, RM, RPE, RPO)
  • PCHL: Jump to address in HL

Stack

  • PUSH: Push register pair onto stack
  • POP: Pop register pair from stack
  • XTHL: Exchange top of stack with HL
  • SPHL: Load SP from HL

I/O

  • IN: Input from port
  • OUT: Output to port

Special

  • NOP: No operation
  • HLT: Halt
  • DI: Disable interrupts
  • EI: Enable interrupts
  • RST: Restart (8 vectors: RST 0-7)
  • DAA: Decimal adjust accumulator
  • CMA: Complement accumulator
  • STC: Set carry flag
  • CMC: Complement carry flag

Addressing Modes

The 8080 supports several addressing modes:

  1. Register: Operates on registers (e.g., MOV A,B)
  2. Immediate: Operand is next byte (e.g., MVI A,42H)
  3. Direct: 16-bit address (e.g., LDA 1234H)
  4. Register Indirect: Address in HL (e.g., MOV A,M)
  5. Register Pair: 16-bit register pair (e.g., LXI H,1234H)

Interrupts

The 8080 supports interrupts with the following mechanism:

  1. INTE (Interrupt Enable) flag controls interrupt handling
  2. When an interrupt occurs, the CPU:
    • Pushes PC onto stack
    • Disables further interrupts (INTE = 0)
    • Jumps to interrupt vector (provided by interrupting device)
  3. Use EI to enable interrupts, DI to disable
  4. Use RET to return from interrupt handler

RST Instructions

The 8080 has 8 restart instructions (RST 0-7) that act as 1-byte CALL instructions:

  • RST 0: CALL $0000
  • RST 1: CALL $0008
  • RST 2: CALL $0010
  • RST 3: CALL $0018
  • RST 4: CALL $0020
  • RST 5: CALL $0028
  • RST 6: CALL $0030
  • RST 7: CALL $0038

I/O Operations

The 8080 has a separate I/O address space (256 ports):

  • IN port: Read from I/O port
  • OUT port: Write to I/O port

Systems must implement port_in() and port_out() to handle I/O.

Timing

The 8080 uses a two-phase clock. Instructions take between 4 and 18 clock cycles (1-4.5 machine cycles). Typical timings:

  • Simple register operations: 4-5 cycles
  • Memory access: 7-10 cycles
  • I/O operations: 10 cycles
  • Jumps/calls: 10-17 cycles

Differences from Z80

The 8080 is a subset of the Z80. Key differences:

  • Z80 adds IX, IY index registers
  • Z80 adds shadow register set
  • Z80 adds more instructions
  • Z80 has different flag behavior for some instructions
  • 8080 has external status signals that Z80 internalizes

Implementation Notes

Parity Flag

The parity flag is set if the number of 1 bits in the result is even. This requires counting bits in the result.

Auxiliary Carry

The auxiliary carry flag is used for BCD (Binary Coded Decimal) operations. It is set if there is a carry from bit 3 to bit 4.

Undocumented Flags

Some flag bits are affected by operations in undocumented ways. This implementation follows the documented behavior.

Systems That Could Use 8080

While not currently used directly in Hemulator, this CPU core could be used for:

  • Space Invaders arcade game
  • CP/M systems
  • Early microcomputers (Altair 8800, IMSAI 8080)

The Z80 and LR35902 (Game Boy) CPUs are derivatives that extend the 8080 architecture.

References