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::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)
|
||||
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
|
||||
}
|
||||
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)
|
||||
_ => XXXXERRORINSTRUCTION
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,11 +43,12 @@ 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 {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user