backend refactoring ;-;

This commit is contained in:
magnusKue
2024-10-15 03:44:50 +02:00
parent b45b22bb6f
commit 165e2004e0
2 changed files with 375 additions and 315 deletions
+20
View File
@@ -0,0 +1,20 @@
pub const FONTSET_SIZE: usize = 80;
pub const FONTSET: [u8; FONTSET_SIZE] = [
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
];
+355 -315
View File
@@ -1,27 +1,7 @@
use rand::Rng;
const FONTSET_SIZE: usize = 80;
const FONTSET: [u8; FONTSET_SIZE] = [
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
];
mod font;
use font::*;
pub const RAM_SIZE: usize = 4096;
pub const REGISTER_COUNT: usize = 16;
@@ -120,12 +100,12 @@ impl Emu {
// STACK
fn push(&mut self, val:u16) {
fn stack_push(&mut self, val:u16) {
self.stack[self.stack_pointer as usize] = val;
self.stack_pointer += 1;
}
fn pop(&mut self) -> u16 {
fn stack_pop(&mut self) -> u16 {
self.stack_pointer -= 1;
self.stack[self.stack_pointer as usize]
}
@@ -158,303 +138,363 @@ impl Emu {
let digit2 = (op & 0x0F00) >> 8;
let digit3 = (op & 0x00F0) >> 4;
let digit4 = op & 0x000F;
let x = digit2 as usize;
let y = digit3 as usize;
let mut framebuffer_modified = false;
let nnn = op & 0xFFF;
let nn = op & 0xFF;
let n = op & 0xF;
match (digit1, digit2, digit3, digit4) {
// NOP
(0, 0, 0, 0) => return false,
// CLS
(0, 0, 0xE, 0) => {
self.screen = [false; SCREEN_WIDTH * SCREEN_HEIGHT];
},
// RET
(0, 0, 0xE, 0xE) => {
let ret_addr = self.pop();
self.programm_counter = ret_addr;
},
// JMP NNN
(1, _, _, _) => {
let nnn = op & 0xFFF;
self.programm_counter = nnn;
},
// CALL NNN
(2, _, _, _) => {
let nnn = op & 0xFFF;
self.push(self.programm_counter);
self.programm_counter = nnn;
},
// SKIP VX == NN
(3, _, _, _) => {
let x = digit2 as usize;
let nn = (op & 0xFF) as u8;
if self.registers[x] == nn {
self.programm_counter += 2;
}
},
// SKIP VX != NN
(4, _, _, _) => {
let x = digit2 as usize;
let nn = (op & 0xFF) as u8;
if self.registers[x] != nn {
self.programm_counter += 2;
}
},
// SKIP VX == VY
(5, _, _, _) => {
let x = digit2 as usize;
let y = digit3 as usize;
if self.registers[x] == self.registers[y] {
self.programm_counter += 2;
}
},
// VX = NN
(6, _, _, _) => {
let x = digit2 as usize;
let nn = (op & 0xFF) as u8;
self.registers[x] = nn;
},
// VX += NN
(7, _, _, _) => {
let x = digit2 as usize;
let nn = (op & 0xFF) as u8;
self.registers[x] = self.registers[x].wrapping_add(nn);
},
// VX = VY
(8, _, _, 0) => {
let x = digit2 as usize;
let y = digit3 as usize;
self.registers[x] = self.registers[y];
},
// VX |= VY
(8, _, _, 1) => {
let x = digit2 as usize;
let y = digit3 as usize;
self.registers[x] |= self.registers[y];
},
// VX &= VY
(8, _, _, 2) => {
let x = digit2 as usize;
let y = digit3 as usize;
self.registers[x] &= self.registers[y];
},
// VX ^= VY
(8, _, _, 3) => {
let x = digit2 as usize;
let y = digit3 as usize;
self.registers[x] ^= self.registers[y];
},
// VX += VY
(8, _, _, 4) => {
let x = digit2 as usize;
let y = digit3 as usize;
let (new_vx, carry) = self.registers[x].overflowing_add(self.registers[y]);
let new_vf = if carry { 1 } else { 0 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
},
// VX -= VY
(8, _, _, 5) => {
let x = digit2 as usize;
let y = digit3 as usize;
let (new_vx, borrow) = self.registers[x].overflowing_sub(self.registers[y]);
let new_vf = if borrow { 0 } else { 1 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
},
// VX >>= 1
(8, _, _, 6) => {
let x = digit2 as usize;
let lsb = self.registers[x] & 1;
self.registers[x] >>= 1;
self.registers[0xF] = lsb;
},
// VX = VY - VX
(8, _, _, 7) => {
let x = digit2 as usize;
let y = digit3 as usize;
let (new_vx, borrow) = self.registers[y].overflowing_sub(self.registers[x]);
let new_vf = if borrow { 0 } else { 1 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
},
// VX <<= 1
(8, _, _, 0xE) => {
let x = digit2 as usize;
let msb = (self.registers[x] >> 7) & 1;
self.registers[x] <<= 1;
self.registers[0xF] = msb;
},
// SKIP VX != VY
(9, _, _, 0) => {
let x = digit2 as usize;
let y = digit3 as usize;
if self.registers[x] != self.registers[y] {
self.programm_counter += 2;
}
},
// I = NNN
(0xA, _, _, _) => {
let nnn = op & 0xFFF;
self.i_register = nnn;
},
// JMP V0 + NNN
(0xB, _, _, _) => {
let nnn = op & 0xFFF;
self.programm_counter = (self.registers[0] as u16) + nnn;
},
// VX = rand() & NN
(0xC, _, _, _) => {
let x = digit2 as usize;
let nn = (op & 0xFF) as u8;
let rng: u8 = rand::thread_rng().gen();
self.registers[x] = rng & nn;
},
// DRAW
(0xD, _, _, _) => {
// Get the (x, y) coords for our sprite
let x_coord = self.registers[digit2 as usize] as u16;
let y_coord = self.registers[digit3 as usize] as u16;
// The last digit determines how many rows high our sprite is
let num_rows = digit4;
// Keep track if any pixels were flipped
let mut flipped = false;
// Iterate over each row of our sprite
for y_line in 0..num_rows {
// Determine which memory address our row's data is stored
let addr = self.i_register + y_line as u16;
let pixels = self.ram[addr as usize];
// Iterate over each column in our row
for x_line in 0..8 {
// Use a mask to fetch current pixel's bit. Only flip if a 1
if (pixels & (0b1000_0000 >> x_line)) != 0 {
// Sprites should wrap around screen, so apply modulo
let x = (x_coord + x_line) as usize % SCREEN_WIDTH;
let y = (y_coord + y_line) as usize % SCREEN_HEIGHT;
// Get our pixel's index in the 1D screen array
let idx = x + SCREEN_WIDTH * y;
// Check if we're about to flip the pixel and set
flipped |= self.screen[idx];
self.screen[idx] ^= true;
}
}
}
// Populate VF register
if flipped {
self.registers[0xF] = 1;
} else {
self.registers[0xF] = 0;
}
framebuffer_modified = true;
},
// SKIP KEY PRESS
(0xE, _, 9, 0xE) => {
let x = digit2 as usize;
let vx = self.registers[x];
let key = self.pressed_keys[vx as usize];
if key {
self.programm_counter += 2;
}
},
// SKIP KEY RELEASE
(0xE, _, 0xA, 1) => {
let x = digit2 as usize;
let vx = self.registers[x];
let key = self.pressed_keys[vx as usize];
if !key {
self.programm_counter += 2;
}
},
// VX = DT
(0xF, _, 0, 7) => {
let x = digit2 as usize;
self.registers[x] = self.delay_timer;
},
// WAIT KEY
(0xF, _, 0, 0xA) => {
let x = digit2 as usize;
let mut pressed = false;
for i in 0..self.pressed_keys.len() {
if self.pressed_keys[i] {
self.registers[x] = i as u8;
pressed = true;
break;
}
}
if !pressed {
// Redo opcode
self.programm_counter -= 2;
}
},
// DT = VX
(0xF, _, 1, 5) => {
let x = digit2 as usize;
self.delay_timer = self.registers[x];
},
// ST = VX
(0xF, _, 1, 8) => {
let x = digit2 as usize;
self.sound_timer = self.registers[x];
},
// I += VX
(0xF, _, 1, 0xE) => {
let x = digit2 as usize;
let vx = self.registers[x] as u16;
self.i_register = self.i_register.wrapping_add(vx);
},
// I = FONT
(0xF, _, 2, 9) => {
let x = digit2 as usize;
let c = self.registers[x] as u16;
self.i_register = c * 5;
},
// BCD
(0xF, _, 3, 3) => {
let x = digit2 as usize;
let vx = self.registers[x] as f32;
// Fetch the hundreds digit by dividing by 100 and tossing the decimal
let hundreds = (vx / 100.0).floor() as u8;
// Fetch the tens digit by dividing by 10, tossing the ones digit and the decimal
let tens = ((vx / 10.0) % 10.0).floor() as u8;
// Fetch the ones digit by tossing the hundreds and the tens
let ones = (vx % 10.0) as u8;
self.ram[self.i_register as usize] = hundreds;
self.ram[(self.i_register + 1) as usize] = tens;
self.ram[(self.i_register + 2) as usize] = ones;
},
// STORE V0 - VX
(0xF, _, 5, 5) => {
let x = digit2 as usize;
let i = self.i_register as usize;
for idx in 0..=x {
self.ram[i + idx] = self.registers[idx];
}
},
// LOAD V0 - VX
(0xF, _, 6, 5) => {
let x = digit2 as usize;
let i = self.i_register as usize;
for idx in 0..=x {
self.registers[idx] = self.ram[i + idx];
}
},
(0, 0, 0, 0) => self.op_0000(),
(0, 0, 0xE, 0) => self.op_00e0(),
(0, 0, 0xE, 0xE) => self.op_00ee(),
(1, _, _, _) => self.op_1nnn(nnn),
(2, _, _, _) => self.op_2nnn(nnn),
(3, _, _, _) => self.op_3xnn(x, nn),
(4, _, _, _) => self.op_4xnn(x, nn),
(5, _, _, _) => self.op_5xy0(x, y),
(6, _, _, _) => self.op_6xnn(x, nn),
(7, _, _, _) => self.op_7xnn(x, nn),
(8, _, _, 0) => self.op_8xy0(x, y),
(8, _, _, 1) => self.op_8xy1(x, y),
(8, _, _, 2) => self.op_8xy2(x, y),
(8, _, _, 3) => self.op_8xy3(x, y),
(8, _, _, 4) => self.op_8xy4(x, y),
(8, _, _, 5) => self.op_8xy5(x, y),
(8, _, _, 6) => self.op_8xy6(x, y),
(8, _, _, 7) => self.op_8xy7(x, y),
(8, _, _, 0xE) => self.op_8xye(x, y),
(9, _, _, 0) => self.op_9xy0(x, y),
(0xA, _, _, _) => self.op_annn(nnn),
(0xB, _, _, _) => self.op_bnnn(nnn),
(0xC, _, _, _) => self.op_cxnn(x, nn),
(0xD, _, _, _) => self.op_dxyn(x, y, n),
(0xE, _, 9, 0xE) => self.op_ex9e(x),
(0xE, _, 0xA, 1) => self.op_exa1(x),
(0xF, _, 0, 7) => self.op_fx07(x),
(0xF, _, 0, 0xA) => self.op_fx0a(x),
(0xF, _, 1, 5) => self.op_fx15(x),
(0xF, _, 1, 8) => self.op_fx18(x),
(0xF, _, 1, 0xE) => self.op_fx1e(x),
(0xF, _, 2, 9) => self.op_fx29(x),
(0xF, _, 3, 3) => self.op_fx33(x),
(0xF, _, 5, 5) => self.op_fx55(x),
(0xF, _, 6, 5) => self.op_fx65(x),
(_, _, _, _) => unimplemented!("Unimplemented opcode: {:#04x}", op),
}
framebuffer_modified
}
// NOP
fn op_0000(&self) -> bool { false }
// CLS
fn op_00e0(&mut self) -> bool {
self.screen = [false; SCREEN_WIDTH * SCREEN_HEIGHT];
false
}
// RETURN FROM SUBROUTINE
fn op_00ee(&mut self) -> bool {
let ret_addr = self.stack_pop();
self.programm_counter = ret_addr;
false
}
// JUMP TO NNN
fn op_1nnn(&mut self, nnn: u16) -> bool {
self.programm_counter = nnn;
false
}
// CALL nnn
fn op_2nnn(&mut self, nnn: u16) -> bool {
self.stack_push(self.programm_counter);
self.programm_counter = nnn;
false
}
// SKIP VX == NN
fn op_3xnn(&mut self, x: usize, nn: u16) -> bool {
if self.registers[x] == nn as u8 {
self.programm_counter += 2;
};
false
}
// SKIP VX != NN
fn op_4xnn(&mut self, x: usize, nn: u16) -> bool {
if self.registers[x] != nn as u8 {
self.programm_counter += 2;
};
false
}
// SKIP VX == VY
fn op_5xy0(&mut self, x: usize, y: usize) -> bool {
if self.registers[x] == self.registers[y] {
self.programm_counter += 2;
};
false
}
// VX = NN
fn op_6xnn(&mut self, x: usize, nn: u16) -> bool {
self.registers[x] = nn as u8;
false
}
// VX += NN
fn op_7xnn(&mut self, x: usize, nn: u16) -> bool {
self.registers[x] = self.registers[x].wrapping_add(nn as u8);
false
}
// VX = VY
fn op_8xy0(&mut self, x: usize, y:usize) -> bool {
self.registers[x] = self.registers[y];
false
}
// VX |= VY
fn op_8xy1(&mut self, x: usize, y:usize) -> bool {
self.registers[x] |= self.registers[y];
false
}
// VX &= VY
fn op_8xy2(&mut self, x: usize, y:usize) -> bool {
self.registers[x] &= self.registers[y];
false
}
// VX ^= VY
fn op_8xy3(&mut self, x: usize, y:usize) -> bool {
self.registers[x] ^= self.registers[y];
false
}
// VX += VY
fn op_8xy4(&mut self, x: usize, y: usize) -> bool {
let (new_vx, carry) = self.registers[x].overflowing_add(self.registers[y]);
let new_vf = if carry { 1 } else { 0 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
false
}
// VX -= VY
fn op_8xy5(&mut self, x: usize, y: usize) -> bool {
let (new_vx, borrow) = self.registers[x].overflowing_sub(self.registers[y]);
let new_vf = if borrow { 0 } else { 1 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
false
}
// VX >>= 1
fn op_8xy6(&mut self, x: usize, _y: usize) -> bool {
let lsb = self.registers[x] & 1;
self.registers[x] >>= 1;
self.registers[0xF] = lsb;
false
}
// VX = VY - VX
fn op_8xy7(&mut self, x: usize, y: usize) -> bool {
let (new_vx, borrow) = self.registers[y].overflowing_sub(self.registers[x]);
let new_vf = if borrow { 0 } else { 1 };
self.registers[x] = new_vx;
self.registers[0xF] = new_vf;
false
}
// VX <<= 1
fn op_8xye(&mut self, x: usize, _y: usize) -> bool {
let msb = (self.registers[x] >> 7) & 1;
self.registers[x] <<= 1;
self.registers[0xF] = msb;
false
}
// SKIP VX != VY
fn op_9xy0(&mut self, x: usize, y: usize) -> bool {
if self.registers[x] != self.registers[y] {
self.programm_counter += 2;
};
false
}
// I = NNN
fn op_annn(&mut self, nnn: u16) -> bool {
self.i_register = nnn;
false
}
// JMP V0 + NNN
fn op_bnnn(&mut self, nnn: u16) -> bool {
self.programm_counter = (self.registers[0] as u16) + nnn;
false
}
// VX = rand() & NN
fn op_cxnn(&mut self, x: usize, nn: u16) -> bool {
let rng: u8 = rand::thread_rng().gen();
self.registers[x] = rng & (nn as u8);
false
}
// DRAW
fn op_dxyn(&mut self, x: usize, y: usize, n: u16) -> bool {
// Get the (x, y) coords for our sprite
let x_coord = self.registers[x] as u16;
let y_coord = self.registers[y] as u16;
// The last digit determines how many rows high our sprite is
let num_rows = n;
// Keep track if any pixels were flipped
let mut flipped = false;
// Iterate over each row of our sprite
for y_line in 0..num_rows {
// Determine which memory address our row's data is stored
let addr = self.i_register + y_line;
let pixels = self.ram[addr as usize];
// Iterate over each column in our row
for x_line in 0..8 {
// Use a mask to fetch current pixel's bit. Only flip if a 1
if (pixels & (0b1000_0000 >> x_line)) != 0 {
// Sprites should wrap around screen, so apply modulo
let x = (x_coord + x_line) as usize % SCREEN_WIDTH;
let y = (y_coord + y_line) as usize % SCREEN_HEIGHT;
// Get our pixel's index in the 1D screen array
let idx = x + SCREEN_WIDTH * y;
// Check if we're about to flip the pixel and set
flipped |= self.screen[idx];
self.screen[idx] ^= true;
}
}
}
// Populate VF register
if flipped {
self.registers[0xF] = 1;
} else {
self.registers[0xF] = 0;
}
true
}
// SKIP KEY PRESS
fn op_ex9e(&mut self, x: usize) -> bool {
let vx = self.registers[x];
let key = self.pressed_keys[vx as usize];
if key {
self.programm_counter += 2;
};
false
}
// SKIP KEY RELEASE
fn op_exa1(&mut self, x: usize) -> bool {
let vx = self.registers[x];
let key = self.pressed_keys[vx as usize];
if !key {
self.programm_counter += 2;
};
false
}
// VX = DT
fn op_fx07(&mut self, x: usize) -> bool {
self.registers[x] = self.delay_timer;
false
}
// WAIT KEY
fn op_fx0a(&mut self, x: usize) -> bool {
let mut pressed = false;
for i in 0..self.pressed_keys.len() {
if self.pressed_keys[i] {
self.registers[x] = i as u8;
pressed = true;
break;
}
}
if !pressed {
// Redo opcode
self.programm_counter -= 2;
};
false
}
// DT = VX
fn op_fx15(&mut self, x: usize) -> bool {
self.delay_timer = self.registers[x];
false
}
// ST = VX
fn op_fx18(&mut self, x: usize) -> bool {
self.sound_timer = self.registers[x];
false
}
// I += VX
fn op_fx1e(&mut self, x: usize) -> bool {
let vx = self.registers[x] as u16;
self.i_register = self.i_register.wrapping_add(vx);
false
}
// I = FONT
fn op_fx29(&mut self, x: usize) -> bool {
let c = self.registers[x] as u16;
self.i_register = c * 5;
false
}
// BCD
fn op_fx33(&mut self, x: usize) -> bool {
let vx = self.registers[x] as f32;
// Fetch the hundreds digit by dividing by 100 and tossing the decimal
let hundreds = (vx / 100.0).floor() as u8;
// Fetch the tens digit by dividing by 10, tossing the ones digit and the decimal
let tens = ((vx / 10.0) % 10.0).floor() as u8;
// Fetch the ones digit by tossing the hundreds and the tens
let ones = (vx % 10.0) as u8;
self.ram[self.i_register as usize] = hundreds;
self.ram[(self.i_register + 1) as usize] = tens;
self.ram[(self.i_register + 2) as usize] = ones;
false
}
// STORE V0 - VX
fn op_fx55(&mut self, x: usize) -> bool {
let i = self.i_register as usize;
for idx in 0..=x {
self.ram[i + idx] = self.registers[idx];
};
false
}
// LOAD V0 - VX
fn op_fx65(&mut self, x: usize) -> bool {
let i = self.i_register as usize;
for idx in 0..=x {
self.registers[idx] = self.ram[i + idx];
};
false
}
// TIMERS
pub fn tick_timers(&mut self) -> bool {