[cln] Code cleanup

Cleanup code
Add a offset delay timer
This commit is contained in:
2024-03-10 14:26:42 +05:30
parent 242919f22a
commit 31fb280fc6
8 changed files with 92 additions and 76 deletions

2
Cargo.lock generated
View File

@@ -191,7 +191,7 @@ dependencies = [
[[package]] [[package]]
name = "porcel8" name = "porcel8"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"clap", "clap",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "porcel8" name = "porcel8"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -37,28 +37,13 @@ impl Device {
} }
impl Device { impl Device {
const DEFAULT_FONT: [u8; 5 * 16] = [
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
];
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
const FONT_HEIGHT: u16 = 5; const FONT_HEIGHT: u16 = 5;
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F; const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
const ROM_START: usize = 0x200; const ROM_START: usize = 0x200;
pub fn cycle(&mut self) -> EmulatorResult<()> { pub fn cycle(&mut self) -> EmulatorResult<()> {
self.device_keyboard.update_keyboard()?; self.device_keyboard.update_keyboard()?;
@@ -312,8 +297,26 @@ impl Device {
} }
pub fn set_default_font(&mut self) { pub fn set_default_font(&mut self) {
const DEFAULT_FONT: [u8; Device::FONT_HEIGHT as usize * 16] = [
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
];
log::info!("Loaded default font from memory"); log::info!("Loaded default font from memory");
self.memory[Self::FONT_DEFAULT_MEM_LOCATION_START..=Self::FONT_DEFAULT_MEM_LOCATION_END].copy_from_slice(&Self::DEFAULT_FONT); self.memory[Self::FONT_DEFAULT_MEM_LOCATION_START..=Self::FONT_DEFAULT_MEM_LOCATION_END].copy_from_slice(&DEFAULT_FONT);
} }
/// load a rom from bytes /// load a rom from bytes
pub fn load_rom(&mut self, rom: &[u8]) { pub fn load_rom(&mut self, rom: &[u8]) {

View File

@@ -2,13 +2,11 @@ use std::sync::mpsc::TryRecvError;
use crate::util::{EmulatorError, EmulatorResult}; use crate::util::{EmulatorError, EmulatorResult};
// display thread sends these to the device thread. /// An emulated keyboard that receives keyboard data as input
// device thread updates on key up and down?
pub struct Keyboard { pub struct Keyboard {
/// Current keyboard state
bitflags: u16, bitflags: u16,
/// Receives keyboard events from main thread
keyboard_event_receiver: std::sync::mpsc::Receiver<KeyboardEvent>, keyboard_event_receiver: std::sync::mpsc::Receiver<KeyboardEvent>,
} }
@@ -26,7 +24,8 @@ impl Keyboard {
} }
} }
/// Update keyboard based on pending keyboard events /// Update keyboard based on pending keyboard events.
/// If no events are presents, it will return without any action.
pub fn update_keyboard(&mut self) -> EmulatorResult<()> { pub fn update_keyboard(&mut self) -> EmulatorResult<()> {
loop { loop {
let keyboard_event_recv_res = self.keyboard_event_receiver.try_recv(); let keyboard_event_recv_res = self.keyboard_event_receiver.try_recv();
@@ -45,7 +44,6 @@ impl Keyboard {
} }
} }
/// Query if key is down /// Query if key is down
pub fn query_key_down(&self, key_num: u8) -> bool { pub fn query_key_down(&self, key_num: u8) -> bool {
(self.bitflags | 1 << key_num) == (1 << key_num) (self.bitflags | 1 << key_num) == (1 << key_num)

View File

@@ -1,9 +1,8 @@
use std::fs::File;
use std::io::Read;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::mpsc::Receiver; use std::sync::mpsc::Sender;
use std::thread; use std::thread;
use std::time::Duration; use std::thread::JoinHandle;
use std::time::{Duration};
use clap::Parser; use clap::Parser;
use log::LevelFilter; use log::LevelFilter;
use sdl2::audio::{AudioQueue, AudioSpecDesired}; use sdl2::audio::{AudioQueue, AudioSpecDesired};
@@ -15,10 +14,10 @@ use sdl2::render::BlendMode;
use sdl2::render::WindowCanvas; use sdl2::render::WindowCanvas;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use device::timer::DeviceTimerManager;
use crate::args::Porcel8ProgramArgs; use crate::args::Porcel8ProgramArgs;
use crate::device::Device; use crate::device::Device;
use crate::device::keyboard::Keyboard;
use crate::util::EmulatorResult; use crate::util::EmulatorResult;
use crate::kb_map::get_key_index; use crate::kb_map::get_key_index;
use crate::sdl_adapters::sdl_audio_adapter::SdlAudioAdapter; use crate::sdl_adapters::sdl_audio_adapter::SdlAudioAdapter;
@@ -30,26 +29,26 @@ mod device;
mod kb_map; mod kb_map;
mod util; mod util;
mod sdl_adapters; mod sdl_adapters;
mod rom;
fn main() -> EmulatorResult<()> { fn main() -> EmulatorResult<()> {
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap(); SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
let Porcel8ProgramArgs { filename, new_chip8_behaviour,draw_scale } = Porcel8ProgramArgs::parse(); let Porcel8ProgramArgs { filename, new_chip8_behaviour, draw_scale } = Porcel8ProgramArgs::parse();
log::info!("Started emulator"); log::info!("Started emulator");
let (mut canvas, mut event_pump, audio_queue) = try_initiate_sdl(draw_scale)?;
let (mut canvas, mut event_pump,audio_queue) = try_initiate_sdl(draw_scale)?; let (mut timer, mut sdl_aud_adapter) = SdlAudioAdapter::new_timers(SdlAudioAdapter::AUDIO_FREQUENCY, 0.85, audio_queue);
let (mut timer,mut sdl_aud_adapter) = SdlAudioAdapter::new_timers(440.0,0.85,audio_queue);
let (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references(); let (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references();
let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard(); let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard();
let (device_termination_signal_sender, device_termination_signal_sender_receiver) = std::sync::mpsc::channel();
timer.start(); timer.start();
let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || {
do_device_loop(timer, frame_buffer_for_device, device_termination_signal_sender_receiver, device_keyboard, filename, new_chip8_behaviour); let device = Device::new(timer, frame_buffer_for_device, device_keyboard, new_chip8_behaviour);
})?;
let (device_termination_signal_sender, compute_handle) = start_compute_thread(filename, device)?;
let mut sdl_graphics_adapter = SdlGraphicsAdapter::new(); let mut sdl_graphics_adapter = SdlGraphicsAdapter::new();
@@ -57,7 +56,11 @@ fn main() -> EmulatorResult<()> {
canvas.set_draw_color(Color::BLACK); canvas.set_draw_color(Color::BLACK);
canvas.clear(); canvas.clear();
// Compute a frame time offset
// Thread will sleep for 60fps - (time spent computing)
let mut frame_timer = std::time::Instant::now();
'running: loop { 'running: loop {
let last_time = frame_timer.elapsed();
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
Event::Quit { .. } | Event::Quit { .. } |
@@ -79,43 +82,46 @@ fn main() -> EmulatorResult<()> {
} }
} }
// lock and draw framebuffer
// The rest of the game loop goes here...
{ {
let lock = frame_buffer_for_display.lock()?; let lock = frame_buffer_for_display.lock()?;
sdl_graphics_adapter.draw_screen(lock, &mut canvas)?; sdl_graphics_adapter.draw_screen(lock, &mut canvas)?;
} }
canvas.present(); canvas.present();
sdl_aud_adapter.process_push_audio()?; sdl_aud_adapter.process_push_audio()?;
// 60fps - small offset to consider for cpu cycle time let sleep_duration = SdlGraphicsAdapter::FRAME_RATE_TIMING - last_time;
thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); thread::sleep(sleep_duration);
frame_timer = std::time::Instant::now();
} }
compute_handle.join().expect("Failed to close compute thread"); compute_handle.join().expect("Failed to close compute thread");
Ok(()) Ok(())
} }
fn do_device_loop(timer: DeviceTimerManager, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>, receiver: Receiver<()>, device_keyboard: Keyboard, rom_file_location_option: Option<String>, new_chip_behaviour: bool) { fn start_compute_thread(filename: Option<String>, mut device: Device) -> EmulatorResult<(Sender<()>, JoinHandle<()>)> {
let mut device = Device::new(timer, frame_buffer, device_keyboard, new_chip_behaviour);
device.set_default_font(); device.set_default_font();
if let Some(rom_file_location) = rom_file_location_option { if let Some(rom_file_location) = filename {
let rom = load_rom(rom_file_location); let rom = rom::load_rom(rom_file_location)?;
device.load_rom(&rom); device.load_rom(&rom);
} }
loop { let (device_termination_signal_sender, device_termination_signal_sender_receiver) = std::sync::mpsc::channel();
let val = receiver.try_recv(); let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || {
if let Ok(()) = val {
break; loop {
} else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val { let val = device_termination_signal_sender_receiver.try_recv();
panic!("Disconnected"); if let Ok(()) = val {
break;
} else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val {
panic!("Disconnected");
}
device.cycle().expect("Failed to execute");
// Put a bit of delay to slow down execution
thread::sleep(Duration::from_millis(2))
} }
device.cycle().expect("Failed to execute"); })?;
// Put a bit of delay to slow down execution Ok((device_termination_signal_sender, compute_handle))
thread::sleep(Duration::from_millis(2))
}
} }
@@ -125,15 +131,10 @@ fn get_frame_buffer_references() -> (Arc<Mutex<Box<[bool; 2048]>>>, Arc<Mutex<Bo
(arc, arc2) (arc, arc2)
} }
const ROM_SIZE: usize = 4096 - 0x200; /// Initiate SDL resources:
/// 1. A window canvas for drawing
fn load_rom(rom_file_location: String) -> [u8; ROM_SIZE] { /// 2. An event pump for use as an event loop,
let mut rom_slice = [0u8; ROM_SIZE]; /// 3. An Audio queue for sound
let mut file = File::open(rom_file_location).expect("could not open");
file.read(&mut rom_slice).expect("Unwrap");
rom_slice
}
fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump, AudioQueue<f32>)> { fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump, AudioQueue<f32>)> {
let sdl_context = sdl2::init().unwrap(); let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap(); let video_subsystem = sdl_context.video().unwrap();
@@ -144,7 +145,7 @@ fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump,
freq: Some(SdlAudioAdapter::SAMPLING_FREQ), freq: Some(SdlAudioAdapter::SAMPLING_FREQ),
}; };
let audio_queue = audio_subsystem.open_queue::<f32,_>(None, &wanted_spec)?; let audio_queue = audio_subsystem.open_queue::<f32, _>(None, &wanted_spec)?;
let window_width = (Device::FRAME_BUFFER_WIDTH as f32 * draw_scale) as u32; let window_width = (Device::FRAME_BUFFER_WIDTH as f32 * draw_scale) as u32;
let window_height = (Device::FRAME_BUFFER_HEIGHT as f32 * draw_scale) as u32; let window_height = (Device::FRAME_BUFFER_HEIGHT as f32 * draw_scale) as u32;
@@ -160,7 +161,7 @@ fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump,
canvas.clear(); canvas.clear();
canvas.present(); canvas.present();
let event_pump = sdl_context.event_pump()?; let event_pump = sdl_context.event_pump()?;
Ok((canvas, event_pump,audio_queue)) Ok((canvas, event_pump, audio_queue))
} }

12
src/rom.rs Normal file
View File

@@ -0,0 +1,12 @@
use std::fs::File;
use std::io::Read;
use crate::util::EmulatorResult;
pub const ROM_SIZE: usize = 4096 - 0x200;
pub fn load_rom(rom_file_location: String) -> EmulatorResult<[u8; ROM_SIZE]> {
let mut rom_slice = [0u8; ROM_SIZE];
let mut file = File::open(rom_file_location)?;
file.read(&mut rom_slice)?;
Ok(rom_slice)
}

View File

@@ -16,6 +16,7 @@ pub struct SdlAudioAdapter {
impl SdlAudioAdapter { impl SdlAudioAdapter {
/// Number of samples per second /// Number of samples per second
pub const SAMPLING_FREQ:i32 = 15360; pub const SAMPLING_FREQ:i32 = 15360;
pub const AUDIO_FREQUENCY: f32 = 440.0;
pub const SAMPLES_PER_FRAME: usize = (Self::SAMPLING_FREQ as usize / 60) * 2; pub const SAMPLES_PER_FRAME: usize = (Self::SAMPLING_FREQ as usize / 60) * 2;
pub fn new_timers(freq: f32, pub fn new_timers(freq: f32,
volume: f32, volume: f32,

View File

@@ -1,15 +1,16 @@
use std::sync::MutexGuard; use std::sync::MutexGuard;
use std::time::Duration;
use sdl2::pixels::PixelFormatEnum; use sdl2::pixels::PixelFormatEnum;
use sdl2::render::{TextureAccess, WindowCanvas}; use sdl2::render::{TextureAccess, WindowCanvas};
use crate::device::Device; use crate::device::Device;
use crate::util::EmulatorResult; use crate::util::EmulatorResult;
#[derive(Debug)]
pub struct SdlGraphicsAdapter { pub struct SdlGraphicsAdapter {
rgb_frame_buffer: Vec<u8>, rgb_frame_buffer: Vec<u8>,
} }
impl SdlGraphicsAdapter { impl SdlGraphicsAdapter {
pub const FRAME_RATE_TIMING: Duration = Duration::new(0, 1_000_000_000u32 / 60);
pub const RGB_COMPONENTS: usize = 3; pub const RGB_COMPONENTS: usize = 3;
pub const RGB_FRAMEBUFFER_SIZE: usize = Self::RGB_COMPONENTS * Device::FRAME_BUFFER_SIZE; pub const RGB_FRAMEBUFFER_SIZE: usize = Self::RGB_COMPONENTS * Device::FRAME_BUFFER_SIZE;
pub fn new() -> SdlGraphicsAdapter { pub fn new() -> SdlGraphicsAdapter {
@@ -18,7 +19,7 @@ impl SdlGraphicsAdapter {
rgb_frame_buffer rgb_frame_buffer
} }
} }
pub fn draw_screen(&mut self, frame_buffer: MutexGuard<Box<[bool; Device::FRAME_BUFFER_SIZE]>>, window_canvas: &mut WindowCanvas) ->EmulatorResult<()> { pub fn draw_screen(&mut self, frame_buffer: MutexGuard<Box<[bool; Device::FRAME_BUFFER_SIZE]>>, window_canvas: &mut WindowCanvas) -> EmulatorResult<()> {
for (i, pixel) in frame_buffer.iter().enumerate() { for (i, pixel) in frame_buffer.iter().enumerate() {
let col_component = if *pixel { 0xff } else { 0 }; let col_component = if *pixel { 0xff } else { 0 };
self.rgb_frame_buffer[3 * i] = col_component; self.rgb_frame_buffer[3 * i] = col_component;