Trevor Merritt cddbe0c46e fixes reset from exection completion
adds 'default.oc8'
adds control window size to state for load/save of settings maybe?
moves TestCompressionTool code into InstructionUtil
remove gemmautil and moves into gemma
2024-11-09 20:07:21 -05:00

243 lines
7.5 KiB
Rust

use crate::chip8::video::Chip8VideoModes::{HighRes, LowRes};
use crate::constants::{
CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT,
SCHIP_VIDEO_WIDTH, SCHIP_VIDE_MEMORY,
};
use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
pub enum Chip8VideoModes {
LowRes,
HighRes,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct Chip8Video {
memory: Vec<bool>,
pub has_frame_changed: bool,
current_res: Chip8VideoModes,
}
impl Chip8Video {
pub fn reset(&mut self) {
self.cls();
self.set_lowres();
self.start_frame();
}
pub fn is_highres(&self) -> bool {
matches!(self.current_res, HighRes)
}
pub fn set_highres(&mut self) {
self.current_res = HighRes;
self.cls();
}
pub fn set_lowres(&mut self) {
self.current_res = LowRes;
self.cls();
}
pub fn get_screen_resolution(&mut self) -> Chip8VideoModes {
self.current_res
}
pub fn cls(&mut self) {
self.memory.clear();
let num_loops = match self.current_res {
LowRes => CHIP8_VIDEO_MEMORY,
HighRes => SCHIP_VIDE_MEMORY,
};
for i in 0..num_loops {
self.memory.push(false);
}
}
pub fn start_frame(&mut self) {
self.has_frame_changed = false;
}
pub fn new(initial_configuration: Box<Vec<bool>>) -> Self {
Self {
memory: *initial_configuration,
has_frame_changed: false,
current_res: Chip8VideoModes::LowRes,
}
}
pub fn peek(&self, address: u16) -> bool {
let loop_value: u16 = if self.is_highres() {
SCHIP_VIDE_MEMORY as u16
} else {
CHIP8_VIDEO_MEMORY as u16
};
let effective_address = if address >= loop_value {
address % loop_value
} else {
address
};
self.memory[effective_address as usize]
}
pub fn poke(&mut self, address: u16, new_value: bool) {
let loop_value: u16 = self.get_memory_size() as u16;
// Loop the address
let effective_address = address % loop_value;
let old_value = self.memory[effective_address as usize];
let xored_value = new_value ^ old_value; // XOR of the video
// if the frame has already changed we dont care if it changed again.
if !self.has_frame_changed && old_value != xored_value {
self.has_frame_changed = true
}
// println!("VIDEO POKE COMPLETE WITH {effective_address} SET TO {xored_value}");
self.memory[effective_address as usize] = xored_value;
}
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);
}
}
pub fn poke_2byte(&mut self, first_address: u16, to_write: [u8; 2]) {
for (idx, _) in to_write.iter().enumerate() {
for i in (0..8).rev() {
self.poke(
first_address + (idx * 8) as u16 + (7 - i),
(to_write[idx] & (1 << i)) != 0,
);
}
}
}
pub fn format_as_string(&self) -> String {
let (width, height) = self.get_resolution();
println!("FORMATTING {width}x{height}");
let mut output = String::new();
for row in 0..height {
for column in 0..width {
let data_offset = row * width + column;
debug!(
"Rendering {data_offset} with value {}",
self.memory[data_offset as usize]
);
if self.memory[data_offset as usize] {
output += "*"
} else {
output += " "
}
}
output += "\n";
}
output
}
pub fn get_resolution(&self) -> (i32, i32) {
if self.is_highres() {
(SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)
} else {
(CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
}
}
fn get_memory_size(&self) -> i32 {
let (width, height) = self.get_resolution();
width * height
}
pub fn tick(&mut self) {
self.has_frame_changed = false;
}
pub fn scroll_right(&mut self) {
let (width, height) = self.get_resolution();
for current_row in 0..height {
let row_offset: usize = (current_row * width) as usize;
// Shift pixels to the right by 4 in the current row
for current_column in (0..(width - 4)).rev() {
let source_address = row_offset + current_column as usize;
let target_address = source_address + 4;
self.memory[target_address] = self.memory[source_address];
}
// Clear the first 4 pixels in the current row
self.memory[row_offset..row_offset + 4].fill(false);
}
}
pub fn scroll_left(&mut self) {
let (width, height) = self.get_resolution();
for current_row in 0..height {
let row_offset = current_row * width;
for current_column in (0..width - 4) {
let target: usize = (row_offset + current_column) as usize;
let source: usize = target + 4;
self.memory[target] = self.memory[source];
}
let clear_start: usize = (row_offset + width - 4) as usize;
let clear_end: usize = clear_start + 4;
self.memory[clear_start..clear_end].fill(false);
}
}
pub fn scroll_up(&mut self, how_far: &u8) {
let how_far = *how_far as i32;
let (width, height) = self.get_resolution();
let row_shift = how_far * width;
for current_source_row in how_far..height {
let current_source_offset = current_source_row * width;
for current_source_column in 0..width {
let base_offset: usize = (current_source_offset + current_source_column) as usize;
let shifted_offset: usize = base_offset - row_shift as usize;
self.memory[shifted_offset] = self.memory[base_offset];
}
}
// Clear the new bottom rows after shifting
let clear_start = ((height - how_far) * width) as usize;
self.memory[clear_start..].fill(false);
}
pub fn scroll_down(&mut self, how_far: i32) {
let (width, height) = self.get_resolution();
let row_shift = how_far * width;
let max_source_row = height - how_far;
for current_source_row in (0..max_source_row).rev() {
let current_source_offset = current_source_row * width;
for current_source_column in 0..width {
let base_offset: usize = (current_source_offset + current_source_column) as usize;
let extended_offset: usize = base_offset + row_shift as usize;
self.memory[extended_offset] = self.memory[base_offset];
}
}
// Clear the new top rows after shifting
let clear_end = (how_far * width) as usize;
self.memory[0..clear_end].fill(false);
}
}
impl Default for Chip8Video {
fn default() -> Self {
let mut mem = vec![];
for _ in 0..CHIP8_VIDEO_MEMORY {
mem.push(false);
}
Chip8Video {
memory: mem,
has_frame_changed: false,
current_res: LowRes,
}
}
}