Created
April 12, 2014 23:51
-
-
Save yuriks/10562583 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![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