add chatgpt driven changes to video rendering

This commit is contained in:
Trevor Merritt 2024-10-16 07:28:03 -04:00
parent 448aeab154
commit 939fd83e80
9 changed files with 148 additions and 68 deletions

View File

@ -11,6 +11,7 @@ use super::{
#[derive(Clone)]
pub struct Chip8Computer {
pub num_cycles: i32,
pub memory: Chip8SystemMemory,
pub registers: Chip8Registers,
pub sound_timer: SoundTimer,
@ -23,6 +24,7 @@ pub struct Chip8Computer {
impl Default for Chip8Computer {
fn default() -> Self {
Self {
num_cycles: 0,
memory: Chip8SystemMemory::default(),
video_memory: Chip8Video::default(),
registers: Chip8Registers::default(),
@ -36,10 +38,12 @@ impl Default for Chip8Computer {
}
impl Chip8Computer {
pub fn reset(&mut self) -> Self{
let mut working = Chip8Computer::new();
working.video_memory.reset();
working
pub fn reset(&mut self) {
self.video_memory.reset();
self.num_cycles = 0;
self.registers.reset();
self.delay_timer.reset();
self.sound_timer.reset();
}
pub fn dump_keypad_to_string(&self) -> String {

View File

@ -13,6 +13,11 @@ impl DelayTimer {
counter: 0xff
}
}
pub fn reset(&mut self) {
self.counter = 0xff;
}
pub fn set_timer(&mut self, new_value: i32) {
self.counter = new_value
}

View File

@ -1,6 +1,6 @@
use std::ops::{BitAnd, Shr};
use log::debug;
use rand::random;
use rand::{random, Rng};
use crate::chip8::computer::{Chip8Computer};
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
@ -240,7 +240,7 @@ impl Chip8CpuInstructions {
match input {
0x00E0 => {
// 00E0 - CLS
// 00E0 - f
// Clear the display.
Chip8CpuInstructions::CLS
}
@ -428,9 +428,7 @@ impl Chip8CpuInstructions {
}
// * 0x00E0 Clear Screen
Chip8CpuInstructions::CLS => {
for i in 0..(64 * 32) {
input.video_memory.poke(i, false);
}
input.video_memory.cls();
}
// 0x00EE Return from Subroutine
Chip8CpuInstructions::RET => {
@ -653,7 +651,8 @@ impl Chip8CpuInstructions {
// 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();
let mut rng = rand::thread_rng();
let new_value: u8 = rng.random();
let and_value: u8 = *byte;
let result = new_value & and_value;
debug!("RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]");
@ -664,7 +663,6 @@ impl Chip8CpuInstructions {
// 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,
@ -676,7 +674,6 @@ impl Chip8CpuInstructions {
let x_offset = input.registers.peek(*x as u8);
let y_offset = input.registers.peek(*y as u8);
// let target_memory_offset = x_offset as u16 * 64 + y_offset as u16;
let num_bytes_to_read = *n;
@ -837,6 +834,7 @@ impl Chip8CpuInstructions {
#[cfg(test)]
mod test {
use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9};
use crate::constants::CHIP8_VIDEO_MEMORY;
use super::*;
#[test]
@ -1129,6 +1127,19 @@ mod test {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::CLS.execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202);
for i in 0..CHIP8_VIDEO_MEMORY {
assert_eq!(x.video_memory.peek(i as u16), false);
}
// draw some thing to the video memory
x.video_memory.poke(0x01, true);
x.video_memory.poke(0x03, true);
x.video_memory.poke(0x05, true);
Chip8CpuInstructions::CLS.execute(&mut x);
for i in 0..CHIP8_VIDEO_MEMORY {
assert_eq!(x.video_memory.peek(i as u16), false);
}
}
#[test]

View File

@ -24,6 +24,12 @@ impl Default for Chip8Registers {
}
impl Chip8Registers {
pub fn reset(&mut self) {
self.registers = [0x00; 16];
self.i_register = 0x00;
self.pc = 0x200;
}
pub fn poke_pc(&mut self, new_pc: u16) {
self.pc = new_pc
}
@ -79,6 +85,7 @@ impl Chip8Registers {
#[cfg(test)]
mod test {
use rand::random;
use crate::chip8::registers::Chip8Registers;
#[test]
@ -118,4 +125,17 @@ mod test {
let result_string = x.format_as_string();
assert_eq!(result_string, String::from("Vx: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\n 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f\nI: 0x0cab\tPC: 0x0abc"));
}
#[test]
fn reset_clears_registers() {
let mut x = Chip8Registers::default();
for register in 0..0x10 {
x.registers[register] = random::<u8>();
}
x.reset();
for register in 0..0x10 {
assert_eq!(x.registers[register], 0x00);
}
}
}

View File

@ -32,6 +32,10 @@ impl SoundTimer {
*/
}
}
pub fn reset(&mut self) {
self.counter = 0x00;
}
}
#[cfg(test)]

View File

@ -31,11 +31,7 @@ impl Chip8Video {
}
pub fn peek(self, address: u16) -> bool {
let effective_address = if address >= 2048 {
address - 2048
} else {
address
};
let effective_address = address % 2048;
self.memory[effective_address as usize]
}
@ -43,24 +39,50 @@ impl Chip8Video {
// println!("OFFSET: {address} - POKING {new_value}");
// Loop the address
let effective_address = if address >= 2048 {
address - 2048
} else {
address
};
let effective_address = address % 2048;
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.
self.has_frame_changed = if xored_value && value_changed { false } else { true };
if value_changed {
self.has_frame_changed = true;
}
self.has_frame_changed = true
};
self.memory[effective_address as usize] = xored_value;
}
/*
CHATGPT
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) {
let effective_address = first_address as usize % CHIP8_VIDEO_MEMORY;
// Loop through each bit of the byte
for i in 0..8 {
let is_set = (to_write & (0x80 >> i)) != 0;
let address = effective_address + i;
if address < CHIP8_VIDEO_MEMORY {
self.poke(address as u16, is_set);
}
}
}
*/
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) {
for i in (0..8).rev() {
let shifted = ((1 << i) & to_write) >> i;
//
let target_address = first_address + (7 - i);
let is_set = (to_write & (1 << i)) != 0;
self.poke(target_address, is_set);
}
}
/*
MINE
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) {
for i in (0..8).rev() {
let shifted = ((1 << i) & to_write) >> i;
@ -71,13 +93,16 @@ impl Chip8Video {
}
}
*/
pub fn poke_sprite(&mut self, first_address: u16, to_write: Vec<u8>) -> Self {
let sprite_length = to_write.len();
for (index, byte) in to_write.iter().enumerate() {
let real_address = index * 64;
self.poke_byte(real_address as u16, *byte);
}
};
self.to_owned()
}
@ -111,7 +136,7 @@ impl Default for Chip8Video {
let new_struct = Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false };
println!("NEW DEFAULT MEMORY : {}", new_struct.format_as_string());
new_struct
new_struct.clone()
}
}
@ -146,10 +171,10 @@ mod test {
let mut x = Chip8Video::default();
for i in 0..CHIP8_VIDEO_MEMORY {
assert!(!x.peek(i as u16));
assert!(!x.clone().peek(i as u16));
// then flip the value and test again.
&x.poke(i as u16, true);
assert!(x.peek(i as u16));
assert!(x.clone().peek(i as u16));
}
}
@ -271,24 +296,24 @@ mod test {
// row 2 column 1
{
assert!(v.peek(0x40));
assert!(v.peek(0x41));
assert!(v.peek(0x42));
assert!(v.peek(0x43));
assert!(v.peek(0x44));
assert!(v.peek(0x45));
assert!(v.peek(0x46));
assert!(v.peek(0x47));
assert!(v.clone().peek(0x40));
assert!(v.clone().peek(0x41));
assert!(v.clone().peek(0x42));
assert!(v.clone().peek(0x43));
assert!(v.clone().peek(0x44));
assert!(v.clone().peek(0x45));
assert!(v.clone().peek(0x46));
assert!(v.clone().peek(0x47));
// row 3 column 1
assert!(!v.peek(0xC0));
assert!(v.peek(0xC1));
assert!(!v.peek(0xC2));
assert!(v.peek(0xC3));
assert!(!v.peek(0xC4));
assert!(v.peek(0xC5));
assert!(!v.peek(0xC6));
assert!(v.peek(0xC7));
assert!(v.clone().peek(0xC1));
assert!(!v.clone().peek(0xC2));
assert!(v.clone().peek(0xC3));
assert!(!v.clone().peek(0xC4));
assert!(v.clone().peek(0xC5));
assert!(!v.clone().peek(0xC6));
assert!(v.clone().peek(0xC7));
}
}
@ -313,23 +338,23 @@ mod test {
let test_offset = (x_offset * 64 + y_offset) as u16;
assert!(!v.peek(test_offset));
assert!(!v.peek(test_offset + 1));
assert!(!v.peek(test_offset + 2));
assert!(!v.peek(test_offset + 3));
assert!(!v.peek(test_offset + 4));
assert!(!v.peek(test_offset + 5));
assert!(!v.peek(test_offset + 6));
assert!(!v.peek(test_offset + 7));
assert!(!v.clone().peek(test_offset + 1));
assert!(!v.clone().peek(test_offset + 2));
assert!(!v.clone().peek(test_offset + 3));
assert!(!v.clone().peek(test_offset + 4));
assert!(!v.clone().peek(test_offset + 5));
assert!(!v.clone().peek(test_offset + 6));
assert!(!v.clone().peek(test_offset + 7));
let test_offset = test_offset + 0x40;
assert!(v.peek(test_offset));
assert!(v.peek(test_offset + 1));
assert!(v.peek(test_offset + 2));
assert!(v.peek(test_offset + 3));
assert!(v.peek(test_offset + 4));
assert!(v.peek(test_offset + 5));
assert!(v.peek(test_offset + 6));
assert!(v.peek(test_offset + 7));
assert!(v.clone().peek(test_offset));
assert!(v.clone().peek(test_offset + 1));
assert!(v.clone().peek(test_offset + 2));
assert!(v.clone().peek(test_offset + 3));
assert!(v.clone().peek(test_offset + 4));
assert!(v.clone().peek(test_offset + 5));
assert!(v.clone().peek(test_offset + 6));
assert!(v.clone().peek(test_offset + 7));
}
#[test]
@ -458,8 +483,8 @@ mod test {
fn peek_out_of_bounds_doesnt_panic() {
let x = Chip8Video::default();
let y = x.peek(2049);
let y = x.peek(0);
let y = x.clone().peek(2049);
let y = x.clone().peek(0);
// if we got here we didn't panic
assert!(true);

View File

@ -34,6 +34,9 @@ fn main() -> eframe::Result {
if state.display_registers {
GemmaEguiSupport::registers_view(&computer, ui);
}
if state.is_running {
computer.step_system();
}
});
})
}

View File

@ -4,7 +4,8 @@ pub struct GemmaEGuiState {
pub display_memory: bool,
pub display_registers: bool,
pub memory_view_min: i32,
pub memory_view_max: i32
pub memory_view_max: i32,
pub is_running: bool
}
impl Default for GemmaEGuiState {
@ -14,7 +15,8 @@ impl Default for GemmaEGuiState {
display_memory: true,
display_registers: true,
memory_view_min: 0x00,
memory_view_max: 0x100
memory_view_max: 0x100,
is_running: false
}
}
}

View File

@ -19,16 +19,18 @@ impl EGuiFileList {
let mut working_filename = selected_filename.clone();
ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
egui::ComboBox::from_label(format!(
"Currently selected string: {}",
selected_filename
))
ComboBox::from_label("Choose an option")
.selected_text(selected_filename.clone())
.show_ui(ui, |ui| {
for option in read_dir(root.as_path()).unwrap() {
ui.label(format!("{:?}", option.unwrap().file_name()));
// Add each option to the ComboBox
let mut label = option.unwrap().file_name();
ui.selectable_value(selected_filename, selected_filename.clone(), label.into_string().unwrap());
}
});
// Display the selected option
ui.label(format!("Selected value: {}", selected_filename));
}
}
@ -39,6 +41,7 @@ impl GemmaEguiSupport {
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
if ui.button("Start").clicked() {
println!("Start");
state.is_running = true;
}
if ui.button("Step").clicked() {
@ -47,6 +50,7 @@ impl GemmaEguiSupport {
if ui.button("Stop").clicked() {
println!("STOP");
state.is_running = false;
}
if ui.button("Reset").clicked() {
system.reset();
@ -60,6 +64,8 @@ impl GemmaEguiSupport {
// ...then feed the system.
system.load_bytes_to_memory(0x200, &read_bin);
}
let mut target = String::new();
EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut target, ui);
});