[cln] Code cleanup
Cleanup code Add a offset delay timer
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -191,7 +191,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "porcel8"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"clap",
|
||||
|
@@ -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
|
||||
|
@@ -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]) {
|
||||
|
@@ -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)
|
||||
|
91
src/main.rs
91
src/main.rs
@@ -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();
|
||||
let Porcel8ProgramArgs { filename, new_chip8_behaviour, draw_scale } = Porcel8ProgramArgs::parse();
|
||||
|
||||
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 (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,43 +82,46 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
loop {
|
||||
let val = receiver.try_recv();
|
||||
if let Ok(()) = val {
|
||||
break;
|
||||
} else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val {
|
||||
panic!("Disconnected");
|
||||
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 = device_termination_signal_sender_receiver.try_recv();
|
||||
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
|
||||
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();
|
||||
@@ -144,7 +145,7 @@ fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump,
|
||||
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_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.present();
|
||||
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
12
src/rom.rs
Normal 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)
|
||||
}
|
@@ -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,
|
||||
|
@@ -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 {
|
||||
@@ -18,7 +19,7 @@ impl SdlGraphicsAdapter {
|
||||
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() {
|
||||
let col_component = if *pixel { 0xff } else { 0 };
|
||||
self.rgb_frame_buffer[3 * i] = col_component;
|
||||
|
Reference in New Issue
Block a user