diff --git a/src/bus.rs b/src/bus.rs new file mode 100644 index 0000000..b182a5f --- /dev/null +++ b/src/bus.rs @@ -0,0 +1,20 @@ +#[derive(Debug, Copy, Clone)] +pub struct MemoryBus {} + +impl MemoryBus { + pub fn read_byte(&self, _address: u16) -> u8 { + unimplemented!() + } + + pub fn write_byte(&mut self, _address: u16) { + unimplemented!() + } + + pub fn read_word(&self, _address: u16) -> u16 { + unimplemented!() + } + + pub fn write_word(&mut self, __address: u16) { + unimplemented!() + } +} diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..ca49eca --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,184 @@ +use super::bus::MemoryBus; +use super::Instruction; +use std::convert::From; + +#[derive(Debug, Copy, Clone)] +pub struct Cpu { + bus: MemoryBus, + reg: Registers, + ime: bool, + pc: u16, + sp: u16, +} + +impl Cpu { + pub fn run(&mut self) -> ! { + loop { + let opcode = self.fetch(); + let instruction = self.decode(opcode); + self.execute(instruction); + } + } + + fn fetch(&self) -> u8 { + unimplemented!() + } + + fn decode(&self, opcode: u8) -> Instruction { + unimplemented!() + } + + fn execute(&mut self, instruction: Instruction) { + unimplemented!() + } +} + +impl Cpu { + pub fn read_byte(&self, address: u16) -> u8 { + self.bus.read_byte(address) + } + + pub fn write_byte(&mut self, address: u16) { + self.bus.write_byte(address) + } + + pub fn read_word(&self, address: u16) -> u16 { + self.bus.read_word(address) + } + + pub fn write_word(&mut self, address: u16) { + self.bus.write_word(address) + } +} + +impl Cpu { + pub fn register_pair(&self, pair: RegisterPair) -> u16 { + match pair { + RegisterPair::AF => { + (self.register(Register::A) as u16) << 8 | self.register(Register::Flag) as u16 + } + RegisterPair::BC => { + (self.register(Register::B) as u16) << 8 | self.register(Register::C) as u16 + } + RegisterPair::DE => { + (self.register(Register::D) as u16) << 8 | self.register(Register::E) as u16 + } + RegisterPair::HL => { + (self.register(Register::H) as u16) << 8 | self.register(Register::L) as u16 + } + RegisterPair::SP => self.sp, + RegisterPair::PC => self.pc, + } + } + + pub fn set_register_pair(&mut self, pair: RegisterPair, value: u16) { + let high = (value >> 8) as u8; + let low = value as u8; + + match pair { + RegisterPair::AF => { + self.set_register(Register::A, high); + self.set_register(Register::Flag, low); + } + RegisterPair::BC => { + self.set_register(Register::B, high); + self.set_register(Register::C, low); + } + RegisterPair::DE => { + self.set_register(Register::D, high); + self.set_register(Register::E, low); + } + RegisterPair::HL => { + self.set_register(Register::H, high); + self.set_register(Register::L, low); + } + RegisterPair::SP => self.sp = value, + RegisterPair::PC => self.pc = value, + } + } + + pub fn register(&self, reg: Register) -> u8 { + match reg { + Register::A => self.reg.a, + Register::B => self.reg.b, + Register::C => self.reg.c, + Register::D => self.reg.d, + Register::E => self.reg.e, + Register::H => self.reg.h, + Register::L => self.reg.l, + Register::Flag => self.reg.f.into(), + } + } + + pub fn set_register(&mut self, reg: Register, value: u8) { + match reg { + Register::A => self.reg.a = value, + Register::B => self.reg.b = value, + Register::C => self.reg.c = value, + Register::D => self.reg.d = value, + Register::E => self.reg.e = value, + Register::H => self.reg.h = value, + Register::L => self.reg.l = value, + Register::Flag => self.reg.f = value.into(), + } + } +} + +#[derive(Debug, Copy, Clone)] +struct Registers { + a: u8, + b: u8, + c: u8, + d: u8, + e: u8, + h: u8, + l: u8, + f: Flag, +} + +#[derive(Debug, Copy, Clone)] +pub enum RegisterPair { + AF, + BC, + DE, + HL, + SP, + PC, +} + +#[derive(Debug, Copy, Clone)] +pub enum Register { + A, + B, + C, + D, + E, + H, + L, + Flag, +} + +#[derive(Debug, Copy, Clone)] +struct Flag { + z: bool, // Zero Flag + n: bool, // Negative Flag + h: bool, // Half-Carry Flag + c: bool, // Carry Flag +} + +impl From for Flag { + fn from(flag: u8) -> Self { + Self { + z: (flag >> 7) == 1, + n: ((flag >> 6) & 0x01) == 1, + h: ((flag >> 5) & 0x01) == 1, + c: ((flag >> 4) & 0x01) == 1, + } + } +} + +impl From for u8 { + fn from(flag: Flag) -> Self { + (flag.z as u8) << 7 | (flag.n as u8) << 6 | (flag.h as u8) << 5 | (flag.c as u8) << 4 + } +} diff --git a/src/instruction.rs b/src/instruction.rs new file mode 100644 index 0000000..41efb2d --- /dev/null +++ b/src/instruction.rs @@ -0,0 +1,126 @@ +use super::cpu::Cpu; +use super::cpu::Register; +use super::cpu::RegisterPair; + +pub enum Instruction { + NOP, + LD(Argument, Argument), + STOP, + JP(Condition, Argument), + JR(Condition, i8), + ADD(Argument, Argument), +} + +pub enum Argument { + ImmediateByte(u8), + ImmediateWord(u16), + IndirectImmediateByte(u16), + Register(Register), + RegisterPair(RegisterPair), + IndirectRegister(RegisterPair), +} + +impl Instruction { + pub fn from_byte(cpu: &Cpu, byte: u8) -> Instruction { + if byte == 0xCB { + Self::from_prefixed_byte(cpu, byte) + } else { + Self::from_unprefixed_byte(cpu, byte) + } + } + + pub fn from_unprefixed_byte(cpu: &Cpu, byte: u8) -> Instruction { + // https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html + let x = (byte >> 6) & 0b00000011; + let y = (byte >> 3) & 0b00000111; + let z = byte & 0b00000111; + let p = y >> 1; + let q = y >> 3 & 0b00000001; + + let n = cpu.read_byte(cpu.register_pair(RegisterPair::PC) + 1); + let nn = cpu.read_word(cpu.register_pair(RegisterPair::PC) + 1); + + match (x, z, q, y, p) { + (0, 0, _, 0, _) => Instruction::NOP, // NOP + (0, 0, _, 1, _) => Instruction::LD( + // LD (nn), SP + Argument::IndirectImmediateByte(nn), + Argument::RegisterPair(RegisterPair::SP), + ), + (0, 0, _, 2, _) => Instruction::STOP, // STOP + (0, 0, _, 3, _) => Instruction::JR(Condition::Always, n as i8), // JR d + (0, 0, _, 4..=7, _) => Instruction::JR(Self::table_cc(y - 4), n as i8), // JR cc[y - 4], d + (0, 1, 0, _, p) => Instruction::LD( + // LD rp[p], nn + Argument::RegisterPair(Self::table_rp(p)), + Argument::IndirectImmediateByte(nn), + ), + (0, 1, 1, _, p) => { + // ADD HL, rp[p] + Instruction::ADD( + Argument::RegisterPair(RegisterPair::HL), + Argument::RegisterPair(Self::table_rp(p)), + ) + } + (0, 2, 0, _, 0) => Instruction::LD( + Argument::IndirectRegister(RegisterPair::BC), + Argument::Register(Register::A), + ), + (0, 2, 0, _, 1) => Instruction::LD( + Argument::IndirectRegister(RegisterPair::DE), + Argument::Register(Register::A), + ), + (0, 2, 1, _, 0) => Instruction::LD( + Argument::Register(Register::A), + Argument::IndirectRegister(RegisterPair::BC), + ), + (0, 2, 1, _, 1) => Instruction::LD( + Argument::Register(Register::A), + Argument::IndirectRegister(RegisterPair::DE), + ), + _ => unreachable!(), + } + } + + pub fn from_prefixed_byte(cpu: &Cpu, byte: u8) -> Instruction { + unimplemented!() + } + + fn table_rp2(index: u8) -> RegisterPair { + match index { + 0 => RegisterPair::BC, + 1 => RegisterPair::DE, + 2 => RegisterPair::HL, + 3 => RegisterPair::AF, + _ => unreachable!(), + } + } + + fn table_rp(index: u8) -> RegisterPair { + match index { + 0 => RegisterPair::BC, + 1 => RegisterPair::DE, + 2 => RegisterPair::HL, + 3 => RegisterPair::SP, + _ => unreachable!(), + } + } + + fn table_cc(index: u8) -> Condition { + match index { + 0 => Condition::NotZero, + 1 => Condition::Zero, + 2 => Condition::NotCarry, + 3 => Condition::Carry, + _ => unreachable!(), + } + } +} + +pub enum Condition { + NotZero, + Zero, + NotCarry, + Carry, + Always, +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aa4a768 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +use instruction::Instruction; + +mod bus; +mod cpu; +mod instruction;