[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]]
name = "porcel8"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"byteorder",
"clap",

View File

@@ -1,6 +1,6 @@
[package]
name = "porcel8"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
# 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 {
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_DEFAULT_MEM_LOCATION_START: usize = 0x50;
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
const ROM_START: usize = 0x200;
pub fn cycle(&mut self) -> EmulatorResult<()> {
self.device_keyboard.update_keyboard()?;
@@ -312,8 +297,26 @@ impl Device {
}
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");
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
pub fn load_rom(&mut self, rom: &[u8]) {

View File

@@ -2,13 +2,11 @@ use std::sync::mpsc::TryRecvError;
use crate::util::{EmulatorError, EmulatorResult};
// display thread sends these to the device thread.
// device thread updates on key up and down?
/// An emulated keyboard that receives keyboard data as input
pub struct Keyboard {
/// Current keyboard state
bitflags: u16,
/// Receives keyboard events from main thread
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<()> {
loop {
let keyboard_event_recv_res = self.keyboard_event_receiver.try_recv();
@@ -45,7 +44,6 @@ impl Keyboard {
}
}
/// Query if key is down
pub fn query_key_down(&self, key_num: u8) -> bool {
(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::mpsc::Receiver;
use std::sync::mpsc::Sender;
use std::thread;
use std::time::Duration;
use std::thread::JoinHandle;
use std::time::{Duration};
use clap::Parser;
use log::LevelFilter;
use sdl2::audio::{AudioQueue, AudioSpecDesired};
@@ -15,10 +14,10 @@ use sdl2::render::BlendMode;
use sdl2::render::WindowCanvas;
use simple_logger::SimpleLogger;
use device::timer::DeviceTimerManager;
use crate::args::Porcel8ProgramArgs;
use crate::device::Device;
use crate::device::keyboard::Keyboard;
use crate::util::EmulatorResult;
use crate::kb_map::get_key_index;
use crate::sdl_adapters::sdl_audio_adapter::SdlAudioAdapter;
@@ -30,26 +29,26 @@ mod device;
mod kb_map;
mod util;
mod sdl_adapters;
mod rom;
fn main() -> EmulatorResult<()> {
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
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 timer,mut sdl_aud_adapter) = SdlAudioAdapter::new_timers(440.0,0.85,audio_queue);
let (mut timer, mut sdl_aud_adapter) = SdlAudioAdapter::new_timers(SdlAudioAdapter::AUDIO_FREQUENCY, 0.85, audio_queue);
let (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references();
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();
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();
@@ -57,7 +56,11 @@ fn main() -> EmulatorResult<()> {
canvas.set_draw_color(Color::BLACK);
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 {
let last_time = frame_timer.elapsed();
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } |
@@ -79,34 +82,35 @@ fn main() -> EmulatorResult<()> {
}
}
// The rest of the game loop goes here...
// lock and draw framebuffer
{
let lock = frame_buffer_for_display.lock()?;
sdl_graphics_adapter.draw_screen(lock, &mut canvas)?;
}
canvas.present();
sdl_aud_adapter.process_push_audio()?;
// 60fps - small offset to consider for cpu cycle time
thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
let sleep_duration = SdlGraphicsAdapter::FRAME_RATE_TIMING - last_time;
thread::sleep(sleep_duration);
frame_timer = std::time::Instant::now();
}
compute_handle.join().expect("Failed to close compute thread");
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) {
let mut device = Device::new(timer, frame_buffer, device_keyboard, new_chip_behaviour);
fn start_compute_thread(filename: Option<String>, mut device: Device) -> EmulatorResult<(Sender<()>, JoinHandle<()>)> {
device.set_default_font();
if let Some(rom_file_location) = rom_file_location_option {
let rom = load_rom(rom_file_location);
if let Some(rom_file_location) = filename {
let rom = rom::load_rom(rom_file_location)?;
device.load_rom(&rom);
}
let (device_termination_signal_sender, device_termination_signal_sender_receiver) = std::sync::mpsc::channel();
let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || {
loop {
let val = receiver.try_recv();
let val = device_termination_signal_sender_receiver.try_recv();
if let Ok(()) = val {
break;
} else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val {
@@ -116,6 +120,8 @@ fn do_device_loop(timer: DeviceTimerManager, frame_buffer: Arc<Mutex<Box<[bool;
// Put a bit of delay to slow down execution
thread::sleep(Duration::from_millis(2))
}
})?;
Ok((device_termination_signal_sender, compute_handle))
}
@@ -125,15 +131,10 @@ fn get_frame_buffer_references() -> (Arc<Mutex<Box<[bool; 2048]>>>, Arc<Mutex<Bo
(arc, arc2)
}
const ROM_SIZE: usize = 4096 - 0x200;
fn load_rom(rom_file_location: String) -> [u8; ROM_SIZE] {
let mut rom_slice = [0u8; ROM_SIZE];
let mut file = File::open(rom_file_location).expect("could not open");
file.read(&mut rom_slice).expect("Unwrap");
rom_slice
}
/// Initiate SDL resources:
/// 1. A window canvas for drawing
/// 2. An event pump for use as an event loop,
/// 3. An Audio queue for sound
fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump, AudioQueue<f32>)> {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();

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 {
/// Number of samples per second
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 fn new_timers(freq: f32,
volume: f32,

View File

@@ -1,15 +1,16 @@
use std::sync::MutexGuard;
use std::time::Duration;
use sdl2::pixels::PixelFormatEnum;
use sdl2::render::{TextureAccess, WindowCanvas};
use crate::device::Device;
use crate::util::EmulatorResult;
#[derive(Debug)]
pub struct SdlGraphicsAdapter {
rgb_frame_buffer: Vec<u8>,
}
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_FRAMEBUFFER_SIZE: usize = Self::RGB_COMPONENTS * Device::FRAME_BUFFER_SIZE;
pub fn new() -> SdlGraphicsAdapter {