Skip to content

Instantly share code, notes, and snippets.

@yuriks
Created April 12, 2014 23:51
Show Gist options
  • Select an option

  • Save yuriks/10562583 to your computer and use it in GitHub Desktop.

Select an option

Save yuriks/10562583 to your computer and use it in GitHub Desktop.
#![allow(dead_code)]
use std::default::Default;
struct PinInput {
d: u8,
wait: bool,
interrupt: bool,
nmi: bool,
reset: bool,
busrq: bool,
}
#[deriving(Default)]
struct PinOutput {
a: Option<u16>,
d: Option<u8>,
m1: bool,
mreq: bool, // Active during a memory R/W operation
iorq: bool, // Active during a IO R/W operation
rd: bool, // Active during a memory/IO read operation
wr: bool, // Active during a memory/IO write operation
rfsh: bool,
halt: bool,
busack: bool,
}
static flag_c: u8 = 1 << 0; // carry
static flag_n: u8 = 1 << 1; // add / subtract
static flag_pv: u8 = 1 << 2; // parity / overflow
static flag_x: u8 = 1 << 3; // unused
static flag_h: u8 = 1 << 4; // half-carry
static flag_i: u8 = 1 << 5; // unused
static flag_z: u8 = 1 << 6; // zero
static flag_s: u8 = 1 << 7; // sign
fn make_u16(h: u8, l: u8) -> u16 {
h as u16 << 8 | l as u16
}
fn split_u16(v: u16) -> (u8, u8) {
((v >> 8) as u8, v as u8)
}
struct CPU {
// Registers
// Set of general purpose registers
a: u8, f: u8,
b: u8, c: u8,
d: u8, e: u8,
h: u8, l: u8,
a_shadow: u8, f_shadow: u8,
b_shadow: u8, c_shadow: u8,
d_shadow: u8, e_shadow: u8,
h_shadow: u8, l_shadow: u8,
interrupt_vector: u8,
memory_refresh: u8,
ix: u16, iy: u16,
sp: u16, pc: u16,
// Internal processor state
clk_high: bool,
m_cycle: uint,
t_cycle: uint,
opcode: u8,
opcode_m_cycles: [MCycle, ..5],
memory_latch_h: u8, // Value read from or written to memory
memory_latch_l: u8, // memory_latch_h is shifted onto this
}
enum BusOpDirection {
Read, Write
}
enum BusOperation {
BusFetch, // 4 T-cycles
BusMem(BusOpDirection, WideRegister), // 3/4 T-cycles (?)
BusIO(BusOpDirection), // 4 T-cycles (?)
BusBusRequest,
BusInterrupt,
BusInternal(uint),
}
#[deriving(FromPrimitive)]
enum Register {
RegB = 0b000,
RegC = 0b001,
RegD = 0b010,
RegE = 0b011,
RegH = 0b100,
RegL = 0b101,
RegMem = 0b110, // Byte read from memory
RegA = 0b111,
}
enum WideRegister {
RegBC = 0b00,
RegDE = 0b01,
RegHL = 0b10,
RegSP, // = 0b11
RegAF, // = 0b11
RegPC,
RegMemWide
}
enum AluOperation {
AluNone,
AluMove(Register, Register), // (dest, src)
AluAdd(Register, Register, Register), // (dest, src1, src2)
AluWideMove(WideRegister, WideRegister), // (dest, src)
AluWideAdd(WideRegister, WideRegister, WideRegister), // (dest, src1, src2)
}
struct MCycle {
alu: AluOperation,
bus: BusOperation,
}
fn parse_reg(r: u8) -> Register {
match r {
0b000 => RegB,
0b001 => RegC,
0b010 => RegD,
0b011 => RegE,
0b100 => RegH,
0b101 => RegL,
0b110 => RegMem,
0b111 => RegA,
_ => unreachable!(),
}
}
fn wide_regSP(r: u8) -> WideRegister {
match r {
0b00 => RegBC,
0b01 => RegDE,
0b10 => RegHL,
0b11 => RegSP,
_ => unreachable!(),
}
}
fn decode_instruction(opcode: u8, cycles: &mut [MCycle, ..5]) {
let instr_x = opcode >> 6 & 0b11;
let instr_y = opcode >> 3 & 0b111;
let instr_z = opcode >> 0 & 0b111;
assert!(instr_x <= 0b11);
assert!(instr_y <= 0b111);
assert!(instr_z <= 0b111);
match (instr_x, instr_y, instr_z) {
(0b00, 0b000, 0b000) => { // NOP
cycles[0] = MCycle { alu: AluNone, bus: BusFetch };
}, (0b00, 0b110, 0b110) => { // LD (HL), n
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[1] = MCycle { alu: AluNone, bus: BusMem(Write, RegHL) };
cycles[2] = MCycle { alu: AluNone, bus: BusFetch };
}, (0b00, rd, 0b110) => { // LD r, n
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[1] = MCycle { alu: AluMove(parse_reg(rd), RegMem), bus: BusFetch };
}, (0b00, y, 0b001) if y & 1 == 0 => { // LD dd, nn
cycles[0] = MCycle { alu: AluNone, bus: BusInternal(4) };
cycles[0] = MCycle { alu: AluNone, bus: BusInternal(3) };
cycles[2] = MCycle { alu: AluWideMove(wide_regSP(y >> 1), RegMemWide), bus: BusFetch };
}, (0b00, y, 0b001) if y & 1 == 1 => { // ADD HL, ss
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[1] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[2] = MCycle { alu: AluWideAdd(RegHL, RegHL, wide_regSP(y >> 1)), bus: BusFetch };
},
(0b01, 0b110, 0b110) => { // HALT
unimplemented!();
}, (0b01, 0b110, rs) => { // LD (HL), r
cycles[0] = MCycle { alu: AluMove(RegMem, parse_reg(rs)), bus: BusMem(Write, RegHL) };
cycles[1] = MCycle { alu: AluNone, bus: BusFetch };
}, (0b01, rd, 0b110) => { // LD r, (HL)
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegHL) };
cycles[1] = MCycle { alu: AluMove(parse_reg(rd), RegMem), bus: BusFetch };
}, (0b01, rd, rs) => { // LD r, r'
cycles[0] = MCycle { alu: AluMove(parse_reg(rd), parse_reg(rs)), bus: BusFetch };
},
(0b10, 0b000, 0b110) => { // ADD A, (HL)
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegHL) };
cycles[1] = MCycle { alu: AluMove(RegA, RegMem), bus: BusFetch };
}, (0b10, 0b000, rs) => { // ADD A, r
cycles[0] = MCycle { alu: AluAdd(RegA, RegA, parse_reg(rs)), bus: BusFetch };
},
(0b11, 0b000, 0b011) => { // JP nn
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[1] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[2] = MCycle { alu: AluWideMove(RegPC, RegMemWide), bus: BusFetch };
}, (0b11, 0b000, 0b110) => { // ADD A, n
cycles[0] = MCycle { alu: AluNone, bus: BusMem(Read, RegPC) };
cycles[1] = MCycle { alu: AluAdd(RegA, RegA, RegMem), bus: BusFetch };
},
( _, _, _) => unimplemented!()
};
}
impl CPU {
fn read_reg(&self, register: Register) -> u8 {
match register {
RegB => self.b,
RegC => self.c,
RegD => self.d,
RegE => self.e,
RegH => self.h,
RegL => self.l,
RegMem => self.memory_latch_h,
RegA => self.a,
}
}
fn write_reg(&mut self, register: Register, value: u8) {
match register {
RegB => { self.b = value },
RegC => { self.c = value },
RegD => { self.d = value },
RegE => { self.e = value },
RegH => { self.h = value },
RegL => { self.l = value },
RegMem => {
self.memory_latch_l = self.memory_latch_h;
self.memory_latch_h = value;
},
RegA => { self.a = value },
};
}
fn read_wide_reg(&self, register: WideRegister) -> u16 {
match register {
RegBC => make_u16(self.b, self.c),
RegDE => make_u16(self.d, self.e),
RegHL => make_u16(self.h, self.l),
RegSP => self.sp,
RegAF => make_u16(self.a, self.f),
RegPC => self.pc,
RegMemWide => make_u16(self.memory_latch_h, self.memory_latch_l),
}
}
fn write_wide_reg(&mut self, register: WideRegister, value: u16) {
let (h, l) = split_u16(value);
match register {
RegBC => { self.b = h; self.c = l; },
RegDE => { self.d = h; self.e = l; },
RegHL => { self.h = h; self.l = l; },
RegSP => { self.sp = value },
RegAF => { self.a = h; self.f = l; },
RegPC => { self.pc = value },
RegMemWide => { self.memory_latch_h = h; self.memory_latch_l = l; },
}
}
fn fetch_cycle(&mut self, input: &PinInput) -> PinOutput {
match self.t_cycle {
0 => {
if !self.clk_high {
self.t_cycle += 1;
}
PinOutput {
m1: true,
a: Some(self.pc),
mreq: !self.clk_high,
rd: !self.clk_high,
..Default::default()
}
},
1 => {
if !self.clk_high {
if !input.wait {
self.t_cycle += 1;
}
}
PinOutput {
m1: true,
a: Some(self.pc),
mreq: true,
rd: true,
..Default::default()
}
},
2 => {
if self.clk_high {
self.opcode = input.d;
} else {
self.t_cycle += 1;
}
PinOutput {
rfsh: true,
a: Some(make_u16(self.interrupt_vector, self.memory_refresh)),
mreq: !self.clk_high,
..Default::default()
}
},
3 => {
if !self.clk_high {
self.t_cycle = 0;
self.m_cycle = 0;
decode_instruction(self.opcode, &mut self.opcode_m_cycles);
}
PinOutput {
rfsh: true,
a: Some(make_u16(self.interrupt_vector, self.memory_refresh)),
mreq: self.clk_high,
..Default::default()
}
},
_ => unreachable!()
}
}
fn mem_cycle(&mut self, input: &PinInput,
direction: BusOpDirection, address_src: WideRegister) -> PinOutput {
let output = PinOutput {
a: Some(self.read_wide_reg(address_src)),
..Default::default()
};
match self.t_cycle {
0 => {
if !self.clk_high {
self.t_cycle += 1;
}
PinOutput {
mreq: !self.clk_high,
rd: match direction { Read => !self.clk_high, _ => false },
wr: false,
d: match direction { Write if !self.clk_high => Some(self.memory_latch_h), _ => None },
..output
}
},
1 => {
if !self.clk_high {
if !input.wait {
// TODO: The value of wr is wrong (inactive, should be active) during the
// first half of the next cycle when a waitstate is inserted.
self.t_cycle += 1;
}
}
PinOutput {
mreq: true,
rd: match direction { Read => true, _ => false },
wr: match direction { Write => !self.clk_high, _ => false },
d: match direction { Write => Some(self.memory_latch_h), _ => None },
..output
}
},
2 => {
if !self.clk_high {
match direction {
Read => self.write_reg(RegMem, input.d), _ => ()
};
self.t_cycle = 0;
self.m_cycle += 1;
}
PinOutput {
mreq: self.clk_high,
rd: match direction { Read => self.clk_high, _ => false },
wr: match direction { Write => self.clk_high, _ => false },
d: match direction { Write => Some(self.memory_latch_h), _ => None },
..output
}
},
_ => unreachable!()
}
}
fn internal_cycle(&mut self, input: &PinInput, length: uint) -> PinOutput {
if !self.clk_high {
self.t_cycle += 1;
if self.t_cycle == length {
self.t_cycle = 0;
self.m_cycle += 1;
}
}
Default::default()
}
pub fn half_tick(&mut self, input: &PinInput) -> PinOutput {
// True if rising clock edge
self.clk_high = !self.clk_high;
let current_cycle = self.opcode_m_cycles[self.m_cycle];
if self.clk_high && self.t_cycle == 0 {
match current_cycle.alu {
AluNone => (),
AluMove(dest, src) => {
let a = self.read_reg(src);
self.write_reg(dest, a);
},
AluAdd(dest, src1, src2) => {
let a = self.read_reg(src1);
let b = self.read_reg(src2);
self.write_reg(dest, a + b); // TODO flags
},
AluWideMove(dest, src) => {
let a = self.read_wide_reg(src);
self.write_wide_reg(dest, a);
},
AluWideAdd(dest, src1, src2) => {
let a = self.read_wide_reg(src1);
let b = self.read_wide_reg(src2);
self.write_wide_reg(dest, a + b); // TODO flags
},
}
}
match current_cycle.bus {
BusFetch => self.fetch_cycle(input),
BusMem(direction, address_src) => self.mem_cycle(input, direction, address_src),
BusInternal(length) => self.internal_cycle(input, length),
_ => unimplemented!()
}
}
}
fn main() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment