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