Implement logical & arithmetic instructions
This commit is contained in:
parent
52c03aa6ae
commit
09528c4cd2
5 changed files with 137 additions and 4 deletions
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<Languages>
|
||||||
|
<language minSize="46" name="Rust" />
|
||||||
|
</Languages>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -76,7 +76,7 @@ impl Display for RatatuiDisplay {
|
||||||
.title("Chip8 Emulator by nuculabs.dev")
|
.title("Chip8 Emulator by nuculabs.dev")
|
||||||
.borders(Borders::ALL),
|
.borders(Borders::ALL),
|
||||||
)
|
)
|
||||||
.marker(Marker::Bar)
|
.marker(Marker::HalfBlock)
|
||||||
.paint(|ctx| {
|
.paint(|ctx| {
|
||||||
for row in 0..DISPLAY_HEIGHT {
|
for row in 0..DISPLAY_HEIGHT {
|
||||||
for column in 0..DISPLAY_WIDTH {
|
for column in 0..DISPLAY_WIDTH {
|
||||||
|
|
|
@ -124,7 +124,7 @@ where
|
||||||
self.execute_instruction(instruction)?;
|
self.execute_instruction(instruction)?;
|
||||||
|
|
||||||
// insert some delay
|
// 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) => {
|
ProcessorInstruction::AddValueToRegister(register, data) => {
|
||||||
trace!("Add to register {} data {:04x}", 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) => {
|
ProcessorInstruction::SetIndexRegister(data) => {
|
||||||
trace!("Set index register to data {:04x}", data);
|
trace!("Set index register to data {:04x}", data);
|
||||||
|
@ -202,6 +203,72 @@ where
|
||||||
// Set PC to subroutine address
|
// Set PC to subroutine address
|
||||||
self.program_counter = 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 => {
|
ProcessorInstruction::UnknownInstruction => {
|
||||||
warn!("Unknown instruction: {:04x}, skipping.", instruction);
|
warn!("Unknown instruction: {:04x}, skipping.", instruction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,26 @@ pub enum ProcessorInstruction {
|
||||||
Call(u16),
|
Call(u16),
|
||||||
/// Pops the stack and sets the PC
|
/// Pops the stack and sets the PC
|
||||||
Return,
|
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
|
/// Unknown instruction
|
||||||
UnknownInstruction,
|
UnknownInstruction,
|
||||||
}
|
}
|
||||||
|
@ -112,6 +132,42 @@ impl Instruction {
|
||||||
*/
|
*/
|
||||||
(0x0, 0x0, 0xE, 0xE) => ProcessorInstruction::Return,
|
(0x0, 0x0, 0xE, 0xE) => ProcessorInstruction::Return,
|
||||||
(0x2, _, _, _) => ProcessorInstruction::Call(Self::grab_inner_data(data)),
|
(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
|
// Unknown instruction
|
||||||
_ => ProcessorInstruction::UnknownInstruction,
|
_ => ProcessorInstruction::UnknownInstruction,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
|
|
||||||
let mut emulator = Emulator::new(RatatuiDisplay::new());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue