lots more docs in the instructions

more speedups
This commit is contained in:
Trevor Merritt 2024-10-16 16:04:45 -04:00
parent 128100c54e
commit 4cc56db489
5 changed files with 390 additions and 388 deletions

View File

@ -1,3 +1,5 @@
use std::arch::x86_64::_mm_xor_pd;
use std::fmt::{Debug, Display, Formatter};
use std::ops::{BitAnd, Shr};
use std::time::Instant;
use log::debug;
@ -20,413 +22,423 @@ pub enum Chip8CpuInstructions {
/// 0nnn
/// Exit to System Call at nnn
SysAddr(i16),
/// Clear Screen
/// 00E0
/// Clear the display.
CLS,
/// Return from Subroutine
/// 00EE
/// Return from a subroutine.
///
/// The interpreter sets the program counter to the address at the top of the stack,
/// then subtracts 1 from the stack pointer.
RET,
/// 1nnn
/// Jump to Address nnn
/// Jump to location nnn.
///
/// The interpreter sets the program counter to nnn.
JpAddr(i16),
/// 2nnn
/// Call Subroutine at nnn
/// Call subroutine at nnn.
///
/// The interpreter increments the stack pointer, then puts the current PC on the top
/// of the stack. The PC is then set to nnn.
CallAddr(i16),
/// 0x3xkk
/// Skip next instruction if Vx == kk
/// Skip next instruction if Vx = kk.
///
/// The interpreter compares register Vx to kk, and if they are equal, increments the
/// program counter by 2.
SeVxByte(u8, u8),
/// 4xkk
/// Skip next instruction if Vx != kk
/// Skip next instruction if Vx != kk.
///
/// The interpreter compares register Vx to kk, and if they are not equal, increments
/// the program counter by 2.
SneVxByte(u8, u8),
/// 5xy0
/// Skip next instruction if Vx == Vy
/// Skip next instruction if Vx = Vy.
///
/// The interpreter compares register Vx to register Vy, and if they are equal, increments
/// the program counter by 2.
SeVxVy(u8, u8),
/// 6xkk
/// Set Vx = kk
LdVxByte(u8, u8),
/// 7xkk
/// Set Vx = Vx + kk
/// The interpreter puts the value kk into register Vx.
AddVxByte(u8, u8),
/// 8xy0
/// Set Vx = Vy
/// Adds the value kk to the value of register Vx, then stores the result in Vx.
LdVxVy(u8, u8),
/// 8xy1
/// Set Vx = Vx OR Vy
/// Stores the value of register Vy in register Vx.
OrVxVy(u8, u8),
/// 8xy2
/// Set Vx = Vx AND Vy
/// Set Vx = Vx OR Vy.
///
/// Performs a bitwise OR on the values of Vx and Vy, then stores the result in Vx.
/// A bitwise OR compares the corrseponding bits from two values, and if either
/// bit is 1, then the same bit in the result is also 1. Otherwise, it is 0.
AndVxVy(u8, u8),
/// 8xy3
/// Set Vx = Vx XOR Vy
/// Set Vx = Vx AND Vy.
///
/// Performs a bitwise AND on the values of Vx and Vy, then stores the result in Vx.
/// A bitwise AND compares the corrseponding bits from two values, and if both bits
/// are 1, then the same bit in the result is also 1. Otherwise, it is 0.
XorVxVy(u8, u8),
/// 8xy4
/// Set Vx = Vx + Vy
/// Set VF=1 if Carry
/// Set Vx = Vx XOR Vy.
///
/// Performs a bitwise exclusive OR on the values of Vx and Vy, then stores the
/// result in Vx. An exclusive OR compares the corrseponding bits from two values,
/// and if the bits are not both the same, then the corresponding bit in the result
/// is set to 1. Otherwise, it is 0.
AddVxVy(u8, u8),
/// 8xy5
/// Set Vx = Vx - Vy
/// Set VF=1 if No Borrow
/// Set Vx = Vx + Vy, set VF = carry.
///
/// The values of Vx and Vy are added together. If the result is greater than 8 bits
/// (i.e., > 255,) VF is set to 1, otherwise 0. Only the lowest 8 bits of the result
/// are kept, and stored in Vx.
SubVxVy(u8, u8),
/// 8xy6
/// Set Vx = Vx SHR 1
/// Set Vx = Vx - Vy, set VF = NOT borrow.
///
/// If Vx > Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the
/// results stored in Vx.
ShrVxVy(u8, u8),
/// 8xy7
/// Set Vx = Vy - Vx
/// Set VF=1 if No Borrow
/// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx
/// is divided by 2.
SubnVxVy(u8, u8),
/// 8xye
/// Set Vx = Vx SHL 1
/// Set Vx = Vy - Vx, set VF = NOT borrow.
//
/// If Vy > Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the
/// results stored in Vx.
ShlVxVy(u8, u8),
/// 9xy0
/// Skip next instruction if Vx != Vy
/// Skip next instruction if Vx != Vy.
///
/// The values of Vx and Vy are compared, and if they are not equal, the program
/// counter is increased by 2.
SneVxVy(u8, u8),
/// Annn
/// Load I register with NNN
/// Set I = nnn.
///
/// The value of register I is set to nnn
LdIAddr(u16),
/// Bnnn
/// Jump to nnn+V0
/// Jump to location nnn + V0.
///
/// The program counter is set to nnn plus the value of V0.
JpV0Addr(u16),
/// Cxkk
/// Set Vx = Random u8 AND kk
/// Set Vx = random byte AND kk.
///
/// The interpreter generates a random number from 0 to 255, which is then
/// ANDed with the value kk. The results are stored in Vx. See instruction
/// 8xy2 for more information on AND.
RndVxByte(u8, u8),
/// Dxyn
/// Display N byte tall sprite starting at Vx, Vy
/// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
///
/// The interpreter reads n bytes from memory, starting at the address stored in I.
/// These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).
/// Sprites are XORed onto the existing screen. If this causes any pixels to be erased,
/// VF is set to 1, otherwise it is set to 0. If the sprite is positioned so part of
/// it is outside the coordinates of the display, it wraps around to the opposite side
/// of the screen. See instruction 8xy3 for more information on XOR, and section 2.4,
/// Display, for more information on the Chip-8 screen and sprites.
DrawVxVyNibble(u8, u8, u8),
/// Ex9E
/// Skip next instruction of key in Vx pressed
/// Skip next instruction if key with the value of Vx is pressed.
///
/// Checks the keyboard, and if the key corresponding to the value of Vx is currently
/// in the down position, PC is increased by 2.
SkpVx(u8),
/// ExA1
/// Skip Next If Key Not Pressed
/// Skip next instruction if key with the value of Vx is not pressed.
///
/// Checks the keyboard, and if the key corresponding to the value of Vx is currently in
/// the up position, PC is increased by 2.
SnkpVx(u8),
/// Fx07
/// Set Vx = Dt
/// The value of DT is placed into Vx.
LdVxDt(u8),
/// Fx0A
/// Wait for Key to be pressed and store
/// in Vx
/// Wait for a key press, store the value of the key in Vx.
///
/// All execution stops until a key is pressed, then the value of that key is stored in Vx.
LdVxK(u8),
/// Fx15
/// Load Value in Delay Timer to Vx
/// Set delay timer = Vx.
///
/// DT is set equal to the value of Vx.
LdDtVx(u8), // 0xFx15 Set Delay Timer
/// Fx18
/// Set Dt = Vx
/// Set sound timer = Vx.
///
/// ST is set equal to the value of Vx.
LdStVx(u8),
AddIVx(u8), // 0xFx1E I = I + Vx
LdFVx(u8), // 0xFx29 Set I = Location of sprite for Digit Vx
LdBVx(u8), // 0xFx33 Store BCD of Vx in I, I+1, I+2
LdIVx(u8), // 0xFx55 Store V0 to Vx in memory starting at I
LdVxI(u8), // 0xFx65 Load V0 to Vx in memory starting at I
/// Fx1E - ADD I, Vx
/// Set I = I + Vx.
///
/// The values of I and Vx are added, and the results are stored in I.
AddIVx(u8),
/// Fx29 - LD F, Vx
/// Set I = location of sprite for digit Vx.
///
/// The value of I is set to the location for the hexadecimal sprite
/// corresponding to the value of Vx. See section 2.4, Display, for
/// more information on the Chip-8 hexadecimal font.
LdFVx(u8),
/// Fx33 - LD B, Vx
/// Store BCD representation of Vx in memory locations I, I+1, and I+2.
///
/// The interpreter takes the decimal value of Vx, and places the hundreds
/// digit in memory at location in I, the tens digit at location I+1, and the
/// ones digit at location I+2.
LdBVx(u8),
/// Fx55 - LD [I], Vx
/// Store registers V0 through Vx in memory starting at location I.
///
/// The interpreter copies the values of registers V0 through Vx into memory,
/// starting at the address in I.
LdIVx(u8),
/// Fx65 - LD Vx, [I]
/// Read registers V0 through Vx from memory starting at location I.
///
/// The interpreter reads values from memory starting at location I into registers
/// V0 through Vx.
LdVxI(u8),
XXXXERRORINSTRUCTION,
}
impl Display for Chip8CpuInstructions {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", format!("{}", match self {
Chip8CpuInstructions::SysAddr(x) => {
format!("SYS 0x{:04x}", x)
}
Chip8CpuInstructions::CLS => {
String::from("CLS")
}
Chip8CpuInstructions::RET => {
String::from("RET")
}
Chip8CpuInstructions::JpAddr(x) => {
format!("JP 0x{:04x}", x)
}
Chip8CpuInstructions::CallAddr(x) => {
format!("SUB 0x{:04x}", x)
}
Chip8CpuInstructions::SeVxByte(x, byte) => {
format!("SeVxByte 0x{:02x}, 0x{:02x}", x, byte)
}
Chip8CpuInstructions::SneVxByte(x, byte) => {
format!("SneVxByte 0x{:02x}, 0x{:02x}", x, byte)
}
Chip8CpuInstructions::SeVxVy(x, y) => {
format!("SeVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::LdVxByte(x, byte) => {
format!("LdVxByte 0x{:02x}, 0x{:02x}", x, byte)
}
Chip8CpuInstructions::AddVxByte(x, byte) => {
format!("AddVxByte 0x{:02x}, 0x{:02x}", x, byte)
}
Chip8CpuInstructions::LdVxVy(x, y) => {
format!("LdVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::OrVxVy(x, y) => {
format!("OrVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::AndVxVy(x, y) => {
format!("AndVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::XorVxVy(x, y) => {
format!("XorVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::AddVxVy(x, y) => {
format!("AddVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::SubVxVy(x, y) => {
format!("SubVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::ShrVxVy(x, y) => {
format!("ShrVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::SubnVxVy(x, y) => {
format!("SubnVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::ShlVxVy(x, y) => {
format!("ShlVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::SneVxVy(x, y) => {
format!("SneVxVy 0x{:02x}, 0x{:02x}", x, y)
}
Chip8CpuInstructions::LdIAddr(addr) => {
format!("LdIAddr 0x{:04x}", addr)
}
Chip8CpuInstructions::JpV0Addr(addr) => {
format!("JpV0Addr 0x{:04x}", addr)
}
Chip8CpuInstructions::RndVxByte(x, byte) => {
format!("RndVxByte 0x:{:02x}, 0x{:02x}", x, byte)
}
Chip8CpuInstructions::DrawVxVyNibble(x, y, nibble) => {
format!("DrawVxVyNibble 0x:{:02x}, 0x{:02x}, 0x{:02x}", x, y, nibble)
}
Chip8CpuInstructions::SkpVx(x) => {
format!("SkpVx 0x{:02x}", x)
}
Chip8CpuInstructions::SnkpVx(x) => {
format!("SnkpVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdVxDt(x) => {
format!("LdVxDt 0x{:02x}", x)
}
Chip8CpuInstructions::LdVxK(x) => {
format!("LdVxK 0x{:02x}", x)
}
Chip8CpuInstructions::LdDtVx(x) => {
format!("LdDtVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdStVx(x) => {
format!("LdStVx 0x{:02x}", x)
}
Chip8CpuInstructions::AddIVx(x) => {
format!("AddIVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdFVx(x) => {
format!("LdFVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdBVx(x) => {
format!("LdBVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdIVx(x) => {
format!("LdIVx 0x{:02x}", x)
}
Chip8CpuInstructions::LdVxI(i) => {
format!("LdVxI 0x{:02x}", i)
}
XXXXERRORINSTRUCTION => {
String::from("INVALID INSTRUCTION")
}
}))
}
}
impl Chip8CpuInstructions {
pub fn encode(&self) -> u16 {
match self {
Chip8CpuInstructions::SysAddr(target) => {
(0x0000 | (target & 0x0FFF)) as u16
}
Chip8CpuInstructions::CLS => {
0x00E0
}
Chip8CpuInstructions::RET => {
0x00EE
}
Chip8CpuInstructions::JpAddr(new_addr) => {
0x1000 | (new_addr & 0x0FFF) as u16
}
Chip8CpuInstructions::CallAddr(address) => {
(0x2000 | (address & 0x0FFF)) as u16
}
Chip8CpuInstructions::SeVxByte(vx_register, byte) => {
0x3000 | ((*vx_register as u16) << 8 | *byte as u16) as u16
}
Chip8CpuInstructions::SneVxByte(vx_register, byte) => {
0x4000u16 | (*vx_register as u16) << 8 | *byte as u16
}
Chip8CpuInstructions::SeVxVy(x_register, y_register) => {
0x5000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::LdVxByte(x_register, byte) => {
0x6000u16 | (*x_register as u16) << 8 | (*byte as u16)
}
Chip8CpuInstructions::AddVxByte(x_register, byte) => {
0x7000u16 | (*x_register as u16) << 8 | *byte as u16
}
Chip8CpuInstructions::LdVxVy(x_register, y_register) => {
0x8000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::OrVxVy(x_register, y_register) => {
0x8001u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::AndVxVy(x_register, y_register) => {
0x8002u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::XorVxVy(x_register, y_register) => {
0x8003u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::AddVxVy(x_register, y_register) => {
0x8004u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::SubVxVy(x_register, y_register) => {
0x8005u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::ShrVxVy(x_register, y_register) => {
0x8006u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::SubnVxVy(x_register, y_register) => {
0x8007u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::ShlVxVy(x_register, y_register) => {
0x800Eu16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::SneVxVy(x_register, y_register) => {
0x9000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
}
Chip8CpuInstructions::LdIAddr(addr) => {
0xA000u16 | addr
}
Chip8CpuInstructions::JpV0Addr(addr) => {
0xB000u16 | addr
}
Chip8CpuInstructions::RndVxByte(x_register, byte) => {
0xC000u16 | (*x_register as u16) << 8 | (*byte as u16)
}
Chip8CpuInstructions::SysAddr(target) => 0x0000 | (target & 0x0FFF) as u16,
Chip8CpuInstructions::CLS => 0x00E0,
Chip8CpuInstructions::RET => 0x00EE,
Chip8CpuInstructions::JpAddr(new_addr) => 0x1000 | (new_addr & 0x0FFF) as u16,
Chip8CpuInstructions::CallAddr(address) => 0x2000 | (address & 0x0FFF) as u16,
Chip8CpuInstructions::SeVxByte(vx_register, byte) => 0x3000 | ((*vx_register as u16) << 8) | (*byte as u16),
Chip8CpuInstructions::SneVxByte(vx_register, byte) => 0x4000 | ((*vx_register as u16) << 8) | (*byte as u16),
Chip8CpuInstructions::SeVxVy(x_register, y_register) => 0x5000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::LdVxByte(x_register, byte) => 0x6000 | ((*x_register as u16) << 8) | (*byte as u16),
Chip8CpuInstructions::AddVxByte(x_register, byte) => 0x7000 | ((*x_register as u16) << 8) | (*byte as u16),
Chip8CpuInstructions::LdVxVy(x_register, y_register) => 0x8000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::OrVxVy(x_register, y_register) => 0x8001 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::AndVxVy(x_register, y_register) => 0x8002 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::XorVxVy(x_register, y_register) => 0x8003 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::AddVxVy(x_register, y_register) => 0x8004 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::SubVxVy(x_register, y_register) => 0x8005 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::ShrVxVy(x_register, y_register) => 0x8006 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::SubnVxVy(x_register, y_register) => 0x8007 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::ShlVxVy(x_register, y_register) => 0x800E | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::SneVxVy(x_register, y_register) => 0x9000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
Chip8CpuInstructions::LdIAddr(addr) => 0xA000 | addr,
Chip8CpuInstructions::JpV0Addr(addr) => 0xB000 | addr,
Chip8CpuInstructions::RndVxByte(x_register, byte) => 0xC000 | ((*x_register as u16) << 8) | (*byte as u16),
Chip8CpuInstructions::DrawVxVyNibble(x_register, y_register, height) => {
0xD000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4 | (*height as u16)
}
Chip8CpuInstructions::SkpVx(x_register) => {
0xE09Eu16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::SnkpVx(x_register) => {
0xE0A1u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdVxDt(x_register) => {
0xF007u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdVxK(x_register) => {
0xF00Au16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdDtVx(x_register) => {
0xF015u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdStVx(x_register) => {
0xF018u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::AddIVx(x_register) => {
0xF01Eu16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdFVx(x_register) => {
0xF029u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdBVx(x_register) => {
0xf033u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdIVx(x_register) => {
0xf055u16 | (*x_register as u16) << 8
}
Chip8CpuInstructions::LdVxI(x_register) => {
0xf065u16 | (*x_register as u16) << 8
}
XXXXERRORINSTRUCTION => {
0xffff
0xD000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4) | (*height as u16)
}
Chip8CpuInstructions::SkpVx(x_register) => 0xE09E | ((*x_register as u16) << 8),
Chip8CpuInstructions::SnkpVx(x_register) => 0xE0A1 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdVxDt(x_register) => 0xF007 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdVxK(x_register) => 0xF00A | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdDtVx(x_register) => 0xF015 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdStVx(x_register) => 0xF018 | ((*x_register as u16) << 8),
Chip8CpuInstructions::AddIVx(x_register) => 0xF01E | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdFVx(x_register) => 0xF029 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdBVx(x_register) => 0xF033 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdIVx(x_register) => 0xF055 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LdVxI(x_register) => 0xF065 | ((*x_register as u16) << 8),
XXXXERRORINSTRUCTION => 0xFFFF,
}
}
pub fn decode(input: u16) -> Chip8CpuInstructions {
let x_param = InstructionUtil::read_x_from_instruction(input) as u8;
let y_param = InstructionUtil::read_y_from_instruction(input) as u8;
let addr_param = InstructionUtil::read_addr_from_instruction(input) as u16;
let byte_param = InstructionUtil::read_byte_from_instruction(input) as u8;
let nibble_param = InstructionUtil::read_nibble_from_instruction(input) as u8;
let ubln = InstructionUtil::read_upper_byte_lower_nibble(input) as u8;
let last_byte = input & 0xFF;
let x_param = InstructionUtil::read_x_from_instruction(input);
let y_param = InstructionUtil::read_y_from_instruction(input);
let addr_param = InstructionUtil::read_addr_from_instruction(input);
let byte_param = InstructionUtil::read_byte_from_instruction(input);
let nibble_param = InstructionUtil::read_nibble_from_instruction(input);
let ubln = InstructionUtil::read_upper_byte_lower_nibble(input);
let last_byte = (input & 0xFF) as u8;
let last_nibble = (input & 0xF) as u8;
match input {
0x00E0 => {
// 00E0 - f
// Clear the display.
Chip8CpuInstructions::CLS
0x00E0 => Chip8CpuInstructions::CLS,
0x00EE => Chip8CpuInstructions::RET,
0x0000..=0x0FFF => Chip8CpuInstructions::SysAddr(addr_param as i16),
0x1000..=0x1FFF => Chip8CpuInstructions::JpAddr(addr_param as i16),
0x2000..=0x2FFF => Chip8CpuInstructions::CallAddr(addr_param as i16),
0x3000..=0x3FFF => Chip8CpuInstructions::SeVxByte(x_param, byte_param),
0x4000..=0x4FFF => Chip8CpuInstructions::SneVxByte(x_param, byte_param),
0x5000..=0x5FF0 if input & 0x01 == 0 => Chip8CpuInstructions::SeVxVy(x_param, y_param),
0x6000..=0x6FFF => Chip8CpuInstructions::LdVxByte(x_param, byte_param),
0x7000..=0x7FFF => Chip8CpuInstructions::AddVxByte(x_param, byte_param),
0x8000..=0x8FFE => match last_nibble {
0x0 => Chip8CpuInstructions::LdVxVy(x_param, y_param),
0x1 => Chip8CpuInstructions::OrVxVy(x_param, y_param),
0x2 => Chip8CpuInstructions::AndVxVy(x_param, y_param),
0x3 => Chip8CpuInstructions::XorVxVy(x_param, y_param),
0x4 => Chip8CpuInstructions::AddVxVy(x_param, y_param),
0x5 => Chip8CpuInstructions::SubVxVy(x_param, y_param),
0x6 => Chip8CpuInstructions::ShrVxVy(x_param, y_param),
0x7 => Chip8CpuInstructions::SubnVxVy(x_param, y_param),
0xE => Chip8CpuInstructions::ShlVxVy(x_param, y_param),
_ => XXXXERRORINSTRUCTION
}
0x00EE => {
// 00EE - RET
// Return from a subroutine.
Chip8CpuInstructions::RET
0x9000..=0x9FF0 if input & 0x01 == 0 => Chip8CpuInstructions::SneVxVy(x_param, y_param),
0xA000..=0xAFFF => Chip8CpuInstructions::LdIAddr(addr_param),
0xB000..=0xBFFF => Chip8CpuInstructions::JpV0Addr(addr_param),
0xC000..=0xCFFF => Chip8CpuInstructions::RndVxByte(x_param, byte_param),
0xD000..=0xDFFF => Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param),
0xE09E..=0xEFA1 => match last_byte {
0x9E => Chip8CpuInstructions::SkpVx(ubln),
0xA1 => Chip8CpuInstructions::SnkpVx(ubln),
_ => XXXXERRORINSTRUCTION
}
0x0000..=0x0FFF => {
// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
Chip8CpuInstructions::SysAddr(addr_param as i16)
}
0x1000..=0x1FFF => {
// 1nnn - JP addr
// Jump to location nnn.
Chip8CpuInstructions::JpAddr(addr_param as i16)
}
0x2000..=0x2FFF => {
// 2nnn - CALL addr
// Call subroutine at nnn.
Chip8CpuInstructions::CallAddr(addr_param as i16)
}
0x3000..=0x3FFF => {
// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
Chip8CpuInstructions::SeVxByte(x_param, byte_param)
}
0x4000..=0x4FFF => {
// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
Chip8CpuInstructions::SneVxByte(x_param, byte_param)
}
0x5000..=0x5FF0 => {
// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
// if the last nibble isn't 0...
if 0xf & input > 0 {
// ... we have a problem.
Chip8CpuInstructions::XXXXERRORINSTRUCTION
} else {
Chip8CpuInstructions::SeVxVy(x_param, y_param)
}
}
0x6000..=0x6FFF => {
// 6xkk - LD Vx, byte
// Set Vx = kk.
Chip8CpuInstructions::LdVxByte(x_param, byte_param)
}
0x7000..=0x7FFF => {
// ADD Vx, Byte
Chip8CpuInstructions::AddVxByte(x_param, byte_param)
}
0x8000..=0x8FFE => {
// 0x8000 Series
let last_nibble = input & 0xF;
match last_nibble {
0x0 => {
// LD Vx, Vy
Chip8CpuInstructions::LdVxVy(x_param, y_param)
}
0x1 => {
// OR Vx, Vy
Chip8CpuInstructions::OrVxVy(x_param, y_param)
}
0x2 => {
// AND Vx, Vy
Chip8CpuInstructions::AndVxVy(x_param, y_param)
}
0x3 => {
// XOR Vx, Vy
Chip8CpuInstructions::XorVxVy(x_param, y_param)
}
0x4 => {
// ADD Vx, Vy
Chip8CpuInstructions::AddVxVy(x_param, y_param)
}
0x5 => {
// SUB Vx, Vy
Chip8CpuInstructions::SubVxVy(x_param, y_param)
}
0x6 => {
// SHR Vx, {, Vy }
Chip8CpuInstructions::ShrVxVy(x_param, y_param)
}
0x7 => {
// SUBN Vx, Vy
Chip8CpuInstructions::SubnVxVy(x_param, y_param)
}
0xE => {
// SHL Vx, {, Vy}
Chip8CpuInstructions::ShlVxVy(x_param, y_param)
}
_ => {
Chip8CpuInstructions::XXXXERRORINSTRUCTION
}
}
}
0x9000..=0x9FF0 => {
// SNE Vx, Vy
if 0xf & input > 0 {
XXXXERRORINSTRUCTION
} else {
Chip8CpuInstructions::SneVxVy(x_param, y_param)
}
}
0xA000..=0xAFFF => {
// LD I, Addr
Chip8CpuInstructions::LdIAddr(addr_param)
}
0xB000..=0xBFFF => {
// JP V0, Addr
Chip8CpuInstructions::JpV0Addr(addr_param)
}
0xC000..=0xCFFF => {
// RND Vx, byte
Chip8CpuInstructions::RndVxByte(x_param, byte_param)
}
0xD000..=0xDFFF => {
// DRAW Vx, Vy, nibble
Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param)
}
0xE09E..=0xEFA1 => {
match last_byte {
0x9E => {
Chip8CpuInstructions::SkpVx(ubln)
}
0xA1 => {
Chip8CpuInstructions::SnkpVx(ubln)
}
_ => {
XXXXERRORINSTRUCTION
}
}
}
0xF007..=0xFF65 => {
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
}
}
}
_ => {
XXXXERRORINSTRUCTION
0xF007..=0xFF65 => 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
}
_ => XXXXERRORINSTRUCTION
}
}
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
print!("INSTRUCTION {:04x}", self.encode());
print!("INSTRUCTION {}", self);
let start_time = Instant::now();
let start_pc = input.registers.peek_pc();
input.registers.poke_pc(start_pc + 2);
let _ = match self {
// 0x0nnn Exit to System Call
Chip8CpuInstructions::SysAddr(new_address) => {
debug!("SysAddr [0x{new_address:3x}]");
input.registers.poke_pc(*new_address as u16);
}
// * 0x00E0 Clear Screen
@ -435,18 +447,14 @@ impl Chip8CpuInstructions {
}
// 0x00EE Return from Subroutine
Chip8CpuInstructions::RET => {
let return_address = input.stack.pop();
debug!("Returning from subroutine to {return_address:03x}");
input.registers.poke_pc(return_address);
input.registers.poke_pc(input.stack.pop());
}
// 0x1nnn Jump to Address
Chip8CpuInstructions::JpAddr(new_address) => {
debug!("JpAddr [0x{new_address:3x}]");
input.registers.poke_pc(*new_address as u16);
}
// 0x2nnn Call Subroutine
Chip8CpuInstructions::CallAddr(new_address) => {
debug!("CALL ADDR 0x{new_address:3x}");
let return_address = input.registers.peek_pc();
input.registers.poke_pc(*new_address as u16);
input.stack.push(&return_address);
@ -456,7 +464,6 @@ impl Chip8CpuInstructions {
if input.registers.peek(*vx_register as u8) == *byte as u8 {
input.registers.advance_pc();
}
debug!("SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]");
}
// 0x4xkk Skip next instruction if Vx != kk
Chip8CpuInstructions::SneVxByte(x, byte) => {
@ -465,41 +472,27 @@ impl Chip8CpuInstructions {
//
// The interpreter compares register Vx to kk, and if they are not equal,
// increments the program counter by 2.
let lhs = input.registers.peek(*x);
let rhs = *byte;
if lhs != rhs {
if input.registers.peek(*x) != *byte {
input.registers.advance_pc();
}
debug!("SneVxByte [0x{x:02x}] [0x{byte:02x}");
}
// 0x5xy0 Skip next instruction if Vx == Vy
Chip8CpuInstructions::SeVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8);
let rhs = input.registers.peek(*y as u8);
if lhs == rhs {
if input.registers.peek(*x as u8) == input.registers.peek(*y as u8) {
input.registers.advance_pc();
}
debug!("SeVxVy [0x{x:1x}] [0x{y:1x}]");
}
// 0x6xkk Set Vx = kk
Chip8CpuInstructions::LdVxByte(register, byte) => {
let byte_value = *byte as u8;
input.registers.poke(*register as u8, byte_value);
debug!("LdVxByte [0x{register:1x}] [0x{byte:2.0x}]");
input.registers.poke(*register, *byte);
}
// 0x7xkk Set Vx = Vx + kk
Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
let x_value: u16 = input.registers.peek(*vx_register) as u16;
let value_to_poke = (x_value + *byte as u16) as u8;
input.registers.poke(*vx_register, value_to_poke);
debug!("AddVxByte [0x{vx_register:1x}] [0x{byte:02x}] [0x{value_to_poke:02x}");
input.registers.poke(*vx_register, (input.registers.peek(*vx_register) as u16 + *byte as u16) as u8);
}
// 0x8xy0 Set value of Vy in Vx
Chip8CpuInstructions::LdVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*y as u8));
debug!("LdVxVy [0x{x:1x}] [0x{y:1x}]");
}
// 0x8xy1 Set Vx = Vx OR Vy
Chip8CpuInstructions::OrVxVy(x, y) => {
@ -509,25 +502,20 @@ impl Chip8CpuInstructions {
// OR them
let working_16_or = working_16_x | working_16_y;
// shift them back to 8 bit.
let to_set = working_16_or as u8;
input.registers.poke(*x as u8, to_set);
input.registers.poke(*x as u8, working_16_or as u8);
debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]")
}
// 0x8xy2 Set Vx = Vx AND Vy
Chip8CpuInstructions::AndVxVy(x, y) => {
let lhs_16 = input.registers.peek(*x as u8) as u16;
let rhs_16 = input.registers.peek(*y as u8) as u16;
let and_16 = lhs_16 & rhs_16;
input.registers.poke(*x as u8, and_16 as u8);
debug!("AndVxVy [0x{x:02x}] [0x{y:02x}] [0b{and_16:08b}");
input.registers.poke(*x as u8, (lhs_16 & rhs_16) as u8);
}
// 0x8xy3 Set Vx = Vx XOR Vy
Chip8CpuInstructions::XorVxVy(x, y) => {
let lhs_16 = input.registers.peek(*x as u8) as u16;
let rhs_16 = input.registers.peek(*y as u8) as u16;
let xor_16 = lhs_16 ^ rhs_16;
input.registers.poke(*x as u8, xor_16 as u8);
input.registers.poke(*x as u8, (lhs_16 ^ rhs_16) as u8);
}
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
Chip8CpuInstructions::AddVxVy(x, y) => {
@ -636,7 +624,7 @@ impl Chip8CpuInstructions {
// Set I = nnn.
//
// The value of register I is set to nnn.
debug!("LdiAddr [0x{new_index:3x}]");
// debug!("LdiAddr [0x{new_index:3x}]");
input.registers.poke_i(*new_index);
}
// 0xBnnn Jump to nnn+V0
@ -674,20 +662,33 @@ impl Chip8CpuInstructions {
// it wraps around to the opposite side of the screen.
let source_memory_offset = input.registers.peek_i();
let x_offset = input.registers.peek(*x);
let y_offset = input.registers.peek(*y);
let x_offset = input.registers.peek(*x) as u16;
let y_offset = input.registers.peek(*y) as u16;
let mut buffer = vec![];
for byte_index in 0..*n {
buffer.push(input.memory.peek(byte_index as u16 + source_memory_offset));
}
for (idx, byte) in buffer.iter().enumerate() {
let local_x = (x_offset + idx as u16) * 64;
for bit_index in 0..8 {
input.video_memory.poke(x_offset + (y_offset + bit_index as u16), (current_byte & (0x80 >> bit_index)) != 0);
}
}
for byte_index in 0..*n {
let current_byte = input.memory.peek(byte_index as u16 + source_memory_offset);
let x_offset: u16 = (x_offset + byte_index) as u16 * 64;
let x_offset: u16 = (x_offset + byte_index as u16) * 64;
for bit_index in 0..8 {
let data_offset = x_offset + (y_offset as u16 + bit_index as u16);
let current_bit = (current_byte & (0x80 >> bit_index)) != 0;
let previous_bit = input.video_memory.peek(data_offset);
input.video_memory.poke(data_offset, current_bit);
input.video_memory.poke(x_offset + (y_offset + bit_index as u16), (current_byte & (0x80 >> bit_index)) != 0);
}
}
if input.video_memory.has_frame_changed {
input.registers.poke(0xf, 1u8);
} else {
@ -823,7 +824,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
};
let cycle_time = Instant::now().duration_since(start_time).as_nanos();
println!("Took {cycle_time}ms");
println!("\t\tTook {cycle_time}ms");
input.to_owned()
}
}

View File

@ -41,14 +41,14 @@ impl Chip8Registers {
self.i_register = new_value;
}
pub fn peek(&self, register_number: u8) -> u8 {
self.registers[(register_number) as usize]
self.registers[register_number as usize]
}
pub fn poke(&mut self, register_number: u8, value: u8) {
if register_number > 0xf {
panic!("INVALID REGISTER");
}
self.registers[(register_number) as usize] = value;
self.registers[register_number as usize] = value;
}
pub fn peek_pc(&self) -> u16 {

View File

@ -39,27 +39,27 @@ impl InstructionUtil {
}
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0x000F
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u8 {
( instruction_to_read_from & 0x000F )as u8
}
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0x0F00).rotate_right(8)
pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u8 {
(instruction_to_read_from & 0x0F00).rotate_right(8) as u8
}
// y - A 4-bit value, the upper 4 bits of the low byte of the instruction
pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0x00F0).rotate_right(4)
pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u8 {
(instruction_to_read_from & 0x00F0).rotate_right(4) as u8
}
// kk or byte - An 8-bit value, the lowest 8 bits of the instruction
pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0x00FF
pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u8 {
(instruction_to_read_from & 0x00FF) as u8
}
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 {
(to_read_from & 0x0f00) >> 8
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u8 {
((to_read_from & 0x0f00) >> 8) as u8
}
}

View File

@ -29,7 +29,9 @@ impl Chip8Video {
}
pub fn peek(self, address: u16) -> bool {
let effective_address = address % 2048;
let effective_address = if address >= 2048 {
address % 2048
} else { address };
self.memory[effective_address as usize]
}
@ -41,17 +43,18 @@ impl Chip8Video {
let old_value = self.memory[effective_address as usize];
let xored_value = new_value ^ old_value; // XOR of the video
let value_changed = old_value != xored_value; // From True to False is a change.
if value_changed {
self.has_frame_changed = true
};
// if the frame has already changed we dont care if it changed again.
if !self.has_frame_changed {
if old_value != xored_value {
self.has_frame_changed = true
};
}
self.memory[effective_address as usize] = xored_value;
}
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) {
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) {
for i in (0..8).rev() {
self.poke(first_address + (7 - i), (to_write & (1 << i)) != 0);
}

View File

@ -64,7 +64,7 @@ fn main() {
// waiting for a keychange...
},
gemma::chip8::cpu_states::Chip8CpuStates::ExecutingInstruction => { // should never see this.
},
},
gemma::chip8::cpu_states::Chip8CpuStates::Error => {
panic!("System in undefined state.");
},
@ -83,8 +83,6 @@ fn main() {
GemmaImguiSupport::registers_view(&system, ui);
}
if ui_state.show_memory {
let active_instruction = system.registers.peek_pc();
GemmaImguiSupport::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);