more tests.
registers are their own struct now
This commit is contained in:
parent
d00d093b11
commit
0075c5ef7d
@ -1,6 +1,7 @@
|
|||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use crate::chip8::delay_timer::DelayTimer;
|
use crate::chip8::delay_timer::DelayTimer;
|
||||||
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
||||||
|
use crate::chip8::registers::Chip8Registers;
|
||||||
use crate::chip8::sound_timer::SoundTimer;
|
use crate::chip8::sound_timer::SoundTimer;
|
||||||
use crate::chip8::util::InstructionUtil;
|
use crate::chip8::util::InstructionUtil;
|
||||||
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
|
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
|
||||||
@ -9,16 +10,13 @@ use super::{
|
|||||||
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,
|
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Chip8Computer {
|
pub struct Chip8Computer {
|
||||||
pub pc: u16,
|
|
||||||
pub sp: u8,
|
pub sp: u8,
|
||||||
pub memory: Chip8SystemMemory,
|
pub memory: Chip8SystemMemory,
|
||||||
pub registers: [u8; 16],
|
pub registers: Chip8Registers,
|
||||||
pub sound_timer: SoundTimer,
|
pub sound_timer: SoundTimer,
|
||||||
pub delay_timer: DelayTimer,
|
pub delay_timer: DelayTimer,
|
||||||
pub i_register: u16,
|
|
||||||
pub video_memory: Chip8Video,
|
pub video_memory: Chip8Video,
|
||||||
pub state: Chip8CpuStates,
|
pub state: Chip8CpuStates,
|
||||||
}
|
}
|
||||||
@ -26,14 +24,12 @@ pub struct Chip8Computer {
|
|||||||
impl Default for Chip8Computer {
|
impl Default for Chip8Computer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pc: 0x0200,
|
|
||||||
sp: 0x00,
|
sp: 0x00,
|
||||||
memory: Chip8SystemMemory::default(),
|
memory: Chip8SystemMemory::default(),
|
||||||
video_memory: Chip8Video::default(),
|
video_memory: Chip8Video::default(),
|
||||||
registers: [0; CHIP8_REGISTER_COUNT as usize],
|
registers: Chip8Registers::default(),
|
||||||
sound_timer: SoundTimer::new(),
|
sound_timer: SoundTimer::new(),
|
||||||
delay_timer: DelayTimer::new(),
|
delay_timer: DelayTimer::new(),
|
||||||
i_register: 0x0000,
|
|
||||||
state: Chip8CpuStates::WaitingForInstruction,
|
state: Chip8CpuStates::WaitingForInstruction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,8 +64,9 @@ impl Chip8Computer {
|
|||||||
// read the next instruction
|
// read the next instruction
|
||||||
|
|
||||||
let mut working_instruction: u16 = 0b0000000000000000;
|
let mut working_instruction: u16 = 0b0000000000000000;
|
||||||
let high_byte = (self.memory.clone().peek(self.pc) as u16).rotate_left(8);
|
let start_pc = self.registers.peek_pc();
|
||||||
let low_byte = self.memory.clone().peek(self.pc + 1) as u16;
|
let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8);
|
||||||
|
let low_byte = self.memory.clone().peek(start_pc + 1) as u16;
|
||||||
|
|
||||||
working_instruction = InstructionUtil::join_bytes(high_byte as u8, low_byte as u8);
|
working_instruction = InstructionUtil::join_bytes(high_byte as u8, low_byte as u8);
|
||||||
|
|
||||||
@ -81,44 +78,6 @@ impl Chip8Computer {
|
|||||||
// todo: THIS IS BAD AND IS A SIDE EFFECT
|
// todo: THIS IS BAD AND IS A SIDE EFFECT
|
||||||
decoded_instruction.execute(self);
|
decoded_instruction.execute(self);
|
||||||
|
|
||||||
/*
|
|
||||||
match (self.state, decoded_instruction) {
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(vx_register, vy_register)) => {
|
|
||||||
debug!("INST* SeVxVy: 0x{vx_register:1x}/0x{vy_register:1x}");
|
|
||||||
if self.registers[vx_register as usize] == self.registers[vy_register as usize] {
|
|
||||||
self.pc += 2;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(vx_register, byte)) => {
|
|
||||||
debug!("INST* LdVxByte: 0x{vx_register:1x}/0x{byte:2x}");
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(vx_register, byte)) => {
|
|
||||||
debug!("INST: AddVxByte: 0x{vx_register:1x}/0x{byte:2x}");
|
|
||||||
self.registers[vx_register as usize] += byte as u8;
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(vx_register, vy_register)) => {
|
|
||||||
debug!("INST: LdVxVy: 0x{vx_register:1x}/0x{vy_register:1x}");
|
|
||||||
self.registers[vx_register as usize] = self.registers[vy_register as usize];
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(vx_register, vy_register)) => {
|
|
||||||
debug!("INST: OrVxVy: {vx_register}/{vy_register:1x}");
|
|
||||||
self.registers[vx_register as usize] = self.registers[vx_register as usize] | self.registers[vy_register as usize];
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(vx_register, vy_register)) => {
|
|
||||||
debug!("INST: AndVxVy: {vx_register:1x}/{vy_register:1x}");
|
|
||||||
self.registers[vx_register as usize] = self.registers[vx_register as usize] & self.registers[vy_register as usize];
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(vx_register, vy_register)) => {
|
|
||||||
debug!("INST: XorVxVy: {vx_register:1x}/{vy_register:1x}");
|
|
||||||
self.registers[vx_register as usize] = self.registers[vx_register as usize] ^ self.registers[vy_register as usize];
|
|
||||||
}
|
|
||||||
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy) => {
|
|
||||||
debug!("INST: AddVxVy: {vx_register:1x}/{vy_register:1x}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
self.sound_timer.tick();
|
self.sound_timer.tick();
|
||||||
self.delay_timer.tick();
|
self.delay_timer.tick();
|
||||||
self
|
self
|
||||||
|
|||||||
@ -1,8 +1,18 @@
|
|||||||
|
use log::debug;
|
||||||
|
use rand::random;
|
||||||
use crate::chip8::computer::{Chip8Computer};
|
use crate::chip8::computer::{Chip8Computer};
|
||||||
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
||||||
use crate::chip8::util::InstructionUtil;
|
use crate::chip8::util::InstructionUtil;
|
||||||
use crate::chip8::video::Chip8Video;
|
use crate::chip8::video::Chip8Video;
|
||||||
|
|
||||||
|
/*
|
||||||
|
nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
||||||
|
n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
||||||
|
x - A 4-bit value, the lower 4 bits of the high byte of the instruction
|
||||||
|
y - A 4-bit value, the upper 4 bits of the low byte of the instruction
|
||||||
|
kk or byte - An 8-bit value, the lowest 8 bits of the instruction
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Chip8CpuInstructions {
|
pub enum Chip8CpuInstructions {
|
||||||
SysAddr(i16), // 0x0nnn Exit to System Call
|
SysAddr(i16), // 0x0nnn Exit to System Call
|
||||||
@ -42,9 +52,6 @@ pub enum Chip8CpuInstructions {
|
|||||||
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
|
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
|
||||||
XXXXERRORINSTRUCTION,
|
XXXXERRORINSTRUCTION,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Chip8CpuInstructions {
|
impl Chip8CpuInstructions {
|
||||||
pub fn encode(&self) -> u16 {
|
pub fn encode(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
@ -84,7 +91,6 @@ impl Chip8CpuInstructions {
|
|||||||
Chip8CpuInstructions::OrVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::OrVxVy(x_register, y_register) => {
|
||||||
0x8001u16 | x_register << 8 | y_register << 4
|
0x8001u16 | x_register << 8 | y_register << 4
|
||||||
}
|
}
|
||||||
|
|
||||||
Chip8CpuInstructions::AndVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::AndVxVy(x_register, y_register) => {
|
||||||
0x8002u16 | x_register << 8 | y_register << 4
|
0x8002u16 | x_register << 8 | y_register << 4
|
||||||
}
|
}
|
||||||
@ -93,15 +99,12 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
Chip8CpuInstructions::AddVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::AddVxVy(x_register, y_register) => {
|
||||||
0x8004u16 | x_register << 8 | y_register << 4
|
0x8004u16 | x_register << 8 | y_register << 4
|
||||||
|
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::SubVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::SubVxVy(x_register, y_register) => {
|
||||||
0x8005u16 | x_register << 8 | y_register << 4
|
0x8005u16 | x_register << 8 | y_register << 4
|
||||||
}
|
}
|
||||||
|
|
||||||
Chip8CpuInstructions::ShrVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::ShrVxVy(x_register, y_register) => {
|
||||||
0x8006u16 | x_register << 8 | y_register << 4
|
0x8006u16 | x_register << 8 | y_register << 4
|
||||||
|
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::SubnVxVy(x_register, y_register) => {
|
Chip8CpuInstructions::SubnVxVy(x_register, y_register) => {
|
||||||
0x8007u16 | x_register << 8 | y_register << 4
|
0x8007u16 | x_register << 8 | y_register << 4
|
||||||
@ -129,7 +132,6 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
Chip8CpuInstructions::SnkpVx(x_register) => {
|
Chip8CpuInstructions::SnkpVx(x_register) => {
|
||||||
0xE0A1u16 | x_register << 8
|
0xE0A1u16 | x_register << 8
|
||||||
|
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::LdVxDt(x_register) => {
|
Chip8CpuInstructions::LdVxDt(x_register) => {
|
||||||
0xF007u16 | x_register << 8
|
0xF007u16 | x_register << 8
|
||||||
@ -145,7 +147,6 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
Chip8CpuInstructions::AddIVx(x_register) => {
|
Chip8CpuInstructions::AddIVx(x_register) => {
|
||||||
0xF01Eu16 | x_register << 8
|
0xF01Eu16 | x_register << 8
|
||||||
|
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::LdFVx(x_register) => {
|
Chip8CpuInstructions::LdFVx(x_register) => {
|
||||||
0xF029u16 | x_register << 8
|
0xF029u16 | x_register << 8
|
||||||
@ -164,9 +165,6 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Chip8CpuInstructions {
|
|
||||||
pub fn decode(input: u16) -> Chip8CpuInstructions {
|
pub fn decode(input: u16) -> Chip8CpuInstructions {
|
||||||
let x_param = InstructionUtil::read_x_from_instruction(input);
|
let x_param = InstructionUtil::read_x_from_instruction(input);
|
||||||
let y_param = InstructionUtil::read_y_from_instruction(input);
|
let y_param = InstructionUtil::read_y_from_instruction(input);
|
||||||
@ -306,7 +304,7 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xF007..=0xFF65 => {
|
0xF007..=0xFF65 => {
|
||||||
println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln);
|
// println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln);
|
||||||
match last_byte {
|
match last_byte {
|
||||||
0x07 => {
|
0x07 => {
|
||||||
Chip8CpuInstructions::LdVxDt(ubln)
|
Chip8CpuInstructions::LdVxDt(ubln)
|
||||||
@ -347,58 +345,127 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&self, mut input: &mut Chip8Computer) -> Chip8Computer {
|
pub fn execute(&self, mut input: &mut Chip8Computer) -> Chip8Computer {
|
||||||
input.pc += 2;
|
let start_pc = input.registers.peek_pc();
|
||||||
|
input.registers.poke_pc(start_pc + 2);
|
||||||
let _ = match self {
|
let _ = match self {
|
||||||
|
// 0x0nnn Exit to System Call
|
||||||
Chip8CpuInstructions::SysAddr(new_address) => {
|
Chip8CpuInstructions::SysAddr(new_address) => {
|
||||||
let mut new_state = input.clone();
|
// println!("SYS TO [{new_address}]");
|
||||||
new_state.pc = *new_address as u16;
|
input.registers.poke_pc(*new_address as u16);
|
||||||
}
|
}
|
||||||
|
// * 0x00E0 Clear Screen
|
||||||
Chip8CpuInstructions::CLS => {
|
Chip8CpuInstructions::CLS => {
|
||||||
for i in 0..(64 * 32) {
|
for i in 0..(64 * 32) {
|
||||||
input.video_memory.poke(i, false);
|
input.video_memory.poke(i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::RET => {}
|
// 0x00EE Return from Subroutine
|
||||||
|
Chip8CpuInstructions::RET => {
|
||||||
|
debug!("RET");
|
||||||
|
}
|
||||||
|
// 0x1nnn Jump to Address
|
||||||
Chip8CpuInstructions::JpAddr(new_address) => {
|
Chip8CpuInstructions::JpAddr(new_address) => {
|
||||||
input.pc = *new_address as u16
|
input.registers.poke_pc(*new_address as u16);
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::CallAddr(_) => {}
|
// 0x2nnn Call Subroutine
|
||||||
|
Chip8CpuInstructions::CallAddr(new_address) => {
|
||||||
|
debug!("CALL ADDR {new_address}");
|
||||||
|
}
|
||||||
|
// 0x3xkk Skip next instruction if Vx = kk.
|
||||||
Chip8CpuInstructions::SeVxByte(vx_register, byte) => {
|
Chip8CpuInstructions::SeVxByte(vx_register, byte) => {
|
||||||
input.registers[*vx_register as usize] = *byte as u8;
|
if input.registers.peek(*vx_register as u8) == *byte as u8 {
|
||||||
}
|
input.registers.advance_pc();
|
||||||
Chip8CpuInstructions::SneVxByte(vx_register, byte) => {
|
|
||||||
if input.registers[*vx_register as usize] == *byte as u8 {
|
|
||||||
input.pc += 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::SeVxVy(_, _) => {}
|
// 0x4xkk Skip next instruction if Vx != kk
|
||||||
|
Chip8CpuInstructions::SneVxByte(x, byte) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = byte.to_be_bytes()[0];
|
||||||
|
if lhs == rhs {
|
||||||
|
input.registers.advance_pc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
// println!("COMPARING [{lhs}] to [{rhs}]");
|
||||||
|
if lhs == rhs {
|
||||||
|
input.registers.advance_pc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 0x6xkk Set Vx = kk
|
||||||
Chip8CpuInstructions::LdVxByte(register, byte) => {
|
Chip8CpuInstructions::LdVxByte(register, byte) => {
|
||||||
input.registers[*register as usize] = byte.to_be_bytes()[0];
|
let start_value = input.registers.peek(*register as u8);
|
||||||
|
let byte_value = *byte as u8;
|
||||||
|
// println!("SETTING REGISTER [{register}] FROM [{start_value}] to [{byte_value}] by loading.");
|
||||||
|
input.registers.poke(*register as u8, byte_value);
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::AddVxByte(_, _) => {}
|
// 0x7xkk Set Vx = Vx + kk
|
||||||
Chip8CpuInstructions::LdVxVy(_, _) => {}
|
Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
|
||||||
Chip8CpuInstructions::OrVxVy(_, _) => {}
|
let to_add = *byte as u8;
|
||||||
Chip8CpuInstructions::AndVxVy(_, _) => {}
|
let old_value = input.registers.peek(*vx_register as u8);
|
||||||
Chip8CpuInstructions::XorVxVy(_, _) => {}
|
println!("Adding [{old_value}] from register [{vx_register}] to [{to_add}] ");
|
||||||
Chip8CpuInstructions::AddVxVy(vx_register, vy_register) => {
|
input.registers.poke(*vx_register as u8, (old_value + to_add));
|
||||||
input.registers[*vx_register as usize] += input.registers[*vy_register as usize];
|
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::SubVxVy(vx_register, vy_register) => {
|
// 0x8xy0 Set value of Vy in Vx
|
||||||
input.registers[*vx_register as usize] -= input.registers[*vy_register as usize];
|
Chip8CpuInstructions::LdVxVy(x, y) => {
|
||||||
|
input.registers.poke(*x as u8, input.registers.peek(*y as u8));
|
||||||
|
}
|
||||||
|
// 0x8xy1 Set Vx = Vx OR Vy
|
||||||
|
Chip8CpuInstructions::OrVxVy(x, y) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = input.registers.peek(*y as u8);
|
||||||
|
input.registers.poke(*x as u8, lhs | rhs);
|
||||||
|
}
|
||||||
|
// 0x8xy2 Set Vx = Vx AND Vy
|
||||||
|
Chip8CpuInstructions::AndVxVy(x, y) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = input.registers.peek(*y as u8);
|
||||||
|
|
||||||
|
input.registers.poke(*x as u8, lhs & rhs);
|
||||||
|
}
|
||||||
|
// 0x8xy3 Set Vx = Vx XOR Vy
|
||||||
|
Chip8CpuInstructions::XorVxVy(x, y) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = input.registers.peek(*y as u8);
|
||||||
|
input.registers.poke(*x as u8, lhs ^ rhs);
|
||||||
|
}
|
||||||
|
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
|
||||||
|
Chip8CpuInstructions::AddVxVy(x, y) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = input.registers.peek(*y as u8);
|
||||||
|
|
||||||
|
let working =(lhs + rhs) as i16;
|
||||||
|
if working > 255 {
|
||||||
|
input.registers.poke(0xf, 0x01);
|
||||||
|
}
|
||||||
|
input.registers.poke(*x as u8, working as u8);
|
||||||
|
}
|
||||||
|
Chip8CpuInstructions::SubVxVy(x,y) => {
|
||||||
|
let lhs = input.registers.peek(*x as u8);
|
||||||
|
let rhs = input.registers.peek(*y as u8);
|
||||||
|
input.registers.poke(*x as u8, lhs - rhs);
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::ShrVxVy(_, _) => {}
|
Chip8CpuInstructions::ShrVxVy(_, _) => {}
|
||||||
Chip8CpuInstructions::SubnVxVy(_, _) => {}
|
Chip8CpuInstructions::SubnVxVy(_, _) => {}
|
||||||
Chip8CpuInstructions::ShlVxVy(_, _) => {}
|
Chip8CpuInstructions::ShlVxVy(_, _) => {}
|
||||||
Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => {
|
Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => {
|
||||||
if input.registers[*vx_register as usize] != input.registers[*vy_register as usize] {
|
let x_reg_value = input.registers.peek(*vx_register as u8);
|
||||||
input.pc += 02;
|
let y_reg_value = input.registers.peek(*vy_register as u8);
|
||||||
|
if x_reg_value != y_reg_value {
|
||||||
|
input.registers.advance_pc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::LdIAddr(new_index) => {
|
Chip8CpuInstructions::LdIAddr(new_index) => {
|
||||||
input.i_register = *new_index;
|
input.registers.poke_i(input.registers.peek(*new_index as u8) as u16);
|
||||||
}
|
}
|
||||||
|
// 0xBnnn Jump to nnn+V0
|
||||||
Chip8CpuInstructions::JpV0Addr(_) => {}
|
Chip8CpuInstructions::JpV0Addr(_) => {}
|
||||||
Chip8CpuInstructions::RndVxByte(_, _) => {}
|
Chip8CpuInstructions::RndVxByte(x, byte) => {
|
||||||
|
let new_value: u8 = random() ;
|
||||||
|
input.registers.poke(*x as u8, (new_value & *byte as u8))
|
||||||
|
}
|
||||||
Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => {
|
Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => {
|
||||||
// read nibble bytes from memory starting at I
|
// read nibble bytes from memory starting at I
|
||||||
|
|
||||||
@ -411,9 +478,9 @@ impl Chip8CpuInstructions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if did_change {
|
if did_change {
|
||||||
input.registers[0xF] = 1u8;
|
input.registers.poke(0x10, 1u8);
|
||||||
} else {
|
} else {
|
||||||
input.registers[0xF] = 0u8;
|
input.registers.poke(0x10, 0u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chip8CpuInstructions::SkpVx(_) => {}
|
Chip8CpuInstructions::SkpVx(_) => {}
|
||||||
@ -425,7 +492,9 @@ impl Chip8CpuInstructions {
|
|||||||
Chip8CpuInstructions::AddIVx(_) => {}
|
Chip8CpuInstructions::AddIVx(_) => {}
|
||||||
Chip8CpuInstructions::LdFVx(_) => {}
|
Chip8CpuInstructions::LdFVx(_) => {}
|
||||||
Chip8CpuInstructions::LdBVx(_) => {}
|
Chip8CpuInstructions::LdBVx(_) => {}
|
||||||
Chip8CpuInstructions::LdIVx(_) => {}
|
Chip8CpuInstructions::LdIVx(x) => {
|
||||||
|
input.registers.poke(*x as u8, input.registers.peek_i() as u8);
|
||||||
|
}
|
||||||
Chip8CpuInstructions::LdVxI(_) => {}
|
Chip8CpuInstructions::LdVxI(_) => {}
|
||||||
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
|
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
|
||||||
};
|
};
|
||||||
@ -522,4 +591,132 @@ mod test {
|
|||||||
assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
|
assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
|
||||||
assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
|
assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// START OF THE EXECUTION TESTS
|
||||||
|
#[test]
|
||||||
|
fn sys_test() {
|
||||||
|
// 0x0nnn Exit to System Call
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::SysAddr(0).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0);
|
||||||
|
Chip8CpuInstructions::SysAddr(0xFA0).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0xFA0);
|
||||||
|
Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x0AF);
|
||||||
|
}
|
||||||
|
fn cls_test() {
|
||||||
|
// * 0x00E0 Clear Screen
|
||||||
|
// todo: Need to write this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ret_test() {
|
||||||
|
// 0x00EE Return from Subroutine
|
||||||
|
// todo: no stack yet.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn jpaddr_test() {
|
||||||
|
// 0x1nnn Jump to Address
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::JpAddr(0).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0);
|
||||||
|
Chip8CpuInstructions::JpAddr(0xABC).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0xABC);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calladdr_test() {
|
||||||
|
// 0x2nnn Call Subroutine
|
||||||
|
// todo: no stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// ** test moved up so it can be used later
|
||||||
|
#[test]
|
||||||
|
fn LdVxByte_test() {
|
||||||
|
// 0x6xkk Set Vx = kk
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x);
|
||||||
|
Chip8CpuInstructions::LdVxByte(2, 0x21).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek(1), 0x12);
|
||||||
|
assert_eq!(x.registers.peek(2), 0x21);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sevxbyte_match_test() {
|
||||||
|
// 0x3xkk Skip next instruction if Vx = kk.
|
||||||
|
// The interpreter compares register Vx to kk,
|
||||||
|
// and if they are equal, increments the program counter by 2.
|
||||||
|
|
||||||
|
// test setup: Load value 0x84 into V1
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::LdVxByte(1, 0x84).execute(&mut x);
|
||||||
|
Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x);
|
||||||
|
// we should be 6 instructions past.
|
||||||
|
// 2 for the LDVXBYTE
|
||||||
|
// 2 for the SEVXBYTE
|
||||||
|
// 2 for skipping.
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x206);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sevxbyte_nomatch_test() {
|
||||||
|
// 0x3xkk Skip next instruction if Vx = kk.
|
||||||
|
// The interpreter compares register Vx to kk,
|
||||||
|
// and if they are equal, increments the program counter by 2.
|
||||||
|
|
||||||
|
// test setup: Load value 0x84 into V1
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x);
|
||||||
|
// PC will be 2 bytes past as we executed an instruction...
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x202);
|
||||||
|
|
||||||
|
Chip8CpuInstructions::SeVxByte(1, 0x48).execute(&mut x);
|
||||||
|
// we should be 2 instructions past.
|
||||||
|
// 2 for what we executed
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x204);
|
||||||
|
|
||||||
|
Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x208);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn SeVxVy_test() {
|
||||||
|
// 0x4xkk Skip next instruction if Vx != kk
|
||||||
|
let mut x = Chip8Computer::new();
|
||||||
|
Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x);
|
||||||
|
Chip8CpuInstructions::LdVxByte(0x02, 0x84).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x204);
|
||||||
|
|
||||||
|
// skip, compare 0x84 to 0x84
|
||||||
|
Chip8CpuInstructions::SeVxVy(0x1, 0x2).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x208);
|
||||||
|
|
||||||
|
// load 0x48 (not matching) into V2
|
||||||
|
Chip8CpuInstructions::LdVxByte(0x2, 0x48).execute(&mut x);
|
||||||
|
// verify its there.
|
||||||
|
assert_eq!(x.registers.peek(2), 0x48);
|
||||||
|
// no skip, compare 0x84 and 0x48
|
||||||
|
Chip8CpuInstructions::SeVxVy(0x01, 0x02).execute(&mut x);
|
||||||
|
assert_eq!(x.registers.peek(2), 0x48);
|
||||||
|
assert_eq!(x.registers.peek_pc(), 0x20C);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn AddVxByte_test() {
|
||||||
|
// 0x7xkk Set Vx = Vx + kk
|
||||||
|
let x = Chip8Computer::new();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn LdVxVy_test() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn OrVxVy_test() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn AndVxVy_test() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn XorVxVy_test() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
53
emma/src/chip8/registers.rs
Normal file
53
emma/src/chip8/registers.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
/// Registers. numbered 1-16 publicly.
|
||||||
|
/// Privately using zero base array so -1 to shift from pub to priv.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Chip8Registers {
|
||||||
|
registers: [u8; 16],
|
||||||
|
i_register: u16,
|
||||||
|
pc: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chip8Registers {
|
||||||
|
pub fn advance_pc(&mut self) {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chip8Registers {
|
||||||
|
fn default() -> Self {
|
||||||
|
Chip8Registers {
|
||||||
|
registers: [0x00; 16],
|
||||||
|
i_register: 0x00,
|
||||||
|
pc: 0x200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chip8Registers {
|
||||||
|
pub fn poke_pc(&mut self, new_pc: u16) {
|
||||||
|
self.pc = new_pc
|
||||||
|
}
|
||||||
|
pub fn peek_i(&self) -> u16 {
|
||||||
|
self.i_register
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poke_i(&mut self, new_value: u16) {
|
||||||
|
self.i_register = new_value;
|
||||||
|
}
|
||||||
|
pub fn peek(&self, register_number: u8) -> u8 {
|
||||||
|
self.registers[(register_number - 1) as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poke(&mut self, register_number: u8, value: u8) {
|
||||||
|
self.registers[(register_number - 1) as usize] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_pc(&mut self) -> u16 {
|
||||||
|
self.pc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pc(&mut self, new_pc: u16) {
|
||||||
|
self.pc = new_pc
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,8 @@ impl InstructionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn join_bytes(high: u8, low: u8) -> u16 {
|
pub fn join_bytes(high: u8, low: u8) -> u16 {
|
||||||
(high as u16 )<< 8 & low as u16
|
let result = (high as u16 )<< 8 | low as u16;
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
||||||
|
|||||||
@ -8,6 +8,7 @@ pub mod chip8 {
|
|||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
pub mod cpu_states;
|
pub mod cpu_states;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod registers;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
Loading…
x
Reference in New Issue
Block a user