From d6f7c73de3d79eb84e6a811fdd87c153c5b30c8b Mon Sep 17 00:00:00 2001 From: Trevor Merritt Date: Mon, 23 Sep 2024 16:12:52 -0400 Subject: [PATCH] add test chip8 file more decoder and executor code lots and lots more tests. lots and lots and lots and lots more needed --- 1-chip8-logo.ch8 | Bin 0 -> 260 bytes Cargo.lock | 1 + emma/Cargo.toml | 1 + emma/src/bin/emmagui.rs | 2 +- emma/src/bin/support/emmagui_support.rs | 19 +- emma/src/chip8/computer.rs | 474 ++++++++++++++++-------- emma/src/chip8/instructions.rs | 4 +- emma/src/chip8/system_memory.rs | 82 ++-- 8 files changed, 396 insertions(+), 187 deletions(-) create mode 100644 1-chip8-logo.ch8 diff --git a/1-chip8-logo.ch8 b/1-chip8-logo.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..19c5cf30f469f86fce62351b46c5a68454d93429 GIT binary patch literal 260 zcmZR0kjR+8u_)kze1gEDcpxdUC=W;~EUE>P8jE^>q`{)u7vvKKfT~sjc>;@e0!fKQ zCxE2FqU%6XW6?7pX|U+)1$iMqekKrLV34;jeDL(aiv_$|8X6i53=%tbSXkKaXOLXF zci~(H1|C^iMqb&ujK?)KHPsmye*8Ii3 Self { + pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: Box>) { + let total_len = to_load.len() as u16; + for current_index in 0..total_len { + let new_value = to_load[current_index as usize]; + let new_location = current_index + offset; + self.memory.poke(new_location, new_value); + } + } + + pub fn step_system(&mut self) -> Self { // read the next instruction let mut working_instruction: u16 = 0b0000000000000000; @@ -48,250 +59,294 @@ impl Chip8Computer { working_instruction = high_byte | low_byte; let decoded_instruction = - Chip8Computer::decode_instruction(self.clone(), working_instruction); + Chip8Computer::decode_instruction(working_instruction); println!("DECODED INSTRUCTION = {:?}", decoded_instruction); match (self.state, decoded_instruction) { (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SysAddr(target_address)) => { + debug!("INST: SYS: {target_address}"); self.pc = target_address as u16; - }, - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(), - (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SysAddr(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CLS) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RET) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(), - (Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SysAddr(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::CLS) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::RET) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::JpAddr(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::CallAddr(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdIAddr(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::JpV0Addr(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SkpVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::SnkpVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdVxDt(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdVxK(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdDtVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdStVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::AddIVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdFVu(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdBVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdIVx(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::LdVxI(_)) => todo!(), - (Chip8CpuStates::Error, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(), + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => { + debug!("INST: CLS"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => { + debug!("INST: RET"); + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(new_address)) => { + debug!("INST: JP_ADDR: {new_address}"); + self.pc = new_address as u16; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(sub_address)) => { + debug!("INST: CALL_ADDR: {sub_address}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(vx_register, byte)) => { + debug!("INST: SeVxByte: {vx_register}/{byte}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(vx_register, byte)) => { + debug!("INST: SneVxByte: {vx_register}/{byte}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(vx_register, vy_register)) => { + debug!("INST: SeVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(vx_register, byte)) => { + debug!("INST: LdVxByte: {vx_register}/{byte}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(vx_register, byte)) => { + debug!("INST: AddVxByte: {vx_register}/{byte}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(vx_register, vy_register)) => { + debug!("INST: LdVxVy: {vx_register}/{vy_register}"); + self.registers[vx_register as usize] = self.registers[vy_register as usize]; + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(vx_register, vy_register)) => { + debug!("INST: OrVxVy: {vx_register}/{vy_register}"); + self.registers[vx_register as usize] = self.registers[vx_register as usize] | self.registers[vy_register as usize]; + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(vx_register, vy_register)) => { + debug!("INST: AndVxVy: {vx_register}/{vy_register}"); + self.registers[vx_register as usize] = self.registers[vx_register as usize] & self.registers[vy_register as usize]; + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(vx_register, vy_register)) => { + debug!("INST: XorVxVy: {vx_register}/{vy_register}"); + self.registers[vx_register as usize] = self.registers[vx_register as usize] ^ self.registers[vy_register as usize]; + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(vx_register, vy_register)) => { + debug!("INST: AddVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(vx_register, vy_register)) => { + debug!("INST: SubVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(vx_register, vy_register)) => { + debug!("INST: ShrVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(vx_register, vy_register)) => { + debug!("INST: SubnVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(vx_register, vy_register)) => { + debug!("INST: ShlVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(vx_register, vy_register)) => { + debug!("INST: SneVxVy: {vx_register}/{vy_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(addr)) => { + debug!("INST: LdIAddr: {addr}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(addr)) => { + debug!("INST: JpV0Addr: {addr}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(vx_register, byte)) => { + debug!("INST: RndVxByte: {vx_register}/{byte}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(vx_register, vy_register, nibble)) => { + debug!("INST: DrawVxVyNibble: {vx_register}/{vy_register}/{nibble}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(vx_register)) => { + debug!("INST: SkpVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(vx_register)) => { + debug!("INST: SnkpVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(vx_register)) => { + debug!("INST: LdVxDt: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(vx_register)) => { + debug!("INST: LdVxK: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(vx_register)) => { + debug!("INST: LdDtVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(vx_register)) => { + debug!("INST: SkpVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(vx_register)) => { + debug!("INST: AddIVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVx(_)) => { + debug!("INST: LdFVu:"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(vx_register)) => { + debug!("INST: LdBVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(vx_register)) => { + debug!("INST: LdIVx: {vx_register}"); + self.pc += 0x2; + } + (Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(vx_register)) => { + debug!("INST: LdVxI: {vx_register}"); + self.pc += 0x2; + } + _ => { + error!("UNABLE TO PROCEED. CPU IN UNKNOWN STATE"); + } } self.clone() } // nnn or addr - A 12-bit value, the lowest 12 bits of the instruction - pub fn read_addr_from_instruction(&self, instruction_to_read_from: u16) -> u16 { + pub fn read_addr_from_instruction(instruction_to_read_from: u16) -> u16 { instruction_to_read_from & 0b0000111111111111 } // n or nibble - A 4-bit value, the lowest 4 bits of the instruction - pub fn read_nibble_from_instruction(&self, instruction_to_read_from: u16) -> u16 { + pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u16 { instruction_to_read_from & 0b0000000000001111 } // x - A 4-bit value, the lower 4 bits of the high byte of the instruction - pub fn read_x_from_instruction(&self, instruction_to_read_from: u16) -> u16 { + pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u16 { (instruction_to_read_from & 0b0000111100000000).rotate_right(8) } // y - A 4-bit value, the upper 4 bits of the low byte of the instruction - pub fn read_y_from_instruction(&self, instruction_to_read_from: u16) -> u16 { + pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u16 { (instruction_to_read_from & 0b0000000011110000).rotate_right(4) } // kk or byte - An 8-bit value, the lowest 8 bits of the instruction - pub fn read_byte_from_instruction(&self, instruction_to_read_from: u16) -> u16 { + pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u16 { (instruction_to_read_from & 0b0000000011111111) } - fn decode_instruction(self, to_read: u16) -> Chip8CpuInstructions { + pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 { + to_read_from & 0x0f00 + } + + fn decode_instruction(to_decode: u16) -> Chip8CpuInstructions { let mut decoded_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION; - let x_param = self.read_x_from_instruction(to_read); - let y_param = self.read_y_from_instruction(to_read); - let addr_param = self.read_addr_from_instruction(to_read); - let byte_param = self.read_byte_from_instruction(to_read); - let nibble_param = self.read_nibble_from_instruction(to_read); + // pull out the various possible parameters for use further along when we + // acutally sort out what kind of instruction we have. + let x_param = Chip8Computer::read_x_from_instruction(to_decode); + let y_param = Chip8Computer::read_y_from_instruction(to_decode); + let addr_param = Chip8Computer::read_addr_from_instruction(to_decode); + let byte_param = Chip8Computer::read_byte_from_instruction(to_decode); + let nibble_param = Chip8Computer::read_nibble_from_instruction(to_decode); + let ubln = u16::rotate_right(Chip8Computer::read_upper_byte_lower_nibble(to_decode), 8); + let last_byte = to_decode & 0xFF; - match to_read { + match to_decode { 0x00E0 => { // 00E0 - CLS // Clear the display. - decoded_instruction = Chip8CpuInstructions::CLS; + Chip8CpuInstructions::CLS } 0x00EE => { // 00EE - RET // Return from a subroutine. - - decoded_instruction = Chip8CpuInstructions::RET; + Chip8CpuInstructions::RET } 0x0000..=0x0FFF => { // 0nnn - SYS addr // Jump to a machine code routine at nnn. - decoded_instruction = Chip8CpuInstructions::SysAddr(addr_param as i16); + Chip8CpuInstructions::SysAddr(addr_param as i16) } 0x1000..=0x1FFF => { // 1nnn - JP addr // Jump to location nnn. - decoded_instruction = Chip8CpuInstructions::JpAddr(addr_param as i16); + Chip8CpuInstructions::JpAddr(addr_param as i16) } 0x2000..=0x2FFF => { // 2nnn - CALL addr // Call subroutine at nnn. - decoded_instruction = Chip8CpuInstructions::CallAddr(addr_param as i16); + Chip8CpuInstructions::CallAddr(addr_param as i16) } 0x3000..=0x3FFF => { // 3xkk - SE Vx, byte // Skip next instruction if Vx = kk. - decoded_instruction = Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16) + Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16) } 0x4000..=0x4FFF => { // 4xkk - SNE Vx, byte // Skip next instruction if Vx != kk. - decoded_instruction = Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16); + Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16) } 0x5000..=0x5FF0 => { // 5xy0 - SE Vx, Vy // Skip next instruction if Vx = Vy. - decoded_instruction = Chip8CpuInstructions::SeVxVy(x_param, y_param) + Chip8CpuInstructions::SeVxVy(x_param, y_param) } 0x6000..=0x6FFF => { // 6xkk - LD Vx, byte // Set Vx = kk. - decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, byte_param); + Chip8CpuInstructions::LdVxByte(x_param, byte_param) } 0x7000..=0x7FFF => { // ADD Vx, Byte - decoded_instruction = Chip8CpuInstructions::AddVxByte(x_param, byte_param); + Chip8CpuInstructions::AddVxByte(x_param, byte_param) } 0x8000..=0x8FFE => { // 0x8000 Series - let last_nibble = to_read | 0x8000; + let last_nibble = to_decode & 0xF; match last_nibble { 0x0 => { // LD Vx, Vy - decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, y_param); + Chip8CpuInstructions::LdVxVy(x_param, y_param) } 0x1 => { // OR Vx, Vy - decoded_instruction = Chip8CpuInstructions::OrVxVy(x_param, y_param); + Chip8CpuInstructions::OrVxVy(x_param, y_param) } 0x2 => { // AND Vx, Vy - decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param); + Chip8CpuInstructions::AndVxVy(x_param, y_param) } 0x3 => { // XOR Vx, Vy - decoded_instruction = Chip8CpuInstructions::XorVxVy(x_param, y_param); + Chip8CpuInstructions::XorVxVy(x_param, y_param) } 0x4 => { - // AND Vx, Vy - decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param); + // ADD Vx, Vy + Chip8CpuInstructions::AddVxVy(x_param, y_param) } 0x5 => { // SUB Vx, Vy - decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param); + Chip8CpuInstructions::SubVxVy(x_param, y_param) } 0x6 => { // SHR Vx, {, Vy } - decoded_instruction = Chip8CpuInstructions::ShrVxVy(x_param, y_param); + Chip8CpuInstructions::ShrVxVy(x_param, y_param) } 0x7 => { // SUBN Vx, Vy - decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param); + Chip8CpuInstructions::SubnVxVy(x_param, y_param) } 0xE => { // SHL Vx, {, Vy} - decoded_instruction = Chip8CpuInstructions::ShlVxVy(x_param, y_param); + Chip8CpuInstructions::ShlVxVy(x_param, y_param) } _ => { panic!("UNABLE TO DECODE 0x8000 SERIES INSTRUCTION"); @@ -300,31 +355,136 @@ impl Chip8Computer { } 0x9000..=0x9FF0 => { // SNE Vx, Vy - decoded_instruction = Chip8CpuInstructions::SneVxVy(x_param, y_param); + Chip8CpuInstructions::SneVxVy(x_param, y_param) } 0xA000..=0xAFFF => { // LD I, Addr - decoded_instruction = Chip8CpuInstructions::LdIAddr(addr_param); + Chip8CpuInstructions::LdIAddr(addr_param) } 0xB000..=0xBFFF => { - decoded_instruction = Chip8CpuInstructions::JpV0Addr(addr_param); + Chip8CpuInstructions::JpV0Addr(addr_param) // JP V0, Addr } 0xC000..=0xCFFF => { // RND Vx, byte - decoded_instruction = Chip8CpuInstructions::RndVxByte(x_param, byte_param); + Chip8CpuInstructions::RndVxByte(x_param, byte_param) } 0xD000..0xDFFF => { // DRAW Vx, Vy, nibble - decoded_instruction = - Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param); + + Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param) + } + 0xE09E..=0xEFA1 => { + match last_byte { + 0x9E => { + Chip8CpuInstructions::SkpVx(ubln) + } + 0xA1 => { + Chip8CpuInstructions::SnkpVx(ubln) + } + _ => { + XXXXERRORINSTRUCTION + } + } + } + 0xF007..0xFF65 => { + println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, to_decode, ubln); + match last_byte { + 0x07 => { + Chip8CpuInstructions::LdVxDt(ubln) + } + 0x0A => { + Chip8CpuInstructions::LdVxK(ubln) + } + 0x15 => { + Chip8CpuInstructions::LdDtVx(ubln) + } + 0x18 => { + Chip8CpuInstructions::LdStVx(ubln) + } + 0x1E => { + Chip8CpuInstructions::AddIVx(ubln) + } + 0x29 => { + Chip8CpuInstructions::LdFVx(ubln) + } + 0x33 => { + Chip8CpuInstructions::LdBVx(ubln) + } + 0x55 => { + Chip8CpuInstructions::LdIVx(ubln) + } + 0x65 => { + Chip8CpuInstructions::LdVxI(ubln) + } + _ => { XXXXERRORINSTRUCTION } + } } - 0xE09E..=0xEFA1 => {} _ => { - panic!("UNABLE TO DECODE INSTRUCTION") + XXXXERRORINSTRUCTION } } - - return decoded_instruction; } } + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn smoke() { + assert!(true) + } + + #[test] + fn decoder_test_valid_instructions() { + // valid instructions + assert!(matches!(Chip8Computer::decode_instruction(0x00E0u16), Chip8CpuInstructions::CLS )); + assert!(matches!(Chip8Computer::decode_instruction(0x00EE), Chip8CpuInstructions::RET)); + assert!(matches!(Chip8Computer::decode_instruction(0x0123), Chip8CpuInstructions::SysAddr(0x123))); + assert!(matches!(Chip8Computer::decode_instruction(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff))); + assert!(matches!(Chip8Computer::decode_instruction(0x1002), Chip8CpuInstructions::JpAddr(0x2))); + assert!(matches!(Chip8Computer::decode_instruction(0x1FF0), Chip8CpuInstructions::JpAddr(0xFF0))); + assert!(matches!(Chip8Computer::decode_instruction(0x2002), Chip8CpuInstructions::CallAddr(0x2))); + assert!(matches!(Chip8Computer::decode_instruction(0x3123), Chip8CpuInstructions::SeVxByte(0x1, 0x23))); + assert!(matches!(Chip8Computer::decode_instruction(0x4abc), Chip8CpuInstructions::SneVxByte(0xa, 0xbc))); + assert!(matches!(Chip8Computer::decode_instruction(0x5ab0), Chip8CpuInstructions::SeVxVy(0xa, 0xb))); + assert!(matches!(Chip8Computer::decode_instruction(0x6aff), Chip8CpuInstructions::LdVxByte(0xa, 0xff))); + assert!(matches!(Chip8Computer::decode_instruction(0x7abc), Chip8CpuInstructions::AddVxByte(0xa, 0xbc))); + assert!(matches!(Chip8Computer::decode_instruction(0x8ab0), Chip8CpuInstructions::LdVxVy(0xa, 0xb))); + assert!(matches!(Chip8Computer::decode_instruction(0x8ba1), Chip8CpuInstructions::OrVxVy(0xb, 0xa))); + assert!(matches!(Chip8Computer::decode_instruction(0x8cd2), Chip8CpuInstructions::AndVxVy(0xc, 0xd))); + assert!(matches!(Chip8Computer::decode_instruction(0x8de3), Chip8CpuInstructions::XorVxVy(0xd, 0xe))); + assert!(matches!(Chip8Computer::decode_instruction(0x8ef4), Chip8CpuInstructions::AddVxVy(0xe, 0xf))); + assert!(matches!(Chip8Computer::decode_instruction(0x8f05), Chip8CpuInstructions::SubVxVy(0xf, 0x0))); + assert!(matches!(Chip8Computer::decode_instruction(0x8016), Chip8CpuInstructions::ShrVxVy(0x0, 0x1))); + assert!(matches!(Chip8Computer::decode_instruction(0x8127), Chip8CpuInstructions::SubnVxVy(0x1, 0x2))); + assert!(matches!(Chip8Computer::decode_instruction(0x834e), Chip8CpuInstructions::ShlVxVy(0x3, 0x4))); + assert!(matches!(Chip8Computer::decode_instruction(0x9ab0), Chip8CpuInstructions::SneVxVy(0xa, 0xb))); + assert!(matches!(Chip8Computer::decode_instruction(0xa123), Chip8CpuInstructions::LdIAddr(0x123))); + assert!(matches!(Chip8Computer::decode_instruction(0xb234), Chip8CpuInstructions::JpV0Addr(0x234))); + assert!(matches!(Chip8Computer::decode_instruction(0xcaca), Chip8CpuInstructions::RndVxByte(0xa, 0xca))); + assert!(matches!(Chip8Computer::decode_instruction(0xdab4), Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4))); + assert!(matches!(Chip8Computer::decode_instruction(0xe19e), Chip8CpuInstructions::SkpVx(0x1))); + assert!(matches!(Chip8Computer::decode_instruction(0xe2a1), Chip8CpuInstructions::SnkpVx(0x2))); + assert!(matches!(Chip8Computer::decode_instruction(0xf107), Chip8CpuInstructions::LdVxDt(0x1))); + assert!(matches!(Chip8Computer::decode_instruction(0xf40a), Chip8CpuInstructions::LdVxK(0x4))); + assert!(matches!(Chip8Computer::decode_instruction(0xf615), Chip8CpuInstructions::LdDtVx(0x6))); + assert!(matches!(Chip8Computer::decode_instruction(0xfb18), Chip8CpuInstructions::LdStVx(0xb))); + assert!(matches!(Chip8Computer::decode_instruction(0xfd1e), Chip8CpuInstructions::AddIVx(0xd))); + assert!(matches!(Chip8Computer::decode_instruction(0xfc29), Chip8CpuInstructions::LdFVx(0xc))); + assert!(matches!(Chip8Computer::decode_instruction(0xfd33), Chip8CpuInstructions::LdBVx(0xd))); + assert!(matches!(Chip8Computer::decode_instruction(0xfe55), Chip8CpuInstructions::LdIVx(0xe))); + assert!(matches!(Chip8Computer::decode_instruction(0xf365), Chip8CpuInstructions::LdVxI(0x3))); + } + + #[test] + fn decoder_test_invalid_instructions() { + // 'bad' instructions that should be dropped... + // assert!(matches!(Chip8Computer::decode_instruction(0x5ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); + // assert!(matches!(Chip8Computer::decode_instruction(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); + // assert!(matches!(Chip8Computer::decode_instruction(0xeaba), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); + } +} + diff --git a/emma/src/chip8/instructions.rs b/emma/src/chip8/instructions.rs index f881a21..d166c9d 100644 --- a/emma/src/chip8/instructions.rs +++ b/emma/src/chip8/instructions.rs @@ -31,7 +31,7 @@ pub enum Chip8CpuInstructions { LdDtVx(u16), // 0xFx15 Set Delay Timer LdStVx(u16), // 0xFx18 Set Sount Timer AddIVx(u16), // 0xFx1E I = I + Vx - LdFVu(u16), // 0xFx29 Set I = Location of sprite for Digit Vx + LdFVx(u16), // 0xFx29 Set I = Location of sprite for Digit Vx LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2 LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I @@ -170,7 +170,7 @@ impl Chip8Instruction for Chip8CpuInstructions { Chip8CpuInstructions::AddIVx(_) => { input } - Chip8CpuInstructions::LdFVu(_) => { + Chip8CpuInstructions::LdFVx(_) => { input } Chip8CpuInstructions::LdBVx(_) => { diff --git a/emma/src/chip8/system_memory.rs b/emma/src/chip8/system_memory.rs index aa8dc31..06901f0 100644 --- a/emma/src/chip8/system_memory.rs +++ b/emma/src/chip8/system_memory.rs @@ -1,6 +1,8 @@ use glium::RawUniformValue::Vec2; +use image::load; use imgui::sys::ImColor; use imgui::{ImColor32, Ui}; +use log::{debug, trace}; use ratatui::{style::Style, widgets::Widget}; use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH}; @@ -30,9 +32,13 @@ pub struct Chip8SystemMemory { impl Default for Chip8SystemMemory { fn default() -> Self { - Self { - memory: Chip8SystemMemory::load_to_memory([0x00; CHIP8_MEMORY_SIZE as usize]), - } + + let mut x = Chip8SystemMemory { + memory: [0x00; CHIP8_MEMORY_SIZE as usize], + }; + + x.load_fonts_to_memory(); + x } } @@ -54,30 +60,32 @@ const cell_width: i32 = 5i32; const cell_height: i32 = 5i32; impl Chip8SystemMemory { - pub fn peek(self, address: u16) -> u8 { + + pub fn poke_multi(&mut self, start_offset: u16, data: Vec) { + // loop through the data... + // poke all the data into memory offset by the start + let data_len = data.len(); + for i in 0..data_len { + self.poke(start_offset + i as u16, data[i]); + } + } + pub fn peek(self, address: u16) -> u8 { + trace!("PEEK: {} / {}", address, self.memory[address as usize].clone()); self.memory[address as usize] } - pub fn poke(mut self, address: u16, value: u8) -> Chip8SystemMemory { + pub fn poke(&mut self, address: u16, value: u8) { + trace!("POKE: {} / {} to {}", address, self.memory[address as usize], value); self.memory[address as usize] = value; - self } - pub fn load_program(to_load_into: [u8; CHIP8_MEMORY_SIZE as usize], program_data: Box>) -> [u8; CHIP8_MEMORY_SIZE as usize] { - let mut load_target = to_load_into.clone(); - - // loop through the program data starting at 0x200 - for load_index in 0..program_data.len() { - load_target[load_index + CHIP8_PROGRAM_LOAD_OFFSET as usize] = program_data[load_index]; + pub fn load_program(&mut self, program_to_load: Box>) { + for load_index in 0..program_to_load.len() { + self.poke((load_index + 0x200) as u16, program_to_load[load_index]); } - - load_target } - pub fn load_to_memory( - to_load_into: [u8; CHIP8_MEMORY_SIZE as usize], - ) -> [u8; CHIP8_MEMORY_SIZE as usize] { - let mut working = to_load_into.clone(); + pub fn load_fonts_to_memory(&mut self) { let all_font_characters = [ CHIP8FONT_0, CHIP8FONT_1, @@ -100,10 +108,42 @@ impl Chip8SystemMemory { for (font_index, current_font) in all_font_characters.iter().enumerate() { for font_mem_offset in 0..=4 { let real_offset = font_index * 5 + font_mem_offset; - working[real_offset] = current_font[font_mem_offset]; + self.poke(real_offset as u16, current_font[font_mem_offset]); } } - println!("__FINISHED LOADING FONTS__"); - working } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn smoke() { + assert!(true) + } + + #[test] + fn model_smoke() { + let m = Chip8SystemMemory::default(); + for i in 0..5 { + assert_eq!(m.peek(i), CHIP8FONT_0[i as usize]); + } + + assert_eq!(m.peek((CHIP8_MEMORY_SIZE - 1) as u16), 0); + } + + #[test] + fn known_data_loaded_correctly() { + let to_load = [ 0x01, 0x02, 0x03, 0x04, 0x05 , 0x06 ]; + let mut x = Chip8SystemMemory::default(); + + for (index, value) in [1..10].iter().enumerate() { + assert_ne!(x.peek(0), 0x01); + x.poke(0, 0x01); + assert_eq!(x.peek(0), 0x01); + } + } + + +}