lots more docs in the instructions
more speedups
This commit is contained in:
parent
128100c54e
commit
4cc56db489
@ -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::ops::{BitAnd, Shr};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@ -20,413 +22,423 @@ pub enum Chip8CpuInstructions {
|
|||||||
/// 0nnn
|
/// 0nnn
|
||||||
/// Exit to System Call at nnn
|
/// Exit to System Call at nnn
|
||||||
SysAddr(i16),
|
SysAddr(i16),
|
||||||
/// Clear Screen
|
/// 00E0
|
||||||
|
/// Clear the display.
|
||||||
CLS,
|
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,
|
RET,
|
||||||
/// 1nnn
|
/// 1nnn
|
||||||
/// Jump to Address nnn
|
/// Jump to location nnn.
|
||||||
|
///
|
||||||
|
/// The interpreter sets the program counter to nnn.
|
||||||
JpAddr(i16),
|
JpAddr(i16),
|
||||||
/// 2nnn
|
/// 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),
|
CallAddr(i16),
|
||||||
/// 0x3xkk
|
/// 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),
|
SeVxByte(u8, u8),
|
||||||
/// 4xkk
|
/// 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),
|
SneVxByte(u8, u8),
|
||||||
/// 5xy0
|
/// 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),
|
SeVxVy(u8, u8),
|
||||||
/// 6xkk
|
/// 6xkk
|
||||||
/// Set Vx = kk
|
/// Set Vx = kk
|
||||||
LdVxByte(u8, u8),
|
LdVxByte(u8, u8),
|
||||||
/// 7xkk
|
/// 7xkk
|
||||||
/// Set Vx = Vx + kk
|
/// The interpreter puts the value kk into register Vx.
|
||||||
AddVxByte(u8, u8),
|
AddVxByte(u8, u8),
|
||||||
/// 8xy0
|
/// 8xy0
|
||||||
/// Set Vx = Vy
|
/// Adds the value kk to the value of register Vx, then stores the result in Vx.
|
||||||
LdVxVy(u8, u8),
|
LdVxVy(u8, u8),
|
||||||
/// 8xy1
|
/// 8xy1
|
||||||
/// Set Vx = Vx OR Vy
|
/// Stores the value of register Vy in register Vx.
|
||||||
OrVxVy(u8, u8),
|
OrVxVy(u8, u8),
|
||||||
/// 8xy2
|
/// 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),
|
AndVxVy(u8, u8),
|
||||||
/// 8xy3
|
/// 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),
|
XorVxVy(u8, u8),
|
||||||
/// 8xy4
|
/// 8xy4
|
||||||
/// Set Vx = Vx + Vy
|
/// Set Vx = Vx XOR Vy.
|
||||||
/// Set VF=1 if Carry
|
///
|
||||||
|
/// 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),
|
AddVxVy(u8, u8),
|
||||||
/// 8xy5
|
/// 8xy5
|
||||||
/// Set Vx = Vx - Vy
|
/// Set Vx = Vx + Vy, set VF = carry.
|
||||||
/// Set VF=1 if No Borrow
|
///
|
||||||
|
/// 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),
|
SubVxVy(u8, u8),
|
||||||
/// 8xy6
|
/// 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),
|
ShrVxVy(u8, u8),
|
||||||
/// 8xy7
|
/// 8xy7
|
||||||
/// Set Vx = Vy - Vx
|
/// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx
|
||||||
/// Set VF=1 if No Borrow
|
/// is divided by 2.
|
||||||
SubnVxVy(u8, u8),
|
SubnVxVy(u8, u8),
|
||||||
/// 8xye
|
/// 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),
|
ShlVxVy(u8, u8),
|
||||||
/// 9xy0
|
/// 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),
|
SneVxVy(u8, u8),
|
||||||
/// Annn
|
/// Annn
|
||||||
/// Load I register with NNN
|
/// Set I = nnn.
|
||||||
|
///
|
||||||
|
/// The value of register I is set to nnn
|
||||||
LdIAddr(u16),
|
LdIAddr(u16),
|
||||||
/// Bnnn
|
/// Bnnn
|
||||||
/// Jump to nnn+V0
|
/// Jump to location nnn + V0.
|
||||||
|
///
|
||||||
|
/// The program counter is set to nnn plus the value of V0.
|
||||||
JpV0Addr(u16),
|
JpV0Addr(u16),
|
||||||
/// Cxkk
|
/// 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),
|
RndVxByte(u8, u8),
|
||||||
/// Dxyn
|
/// 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),
|
DrawVxVyNibble(u8, u8, u8),
|
||||||
/// Ex9E
|
/// 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),
|
SkpVx(u8),
|
||||||
/// ExA1
|
/// 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),
|
SnkpVx(u8),
|
||||||
/// Fx07
|
/// Fx07
|
||||||
/// Set Vx = Dt
|
/// The value of DT is placed into Vx.
|
||||||
LdVxDt(u8),
|
LdVxDt(u8),
|
||||||
/// Fx0A
|
/// Fx0A
|
||||||
/// Wait for Key to be pressed and store
|
/// Wait for a key press, store the value of the key in Vx.
|
||||||
/// in Vx
|
///
|
||||||
|
/// All execution stops until a key is pressed, then the value of that key is stored in Vx.
|
||||||
LdVxK(u8),
|
LdVxK(u8),
|
||||||
/// Fx15
|
/// 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
|
LdDtVx(u8), // 0xFx15 Set Delay Timer
|
||||||
/// Fx18
|
/// Fx18
|
||||||
/// Set Dt = Vx
|
/// Set sound timer = Vx.
|
||||||
|
///
|
||||||
|
/// ST is set equal to the value of Vx.
|
||||||
LdStVx(u8),
|
LdStVx(u8),
|
||||||
AddIVx(u8), // 0xFx1E I = I + Vx
|
/// Fx1E - ADD I, Vx
|
||||||
LdFVx(u8), // 0xFx29 Set I = Location of sprite for Digit Vx
|
/// Set I = I + 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
|
/// The values of I and Vx are added, and the results are stored in I.
|
||||||
LdVxI(u8), // 0xFx65 Load V0 to Vx in memory starting at 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,
|
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 {
|
impl Chip8CpuInstructions {
|
||||||
pub fn encode(&self) -> u16 {
|
pub fn encode(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
Chip8CpuInstructions::SysAddr(target) => {
|
Chip8CpuInstructions::SysAddr(target) => 0x0000 | (target & 0x0FFF) as u16,
|
||||||
(0x0000 | (target & 0x0FFF)) as u16
|
Chip8CpuInstructions::CLS => 0x00E0,
|
||||||
}
|
Chip8CpuInstructions::RET => 0x00EE,
|
||||||
Chip8CpuInstructions::CLS => {
|
Chip8CpuInstructions::JpAddr(new_addr) => 0x1000 | (new_addr & 0x0FFF) as u16,
|
||||||
0x00E0
|
Chip8CpuInstructions::CallAddr(address) => 0x2000 | (address & 0x0FFF) as u16,
|
||||||
}
|
Chip8CpuInstructions::SeVxByte(vx_register, byte) => 0x3000 | ((*vx_register as u16) << 8) | (*byte as u16),
|
||||||
Chip8CpuInstructions::RET => {
|
Chip8CpuInstructions::SneVxByte(vx_register, byte) => 0x4000 | ((*vx_register as u16) << 8) | (*byte as u16),
|
||||||
0x00EE
|
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::JpAddr(new_addr) => {
|
Chip8CpuInstructions::AddVxByte(x_register, byte) => 0x7000 | ((*x_register as u16) << 8) | (*byte as u16),
|
||||||
0x1000 | (new_addr & 0x0FFF) 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::CallAddr(address) => {
|
Chip8CpuInstructions::AndVxVy(x_register, y_register) => 0x8002 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
|
||||||
(0x2000 | (address & 0x0FFF)) as u16
|
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::SeVxByte(vx_register, byte) => {
|
Chip8CpuInstructions::SubVxVy(x_register, y_register) => 0x8005 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
|
||||||
0x3000 | ((*vx_register as u16) << 8 | *byte as u16) as u16
|
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::SneVxByte(vx_register, byte) => {
|
Chip8CpuInstructions::ShlVxVy(x_register, y_register) => 0x800E | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
|
||||||
0x4000u16 | (*vx_register as u16) << 8 | *byte as u16
|
Chip8CpuInstructions::SneVxVy(x_register, y_register) => 0x9000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4),
|
||||||
}
|
Chip8CpuInstructions::LdIAddr(addr) => 0xA000 | addr,
|
||||||
Chip8CpuInstructions::SeVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::JpV0Addr(addr) => 0xB000 | addr,
|
||||||
0x5000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4
|
Chip8CpuInstructions::RndVxByte(x_register, byte) => 0xC000 | ((*x_register as u16) << 8) | (*byte as u16),
|
||||||
}
|
|
||||||
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::DrawVxVyNibble(x_register, y_register, height) => {
|
Chip8CpuInstructions::DrawVxVyNibble(x_register, y_register, height) => {
|
||||||
0xD000u16 | (*x_register as u16) << 8 | (*y_register as u16) << 4 | (*height as u16)
|
0xD000 | ((*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
|
|
||||||
}
|
}
|
||||||
|
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 {
|
pub fn decode(input: u16) -> Chip8CpuInstructions {
|
||||||
let x_param = InstructionUtil::read_x_from_instruction(input) as u8;
|
let x_param = InstructionUtil::read_x_from_instruction(input);
|
||||||
let y_param = InstructionUtil::read_y_from_instruction(input) as u8;
|
let y_param = InstructionUtil::read_y_from_instruction(input);
|
||||||
let addr_param = InstructionUtil::read_addr_from_instruction(input) as u16;
|
let addr_param = InstructionUtil::read_addr_from_instruction(input);
|
||||||
let byte_param = InstructionUtil::read_byte_from_instruction(input) as u8;
|
let byte_param = InstructionUtil::read_byte_from_instruction(input);
|
||||||
let nibble_param = InstructionUtil::read_nibble_from_instruction(input) as u8;
|
let nibble_param = InstructionUtil::read_nibble_from_instruction(input);
|
||||||
let ubln = InstructionUtil::read_upper_byte_lower_nibble(input) as u8;
|
let ubln = InstructionUtil::read_upper_byte_lower_nibble(input);
|
||||||
let last_byte = input & 0xFF;
|
let last_byte = (input & 0xFF) as u8;
|
||||||
|
let last_nibble = (input & 0xF) as u8;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
0x00E0 => {
|
0x00E0 => Chip8CpuInstructions::CLS,
|
||||||
// 00E0 - f
|
0x00EE => Chip8CpuInstructions::RET,
|
||||||
// Clear the display.
|
0x0000..=0x0FFF => Chip8CpuInstructions::SysAddr(addr_param as i16),
|
||||||
Chip8CpuInstructions::CLS
|
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 => {
|
0x9000..=0x9FF0 if input & 0x01 == 0 => Chip8CpuInstructions::SneVxVy(x_param, y_param),
|
||||||
// 00EE - RET
|
0xA000..=0xAFFF => Chip8CpuInstructions::LdIAddr(addr_param),
|
||||||
// Return from a subroutine.
|
0xB000..=0xBFFF => Chip8CpuInstructions::JpV0Addr(addr_param),
|
||||||
Chip8CpuInstructions::RET
|
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 => {
|
0xF007..=0xFF65 => match last_byte {
|
||||||
// 0nnn - SYS addr
|
0x07 => Chip8CpuInstructions::LdVxDt(ubln),
|
||||||
// Jump to a machine code routine at nnn.
|
0x0A => Chip8CpuInstructions::LdVxK(ubln),
|
||||||
Chip8CpuInstructions::SysAddr(addr_param as i16)
|
0x15 => Chip8CpuInstructions::LdDtVx(ubln),
|
||||||
}
|
0x18 => Chip8CpuInstructions::LdStVx(ubln),
|
||||||
0x1000..=0x1FFF => {
|
0x1E => Chip8CpuInstructions::AddIVx(ubln),
|
||||||
// 1nnn - JP addr
|
0x29 => Chip8CpuInstructions::LdFVx(ubln),
|
||||||
// Jump to location nnn.
|
0x33 => Chip8CpuInstructions::LdBVx(ubln),
|
||||||
Chip8CpuInstructions::JpAddr(addr_param as i16)
|
0x55 => Chip8CpuInstructions::LdIVx(ubln),
|
||||||
}
|
0x65 => Chip8CpuInstructions::LdVxI(ubln),
|
||||||
0x2000..=0x2FFF => {
|
_ => XXXXERRORINSTRUCTION
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
_ => XXXXERRORINSTRUCTION
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
|
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
|
||||||
print!("INSTRUCTION {:04x}", self.encode());
|
print!("INSTRUCTION {}", self);
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let start_pc = input.registers.peek_pc();
|
let start_pc = input.registers.peek_pc();
|
||||||
input.registers.poke_pc(start_pc + 2);
|
input.registers.poke_pc(start_pc + 2);
|
||||||
let _ = match self {
|
let _ = match self {
|
||||||
// 0x0nnn Exit to System Call
|
// 0x0nnn Exit to System Call
|
||||||
Chip8CpuInstructions::SysAddr(new_address) => {
|
Chip8CpuInstructions::SysAddr(new_address) => {
|
||||||
debug!("SysAddr [0x{new_address:3x}]");
|
|
||||||
|
|
||||||
input.registers.poke_pc(*new_address as u16);
|
input.registers.poke_pc(*new_address as u16);
|
||||||
}
|
}
|
||||||
// * 0x00E0 Clear Screen
|
// * 0x00E0 Clear Screen
|
||||||
@ -435,18 +447,14 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
// 0x00EE Return from Subroutine
|
// 0x00EE Return from Subroutine
|
||||||
Chip8CpuInstructions::RET => {
|
Chip8CpuInstructions::RET => {
|
||||||
let return_address = input.stack.pop();
|
input.registers.poke_pc(input.stack.pop());
|
||||||
debug!("Returning from subroutine to {return_address:03x}");
|
|
||||||
input.registers.poke_pc(return_address);
|
|
||||||
}
|
}
|
||||||
// 0x1nnn Jump to Address
|
// 0x1nnn Jump to Address
|
||||||
Chip8CpuInstructions::JpAddr(new_address) => {
|
Chip8CpuInstructions::JpAddr(new_address) => {
|
||||||
debug!("JpAddr [0x{new_address:3x}]");
|
|
||||||
input.registers.poke_pc(*new_address as u16);
|
input.registers.poke_pc(*new_address as u16);
|
||||||
}
|
}
|
||||||
// 0x2nnn Call Subroutine
|
// 0x2nnn Call Subroutine
|
||||||
Chip8CpuInstructions::CallAddr(new_address) => {
|
Chip8CpuInstructions::CallAddr(new_address) => {
|
||||||
debug!("CALL ADDR 0x{new_address:3x}");
|
|
||||||
let return_address = input.registers.peek_pc();
|
let return_address = input.registers.peek_pc();
|
||||||
input.registers.poke_pc(*new_address as u16);
|
input.registers.poke_pc(*new_address as u16);
|
||||||
input.stack.push(&return_address);
|
input.stack.push(&return_address);
|
||||||
@ -456,7 +464,6 @@ impl Chip8CpuInstructions {
|
|||||||
if input.registers.peek(*vx_register as u8) == *byte as u8 {
|
if input.registers.peek(*vx_register as u8) == *byte as u8 {
|
||||||
input.registers.advance_pc();
|
input.registers.advance_pc();
|
||||||
}
|
}
|
||||||
debug!("SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]");
|
|
||||||
}
|
}
|
||||||
// 0x4xkk Skip next instruction if Vx != kk
|
// 0x4xkk Skip next instruction if Vx != kk
|
||||||
Chip8CpuInstructions::SneVxByte(x, byte) => {
|
Chip8CpuInstructions::SneVxByte(x, byte) => {
|
||||||
@ -465,41 +472,27 @@ impl Chip8CpuInstructions {
|
|||||||
//
|
//
|
||||||
// The interpreter compares register Vx to kk, and if they are not equal,
|
// The interpreter compares register Vx to kk, and if they are not equal,
|
||||||
// increments the program counter by 2.
|
// increments the program counter by 2.
|
||||||
let lhs = input.registers.peek(*x);
|
if input.registers.peek(*x) != *byte {
|
||||||
let rhs = *byte;
|
|
||||||
|
|
||||||
if lhs != rhs {
|
|
||||||
input.registers.advance_pc();
|
input.registers.advance_pc();
|
||||||
}
|
}
|
||||||
debug!("SneVxByte [0x{x:02x}] [0x{byte:02x}");
|
|
||||||
}
|
}
|
||||||
// 0x5xy0 Skip next instruction if Vx == Vy
|
// 0x5xy0 Skip next instruction if Vx == Vy
|
||||||
Chip8CpuInstructions::SeVxVy(x, y) => {
|
Chip8CpuInstructions::SeVxVy(x, y) => {
|
||||||
let lhs = input.registers.peek(*x as u8);
|
if input.registers.peek(*x as u8) == input.registers.peek(*y as u8) {
|
||||||
let rhs = input.registers.peek(*y as u8);
|
|
||||||
|
|
||||||
if lhs == rhs {
|
|
||||||
input.registers.advance_pc();
|
input.registers.advance_pc();
|
||||||
}
|
}
|
||||||
debug!("SeVxVy [0x{x:1x}] [0x{y:1x}]");
|
|
||||||
}
|
}
|
||||||
// 0x6xkk Set Vx = kk
|
// 0x6xkk Set Vx = kk
|
||||||
Chip8CpuInstructions::LdVxByte(register, byte) => {
|
Chip8CpuInstructions::LdVxByte(register, byte) => {
|
||||||
let byte_value = *byte as u8;
|
input.registers.poke(*register, *byte);
|
||||||
input.registers.poke(*register as u8, byte_value);
|
|
||||||
debug!("LdVxByte [0x{register:1x}] [0x{byte:2.0x}]");
|
|
||||||
}
|
}
|
||||||
// 0x7xkk Set Vx = Vx + kk
|
// 0x7xkk Set Vx = Vx + kk
|
||||||
Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
|
Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
|
||||||
let x_value: u16 = input.registers.peek(*vx_register) as u16;
|
input.registers.poke(*vx_register, (input.registers.peek(*vx_register) as u16 + *byte as u16) as u8);
|
||||||
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}");
|
|
||||||
}
|
}
|
||||||
// 0x8xy0 Set value of Vy in Vx
|
// 0x8xy0 Set value of Vy in Vx
|
||||||
Chip8CpuInstructions::LdVxVy(x, y) => {
|
Chip8CpuInstructions::LdVxVy(x, y) => {
|
||||||
input.registers.poke(*x as u8, input.registers.peek(*y as u8));
|
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
|
// 0x8xy1 Set Vx = Vx OR Vy
|
||||||
Chip8CpuInstructions::OrVxVy(x, y) => {
|
Chip8CpuInstructions::OrVxVy(x, y) => {
|
||||||
@ -509,25 +502,20 @@ impl Chip8CpuInstructions {
|
|||||||
// OR them
|
// OR them
|
||||||
let working_16_or = working_16_x | working_16_y;
|
let working_16_or = working_16_x | working_16_y;
|
||||||
// shift them back to 8 bit.
|
// shift them back to 8 bit.
|
||||||
let to_set = working_16_or as u8;
|
input.registers.poke(*x as u8, working_16_or as u8);
|
||||||
input.registers.poke(*x as u8, to_set);
|
|
||||||
debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]")
|
debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]")
|
||||||
}
|
}
|
||||||
// 0x8xy2 Set Vx = Vx AND Vy
|
// 0x8xy2 Set Vx = Vx AND Vy
|
||||||
Chip8CpuInstructions::AndVxVy(x, y) => {
|
Chip8CpuInstructions::AndVxVy(x, y) => {
|
||||||
let lhs_16 = input.registers.peek(*x as u8) as u16;
|
let lhs_16 = input.registers.peek(*x as u8) as u16;
|
||||||
let rhs_16 = input.registers.peek(*y 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, (lhs_16 & rhs_16) as u8);
|
||||||
|
|
||||||
input.registers.poke(*x as u8, and_16 as u8);
|
|
||||||
debug!("AndVxVy [0x{x:02x}] [0x{y:02x}] [0b{and_16:08b}");
|
|
||||||
}
|
}
|
||||||
// 0x8xy3 Set Vx = Vx XOR Vy
|
// 0x8xy3 Set Vx = Vx XOR Vy
|
||||||
Chip8CpuInstructions::XorVxVy(x, y) => {
|
Chip8CpuInstructions::XorVxVy(x, y) => {
|
||||||
let lhs_16 = input.registers.peek(*x as u8) as u16;
|
let lhs_16 = input.registers.peek(*x as u8) as u16;
|
||||||
let rhs_16 = input.registers.peek(*y 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, (lhs_16 ^ rhs_16) as u8);
|
||||||
input.registers.poke(*x as u8, xor_16 as u8);
|
|
||||||
}
|
}
|
||||||
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
|
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
|
||||||
Chip8CpuInstructions::AddVxVy(x, y) => {
|
Chip8CpuInstructions::AddVxVy(x, y) => {
|
||||||
@ -636,7 +624,7 @@ impl Chip8CpuInstructions {
|
|||||||
// Set I = nnn.
|
// Set I = nnn.
|
||||||
//
|
//
|
||||||
// The value of register I is set to 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);
|
input.registers.poke_i(*new_index);
|
||||||
}
|
}
|
||||||
// 0xBnnn Jump to nnn+V0
|
// 0xBnnn Jump to nnn+V0
|
||||||
@ -674,20 +662,33 @@ impl Chip8CpuInstructions {
|
|||||||
// it wraps around to the opposite side of the screen.
|
// it wraps around to the opposite side of the screen.
|
||||||
|
|
||||||
let source_memory_offset = input.registers.peek_i();
|
let source_memory_offset = input.registers.peek_i();
|
||||||
let x_offset = input.registers.peek(*x);
|
let x_offset = input.registers.peek(*x) as u16;
|
||||||
let y_offset = input.registers.peek(*y);
|
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 {
|
for byte_index in 0..*n {
|
||||||
let current_byte = input.memory.peek(byte_index as u16 + source_memory_offset);
|
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 {
|
for bit_index in 0..8 {
|
||||||
let data_offset = x_offset + (y_offset as u16 + bit_index as u16);
|
input.video_memory.poke(x_offset + (y_offset + bit_index as u16), (current_byte & (0x80 >> bit_index)) != 0);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if input.video_memory.has_frame_changed {
|
if input.video_memory.has_frame_changed {
|
||||||
input.registers.poke(0xf, 1u8);
|
input.registers.poke(0xf, 1u8);
|
||||||
} else {
|
} else {
|
||||||
@ -823,7 +824,7 @@ impl Chip8CpuInstructions {
|
|||||||
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
|
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
|
||||||
};
|
};
|
||||||
let cycle_time = Instant::now().duration_since(start_time).as_nanos();
|
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()
|
input.to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,14 +41,14 @@ impl Chip8Registers {
|
|||||||
self.i_register = new_value;
|
self.i_register = new_value;
|
||||||
}
|
}
|
||||||
pub fn peek(&self, register_number: u8) -> u8 {
|
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) {
|
pub fn poke(&mut self, register_number: u8, value: u8) {
|
||||||
if register_number > 0xf {
|
if register_number > 0xf {
|
||||||
panic!("INVALID REGISTER");
|
panic!("INVALID REGISTER");
|
||||||
}
|
}
|
||||||
self.registers[(register_number) as usize] = value;
|
self.registers[register_number as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek_pc(&self) -> u16 {
|
pub fn peek_pc(&self) -> u16 {
|
||||||
|
|||||||
@ -39,27 +39,27 @@ impl InstructionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
// 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 {
|
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u8 {
|
||||||
instruction_to_read_from & 0x000F
|
( instruction_to_read_from & 0x000F )as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
|
// 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 {
|
pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u8 {
|
||||||
(instruction_to_read_from & 0x0F00).rotate_right(8)
|
(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
|
// 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 {
|
pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u8 {
|
||||||
(instruction_to_read_from & 0x00F0).rotate_right(4)
|
(instruction_to_read_from & 0x00F0).rotate_right(4) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
// kk or byte - An 8-bit value, the lowest 8 bits of the instruction
|
// 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 {
|
pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u8 {
|
||||||
instruction_to_read_from & 0x00FF
|
(instruction_to_read_from & 0x00FF) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 {
|
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u8 {
|
||||||
(to_read_from & 0x0f00) >> 8
|
((to_read_from & 0x0f00) >> 8) as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,9 @@ impl Chip8Video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(self, address: u16) -> bool {
|
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]
|
self.memory[effective_address as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,17 +43,18 @@ impl Chip8Video {
|
|||||||
|
|
||||||
let old_value = self.memory[effective_address as usize];
|
let old_value = self.memory[effective_address as usize];
|
||||||
let xored_value = new_value ^ old_value; // XOR of the video
|
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 the frame has already changed we dont care if it changed again.
|
||||||
|
if !self.has_frame_changed {
|
||||||
if value_changed {
|
if old_value != xored_value {
|
||||||
self.has_frame_changed = true
|
self.has_frame_changed = true
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
self.memory[effective_address as usize] = xored_value;
|
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() {
|
for i in (0..8).rev() {
|
||||||
self.poke(first_address + (7 - i), (to_write & (1 << i)) != 0);
|
self.poke(first_address + (7 - i), (to_write & (1 << i)) != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,7 @@ fn main() {
|
|||||||
// waiting for a keychange...
|
// waiting for a keychange...
|
||||||
},
|
},
|
||||||
gemma::chip8::cpu_states::Chip8CpuStates::ExecutingInstruction => { // should never see this.
|
gemma::chip8::cpu_states::Chip8CpuStates::ExecutingInstruction => { // should never see this.
|
||||||
},
|
},
|
||||||
gemma::chip8::cpu_states::Chip8CpuStates::Error => {
|
gemma::chip8::cpu_states::Chip8CpuStates::Error => {
|
||||||
panic!("System in undefined state.");
|
panic!("System in undefined state.");
|
||||||
},
|
},
|
||||||
@ -83,8 +83,6 @@ fn main() {
|
|||||||
GemmaImguiSupport::registers_view(&system, ui);
|
GemmaImguiSupport::registers_view(&system, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ui_state.show_memory {
|
if ui_state.show_memory {
|
||||||
let active_instruction = system.registers.peek_pc();
|
let active_instruction = system.registers.peek_pc();
|
||||||
GemmaImguiSupport::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
|
GemmaImguiSupport::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user