trevors_chip8_toy/emma/src/chip8/instructions.rs
Trevor Merritt 24ea413848 more test etc.
video needs stuff bad
2024-09-28 22:20:51 -04:00

1141 lines
49 KiB
Rust

use std::ops::{Shl, Shr};
use imgui::ColorPicker3;
use log::debug;
use rand::random;
use crate::chip8::computer::{Chip8Computer};
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
use crate::chip8::util::InstructionUtil;
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)]
pub enum Chip8CpuInstructions {
SysAddr(i16), // 0x0nnn Exit to System Call
CLS, // * 0x00E0 Clear Screen
RET, // 0x00EE Return from Subroutine
JpAddr(i16), // 0x1nnn Jump to Address
CallAddr(i16), // 0x2nnn Call Subroutine
SeVxByte(i16, i16), // 0x3xkk Skip next instruction if Vx = kk.
SneVxByte(i16, i16), // 0x4xkk Skip next instruction if Vx != kk
SeVxVy(u16, u16), // 0x5xy0 Skip next instruction if Vx == Vy
LdVxByte(u16, u16), // * 0x6xkk Set Vx = kk
AddVxByte(u16, u16), // 0x7xkk Set Vx = Vx + kk
LdVxVy(u16, u16), // 0x8xy0 Set value of Vy in Vx
OrVxVy(u16, u16), // 0x8xy1 Set Vx = Vx OR Vy
AndVxVy(u16, u16), // 0x8xy2 Set Vx = Vx AND Vy
XorVxVy(u16, u16), // 0x8xy3 Set Vx = Vx XOR Vy
AddVxVy(u16, u16), // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
SubVxVy(u16, u16), // 0x8xy5 Set Vx = Vx - Vy (Set VF NOT Borrow)
ShrVxVy(u16, u16), // 0x8xy6 Set Vx = Vx SHR 1 (Shift Rotated Right 1)
SubnVxVy(u16, u16), // 0x8xy7 Set Vx = Vy - Vx (Set VF NOT Borrow)
ShlVxVy(u16, u16), // 0x8xyE Shift Left
SneVxVy(u16, u16), // 0x9xy0 Skip next instruction if Vx != Vy
LdIAddr(u16), // * 0xAnnn VI = nnn
JpV0Addr(u16), // 0xBnnn Jump to nnn+V0
RndVxByte(u16, u16), // 0xCxkk Vx = random byte AND kk
DrawVxVyNibble(u16, u16, u16), // * 0xDxyn Display N byte sprite starting at Vx to Vy
SkpVx(u16), // 0xE09E Skip next instruction if key in Vx pressed
SnkpVx(u16), // 0xE0A1 Skip next instruction if key in Vx NOT pressed
LdVxDt(u16), // 0xFx07 Set Vx = Delay timer
LdVxK(u16), // 0xFx0A Wait for key, put in Vx
LdDtVx(u16), // 0xFx15 Set Delay Timer
LdStVx(u16), // 0xFx18 Set Sount Timer
AddIVx(u16), // 0xFx1E I = I + Vx
LdFVx(u16), // 0xFx29 Set I = Location of sprite for Digit Vx
LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2
LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
XXXXERRORINSTRUCTION,
}
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 << 8 | byte) as u16)
}
Chip8CpuInstructions::SneVxByte(vx_register, byte) => {
(0x4000i16 | vx_register << 8 | byte) as u16
}
Chip8CpuInstructions::SeVxVy(x_register, y_register) => {
0x5000u16 | (x_register << 8 | y_register << 4)
}
Chip8CpuInstructions::LdVxByte(x_register, byte) => {
0x6000u16 | x_register << 8 | byte
}
Chip8CpuInstructions::AddVxByte(x_register, byte) => {
0x7000u16 | x_register << 8 | byte
}
Chip8CpuInstructions::LdVxVy(x_register, y_register) => {
0x8000u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::OrVxVy(x_register, y_register) => {
0x8001u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::AndVxVy(x_register, y_register) => {
0x8002u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::XorVxVy(x_register, y_register) => {
0x8003u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::AddVxVy(x_register, y_register) => {
0x8004u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::SubVxVy(x_register, y_register) => {
0x8005u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::ShrVxVy(x_register, y_register) => {
0x8006u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::SubnVxVy(x_register, y_register) => {
0x8007u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::ShlVxVy(x_register, y_register) => {
0x800Eu16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::SneVxVy(x_register, y_register) => {
0x9000u16 | x_register << 8 | y_register << 4
}
Chip8CpuInstructions::LdIAddr(addr) => {
0xA000u16 | addr
}
Chip8CpuInstructions::JpV0Addr(addr) => {
0xB000u16 | addr
}
Chip8CpuInstructions::RndVxByte(x_register, byte) => {
0xC000u16 | x_register << 8 | byte
}
Chip8CpuInstructions::DrawVxVyNibble(x_register, y_register, height) => {
0xD000u16 | x_register << 8 | y_register << 4 | height
}
Chip8CpuInstructions::SkpVx(x_register) => {
0xE09Eu16 | x_register << 8
}
Chip8CpuInstructions::SnkpVx(x_register) => {
0xE0A1u16 | x_register << 8
}
Chip8CpuInstructions::LdVxDt(x_register) => {
0xF007u16 | x_register << 8
}
Chip8CpuInstructions::LdVxK(x_register) => {
0xF00Au16 | x_register << 8
}
Chip8CpuInstructions::LdDtVx(x_register) => {
0xF015u16 | x_register << 8
}
Chip8CpuInstructions::LdStVx(x_register) => {
0xF018u16 | x_register << 8
}
Chip8CpuInstructions::AddIVx(x_register) => {
0xF01Eu16 | x_register << 8
}
Chip8CpuInstructions::LdFVx(x_register) => {
0xF029u16 | x_register << 8
}
Chip8CpuInstructions::LdBVx(x_register) => {
0xf033u16 | x_register << 8
}
Chip8CpuInstructions::LdIVx(x_register) => {
0xf055u16 | x_register << 8
}
Chip8CpuInstructions::LdVxI(x_register) => {
0xf065u16 | x_register << 8
}
_ => {
0xffff
}
}
}
pub fn decode(input: u16) -> Chip8CpuInstructions {
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;
match input {
0x00E0 => {
// 00E0 - CLS
// Clear the display.
Chip8CpuInstructions::CLS
}
0x00EE => {
// 00EE - RET
// Return from a subroutine.
Chip8CpuInstructions::RET
}
0x0000..=0x0FFF => {
// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
Chip8CpuInstructions::SysAddr(addr_param as i16)
}
0x1000..=0x1FFF => {
// 1nnn - JP addr
// Jump to location nnn.
Chip8CpuInstructions::JpAddr(addr_param as i16)
}
0x2000..=0x2FFF => {
// 2nnn - CALL addr
// Call subroutine at nnn.
Chip8CpuInstructions::CallAddr(addr_param as i16)
}
0x3000..=0x3FFF => {
// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16)
}
0x4000..=0x4FFF => {
// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16)
}
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 => {
println!("DECODING {:4x}", input);
println!("UBLN: {:4x}", ubln);
Chip8CpuInstructions::SkpVx(ubln)
}
0xA1 => {
Chip8CpuInstructions::SnkpVx(ubln)
}
_ => {
XXXXERRORINSTRUCTION
}
}
}
0xF007..=0xFF65 => {
// println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln);
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, mut input: &mut Chip8Computer) -> Chip8Computer {
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) => {
// println!("SYS TO [{new_address}]");
input.registers.poke_pc(*new_address as u16);
}
// * 0x00E0 Clear Screen
Chip8CpuInstructions::CLS => {
for i in 0..(64 * 32) {
input.video_memory.poke(i, false);
}
}
// 0x00EE Return from Subroutine
Chip8CpuInstructions::RET => {
debug!("RET");
}
// 0x1nnn Jump to Address
Chip8CpuInstructions::JpAddr(new_address) => {
input.registers.poke_pc(*new_address as u16);
}
// 0x2nnn Call Subroutine
Chip8CpuInstructions::CallAddr(new_address) => {
debug!("CALL ADDR {new_address}");
}
// 0x3xkk Skip next instruction if Vx = kk.
Chip8CpuInstructions::SeVxByte(vx_register, byte) => {
if input.registers.peek(*vx_register as u8) == *byte as u8 {
input.registers.advance_pc();
}
}
// 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) => {
input.registers.poke(*register as u8, *byte as u8);
}
// 0x7xkk Set Vx = Vx + kk
Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
input.registers.poke(*vx_register as u8, (input.registers.peek(*vx_register as u8) + *byte 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));
}
// 0x8xy1 Set Vx = Vx OR Vy
Chip8CpuInstructions::OrVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) | input.registers.peek(*y as u8));
}
// 0x8xy2 Set Vx = Vx AND Vy
Chip8CpuInstructions::AndVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8));
}
// 0x8xy3 Set Vx = Vx XOR Vy
Chip8CpuInstructions::XorVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) ^ input.registers.peek(*y as u8));
}
// 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 as i16 + rhs as i16) as i16;
if working > 255 {
input.registers.poke(0xf, 0x01);
}
input.registers.poke(*x as u8, working as u8);
}
Chip8CpuInstructions::SubVxVy(x, y) => {
// 8xy5 - SUB Vx, Vy
// 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.
input.registers.poke(*x as u8, input.registers.peek(*x as u8) - input.registers.peek(*y as u8));
}
Chip8CpuInstructions::ShrVxVy(x, y) => {
// 8xy6 - SHR Vx {, Vy}
// Set Vx = Vx SHR 1.
//
// If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.
let initial_value = input.registers.peek(*x as u8);
if 0xb1 & initial_value == 1 {
input.registers.poke(0xf, 1);
}
input.registers.poke(*x as u8, initial_value.shr(1));
}
Chip8CpuInstructions::SubnVxVy(x, y) => {
// 8xy7 - SUBN Vx, Vy
// 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.
let y_register = input.registers.peek(*y as u8);
let x_register = input.registers.peek(*x as u8);
let new_value = if y_register > x_register { 1 } else { 0 };
input.registers.poke(0xf, new_value);
input.registers.poke(*x as u8, x_register - y_register);
}
Chip8CpuInstructions::ShlVxVy(x, y) => {
// 8xyE - SHL Vx {, Vy}
// Set Vx = Vx SHL 1.
//
// If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.
let initial_value = input.registers.peek(*x as u8);
if 0x80 & initial_value == 0x80 {
input.registers.poke(0xf, 1);
}
input.registers.poke(*x as u8, initial_value.shl(1));
}
Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => {
// 9xy0 - SNE 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.
let x_reg_value = input.registers.peek(*vx_register as u8);
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) => {
// Annn - LD I, addr
// Set I = nnn.
//
// The value of register I is set to nnn.
println!("SETTING I to {new_index}");
input.registers.poke_i(*new_index);
}
// 0xBnnn Jump to nnn+V0
Chip8CpuInstructions::JpV0Addr(addr) => {
// Bnnn - JP V0, addr
// Jump to location nnn + V0.
//
// The program counter is set to nnn plus the value of V0.
input.registers.poke_pc(input.registers.peek(0) as u16 + addr);
}
Chip8CpuInstructions::RndVxByte(x, byte) => {
// Cxkk - RND Vx, byte
// 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.
let new_value: u8 = random();
input.registers.poke(*x as u8, (new_value & *byte as u8))
}
Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => {
// 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.
//
// read nibble bytes from memory starting at I
let start_position = input.registers.peek_i();
let num_bytes_to_read = *n;
for i in start_position..start_position + num_bytes_to_read {
// let current_byte = input.memory[i as usize];
// println!("READ BYTE [{current_byte:8b}");
}
let mut did_change: bool = false;
for draw_x in 0..*n {
// let mut new_value = input.memory[(input.i_register + draw_x) as usize];
//for draw_y in 0..8 {
// }
}
if did_change {
input.registers.poke(0xf, 1u8);
} else {
input.registers.poke(0xf, 0u8);
}
}
Chip8CpuInstructions::SkpVx(x) => {
// Ex9E - SKP Vx
// 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.
let key_to_check = input.registers.peek(*x as u8);
}
Chip8CpuInstructions::SnkpVx(x) => {
// ExA1 - SKNP Vx
// 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.
let key_to_check = input.registers.peek(*x as u8);
let is_pressed = input.keypad.pressed(*x as u8);
if is_pressed {
input.registers.advance_pc();
}
}
Chip8CpuInstructions::LdVxDt(x) => {
// Fx07 - LD Vx, DT
// Set Vx = delay timer value.
//
// The value of DT is placed into Vx.
let value_to_set = input.registers.peek(*x as u8);
input.delay_timer.set_timer(value_to_set as i32);
}
Chip8CpuInstructions::LdVxK(x) => {
// Fx0A - LD Vx, K
// 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.
}
Chip8CpuInstructions::LdDtVx(source_register) => {
// Fx15 - LD DT, Vx
// Set delay timer = Vx.
//
// DT is set equal to the value of Vx.
let new_time = input.registers.peek(*source_register as u8);
input.delay_timer.set_timer(new_time as i32);
}
Chip8CpuInstructions::LdStVx(new_time) => {
input.sound_timer.set_timer(*new_time as i32);
}
Chip8CpuInstructions::AddIVx(x) => {
// Fx1E - ADD I, Vx
// Set I = I + Vx.
//
// The values of I and Vx are added, and the results are stored in I.
let base = input.registers.peek_i();
let x_value = input.registers.peek(*x as u8);
input.registers.poke_i(base + x_value as u16);
}
Chip8CpuInstructions::LdFVx(x) => {
// 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.
}
Chip8CpuInstructions::LdBVx(x) => {
// 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.
}
Chip8CpuInstructions::LdIVx(x) => {
// 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.
let offset = input.registers.peek_i();
for i in 0..*x {
input.memory.poke(offset + i, input.registers.peek(i as u8));
}
}
Chip8CpuInstructions::LdVxI(x) => {
// 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.
let offset = input.registers.peek_i();
let num_loops = input.registers.peek(*x as u8);
for index in 0..num_loops {
input.registers.poke(index, input.memory.peek(index as u16 + offset));
}
}
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
};
*input
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn smoke() {
assert!(true)
}
#[test]
fn encode_decode_test() {
assert_eq!(Chip8CpuInstructions::CLS.encode(), 0x00E0);
assert_eq!(Chip8CpuInstructions::RET.encode(), 0x00EE);
assert_eq!(Chip8CpuInstructions::SysAddr(0x123).encode(), 0x0123);
assert_eq!(Chip8CpuInstructions::JpAddr(0x234).encode(), 0x1234);
assert_eq!(Chip8CpuInstructions::CallAddr(0x345).encode(), 0x2345);
assert_eq!(Chip8CpuInstructions::SeVxByte(0x4, 0x56).encode(), 0x3456);
assert_eq!(Chip8CpuInstructions::SneVxByte(0xa, 0xbc).encode(), 0x4abc);
assert_eq!(Chip8CpuInstructions::SeVxVy(0xa, 0xb).encode(), 0x5ab0);
assert_eq!(Chip8CpuInstructions::LdVxByte(0xa, 0xff).encode(), 0x6aff);
assert_eq!(Chip8CpuInstructions::AddVxByte(0xa, 0xbc).encode(), 0x7abc);
assert_eq!(Chip8CpuInstructions::LdVxVy(0xa, 0xb).encode(), 0x8ab0);
assert_eq!(Chip8CpuInstructions::OrVxVy(0xb, 0xa).encode(), 0x8ba1);
assert_eq!(Chip8CpuInstructions::AndVxVy(0xc, 0xd).encode(), 0x8cd2);
assert_eq!(Chip8CpuInstructions::XorVxVy(0xd, 0xe).encode(), 0x8de3);
assert_eq!(Chip8CpuInstructions::AddVxVy(0xe, 0xf).encode(), 0x8ef4);
assert_eq!(Chip8CpuInstructions::SubVxVy(0xf, 0x0).encode(), 0x8f05);
assert_eq!(Chip8CpuInstructions::ShrVxVy(0x0, 0x1).encode(), 0x8016);
assert_eq!(Chip8CpuInstructions::SubnVxVy(0x1, 0x2).encode(), 0x8127);
assert_eq!(Chip8CpuInstructions::ShlVxVy(0x3, 0x4).encode(), 0x834e);
assert_eq!(Chip8CpuInstructions::SneVxVy(0xa, 0xb).encode(), 0x9ab0);
assert_eq!(Chip8CpuInstructions::LdIAddr(0x123).encode(), 0xa123);
assert_eq!(Chip8CpuInstructions::JpV0Addr(0x234).encode(), 0xb234);
assert_eq!(Chip8CpuInstructions::RndVxByte(0xa, 0xca).encode(), 0xcaca);
assert_eq!(Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4).encode(), 0xdab4);
assert_eq!(Chip8CpuInstructions::SkpVx(0x1).encode(), 0xe19e);
assert_eq!(Chip8CpuInstructions::SnkpVx(0x2).encode(), 0xe2a1);
assert_eq!(Chip8CpuInstructions::LdVxDt(0x1).encode(), 0xf107);
assert_eq!(Chip8CpuInstructions::LdVxK(0x4).encode(), 0xf40a);
assert_eq!(Chip8CpuInstructions::LdDtVx(0x6).encode(), 0xf615);
assert_eq!(Chip8CpuInstructions::LdStVx(0xb).encode(), 0xfb18);
assert_eq!(Chip8CpuInstructions::AddIVx(0xd).encode(), 0xfd1e);
assert_eq!(Chip8CpuInstructions::LdFVx(0xc).encode(), 0xfc29);
assert_eq!(Chip8CpuInstructions::LdBVx(0xd).encode(), 0xfd33);
assert_eq!(Chip8CpuInstructions::LdIVx(0xe).encode(), 0xfe55);
assert_eq!(Chip8CpuInstructions::LdVxI(0x3).encode(), 0xf365);
assert!(matches!(Chip8CpuInstructions::decode(0x00E0u16), Chip8CpuInstructions::CLS));
assert!(matches!(Chip8CpuInstructions::decode(0x00EEu16), Chip8CpuInstructions::RET));
assert!(matches!(Chip8CpuInstructions::decode(0x0123), Chip8CpuInstructions::SysAddr(0x123)));
assert!(matches!(Chip8CpuInstructions::decode(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff)));
assert!(matches!(Chip8CpuInstructions::decode(0x1002), Chip8CpuInstructions::JpAddr(0x2)));
assert!(matches!(Chip8CpuInstructions::decode(0x1FF0), Chip8CpuInstructions::JpAddr(0xFF0)));
assert!(matches!(Chip8CpuInstructions::decode(0x2002), Chip8CpuInstructions::CallAddr(0x2)));
assert!(matches!(Chip8CpuInstructions::decode(0x3123), Chip8CpuInstructions::SeVxByte(0x1, 0x23)));
assert!(matches!(Chip8CpuInstructions::decode(0x4abc), Chip8CpuInstructions::SneVxByte(0xa, 0xbc)));
assert!(matches!(Chip8CpuInstructions::decode(0x5ab0), Chip8CpuInstructions::SeVxVy(0xa, 0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0x6aff), Chip8CpuInstructions::LdVxByte(0xa, 0xff)));
assert!(matches!(Chip8CpuInstructions::decode(0x7abc), Chip8CpuInstructions::AddVxByte(0xa, 0xbc)));
assert!(matches!(Chip8CpuInstructions::decode(0x8ab0), Chip8CpuInstructions::LdVxVy(0xa, 0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0x8ba1), Chip8CpuInstructions::OrVxVy(0xb, 0xa)));
assert!(matches!(Chip8CpuInstructions::decode(0x8cd2), Chip8CpuInstructions::AndVxVy(0xc, 0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0x8de3), Chip8CpuInstructions::XorVxVy(0xd, 0xe)));
assert!(matches!(Chip8CpuInstructions::decode(0x8ef4), Chip8CpuInstructions::AddVxVy(0xe, 0xf)));
assert!(matches!(Chip8CpuInstructions::decode(0x8f05), Chip8CpuInstructions::SubVxVy(0xf, 0x0)));
assert!(matches!(Chip8CpuInstructions::decode(0x8016), Chip8CpuInstructions::ShrVxVy(0x0, 0x1)));
assert!(matches!(Chip8CpuInstructions::decode(0x8127), Chip8CpuInstructions::SubnVxVy(0x1, 0x2)));
assert!(matches!(Chip8CpuInstructions::decode(0x834e), Chip8CpuInstructions::ShlVxVy(0x3, 0x4)));
assert!(matches!(Chip8CpuInstructions::decode(0x9ab0), Chip8CpuInstructions::SneVxVy(0xa, 0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0xa123), Chip8CpuInstructions::LdIAddr(0x123)));
assert!(matches!(Chip8CpuInstructions::decode(0xb234), Chip8CpuInstructions::JpV0Addr(0x234)));
assert!(matches!(Chip8CpuInstructions::decode(0xcaca), Chip8CpuInstructions::RndVxByte(0xa, 0xca)));
assert!(matches!(Chip8CpuInstructions::decode(0xdab4), Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4)));
assert!(matches!(Chip8CpuInstructions::decode(0xe19e), Chip8CpuInstructions::SkpVx(0x1)));
assert!(matches!(Chip8CpuInstructions::decode(0xe2a1), Chip8CpuInstructions::SnkpVx(0x2)));
assert!(matches!(Chip8CpuInstructions::decode(0xf107), Chip8CpuInstructions::LdVxDt(0x1)));
assert!(matches!(Chip8CpuInstructions::decode(0xf40a), Chip8CpuInstructions::LdVxK(0x4)));
assert!(matches!(Chip8CpuInstructions::decode(0xf615), Chip8CpuInstructions::LdDtVx(0x6)));
assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LdStVx(0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0xfd1e), Chip8CpuInstructions::AddIVx(0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0xfc29), Chip8CpuInstructions::LdFVx(0xc)));
assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
}
#[test]
fn decoder_test_invalid_instructions() {
let invalid_to_encode = [
0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf,
0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0,
0xeaa2, 0xf006, 0xf008
];
for i in invalid_to_encode {
assert_eq!(Chip8CpuInstructions::decode(i).encode(), 0xffff);
assert!(matches!(Chip8CpuInstructions::decode(i), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
}
}
/// 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
let mut x = Chip8Computer::new();
}
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);
assert_eq!(x.registers.peek(1), 0x12);
assert_eq!(x.registers.peek_pc(), 0x202);
Chip8CpuInstructions::LdVxByte(2, 0x21).execute(&mut x);
assert_eq!(x.registers.peek(2), 0x21);
assert_eq!(x.registers.peek_pc(), 0x204);
}
#[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 mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x204);
Chip8CpuInstructions::AddVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x03);
}
#[test]
fn LdVxVy_test() {
// 0x8xy0 Set value of Vy in Vx
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x204);
assert_eq!(x.registers.peek(1), 0x01);
Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn OrVxVy_test() {
// 0x8xy1 Set Vx = Vx OR Vy
// 0b0101 0000 (0x50)
// | 0b0000 1010 (0x0A)
// 0b0101 1010 (0x5A)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x50).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0x0A).execute(&mut x);
Chip8CpuInstructions::OrVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x5A);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn AndVxVy_test() {
// 0x8xy2 Set Vx = Vx AND Vy
// 0b1111 1100 (0xFC)
// & 0b1100 1010 (0xCA)
// 0b1100 1000 (0xC8)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x);
Chip8CpuInstructions::AndVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xC8);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn XorVxVy_test() {
// 0x8xy3 Set Vx = Vx XOR Vy
// 0b1111 1100 (0xFC)
// ^ 0b1100 1010 (0xCA)
// 0b0011 0110 (0x36)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x);
Chip8CpuInstructions::XorVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x36);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn AddVxVy_test() {
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
// T1 T2: Judgement Test
// 0x01 0xFF
// + 0x01 0x01
// 0x02 F0 0x00 F1
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(1, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x);
// T1
assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek(1), 2);
assert_eq!(x.registers.peek_pc(), 0x208);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0xff).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0x01).execute(&mut x);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x);
// T2
assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek(1), 0);
assert_eq!(x.registers.peek_pc(), 0x208)
}
/* #[test]
fn SubVxVy_test() {
todo: this test sucks. dont have the borrow concept in here.
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.
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::SubVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208);
assert_eq!(x.registers.peek(1), 0xF);
assert_eq!(x.registers.peek(0x10), 0);
}
*/
#[test]
fn ShrVxVy_test() {
/*
Set Vx = Vx SHR 1.
If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.
*/
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0x08).execute(&mut x); // 0b0000 1000 (0x08)
Chip8CpuInstructions::LdVxByte(0x2, 0x2).execute(&mut x);
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set)
assert_eq!(x.registers.peek(1), 0x04);
assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek_pc(), 0x208);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0b00001001).execute(&mut x); // 0b0000 1001 (0x09)
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0100 (0x02) (Set)
Chip8CpuInstructions::ShrVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0b00000010);
assert_eq!(x.registers.peek(0xf), 0x1);
assert_eq!(x.registers.peek_pc(), 0x208);
}
#[test]
fn SneVxVy_test() {
// 9xy0 - SNE 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.
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0xab).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0xba).execute(&mut x);
// they are not the same. we should skip.
assert_eq!(x.registers.peek_pc(), 0x204);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208);
Chip8CpuInstructions::LdVxByte(0x02, 0xab).execute(&mut x);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20C);
}
#[test]
fn LdiAddr_test() {
// Annn - LD I, addr
// Set I = nnn.
//
// The value of register I is set to nnn.
let mut x = Chip8Computer::new();
let value_for_memory = 0xbe;
// load the value into V0
Chip8CpuInstructions::LdIAddr(0xfab).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0xfab);
}
fn JpV0Addr_test() {
// Bnnn - JP V0, addr
// Jump to location nnn + V0.
//
// The program counter is set to nnn plus the value of V0.
}
#[test]
fn RndVxByte_test() {
let mut x = Chip8Computer::new();
// generate random number masked by 0xF0;
let mask = 0xF0u8;
Chip8CpuInstructions::RndVxByte(0x0, mask as u16).execute(&mut x);
let register_value = x.registers.peek(0x0);
assert!(register_value < mask);
// generate random number masked by 0x0F;
let mask2 = 0x0Fu8;
Chip8CpuInstructions::RndVxByte(0x1, mask2 as u16).execute(&mut x);
let register_value = x.registers.peek(0x1);
assert!(register_value < mask);
}
fn DrawVxVyNibble_test() {}
fn SkpVx_test() {
// skip if key pressed
}
fn SnKpVx_test() {
// skip key not pressed
}
#[test]
fn LdVxDt_test() {
// delay timer reading
let mut x = Chip8Computer::new();
// set the value we want in the timer to V0...
Chip8CpuInstructions::LdVxByte(0x0, 0x10).execute(&mut x);
// ...then tell the CPU to use that value for the timer.
Chip8CpuInstructions::LdVxDt(0x0).execute(&mut x);
x.delay_timer.tick();
x.delay_timer.tick();
x.delay_timer.tick();
assert_eq!(x.delay_timer.current(), 0xd);
for i in 0..0x10 {
x.delay_timer.tick();
}
assert_eq!(x.delay_timer.current(), 0x00);
x.delay_timer.tick();
assert_eq!(x.delay_timer.current(), 0x00);
}
fn LdVxK_test() {
// 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.
}
#[test]
fn LdStVx_test() {
// sound timer setting
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdStVx(0x10).execute(&mut x);
// tick from 0x8 to 0x1
for i in 0..6 { x.sound_timer.tick(); }
assert_eq!(x.sound_timer.current(), 0xA);
}
fn LdIVx_test() {
// 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.
}
fn LdVxI_test() {}
// 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.
/*
#[test]
fn LdDtVx_test() {
// delay timer setting
let mut x = Chip8Computer::new();
// lets set our delay timer...
Chip8CpuInstructions::LdVxByte(0x0, 0x80).execute(&mut x);
Chip8CpuInstructions::LdDtVx(0x0).execute(&mut x);
// now that we have our timer set to 0x80 we should tick it 0x10 times
// so we are then down to 0x70
for i in 0..0x10 {
x.delay_timer.tick();
}
// Then tell the CPU to copy that timer over into our V0
Chip8CpuInstructions::LdVxK(0x0).execute(&mut x);
let register_value = x.registers.peek(0);
// assert_eq!(register_value, 0x70);
}
*/
}