Add sdl2 handlers and bunch of tests
First semi-working state, games can be loaded and they open a sdl2 window \o/ TODOS: + Stacks offset is wrong somewhere + Input not working (maybe because of above reason?) + Add mising tests
This commit is contained in:
29
src/cartridge.rs
Normal file
29
src/cartridge.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
const BUFFER_SIZE: usize = 3584;
|
||||||
|
|
||||||
|
pub struct Cartridge {
|
||||||
|
pub rom: [u8; BUFFER_SIZE],
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cartridge {
|
||||||
|
pub fn new(filename: &str) -> Self {
|
||||||
|
let mut file = File::open(filename).expect("Error while opening file!");
|
||||||
|
let mut buffer = [0u8; BUFFER_SIZE];
|
||||||
|
|
||||||
|
// either read a byte, or noting (0)
|
||||||
|
let bytes = if let Ok(bytes) = file.read(&mut buffer) {
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
Cartridge {
|
||||||
|
rom: buffer,
|
||||||
|
size: bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/display.rs
Normal file
52
src/display.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use sdl2;
|
||||||
|
use sdl2::{render::Canvas, video::Window, pixels, rect::Rect};
|
||||||
|
|
||||||
|
pub struct Display {
|
||||||
|
canvas: Canvas<Window>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display {
|
||||||
|
pub fn new(sdl_ctx: &sdl2::Sdl) -> Self {
|
||||||
|
let video = sdl_ctx.video().unwrap();
|
||||||
|
let window = video
|
||||||
|
.window(
|
||||||
|
"FooBar",
|
||||||
|
(crate::SCREEN_WIDTH * crate::SCREEN_SCALE) as u32,
|
||||||
|
(crate::SCREEN_HEIGHT * crate::SCREEN_SCALE) as u32)
|
||||||
|
.position_centered()
|
||||||
|
.opengl()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut canvas = window
|
||||||
|
.into_canvas()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
canvas.set_draw_color(pixels::Color::RGB(0, 0, 0));
|
||||||
|
canvas.clear();
|
||||||
|
canvas.present();
|
||||||
|
|
||||||
|
Display {
|
||||||
|
canvas,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, pixel: &[[u8; crate::SCREEN_WIDTH]; crate::SCREEN_HEIGHT]) {
|
||||||
|
for (y, &row) in pixel.iter().enumerate() {
|
||||||
|
for (x, &column) in row.iter().enumerate() {
|
||||||
|
self.canvas.set_draw_color(
|
||||||
|
if column == 1 {
|
||||||
|
pixels::Color::RGB(255, 255, 255)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pixels::Color::RGB(0, 0, 0)
|
||||||
|
});
|
||||||
|
let _ = self.canvas
|
||||||
|
.fill_rect(
|
||||||
|
Rect::new((x * crate::SCREEN_SCALE) as i32, (y * crate::SCREEN_SCALE) as i32, crate::SCREEN_SCALE as u32, crate::SCREEN_SCALE as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.canvas.present();
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/input.rs
Normal file
61
src/input.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
use sdl2;
|
||||||
|
use sdl2::{event::Event, keyboard::Keycode};
|
||||||
|
|
||||||
|
pub struct Input {
|
||||||
|
events: sdl2::EventPump,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn new(sdl_ctx: &sdl2::Sdl) -> Self {
|
||||||
|
Input {
|
||||||
|
events: sdl_ctx.event_pump().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch(&mut self) -> Result<[bool; 16], ()> {
|
||||||
|
// error if we don't get input
|
||||||
|
for event in self.events.poll_iter() {
|
||||||
|
if let Event::Quit { .. } = event {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all inputs
|
||||||
|
let input: Vec<Keycode> = self.events
|
||||||
|
.keyboard_state()
|
||||||
|
.pressed_scancodes()
|
||||||
|
.filter_map(Keycode::from_scancode)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// set all no not-pressed as default
|
||||||
|
let mut keys = [false; 16];
|
||||||
|
|
||||||
|
// map to internal values
|
||||||
|
for key in input {
|
||||||
|
let index = match key {
|
||||||
|
Keycode::Num1 => Some(0x1),
|
||||||
|
Keycode::Num2 => Some(0x2),
|
||||||
|
Keycode::Num3 => Some(0x3),
|
||||||
|
Keycode::Num4 => Some(0xc),
|
||||||
|
Keycode::Q => Some(0x4),
|
||||||
|
Keycode::W => Some(0x5),
|
||||||
|
Keycode::E => Some(0x6),
|
||||||
|
Keycode::R => Some(0xd),
|
||||||
|
Keycode::A => Some(0x7),
|
||||||
|
Keycode::S => Some(0x8),
|
||||||
|
Keycode::D => Some(0x9),
|
||||||
|
Keycode::F => Some(0xe),
|
||||||
|
Keycode::Z => Some(0xa),
|
||||||
|
Keycode::X => Some(0x0),
|
||||||
|
Keycode::C => Some(0xb),
|
||||||
|
Keycode::V => Some(0xf),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(pos) = index {
|
||||||
|
keys[pos] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main.rs
17
src/main.rs
@@ -5,12 +5,22 @@ use std::env;
|
|||||||
|
|
||||||
mod processor;
|
mod processor;
|
||||||
mod fontset;
|
mod fontset;
|
||||||
|
mod display;
|
||||||
|
mod input;
|
||||||
|
mod cartridge;
|
||||||
|
|
||||||
use crate::processor::Processor;
|
use crate::processor::Processor;
|
||||||
|
use crate::cartridge::Cartridge;
|
||||||
|
|
||||||
|
const MEMORY_SIZE: usize = 4096;
|
||||||
|
const GAME_ENTRY: usize = 0x200; // most games load into 0x200
|
||||||
|
const SCREEN_WIDTH: usize = 64;
|
||||||
|
const SCREEN_HEIGHT: usize = 32;
|
||||||
|
const SCREEN_SCALE: usize = 20;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cartridge = env::args().nth(1);
|
let game_file = env::args().nth(1);
|
||||||
match cartridge {
|
match game_file {
|
||||||
Some(_) => println!("Found a cartridge file! Trying to load..."),
|
Some(_) => println!("Found a cartridge file! Trying to load..."),
|
||||||
None => {
|
None => {
|
||||||
println!("No cartridge file found! Exiting!");
|
println!("No cartridge file found! Exiting!");
|
||||||
@@ -21,7 +31,8 @@ fn main() {
|
|||||||
let mut processor = Processor::new();
|
let mut processor = Processor::new();
|
||||||
|
|
||||||
// load cartridge file
|
// load cartridge file
|
||||||
|
let cartridge = Cartridge::new(&game_file.unwrap());
|
||||||
|
|
||||||
processor.start();
|
processor.start(&cartridge.rom);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
308
src/processor.rs
308
src/processor.rs
@@ -1,21 +1,21 @@
|
|||||||
|
// TODO add all comments
|
||||||
|
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::fontset::FONT;
|
use crate::fontset::FONT;
|
||||||
|
use crate::display::Display;
|
||||||
|
use crate::input::Input;
|
||||||
|
|
||||||
const MEMORY_SIZE: usize = 4096;
|
|
||||||
const GAME_ENTRY: usize = 0x200; // most games load into 0x200
|
|
||||||
const SCREEN_WIDTH: usize = 64;
|
|
||||||
const SCREEN_HEIGHT: usize = 32;
|
|
||||||
const OPCODE_SIZE: usize = 2;
|
const OPCODE_SIZE: usize = 2;
|
||||||
|
|
||||||
pub struct Processor {
|
pub struct Processor {
|
||||||
memory: [u8; MEMORY_SIZE],
|
memory: [u8; crate::MEMORY_SIZE],
|
||||||
register: [u8; 16],
|
register: [u8; 16],
|
||||||
index: usize, // used to store memory addresses
|
index: usize, // used to store memory addresses
|
||||||
pc: usize, // programm counter
|
pc: usize, // programm counter
|
||||||
screen: [[u8; SCREEN_WIDTH]; SCREEN_HEIGHT],
|
screen: [[u8; crate::SCREEN_WIDTH]; crate::SCREEN_HEIGHT],
|
||||||
draw_flag: bool, // redraw screen if true
|
draw_flag: bool, // redraw screen if true
|
||||||
delay_timer: usize,
|
delay_timer: usize,
|
||||||
sound_timer: usize,
|
sound_timer: usize,
|
||||||
@@ -28,7 +28,7 @@ pub struct Processor {
|
|||||||
|
|
||||||
impl Processor {
|
impl Processor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut mem = [0; MEMORY_SIZE];
|
let mut mem = [0; crate::MEMORY_SIZE];
|
||||||
// load font
|
// load font
|
||||||
for (pos, &val) in FONT.iter().enumerate() {
|
for (pos, &val) in FONT.iter().enumerate() {
|
||||||
mem[pos] = val;
|
mem[pos] = val;
|
||||||
@@ -38,8 +38,8 @@ impl Processor {
|
|||||||
memory: mem,
|
memory: mem,
|
||||||
register: [0; 16],
|
register: [0; 16],
|
||||||
index: 0,
|
index: 0,
|
||||||
pc: GAME_ENTRY,
|
pc: crate::GAME_ENTRY,
|
||||||
screen: [[0; SCREEN_WIDTH]; SCREEN_HEIGHT],
|
screen: [[0; crate::SCREEN_WIDTH]; crate::SCREEN_HEIGHT],
|
||||||
draw_flag: false,
|
draw_flag: false,
|
||||||
delay_timer: 0,
|
delay_timer: 0,
|
||||||
sound_timer: 0,
|
sound_timer: 0,
|
||||||
@@ -51,18 +51,23 @@ impl Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cartridge needed
|
pub fn start(&mut self, game: &[u8]) {
|
||||||
pub fn start(&mut self) {
|
|
||||||
let sdl_ctx = sdl2::init().unwrap();
|
let sdl_ctx = sdl2::init().unwrap();
|
||||||
|
let mut display = Display::new(&sdl_ctx);
|
||||||
|
let mut input = Input::new(&sdl_ctx);
|
||||||
|
|
||||||
|
self.load_game(&game);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// get keyinput using sdl2
|
// get keyinput using sdl2
|
||||||
|
self.key = input.fetch().unwrap();
|
||||||
|
|
||||||
// emulate one cycle
|
// emulate one cycle
|
||||||
self.cycle();
|
self.cycle();
|
||||||
|
|
||||||
// draw to screen using sdl2
|
// draw to screen using sdl2
|
||||||
if self.draw_flag {
|
if self.draw_flag {
|
||||||
|
display.draw(&self.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// play sound using sdl2
|
// play sound using sdl2
|
||||||
@@ -72,7 +77,6 @@ impl Processor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cycle(&mut self) {
|
pub fn cycle(&mut self) {
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
self.draw_flag = false;
|
self.draw_flag = false;
|
||||||
|
|
||||||
@@ -102,10 +106,9 @@ impl Processor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_game(&mut self, game: &[u8]) {
|
pub fn load_game(&mut self, game: &[u8]) {
|
||||||
// load game
|
|
||||||
for (pos, &val) in game.iter().enumerate() {
|
for (pos, &val) in game.iter().enumerate() {
|
||||||
let position = GAME_ENTRY + pos;
|
let position = crate::GAME_ENTRY + pos;
|
||||||
if position < MEMORY_SIZE { // don't go above mem limit
|
if position < crate::MEMORY_SIZE { // don't go above mem limit
|
||||||
self.memory[position] = val;
|
self.memory[position] = val;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -122,11 +125,10 @@ impl Processor {
|
|||||||
pub fn decode_opcode(&mut self, opcode: u16) {
|
pub fn decode_opcode(&mut self, opcode: u16) {
|
||||||
// values from http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#3.0
|
// values from http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#3.0
|
||||||
|
|
||||||
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
|
||||||
let nibbles = (
|
let nibbles = (
|
||||||
(opcode & 0xF000 >> 12) as usize,
|
((opcode & 0xF000) >> 12) as usize,
|
||||||
(opcode & 0x0F00 >> 8) as usize,
|
((opcode & 0x0F00) >> 8) as usize,
|
||||||
(opcode & 0x00F0 >> 4) as usize,
|
((opcode & 0x00F0) >> 4) as usize,
|
||||||
(opcode & 0x000F) as usize
|
(opcode & 0x000F) as usize
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -187,7 +189,8 @@ impl Processor {
|
|||||||
|
|
||||||
// Clear screen
|
// Clear screen
|
||||||
fn code_00e0(&mut self) {
|
fn code_00e0(&mut self) {
|
||||||
self.screen = [[0; SCREEN_WIDTH]; SCREEN_HEIGHT];
|
self.screen = [[0; crate::SCREEN_WIDTH]; crate::SCREEN_HEIGHT];
|
||||||
|
self.draw_flag = true;
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +207,8 @@ impl Processor {
|
|||||||
|
|
||||||
// Call subroutine at nnn
|
// Call subroutine at nnn
|
||||||
fn code_2nnn(&mut self, nnn: usize) {
|
fn code_2nnn(&mut self, nnn: usize) {
|
||||||
self.sp += 1;
|
|
||||||
self.stack[self.sp] = self.pc;
|
self.stack[self.sp] = self.pc;
|
||||||
|
self.sp += 1;
|
||||||
self.pc = nnn;
|
self.pc = nnn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +250,8 @@ impl Processor {
|
|||||||
|
|
||||||
// Set Vx = Vx + kk
|
// Set Vx = Vx + kk
|
||||||
fn code_7xkk(&mut self, x: usize, kk: u8) {
|
fn code_7xkk(&mut self, x: usize, kk: u8) {
|
||||||
self.register[x] = self.register[x] + kk;
|
let val = self.register[x] as u16;
|
||||||
|
self.register[x] = (val + kk as u16) as u8;
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +294,7 @@ impl Processor {
|
|||||||
let y_val = self.register[y];
|
let y_val = self.register[y];
|
||||||
|
|
||||||
self.register[0x0f] = (x_val > y_val) as u8;
|
self.register[0x0f] = (x_val > y_val) as u8;
|
||||||
self.register[x] = (x_val - y_val) as u8;
|
self.register[x] = x_val.wrapping_sub(y_val) as u8;
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,14 +308,14 @@ impl Processor {
|
|||||||
// Set Vx = Vy - Vx, set VF = NOT borrow
|
// Set Vx = Vy - Vx, set VF = NOT borrow
|
||||||
fn code_8xy7(&mut self, x: usize, y: usize) {
|
fn code_8xy7(&mut self, x: usize, y: usize) {
|
||||||
self.register[0x0f] = (self.register[y] > self.register[x]) as u8;
|
self.register[0x0f] = (self.register[y] > self.register[x]) as u8;
|
||||||
self.register[x] = self.register[y] - self.register[x];
|
self.register[x] = self.register[y].wrapping_sub(self.register[x]);
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Vx = Vx SHL 1
|
// Set Vx = Vx SHL 1
|
||||||
fn code_8xye(&mut self, x: usize, _y: usize) {
|
fn code_8xye(&mut self, x: usize, _y: usize) {
|
||||||
self.register[0x0f] = (self.register[x] & 0b10000000) >> 7;
|
self.register[0x0f] = (self.register[x] & 0b10000000) >> 7;
|
||||||
self.register[x] = self.register[x] * 2;
|
self.register[x] <<= 1;
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,19 +349,18 @@ impl Processor {
|
|||||||
|
|
||||||
// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
|
// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
|
||||||
fn code_dxyn(&mut self, x: usize, y: usize, n: usize) {
|
fn code_dxyn(&mut self, x: usize, y: usize, n: usize) {
|
||||||
// TODO
|
|
||||||
self.register[0x0f] = 0;
|
self.register[0x0f] = 0;
|
||||||
for byte in 0..n {
|
for byte in 0..n {
|
||||||
// get y coord, which we want to draw -> use modulo, so we don't overlap
|
// get y coord, which we want to draw -> use modulo, so we don't overlap
|
||||||
let y = (self.register[y] as usize + byte) % SCREEN_HEIGHT;
|
let y = (self.register[y] as usize + byte) % crate::SCREEN_HEIGHT;
|
||||||
for bit in 0..8 {
|
for bit in 0..8 {
|
||||||
// get x coord, just as above
|
// get x coord, just as above
|
||||||
let x = (self.register[x] as usize + bit) % SCREEN_WIDTH;
|
let x = (self.register[x] as usize + bit) % crate::SCREEN_WIDTH;
|
||||||
// bit hack to get every bit in a row
|
// bit hack to get every bit in a row
|
||||||
let pixel_to_draw = (self.memory[self.index + byte] >> (7 - bit)) & 1;
|
let pixel_to_draw = (self.memory[self.index + byte] >> (7 - bit)) & 1;
|
||||||
// check if we will overwrite an existing pixel
|
// check if we will overwrite an existing pixel
|
||||||
self.register[0x0f] = self.register[0x0f] | (pixel_to_draw & self.screen[x][y]);
|
self.register[0x0f] = self.register[0x0f] | (pixel_to_draw & self.screen[y][x]);
|
||||||
self.screen[x][y] = self.screen[x][y] ^ pixel_to_draw;
|
self.screen[y][x] = self.screen[y][x] ^ pixel_to_draw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.draw_flag = true;
|
self.draw_flag = true;
|
||||||
@@ -435,7 +438,7 @@ impl Processor {
|
|||||||
// Store registers V0 through Vx in memory starting at location I
|
// Store registers V0 through Vx in memory starting at location I
|
||||||
fn code_fx55(&mut self, x: usize) {
|
fn code_fx55(&mut self, x: usize) {
|
||||||
// TODO offset correct?
|
// TODO offset correct?
|
||||||
for reg_i in 0..x {
|
for reg_i in 0..x + 1 {
|
||||||
self.memory[self.index + reg_i] = self.register[reg_i];
|
self.memory[self.index + reg_i] = self.register[reg_i];
|
||||||
}
|
}
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
@@ -443,9 +446,250 @@ impl Processor {
|
|||||||
|
|
||||||
// Read registers V0 through Vx from memory starting at location I
|
// Read registers V0 through Vx from memory starting at location I
|
||||||
fn code_fx65(&mut self, x: usize) {
|
fn code_fx65(&mut self, x: usize) {
|
||||||
for reg_i in 0..x {
|
for reg_i in 0..x + 1 {
|
||||||
self.register[reg_i] = self.memory[self.index + reg_i];
|
self.register[reg_i] = self.memory[self.index + reg_i];
|
||||||
}
|
}
|
||||||
self.pc += OPCODE_SIZE;
|
self.pc += OPCODE_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests{
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn new_processor() -> Processor {
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
processor.register = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8];
|
||||||
|
processor
|
||||||
|
}
|
||||||
|
|
||||||
|
const ENTRY: usize = crate::GAME_ENTRY;
|
||||||
|
const SKIP: usize = ENTRY + OPCODE_SIZE * 2;
|
||||||
|
const NEXT: usize = ENTRY + OPCODE_SIZE;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_initial_state() {
|
||||||
|
let processor = Processor::new();
|
||||||
|
assert_eq!(processor.pc, 0x200);
|
||||||
|
assert_eq!(processor.sp, 0);
|
||||||
|
assert_eq!(processor.stack, [0; 16]);
|
||||||
|
// Font loading
|
||||||
|
assert_eq!(processor.memory[0..5], [0xF0, 0x90, 0x90, 0x90, 0xF0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_game() {
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
processor.load_game(&[1, 2, 3]);
|
||||||
|
assert_eq!(processor.memory[crate::GAME_ENTRY], 1);
|
||||||
|
assert_eq!(processor.memory[crate::GAME_ENTRY + 1], 2);
|
||||||
|
assert_eq!(processor.memory[crate::GAME_ENTRY + 2], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_00e0() {
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
processor.screen = [[1; crate::SCREEN_WIDTH]; crate::SCREEN_HEIGHT];
|
||||||
|
processor.decode_opcode(0x00e0);
|
||||||
|
for y in 0..crate::SCREEN_HEIGHT {
|
||||||
|
for x in 0..crate::SCREEN_WIDTH {
|
||||||
|
assert_eq!(processor.screen[y][x], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_00ee() {
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
processor.sp = 3;
|
||||||
|
processor.stack[2] = 0x1337;
|
||||||
|
processor.decode_opcode(0x00ee);
|
||||||
|
assert_eq!(processor.sp, 2);
|
||||||
|
assert_eq!(processor.pc, 0x1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_1nnn() {
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
processor.decode_opcode(0x1222);
|
||||||
|
assert_eq!(processor.pc, 0x0222);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_2nnn() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x2333);
|
||||||
|
assert_eq!(processor.sp, 1);
|
||||||
|
assert_eq!(processor.pc, 0x0333);
|
||||||
|
assert_eq!(processor.stack[0], NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_3xkk() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x3202);
|
||||||
|
assert_eq!(processor.pc, SKIP);
|
||||||
|
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x3206);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_4xkk() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x3206);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x3202);
|
||||||
|
assert_eq!(processor.pc, SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_5xy0() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x5010);
|
||||||
|
assert_eq!(processor.pc, SKIP);
|
||||||
|
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x5070);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_6xkk() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x6133);
|
||||||
|
assert_eq!(processor.register[1], 0x33);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_7xkk() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x7001);
|
||||||
|
assert_eq!(processor.register[0], 0x02);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy0() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8f00);
|
||||||
|
assert_eq!(processor.register[0], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy1() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8011);
|
||||||
|
assert_eq!(processor.register[0], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy2() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8142);
|
||||||
|
assert_eq!(processor.register[1], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy3() {
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8143);
|
||||||
|
assert_eq!(processor.register[2], 2);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy4() {
|
||||||
|
// no carry
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8124);
|
||||||
|
assert_eq!(processor.register[1], 3);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
// carry
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.register[2] = 254;
|
||||||
|
processor.decode_opcode(0x8324);
|
||||||
|
assert_eq!(processor.register[1], 1);
|
||||||
|
assert_eq!(processor.register[0x0f], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy5() {
|
||||||
|
// set carry
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8205);
|
||||||
|
assert_eq!(processor.register[2], 1);
|
||||||
|
assert_eq!(processor.register[0x0f], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
// don't set carry
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8065);
|
||||||
|
assert_eq!(processor.register[0], 253);
|
||||||
|
assert_eq!(processor.register[0x0f], 0);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy6() {
|
||||||
|
// set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8416);
|
||||||
|
assert_eq!(processor.register[0x0f], 1);
|
||||||
|
assert_eq!(processor.register[4], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
// don't set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8216);
|
||||||
|
assert_eq!(processor.register[0x0f], 0);
|
||||||
|
assert_eq!(processor.register[2], 1);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xy7() {
|
||||||
|
// set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8937);
|
||||||
|
assert_eq!(processor.register[0x0f], 0);
|
||||||
|
assert_eq!(processor.register[9], 253);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
// don't set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x8397);
|
||||||
|
assert_eq!(processor.register[0x0f], 1);
|
||||||
|
assert_eq!(processor.register[3], 3);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_code_8xye() {
|
||||||
|
// set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.register[0] = 0b10000000;
|
||||||
|
processor.decode_opcode(0x801e);
|
||||||
|
assert_eq!(processor.register[0x0f], 1);
|
||||||
|
assert_eq!(processor.register[0], 0);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
|
||||||
|
// don't set VF
|
||||||
|
let mut processor = new_processor();
|
||||||
|
processor.decode_opcode(0x801e);
|
||||||
|
assert_eq!(processor.register[0x0f], 0);
|
||||||
|
assert_eq!(processor.register[0], 2);
|
||||||
|
assert_eq!(processor.pc, NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user