From f47228bf8c704431a4a36588171d4cf7a9bffdd5 Mon Sep 17 00:00:00 2001 From: Denis Nutiu Date: Wed, 4 Dec 2024 23:48:59 +0200 Subject: [PATCH] refactor display struct --- src/display.rs | 27 +++++-------------------- src/emulator.rs | 53 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/display.rs b/src/display.rs index 47abfa7..1e80dc6 100644 --- a/src/display.rs +++ b/src/display.rs @@ -9,22 +9,16 @@ pub trait Display { /// Re-draws the display. fn clear(&self); /// Renders the display data on screen. - fn render(&mut self); - /// Draws the pixels at x and y coordinates - fn draw(&mut self, x: u8, y: u8, pixels: u8) -> bool; + fn render(&mut self, display_data: &[bool; DISPLAY_WIDTH * DISPLAY_HEIGHT]); } /// Display models the Chip8's display. pub struct TerminalDisplay { - /// Holds the display data, each bit corresponds to a pixel. - display_data: [bool; DISPLAY_WIDTH * DISPLAY_HEIGHT], } impl TerminalDisplay { pub fn new() -> TerminalDisplay { - TerminalDisplay { - display_data: [false; DISPLAY_WIDTH * DISPLAY_HEIGHT], - } + TerminalDisplay {} } } @@ -36,11 +30,11 @@ impl Display for TerminalDisplay { print!("{esc}[2J{esc}[1;1H", esc = 27 as char); } /// Renders the display data on screen. - fn render(&mut self) { + fn render(&mut self, display_data: &[bool; DISPLAY_WIDTH * DISPLAY_HEIGHT]) { for row in 0..32 { for column in 0..64 { - if self.display_data[row * DISPLAY_WIDTH + column] { - print!("█") + if display_data[row * DISPLAY_WIDTH + column] { + print!("#") } else { print!(" ") } @@ -48,15 +42,4 @@ impl Display for TerminalDisplay { print!("\n") } } - - /// Draws the pixels at x and y coordinates - fn draw(&mut self, x: u8, y: u8, pixels: u8) -> bool { - let row = y as usize; - let column = x as usize; - for pixel in 0..=pixels { - self.display_data[row * DISPLAY_WIDTH + column + pixel as usize] = true; - } - self.render(); - true - } } diff --git a/src/emulator.rs b/src/emulator.rs index bbbe2ae..97ff3db 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -8,6 +8,12 @@ use std::io::Read; use std::path::Path; use std::{thread, time}; +/// Represents the display's width in pixels. +const DISPLAY_WIDTH: usize = 64; + +/// Represents the display's height pixels. +const DISPLAY_HEIGHT: usize = 32; + const MEMORY_SIZE: usize = 4096; const NUMBER_OF_REGISTERS: usize = 16; const FONT_SPRITES: [u8; 80] = [ @@ -51,7 +57,10 @@ where stack_pointer: u8, /// The display_data holds all the data associated with the display display: D, + /// The stack of the emulator. stack: Stack, + /// Holds the display data, each bit corresponds to a pixel. + display_data: [bool; DISPLAY_WIDTH * DISPLAY_HEIGHT], } impl Emulator @@ -71,6 +80,7 @@ where sound_timer: 0, stack_pointer: 0, stack: Stack::new(), + display_data: [false; DISPLAY_WIDTH * DISPLAY_HEIGHT], }; emulator.load_font_data(); @@ -136,12 +146,43 @@ where trace!("Add to register {} data {:04x}", register, data); self.registers[register as usize] += data } - ProcessorInstruction::Draw(vx_register, vy_register, pixels) => { - trace!("Draw vx_register={vx_register} vy_register={vy_register} pixels={pixels}"); - let x_coordinate = self.registers[vx_register as usize] & 63; - let y_coordinate = self.registers[vy_register as usize] & 31; - self.registers[0xF] = 0; - self.display.draw(x_coordinate, y_coordinate, pixels); + ProcessorInstruction::Draw(vx_register, vy_register, num_rows) => { + trace!("Draw vx_register={vx_register} vy_register={vy_register} pixels={num_rows}"); + let x_coordinate = self.registers[vx_register as usize]; + let y_coordinate = self.registers[vy_register as usize]; + + // 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.index_register + y_line as u16; + let pixels = self.memory[0xf0 + 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_coordinate + x_line) as usize % DISPLAY_WIDTH; + let y = (y_coordinate + y_line) as usize % DISPLAY_HEIGHT; + + // Get our pixel's index for our 1D screen array + let index = x + DISPLAY_WIDTH * y; + // Check if we're about to flip the pixel and set + flipped |= self.display_data[index]; + self.display_data[index] ^= true; + } + } + } + + if flipped { + self.registers[0xF] = 1; + } else { + self.registers[0xF] = 0; + } + + self.display.render(&self.display_data); } ProcessorInstruction::UnknownInstruction => { warn!("Unknown instruction: {:04x}, skipping.", instruction);