diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..e298484 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/display.rs b/src/display.rs index b5bcfa5..a6259d9 100644 --- a/src/display.rs +++ b/src/display.rs @@ -76,7 +76,7 @@ impl Display for RatatuiDisplay { .title("Chip8 Emulator by nuculabs.dev") .borders(Borders::ALL), ) - .marker(Marker::Bar) + .marker(Marker::HalfBlock) .paint(|ctx| { for row in 0..DISPLAY_HEIGHT { for column in 0..DISPLAY_WIDTH { diff --git a/src/emulator.rs b/src/emulator.rs index 64d1b22..53fcbe5 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -124,7 +124,7 @@ where self.execute_instruction(instruction)?; // insert some delay - thread::sleep(time::Duration::from_millis(50)); + thread::sleep(time::Duration::from_millis(10)); } } @@ -144,7 +144,8 @@ where } ProcessorInstruction::AddValueToRegister(register, data) => { trace!("Add to register {} data {:04x}", register, data); - self.registers[register as usize] += data + let (result, _) = self.registers[register as usize].overflowing_add(data); + self.registers[register as usize] = result; } ProcessorInstruction::SetIndexRegister(data) => { trace!("Set index register to data {:04x}", data); @@ -202,6 +203,72 @@ where // Set PC to subroutine address self.program_counter = address; } + ProcessorInstruction::Set(vx, vy) => { + trace!("Set VX={vx:04x} VY={vy:04x}"); + self.registers[vx as usize] = vy; + } + ProcessorInstruction::BinaryOr(vx, vy) => { + trace!("BinaryOr VX={vx:04x} VY={vy:04x}"); + self.registers[vx as usize] |= self.registers[vy as usize] + } + ProcessorInstruction::BinaryAnd(vx, vy) => { + trace!("BinaryAnd VX={vx:04x} VY={vy:04x}"); + self.registers[vx as usize] &= self.registers[vy as usize] + } + ProcessorInstruction::BinaryXor(vx, vy) => { + trace!("BinaryXor VX={vx:04x} VY={vy:04x}"); + self.registers[vx as usize] ^= self.registers[vy as usize] + } + ProcessorInstruction::Add(vx, vy) => { + trace!("Add VX={vx:04x} VY={vy:04x}"); + let (result, overflow) = + self.registers[vx as usize].overflowing_add(self.registers[vy as usize]); + + self.registers[vx as usize] = result; + if overflow { + self.registers[0xF] = 1; + } else { + self.registers[0xF] = 0; + } + } + ProcessorInstruction::SubtractVX(vx, vy) => { + trace!("SubtractVX VX={vx:04x} VY={vy:04x}"); + if self.registers[vx as usize] > self.registers[vy as usize] { + self.registers[0xF] = 1 + } else { + // The register 0xF will be 0 if there's an underflow. + self.registers[0xF] = 0 + } + let (result, _) = + self.registers[vx as usize].overflowing_sub(self.registers[vy as usize]); + self.registers[vx as usize] = result; + } + ProcessorInstruction::SubtractVY(vx, vy) => { + trace!("SubtractVY VX={vx:04x} VY={vy:04x}"); + if self.registers[vy as usize] > self.registers[vx as usize] { + self.registers[0xF] = 1 + } else { + // The register 0xF will be 0 if there's an underflow. + self.registers[0xF] = 0 + } + let (result, _) = + self.registers[vy as usize].overflowing_sub(self.registers[vx as usize]); + self.registers[vx as usize] = result; + } + ProcessorInstruction::ShiftLeft(vx, vy) => { + trace!("ShiftLeft VX={vx:04x} VY={vy:04x}"); + // Original chip8 behavior + self.registers[0xF] = (self.registers[vy as usize] & 0x10) >> 4; + self.registers[vx as usize] = self.registers[vy as usize]; + self.registers[vx as usize] <<= 1; + } + ProcessorInstruction::ShiftRight(vx, vy) => { + trace!("ShiftRight VX={vx:04x} VY={vy:04x}"); + // Original chip8 behavior + self.registers[0xF] = self.registers[vy as usize] & 0x01; + self.registers[vx as usize] = self.registers[vy as usize]; + self.registers[vx as usize] >>= 1; + } ProcessorInstruction::UnknownInstruction => { warn!("Unknown instruction: {:04x}, skipping.", instruction); } diff --git a/src/instruction.rs b/src/instruction.rs index 20bf7de..6453565 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -30,6 +30,26 @@ pub enum ProcessorInstruction { Call(u16), /// Pops the stack and sets the PC Return, + /// Set VX to the value of VY + Set(u8, u8), + /// Or VX with VY and store in VX. + BinaryOr(u8, u8), + /// And VX with VY and store in VX. + BinaryAnd(u8, u8), + /// XOR VX with VY and store in VX. + BinaryXor(u8, u8), + /// Add VX with VY and store in VX, if addition overflows set the carry flag 0xVF + Add(u8, u8), + /// Subtract VX from VY and set to VX. VX = VX - VY. Affects the carry flag. + SubtractVX(u8, u8), + /// Subtract VY from VX and set to VX. VX = VY - VX. Affects the carry flag. + SubtractVY(u8, u8), + /// Set VX = VY >> 1. VF needs to be set to the bit that is shifted out. + /// This instruction has different behaviour on CHIP-48 and SUPER-CHIP. + ShiftRight(u8, u8), + /// Set VX = VY << 1 VF needs to be set to the bit that is shifted out. + /// This instruction has different behaviour on CHIP-48 and SUPER-CHIP. + ShiftLeft(u8, u8), /// Unknown instruction UnknownInstruction, } @@ -112,6 +132,42 @@ impl Instruction { */ (0x0, 0x0, 0xE, 0xE) => ProcessorInstruction::Return, (0x2, _, _, _) => ProcessorInstruction::Call(Self::grab_inner_data(data)), + (0x8, _, _, 0x0) => ProcessorInstruction::Set( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x1) => ProcessorInstruction::BinaryOr( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x2) => ProcessorInstruction::BinaryAnd( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x3) => ProcessorInstruction::BinaryXor( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x4) => ProcessorInstruction::Add( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x5) => ProcessorInstruction::SubtractVX( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x7) => ProcessorInstruction::SubtractVY( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0x6) => ProcessorInstruction::ShiftRight( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), + (0x8, _, _, 0xE) => ProcessorInstruction::ShiftLeft( + Self::grab_first_nibble(data), + Self::grab_middle_nibble(data), + ), // Unknown instruction _ => ProcessorInstruction::UnknownInstruction, } diff --git a/src/main.rs b/src/main.rs index a44df51..893c006 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ fn main() -> Result<(), anyhow::Error> { let mut emulator = Emulator::new(RatatuiDisplay::new()); - emulator.emulate(String::from("./roms/ibm-logo.ch8"))?; + emulator.emulate(String::from("./roms/1-chip8-logo.ch8"))?; Ok(()) }