More work on message system for rendering, sound, and input

main
John Zacarias Jekel 2 years ago
parent a64669b6a9
commit 2836f4a641
  1. 2
      README.md
  2. 56
      lib/interpreter.rs
  3. 41
      lib/interpreter/input_sender.rs
  4. 27
      lib/interpreter/peripherals.rs
  5. 36
      lib/interpreter/peripherals/bios.rs
  6. 2
      lib/interpreter/peripherals/io.rs
  7. 4
      lib/interpreter/peripherals/rom_bios.rs
  8. 41
      lib/interpreter/render_reciever.rs
  9. 41
      lib/interpreter/sound_reciever.rs
  10. 3
      src/gui.rs

@ -2,7 +2,7 @@
VSmile EMUlator in Rust
To make things a bit more interesting, I decided to switch my VSEMU project (http://git.jekel.ca/JZJ/VSEMU) to being written in Rust. Not only is this a good excuse to learn Rust, which I would have had to at some point anyways, but also makes the project a bit more novel in comparison to the emulator that already exists in MAME!
To make things a bit more interesting, I decided to switch my VSEMU project (https://git.jekel.ca/JZJ/VSEMU) to being written in Rust. Not only is this a good excuse to learn Rust, which I would have had to at some point anyways, but also makes the project a bit more novel in comparison to the emulator that already exists in MAME!
Note: When developing this, I try to gleam as much of my understanding of the behaviour of the system/CPU architecture as possible for publically available documentation. However, at times, it becomes
necessary to view MAME's implementation when the documents available are unclear. I try to avoid this as much as possible, and rarely if ever actually copy code verbatim, but to be safe I consider this project to be a partial derivative work of MAME, and am (to the best of my knowledge) following its GPLv2 license properly.

@ -54,17 +54,24 @@
//!}
//!```
//TODO caching interpreter in CPU that is aware of bank switching
/* Imports */
mod cpu;
mod common;
mod peripherals;
mod render_reciever;
mod sound_reciever;
mod input_sender;
pub use render_reciever::RenderReciever;
pub use sound_reciever::SoundReciever;
pub use input_sender::InputSender;
use std::thread;
use std::sync::mpsc::SyncSender;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use std::sync::mpsc::sync_channel;
@ -88,18 +95,6 @@ use crate::logging::log_increment_ticks;
/* Types */
pub struct RenderMessage {
//TODO struct returned by a channel from the renderer containing the data/methods needed to render a frame or access the already rendered frame depending on how things go
}
pub struct SoundMessage {
//TODO struct returned by a channel from the renderer containing the data/methods indicating how to change the audio being output
}
pub struct InputMessage {
//TODO message type sent from the user to the channel indicating what to change the state of the inputs to
}
///VSEMUR Interpreter primary emulation struct
///
///Holds all information needed to store the state of an emulated VSmile system, in addition to data to manage threading and message-passing
@ -162,21 +157,21 @@ impl Emulator {
}
//TODO these will be valid across launches and stops of the emulation thread, but can be called whenever we're stopped to recreate them if needed
pub fn get_render_reciever(self: &mut Self) -> Receiver<RenderMessage> {
pub fn get_render_reciever(self: &mut Self) -> RenderReciever {
debug_assert!(!self.thread_running());
//return self.peripherals.as_mut().unwrap().get_render_reciever();
return self.peripherals.as_mut().unwrap().get_render_reciever();
todo!();
}
pub fn get_sound_reciever(self: &mut Self) -> Receiver<SoundMessage> {
pub fn get_sound_reciever(self: &mut Self) -> SoundReciever {
debug_assert!(!self.thread_running());
//return self.peripherals.as_mut().unwrap().get_sound_reciever();
return self.peripherals.as_mut().unwrap().get_sound_reciever();
todo!();
}
pub fn get_input_sender(self: &mut Self) -> Sender<InputMessage> {
pub fn get_input_sender(self: &mut Self) -> InputSender {
debug_assert!(!self.thread_running());
//return self.peripherals.as_mut().unwrap().get_input_sender();
return self.peripherals.as_mut().unwrap().get_input_sender();
todo!();
}
@ -254,11 +249,16 @@ impl Emulator {
//debug_assert!(state_for_thread.ready());//TODO
//Launch the thread
self.emulation_thread_join_handle.replace(thread::spawn(
move || -> (cpu::CPUState, peripherals::Peripherals) {
return Emulator::emulation_thread(cpu_for_thread, peripherals_for_thread, rx);
}
));
self.emulation_thread_join_handle.replace(
thread::Builder::new()
.name("VSEMUR emulation thread".to_string())
.spawn(
move || -> (cpu::CPUState, peripherals::Peripherals) {
return Emulator::emulation_thread(cpu_for_thread, peripherals_for_thread, rx);
}
)
.expect("Failed to launch VSEMUR emulation thread")
);
}
///Stops the currently running thread, blocking until it finishes and exits.
@ -273,11 +273,11 @@ impl Emulator {
//Request the thread to stop and get the state back from it; also destroy the stop request channel
let moved_stop_request_sender = self.stop_request_sender.take().unwrap();
if matches!(moved_stop_request_sender.send(()), Err(_)) {
panic!("Emulation thread dropped reciever (likely due to panic)");
panic!("VSEMUR emulation thread dropped reciever (likely due to panic)");
}
let old_join_handle = self.emulation_thread_join_handle.take().unwrap();
let (cpu_from_thread, peripherals_from_thread) = old_join_handle.join().expect("Emulation thread panicked");
let (cpu_from_thread, peripherals_from_thread) = old_join_handle.join().expect("VSEMUR emulation thread panicked");
drop(moved_stop_request_sender);
@ -315,7 +315,7 @@ impl Emulator {
cpu.tick(&mut peripherals);
peripherals.tick();
if periperhals.frame_ended() {//We want to sync the number of ticks we perform with actual frames, not just use frames as a measure of rate-limiting
if peripherals.frame_ended() {//We want to sync the number of ticks we perform with actual frames, not just use frames as a measure of rate-limiting
break;
}

@ -0,0 +1,41 @@
/* input_sender.rs
* By: John Jekel
*
* Per the user's code's wishes, sends InputMessages to the emulation thread to be processed (ex. buttons, the joystick, etc.)
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
///TODO cargo doc for this
pub struct InputSender {//Public-facing struct for sending InputMessages
}
pub(super) struct InputMessage {
//TODO message type sent from the user to the channel indicating what to change the state of the inputs to
}
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -16,6 +16,10 @@ use super::common::Memory;
use super::common::MEM_SIZE_WORDS;
use crate::interpreter::common::PHYSICAL_MEM_SIZE_WORDS;
use super::render_reciever::RenderReciever;
use super::sound_reciever::SoundReciever;
use super::input_sender::InputSender;
mod io;
mod render;
mod sound;
@ -23,6 +27,7 @@ mod rom_bios;
/* Constants */
//All inclusive
const WORK_RAM_BEGIN_ADDR: u32 = 0x000000;
const WORK_RAM_END_ADDR: u32 = 0x0027FF;
const RENDER_BEGIN_ADDR: u32 = 0x002800;
@ -33,10 +38,10 @@ const IO_BEGIN_ADDR: u32 = 0x003D00;
const IO_END_ADDR: u32 = 0x003DFF;
const DMA_BEGIN_ADDR: u32 = 0x003E00;
const DMA_END_ADDR: u32 = 0x003E03;
const BIOS_BEGIN_ADDR: u32 = 0x003E04;//TODO figure out what this is
const BIOS_END_ADDR: u32 = 0x0FFFFF;//TODO figure out what this is
const ROM_BEGIN_ADDR: u32 = 0x100000;//TODO figure out what this is
const ROM_END_ADDR: u32 = 0x3FFFFF;//TODO figure out what this is
const BIOS_BEGIN_ADDR: u32 = 0x004000;
const BIOS_END_ADDR: u32 = 0x0FFFFF;
const ROM_BEGIN_ADDR: u32 = 0x100000;
const ROM_END_ADDR: u32 = 0x3FFFFF;
/* Macros */
@ -53,7 +58,7 @@ pub(super) struct Peripherals {
sound: sound::SoundState,
io: io::IOState,
work_ram: Box<[u16]>,
rom_bios: rom_bios::RomAndBiosState,
rom_bios: rom_bios::RomAndBiosState,//TODO split this into two seperate parts
}
/* Associated Functions and Methods */
@ -90,6 +95,18 @@ impl Peripherals {
return false;//TODO
}
pub fn get_render_reciever(self: &mut Self) -> RenderReciever {
todo!();
}
pub fn get_sound_reciever(self: &mut Self) -> SoundReciever {
todo!();
}
pub fn get_input_sender(self: &mut Self) -> InputSender {
todo!();
}
pub fn load_bios_file(self: &mut Self, path: &str) -> Result<(), ()> {
return self.rom_bios.load_bios_file(path);
}

@ -0,0 +1,36 @@
/* NAME//TODO
* By: John Jekel
*
* TODO description
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
pub(super) struct Bios {
//TODO
}
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -56,7 +56,7 @@ mod offset {
pub(super) const REG_SYSTEM_CTRL: u32 = 0x0020;
pub(super) const REG_INT_CTRL: u32 = 0x0021;
pub(super) const REG_INT_CLEAR: u32 = 0x0022;
pub(super) const REG_EXT_MEMORY_CTRL: u32 = 0x0023;
pub(super) const REG_EXT_MEMORY_CTRL: u32 = 0x0023;//TODO this address should be handled in rom_bios.rs instead; or perhaps make rom_bios a submodule of io?
pub(super) const REG_WATCHDOG_CLEAR: u32 = 0x0024;
pub(super) const REG_ADC_CTRL: u32 = 0x0025;
pub(super) const REG_ADC_PAD: u32 = 0x0026;

@ -54,8 +54,10 @@ impl RomAndBiosState {
};
}
//TODO reset function to save chip-select values, etc
pub(super) fn load_bios_file(self: &mut Self, path: &str) -> Result<(), ()> {
let result = load_file_u16(path, &mut self.bios);
let result = load_file_u16(path, &mut self.bios);//TODO we only really need to load the part of the file from 0x004000 to 0x0FFFFF
if matches!(result, Ok(())) {
self.bios_loaded = true;
}

@ -0,0 +1,41 @@
/* render_reciever.rs
* By: John Jekel
*
* Recieves RenderMessages from the emulation thread and renders frames in the user's thread for their code to consume
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
///TODO cargo doc for this
pub struct RenderReciever {//Public-facing struct for recieving RenderMessages
}
pub(super) struct RenderMessage {
//TODO struct returned by a channel from the renderer containing the data/methods needed to render a frame or access the already rendered frame depending on how things go
}
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -0,0 +1,41 @@
/* sound_reciever.rs
* By: John Jekel
*
* Recieves SoundMessages from the emulation thread and processes sound in the user's thread for their code to consume
*
*/
/* Imports */
//TODO (include "use" and "mod" here)
/* Constants */
//TODO
/* Macros */
//TODO (also pub(crate) use the_macro statements here too)
/* Static Variables */
//TODO
/* Types */
///TODO cargo doc for this
pub struct SoundReciever {//Public-facing struct for recieving RenderMessages
}
pub(super) struct SoundMessage {
//TODO struct returned by a channel from the renderer containing the data/methods indicating how to change the audio being output
}
/* Associated Functions and Methods */
//TODO
/* Functions */
//TODO

@ -10,7 +10,8 @@
/* Imports */
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Text};
use gtk::{Application, ApplicationWindow, Button};
//use gtk::Text
/* Constants */

Loading…
Cancel
Save