Skip to content

Commit 2d8f50a

Browse files
committed
Initial impl for the SDL frontend
1 parent 0f8770a commit 2d8f50a

3 files changed

Lines changed: 77 additions & 24 deletions

File tree

common/src/menu.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ pub struct MenuOption<T, S:AsRef<str>>{
88
#[derive(Debug, Clone, Copy)]
99
pub enum EmulatorMenuOption{
1010
Resume,
11+
Turbo,
1112
Restart,
1213
Shutdown
1314
}
1415

15-
pub const GAME_MENU_OPTIONS:[MenuOption<EmulatorMenuOption, &str>;3] = [
16+
pub const GAME_MENU_OPTIONS:[MenuOption<EmulatorMenuOption, &str>; 4] = [
1617
MenuOption{prompt:"Resume", value:EmulatorMenuOption::Resume},
18+
MenuOption{prompt:"Turbo", value:EmulatorMenuOption::Turbo},
1719
MenuOption{prompt:"Restart", value:EmulatorMenuOption::Restart},
1820
MenuOption{prompt:"Shutdown", value:EmulatorMenuOption::Shutdown}
1921
];
@@ -25,15 +27,22 @@ cfg_if::cfg_if!{ if #[cfg(feature = "std")]{
2527

2628
pub struct MagenBoyState{
2729
// Use atomic bool, normal bool doesnt works on arm (probably cause of the memory model)
28-
pub running:AtomicBool,
29-
pub pause:AtomicBool,
30-
pub exit:AtomicBool,
31-
pub state_mutex:Mutex<()>
30+
pub running: AtomicBool,
31+
pub turbo: AtomicBool,
32+
pub pause: AtomicBool,
33+
pub exit: AtomicBool,
34+
pub state_mutex: Mutex<()>
3235
}
3336

3437
impl MagenBoyState{
3538
pub const fn new() -> Self {
36-
Self { running: AtomicBool::new(true), pause: AtomicBool::new(false), exit: AtomicBool::new(false), state_mutex: Mutex::new(()) }
39+
Self {
40+
running: AtomicBool::new(true),
41+
turbo: AtomicBool::new(false),
42+
pause: AtomicBool::new(false),
43+
exit: AtomicBool::new(false),
44+
state_mutex: Mutex::new(())
45+
}
3746
}
3847
}
3948

@@ -47,9 +56,19 @@ cfg_if::cfg_if!{ if #[cfg(feature = "std")]{
4756
Self { provider, header }
4857
}
4958

50-
pub fn pop_game_menu<GFX:GfxDevice>(&mut self, state:&MagenBoyState, gfx_device:&mut GFX, receiver:crossbeam_channel::Receiver<usize>){
59+
pub fn pop_game_menu<GFX: GfxDevice>(
60+
&mut self,
61+
state: &MagenBoyState,
62+
gfx_device: &mut GFX,
63+
receiver: crossbeam_channel::Receiver<usize>
64+
) {
5165
match self.get_game_menu_selection(state, gfx_device, receiver){
5266
EmulatorMenuOption::Resume => {},
67+
EmulatorMenuOption::Turbo => {
68+
let new_turbo_state = !state.turbo.load(std::sync::atomic::Ordering::Relaxed);
69+
state.turbo.store(new_turbo_state, std::sync::atomic::Ordering::Relaxed);
70+
log::info!("Turbo mode is: {new_turbo_state}");
71+
},
5372
EmulatorMenuOption::Restart => state.running.store(false, std::sync::atomic::Ordering::Relaxed),
5473
EmulatorMenuOption::Shutdown => {
5574
state.running.store(false, std::sync::atomic::Ordering::Relaxed);
@@ -58,7 +77,12 @@ cfg_if::cfg_if!{ if #[cfg(feature = "std")]{
5877
}
5978
}
6079

61-
fn get_game_menu_selection<GFX:GfxDevice>(&mut self, state:&MagenBoyState,gfx_device:&mut GFX, emulation_framebuffer_channel:crossbeam_channel::Receiver<usize>)->&EmulatorMenuOption{
80+
fn get_game_menu_selection<GFX: GfxDevice>(
81+
&mut self,
82+
state: &MagenBoyState,
83+
gfx_device: &mut GFX,
84+
emulation_framebuffer_channel: crossbeam_channel::Receiver<usize>
85+
) -> &EmulatorMenuOption {
6286
let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(gfx_device);
6387

6488
let mut menu = JoypadMenu::new(&GAME_MENU_OPTIONS, &self.header, menu_renderer);
@@ -67,10 +91,14 @@ cfg_if::cfg_if!{ if #[cfg(feature = "std")]{
6791
state.pause.store(true, std::sync::atomic::Ordering::SeqCst);
6892
loop{
6993
if let Ok(_lock) = state.state_mutex.try_lock(){
94+
// Turn off turbo to have a regular speed menu
95+
let last_turbo = state.turbo.swap(false, core::sync::atomic::Ordering::Relaxed);
7096
let selection = menu.get_menu_selection(&mut self.provider);
97+
// restore turbo state
98+
state.turbo.store(last_turbo, core::sync::atomic::Ordering::Relaxed);
7199
state.pause.store(false, std::sync::atomic::Ordering::SeqCst);
72100
return selection;
73-
}else{
101+
} else {
74102
// try recv in order to clear frames from the channel
75103
// in order to not block the emualtion thread and allow it to finish the frame
76104
let _ = emulation_framebuffer_channel.try_recv();
@@ -80,7 +108,11 @@ cfg_if::cfg_if!{ if #[cfg(feature = "std")]{
80108

81109
}
82110

83-
pub fn get_rom_selection<MR:MenuRenderer<PathBuf, String>, JP:MenuJoypadProvider + JoypadProvider>(roms_path:&str, menu_renderer:MR, jp:&mut JP)->String{
111+
pub fn get_rom_selection<MR:MenuRenderer<PathBuf, String>, JP:MenuJoypadProvider + JoypadProvider>(
112+
roms_path: &str,
113+
menu_renderer: MR,
114+
jp:&mut JP
115+
) -> String {
84116
let mut menu_options = Vec::new();
85117
let dir_entries = std::fs::read_dir(roms_path).expect(std::format!("Error openning the roms directory: {}",roms_path).as_str());
86118
for entry in dir_entries{

sdl/src/main.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use sdl2::sys::*;
1313

1414
use crate::{sdl_gfx_device::SdlGfxDevice, audio::*, SdlAudioDevice};
1515

16-
const TURBO_MUL:u8 = 1;
16+
const TURBO_FACTOR:u8 = 4;
1717

1818
const SCREEN_SCALE:usize = 4;
1919
use sdl2::sys::SDL_Scancode;
@@ -38,8 +38,13 @@ fn main() {
3838
}
3939

4040
// Initialize the gfx first cause it initialize both the screen and the sdl context for the joypad
41-
let mut gfx_device: SdlGfxDevice = SdlGfxDevice::new(header.as_str(), SCREEN_SCALE, TURBO_MUL,
42-
check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen"));
41+
let mut gfx_device: SdlGfxDevice = SdlGfxDevice::new(
42+
header.as_str(),
43+
SCREEN_SCALE,
44+
TURBO_FACTOR,
45+
check_for_terminal_feature_flag(&args, "--no-vsync"),
46+
check_for_terminal_feature_flag(&args, "--full-screen")
47+
);
4348

4449
while !(EMULATOR_STATE.exit.load(std::sync::atomic::Ordering::Relaxed)){
4550
let mut provider = sdl_joypad_provider::SdlJoypadProvider::new(KEYBOARD_MAPPING, true);
@@ -112,7 +117,7 @@ fn main() {
112117
// Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread
113118
fn emulation_thread_main(args: Vec<String>, program_name: String, spsc_gfx_device: MpmcGfxDevice, #[cfg(feature = "dbg")] debugger_sender: crossbeam_channel::Sender<terminal_debugger::PpuLayerResult>) {
114119
let mut devices: Vec::<Box::<dyn AudioDevice>> = Vec::new();
115-
let audio_device = SdlAudioDevice::<ManualAudioResampler>::new(44100, TURBO_MUL);
120+
let audio_device = SdlAudioDevice::<ManualAudioResampler>::new(44100, TURBO_FACTOR);
116121
devices.push(Box::new(audio_device));
117122

118123
if check_for_terminal_feature_flag(&args, "--file-audio"){

sdl/src/sdl_gfx_device.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use std::ffi::{CString, c_void};
2+
23
use sdl2::sys::*;
4+
5+
use magenboy_common::EMULATOR_STATE;
36
use magenboy_core::{ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, utils::vec2::Vec2, GfxDevice, Pixel};
7+
48
use super::utils::get_sdl_error_message;
59

610
// The bit order is high bits -> low bits as opposed to RGB555 in the gbdev docs which is low -> high.
@@ -75,23 +79,32 @@ impl Drop for SdlWindow{
7579

7680
pub struct SdlGfxDevice{
7781
sdl_window:SdlWindow,
78-
discard:u8,
79-
turbo_mul:u8,
82+
turbo_counter:u8,
83+
turbo_factor:u8,
8084
}
8185

8286
impl SdlGfxDevice{
83-
pub fn new(window_name:&str, screen_scale: usize, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{
87+
pub fn new(window_name: &str, screen_scale: usize, turbo_factor:u8, disable_vsync:bool, full_screen:bool)->Self{
8488

85-
let window_flags = if full_screen{
89+
let window_flags = if full_screen {
8690
// Hide cursor
8791
unsafe{SDL_ShowCursor(0);}
8892
SDL_WindowFlags::SDL_WINDOW_FULLSCREEN_DESKTOP as u32
89-
}
90-
else{
93+
} else {
9194
SDL_WindowFlags::SDL_WINDOW_RESIZABLE as u32
9295
};
9396

94-
return Self{discard:0, turbo_mul, sdl_window: SdlWindow::new(window_name, Vec2{x:SCREEN_WIDTH, y:SCREEN_HEIGHT}, screen_scale, disable_vsync, window_flags)};
97+
return Self{
98+
turbo_counter: 0,
99+
turbo_factor,
100+
sdl_window: SdlWindow::new(
101+
window_name,
102+
Vec2{x: SCREEN_WIDTH, y: SCREEN_HEIGHT},
103+
screen_scale,
104+
disable_vsync,
105+
window_flags
106+
)
107+
};
95108
}
96109

97110
pub fn poll_event(&self)->Option<SDL_Event>{
@@ -109,10 +122,13 @@ impl SdlGfxDevice{
109122

110123
impl GfxDevice for SdlGfxDevice{
111124
fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) {
112-
self.discard = (self.discard + 1) % self.turbo_mul;
113-
if self.discard != 0{
114-
return;
125+
if EMULATOR_STATE.turbo.load(std::sync::atomic::Ordering::Relaxed) {
126+
self.turbo_counter = (self.turbo_counter + 1) % self.turbo_factor;
127+
if self.turbo_counter != 0{
128+
return;
129+
}
115130
}
131+
116132
self.sdl_window.render(buffer);
117133
}
118134
}

0 commit comments

Comments
 (0)