From b86b940bce754939c60c3a2917b7ba76a604a720 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 17 Dec 2021 20:07:20 +0200 Subject: [PATCH 01/70] Full screen now hides the cursor --- gb/src/sdl_gfx_device.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 28b51a5e..c64d8628 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -24,6 +24,8 @@ impl SdlGfxDevice{ #[cfg(feature = "static-scale")] log::warn!("Please notice that this binary have been compiled with the static-scale feature and you are running with the full screen option.\nThe rendering window might be in wrong scale."); + // Hide cursor + SDL_ShowCursor(0); SDL_WindowFlags::SDL_WINDOW_FULLSCREEN_DESKTOP as u32 } else{ From 55b8ffeb091676d769333741c5d1d6939f224e5c Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 18 Dec 2021 01:19:38 +0200 Subject: [PATCH 02/70] Basic POC for gpio joypad --- Cargo.lock | 11 +++++++++++ gb/Cargo.toml | 1 + gb/src/gpio_joypad_provider.rs | 36 ++++++++++++++++++++++++++++++++++ gb/src/main.rs | 4 +++- 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 gb/src/gpio_joypad_provider.rs diff --git a/Cargo.lock b/Cargo.lock index 0660ac1c..76f01e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "fern", "lib_gb", "log", + "rppal", "sdl2", "wav", ] @@ -1012,6 +1013,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" +[[package]] +name = "rppal" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c88c9c6248de4d337747b619d8f671055ef48a87dc21b97998833f189a0bbd4f" +dependencies = [ + "lazy_static", + "libc", +] + [[package]] name = "rustc_version" version = "0.4.0" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 7eac1e63..5111bfda 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -18,6 +18,7 @@ sdl2 = "0.34" wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" +rppal = "0.13" [features] default = ["static-sdl"] diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs new file mode 100644 index 00000000..788180b1 --- /dev/null +++ b/gb/src/gpio_joypad_provider.rs @@ -0,0 +1,36 @@ +use lib_gb::keypad::{ + joypad::Joypad, + joypad_provider::JoypadProvider, + button::Button +}; +use rppal::gpio::{ + Gpio, + Level +}; + +pub struct GpioJoypadProvider{ + gpio: Gpio +} + +impl GpioJoypadProvider{ + pub fn new()->Self{ + Self{ + gpio: Gpio::new().unwrap() + } + } + + fn read_pin(&self, bcm_pin_number:u8)->bool{ + let pin = self.gpio.get(bcm_pin_number).unwrap(); + return match pin.read(){ + Level::High=>false, + Level::Low=>true + }; + } +} + +impl JoypadProvider for GpioJoypadProvider{ + fn provide(&mut self, joypad:&mut Joypad){ + joypad.buttons[Button::A as usize] = self.read_pin(2); + joypad.buttons[Button::B as usize] = self.read_pin(3); + } +} \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index ce6dee6a..91dace62 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,5 +1,6 @@ mod mbc_handler; mod sdl_joypad_provider; +mod gpio_joypad_provider; mod sdl_gfx_device; mod mpmc_gfx_device; mod audio; @@ -117,7 +118,8 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic } let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); - let joypad_provider = SdlJoypadProvider::new(buttons_mapper); + // let joypad_provider = SdlJoypadProvider::new(buttons_mapper); + let joypad_provider = gpio_joypad_provider::GpioJoypadProvider::new(); let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ let index = args.iter().position(|v| *v == String::from("--bootrom")).unwrap(); args.get(index + 1).expect("Error! you must specify a value for the --bootrom parameter").clone() From 69ae57860875eec7dd38ef2d6d6aa581462b030f Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 14 Jan 2022 13:54:37 +0200 Subject: [PATCH 03/70] Add most of the gpio buttons --- gb/src/gpio_joypad_provider.rs | 40 ++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index 788180b1..9c7da6a5 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -1,36 +1,48 @@ use lib_gb::keypad::{ - joypad::Joypad, + joypad::{Joypad, NUM_OF_KEYS}, joypad_provider::JoypadProvider, button::Button }; use rppal::gpio::{ Gpio, - Level + InputPin }; pub struct GpioJoypadProvider{ - gpio: Gpio + input_pins:[Option;NUM_OF_KEYS] } impl GpioJoypadProvider{ pub fn new()->Self{ + + let gpio = Gpio::new().unwrap(); + let a_pin = gpio.get(18).unwrap().into_input(); + let b_pin = gpio.get(17).unwrap().into_input(); + let up_pin = gpio.get(16).unwrap().into_input(); + let down_pin = gpio.get(20).unwrap().into_input(); + let right_pin = gpio.get(21).unwrap().into_input(); + let left_pin = gpio.get(19).unwrap().into_input(); + + let mut pins:[Option;NUM_OF_KEYS] = [None;NUM_OF_KEYS]; + pins[Button::A as u8] = Some(a_pin); + pins[Button::B as u8] = Some(b_pin); + pins[Button::Up as u8] = Some(up_pin); + pins[Button::Down as u8] = Some(down_pin); + pins[Button::Right as u8] = Some(right_pin); + pins[Button::Left as u8] = Some(left_pin); + Self{ - gpio: Gpio::new().unwrap() + input_pins:pins } } - - fn read_pin(&self, bcm_pin_number:u8)->bool{ - let pin = self.gpio.get(bcm_pin_number).unwrap(); - return match pin.read(){ - Level::High=>false, - Level::Low=>true - }; - } } impl JoypadProvider for GpioJoypadProvider{ fn provide(&mut self, joypad:&mut Joypad){ - joypad.buttons[Button::A as usize] = self.read_pin(2); - joypad.buttons[Button::B as usize] = self.read_pin(3); + for i in 0..NUM_OF_KEYS{ + if self.input_pins[i] = Some(pin){ + joypad[i] = pin.is_high(); + } + } } } \ No newline at end of file From 46d2f40e5fbc5324921bb2fa22744cc8308a6a61 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 14 Jan 2022 17:05:43 +0200 Subject: [PATCH 04/70] Fixed the gpio input --- gb/src/gpio_joypad_provider.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index 9c7da6a5..ad8cf302 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -3,6 +3,7 @@ use lib_gb::keypad::{ joypad_provider::JoypadProvider, button::Button }; +use lib_gb::utils::create_default_array; use rppal::gpio::{ Gpio, InputPin @@ -23,13 +24,13 @@ impl GpioJoypadProvider{ let right_pin = gpio.get(21).unwrap().into_input(); let left_pin = gpio.get(19).unwrap().into_input(); - let mut pins:[Option;NUM_OF_KEYS] = [None;NUM_OF_KEYS]; - pins[Button::A as u8] = Some(a_pin); - pins[Button::B as u8] = Some(b_pin); - pins[Button::Up as u8] = Some(up_pin); - pins[Button::Down as u8] = Some(down_pin); - pins[Button::Right as u8] = Some(right_pin); - pins[Button::Left as u8] = Some(left_pin); + let mut pins:[Option;NUM_OF_KEYS] = create_default_array(); + pins[Button::A as usize] = Some(a_pin); + pins[Button::B as usize] = Some(b_pin); + pins[Button::Up as usize] = Some(up_pin); + pins[Button::Down as usize] = Some(down_pin); + pins[Button::Right as usize] = Some(right_pin); + pins[Button::Left as usize] = Some(left_pin); Self{ input_pins:pins @@ -40,8 +41,8 @@ impl GpioJoypadProvider{ impl JoypadProvider for GpioJoypadProvider{ fn provide(&mut self, joypad:&mut Joypad){ for i in 0..NUM_OF_KEYS{ - if self.input_pins[i] = Some(pin){ - joypad[i] = pin.is_high(); + if let Some(pin) = &self.input_pins[i] { + joypad.buttons[i] = pin.is_high(); } } } From c900802acb02ecf06240d73d51e44773deb0db44 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 15 Jan 2022 02:33:58 +0200 Subject: [PATCH 05/70] Better gfx error handling --- gb/src/sdl_gfx_device.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index c64d8628..bfb7a771 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -18,7 +18,9 @@ impl SdlGfxDevice{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ - SDL_Init(SDL_INIT_VIDEO); + if SDL_Init(SDL_INIT_VIDEO){ + std::panic!("Init error: {}", Self::get_sdl_error_message()); + } let window_flags = if full_screen{ #[cfg(feature = "static-scale")] @@ -55,7 +57,7 @@ impl SdlGfxDevice{ } else{ if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{ - std::panic!("Error while setting logical rendering"); + std::panic!("Error while setting logical rendering\nError:{}", Self::get_sdl_error_message()); } texture_height = SCREEN_HEIGHT as i32; texture_width = SCREEN_WIDTH as i32; @@ -80,6 +82,14 @@ impl SdlGfxDevice{ } } + fn get_sdl_error_message()->&'static str{ + unsafe{ + let error_message:*const c_char = SDL_GetError(); + + return CStr::from_ptr(error_message).to_str().unwrap(); + } + } + #[cfg(feature = "static-scale")] fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ let mut new_vec = vec![0;vec.len()*scale*scale]; From 1a59103f3457dd359e29694860f15f9e2bf285ea Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 15 Jan 2022 02:40:46 +0200 Subject: [PATCH 06/70] Some fixes to compile --- gb/src/sdl_gfx_device.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index bfb7a771..4ccf5ef1 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -1,4 +1,7 @@ use std::ffi::{CString, c_void}; +use std::{ffi::CStr, mem::MaybeUninit}; +use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; +use sdl2::{libc::c_char, sys::*}; use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; use sdl2::sys::*; @@ -18,7 +21,7 @@ impl SdlGfxDevice{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ - if SDL_Init(SDL_INIT_VIDEO){ + if SDL_Init(SDL_INIT_VIDEO) != 0{ std::panic!("Init error: {}", Self::get_sdl_error_message()); } From 0b73f373acad8e10c040f6133df075fdedaeb91e Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 15 Jan 2022 22:27:10 +0200 Subject: [PATCH 07/70] Rearange the filetree, in order to add target --- Cargo.lock | 43 +++++++ gb/Cargo.toml | 6 +- gb/src/audio/audio_resampler.rs | 29 +++++ gb/src/audio/magen_audio_resampler.rs | 3 +- gb/src/audio/mod.rs | 86 -------------- gb/src/audio/wav_file_audio_device.rs | 3 +- gb/src/gpio_joypad_provider.rs | 21 ++-- gb/src/main.rs | 110 ++++++++++++++---- gb/src/{audio => sdl}/sdl_audio_resampler.rs | 3 +- gb/src/{ => sdl}/sdl_gfx_device.rs | 19 +-- gb/src/{ => sdl}/sdl_joypad_provider.rs | 0 .../{audio => sdl}/sdl_pull_audio_device.rs | 4 +- .../{audio => sdl}/sdl_push_audio_device.rs | 5 +- gb/src/sdl/utils.rs | 34 ++++++ 14 files changed, 223 insertions(+), 143 deletions(-) create mode 100644 gb/src/audio/audio_resampler.rs delete mode 100644 gb/src/audio/mod.rs rename gb/src/{audio => sdl}/sdl_audio_resampler.rs (97%) rename gb/src/{ => sdl}/sdl_gfx_device.rs (90%) rename gb/src/{ => sdl}/sdl_joypad_provider.rs (100%) rename gb/src/{audio => sdl}/sdl_pull_audio_device.rs (97%) rename gb/src/{audio => sdl}/sdl_push_audio_device.rs (94%) create mode 100644 gb/src/sdl/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 76f01e0e..4f593f92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,27 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fern" version = "0.6.0" @@ -363,6 +384,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "framebuffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878caaaf1bb92c9f707dc6eef90933e07e913dac4bb8e11e145eaabaa94ef149" +dependencies = [ + "errno", + "libc", + "memmap", +] + [[package]] name = "futures-channel" version = "0.3.17" @@ -420,6 +452,7 @@ dependencies = [ "chrono", "crossbeam-channel", "fern", + "framebuffer", "lib_gb", "log", "rppal", @@ -643,6 +676,16 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "memoffset" version = "0.6.4" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 5111bfda..a3c354b4 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -18,11 +18,13 @@ sdl2 = "0.34" wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" -rppal = "0.13" +rppal = {version = "0.13", optional = true} +framebuffer = "0.3" [features] default = ["static-sdl"] sdl-resample = [] push-audio = [] static-sdl = ["sdl2/bundled", "sdl2/static-link"] -static-scale = [] \ No newline at end of file +static-scale = [] +gpio = ["rppal"] \ No newline at end of file diff --git a/gb/src/audio/audio_resampler.rs b/gb/src/audio/audio_resampler.rs new file mode 100644 index 00000000..002e2eef --- /dev/null +++ b/gb/src/audio/audio_resampler.rs @@ -0,0 +1,29 @@ +use lib_gb::apu::audio_device::{Sample, AudioDevice, StereoSample, BUFFER_SIZE}; + +pub trait AudioResampler{ + fn new(original_frequency:u32, target_frequency:u32)->Self; + fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec; +} + +pub trait ResampledAudioDevice : AudioDevice{ + const VOLUME:Sample = 10 as Sample; + + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ + let resample = self.get_resampler().resample(buffer); + for sample in resample{ + let(buffer, index) = self.get_audio_buffer(); + buffer[*index] = sample.left_sample * Self::VOLUME; + buffer[*index + 1] = sample.left_sample * Self::VOLUME; + *index += 2; + if *index == BUFFER_SIZE{ + *index = 0; + self.full_buffer_callback().unwrap(); + } + } + } + + fn get_audio_buffer(&mut self)->(&mut [Sample;BUFFER_SIZE], &mut usize); + fn get_resampler(&mut self)->&mut AR; + fn full_buffer_callback(&mut self)->Result<(), String>; + fn new(frequency:i32, turbo_mul:u8)->Self; +} diff --git a/gb/src/audio/magen_audio_resampler.rs b/gb/src/audio/magen_audio_resampler.rs index 69f868c1..06b49571 100644 --- a/gb/src/audio/magen_audio_resampler.rs +++ b/gb/src/audio/magen_audio_resampler.rs @@ -1,5 +1,6 @@ use lib_gb::apu::audio_device::{BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}; -use super::AudioResampler; +use super::audio_resampler::AudioResampler; + pub struct MagenAudioResampler{ to_skip:u32, diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs deleted file mode 100644 index bb17c34e..00000000 --- a/gb/src/audio/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -pub mod multi_device_audio; -pub mod wav_file_audio_device; -cfg_if::cfg_if!{ - if #[cfg(feature = "push-audio")]{ - pub mod sdl_push_audio_device; - pub type ChosenAudioDevice = sdl_push_audio_device::SdlPushAudioDevice; - } - else{ - pub mod sdl_pull_audio_device; - pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; - } -} -cfg_if::cfg_if!{ - if #[cfg(feature = "sdl-resample")]{ - pub mod sdl_audio_resampler; - pub type ChosenResampler = sdl_audio_resampler::SdlAudioResampler; - } - else{ - pub mod magen_audio_resampler; - pub type ChosenResampler = magen_audio_resampler::MagenAudioResampler; - } -} - -use std::{ffi::CStr, mem::MaybeUninit}; -use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; -use sdl2::{libc::c_char, sys::*}; - -fn get_sdl_error_message()->&'static str{ - unsafe{ - let error_message:*const c_char = SDL_GetError(); - - return CStr::from_ptr(error_message).to_str().unwrap(); - } -} - -fn init_sdl_audio_device(audio_spec:&SDL_AudioSpec)->SDL_AudioDeviceID{ - let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); - - unsafe{ - SDL_Init(SDL_INIT_AUDIO); - SDL_ClearError(); - let id = SDL_OpenAudioDevice(std::ptr::null(), 0, audio_spec, uninit_audio_spec.as_mut_ptr() , 0); - - if id == 0{ - std::panic!("{}", get_sdl_error_message()); - } - - let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); - - if init_audio_spec.freq != audio_spec.freq { - std::panic!("Error initializing audio could not use the frequency: {}", audio_spec.freq); - } - - //This will start the audio processing - SDL_PauseAudioDevice(id, 0); - return id; - } -} - -pub trait AudioResampler{ - fn new(original_frequency:u32, target_frequency:u32)->Self; - fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec; -} - -pub trait ResampledAudioDevice : AudioDevice{ - const VOLUME:Sample = 10 as Sample; - - fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ - let resample = self.get_resampler().resample(buffer); - for sample in resample{ - let(buffer, index) = self.get_audio_buffer(); - buffer[*index] = sample.left_sample * Self::VOLUME; - buffer[*index + 1] = sample.left_sample * Self::VOLUME; - *index += 2; - if *index == BUFFER_SIZE{ - *index = 0; - self.full_buffer_callback().unwrap(); - } - } - } - - fn get_audio_buffer(&mut self)->(&mut [Sample;BUFFER_SIZE], &mut usize); - fn get_resampler(&mut self)->&mut AR; - fn full_buffer_callback(&mut self)->Result<(), String>; - fn new(frequency:i32, turbo_mul:u8)->Self; -} diff --git a/gb/src/audio/wav_file_audio_device.rs b/gb/src/audio/wav_file_audio_device.rs index e2377fcc..f12dd1e6 100644 --- a/gb/src/audio/wav_file_audio_device.rs +++ b/gb/src/audio/wav_file_audio_device.rs @@ -1,5 +1,6 @@ use lib_gb::apu::audio_device::*; -use super::AudioResampler; + +use super::audio_resampler::AudioResampler; pub struct WavfileAudioDevice{ target_frequency:u32, diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index ad8cf302..ad639466 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -4,25 +4,24 @@ use lib_gb::keypad::{ button::Button }; use lib_gb::utils::create_default_array; -use rppal::gpio::{ - Gpio, - InputPin -}; +use rppal::gpio::{Gpio, InputPin}; + +pub type GpioPin = u8; pub struct GpioJoypadProvider{ input_pins:[Option;NUM_OF_KEYS] } impl GpioJoypadProvider{ - pub fn new()->Self{ + pub fn newGpioPin>(mapper:F)->Self{ let gpio = Gpio::new().unwrap(); - let a_pin = gpio.get(18).unwrap().into_input(); - let b_pin = gpio.get(17).unwrap().into_input(); - let up_pin = gpio.get(16).unwrap().into_input(); - let down_pin = gpio.get(20).unwrap().into_input(); - let right_pin = gpio.get(21).unwrap().into_input(); - let left_pin = gpio.get(19).unwrap().into_input(); + let a_pin = gpio.get(mapper(Button::A)).unwrap().into_input(); + let b_pin = gpio.get(mapper(Button::B)).unwrap().into_input(); + let up_pin = gpio.get(mapper(Button::Up)).unwrap().into_input(); + let down_pin = gpio.get(mapper(Button::Down)).unwrap().into_input(); + let right_pin = gpio.get(mapper(Button::Right)).unwrap().into_input(); + let left_pin = gpio.get(mapper(Button::Left)).unwrap().into_input(); let mut pins:[Option;NUM_OF_KEYS] = create_default_array(); pins[Button::A as usize] = Some(a_pin); diff --git a/gb/src/main.rs b/gb/src/main.rs index 91dace62..b53c037f 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,15 +1,88 @@ mod mbc_handler; -mod sdl_joypad_provider; -mod gpio_joypad_provider; -mod sdl_gfx_device; mod mpmc_gfx_device; -mod audio; +// mod linuxfb_gfx_device; + +mod audio{ + pub mod audio_resampler; + pub mod multi_device_audio; + pub mod wav_file_audio_device; + #[cfg(not(feature = "sdl-resample"))] + pub mod magen_audio_resampler; +} + +mod sdl{ + pub mod utils; + pub mod sdl_gfx_device; + #[cfg(feature = "sdl-resample")] + pub mod sdl_audio_resampler; + + cfg_if::cfg_if!{ + if #[cfg(feature = "push-audio")]{ + pub mod sdl_push_audio_device; + pub type ChosenAudioDevice = sdl_push_audio_device::SdlPushAudioDevice; + } + else{ + pub mod sdl_pull_audio_device; + pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; + } + } + + cfg_if::cfg_if!{ + if #[cfg(not(feature = "gpio"))]{ + pub mod sdl_joypad_provider; + } + } +} + +cfg_if::cfg_if!{ + if #[cfg(feature = "sdl-resample")]{ + pub type ChosenResampler = sdl::sdl_audio_resampler::SdlAudioResampler; + } + else{ + pub type ChosenResampler = audio::magen_audio_resampler::MagenAudioResampler; + } +} -use crate::{audio::{ChosenResampler, multi_device_audio::*, ResampledAudioDevice}, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice, sdl_joypad_provider::*}; -use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +cfg_if::cfg_if!{ + if #[cfg(feature = "gpio")]{ + mod gpio_joypad_provider; + use crate::gpio_joypad_provider::*; + fn buttons_mapper(button:Button)->GpioPin{ + match button{ + Button::A => 18, + Button::B => 17, + Button::Start => 0, + Button::Select => 0, + Button::Up => 16, + Button::Down => 20, + Button::Right => 21, + Button::Left => 19 + } + } + } + else{ + use lib_gb::keypad::button::Button; + use sdl2::sys::SDL_Scancode; + fn buttons_mapper(button:Button)->SDL_Scancode{ + match button{ + Button::A => SDL_Scancode::SDL_SCANCODE_X, + Button::B => SDL_Scancode::SDL_SCANCODE_Z, + Button::Start => SDL_Scancode::SDL_SCANCODE_S, + Button::Select => SDL_Scancode::SDL_SCANCODE_A, + Button::Up => SDL_Scancode::SDL_SCANCODE_UP, + Button::Down => SDL_Scancode::SDL_SCANCODE_DOWN, + Button::Right => SDL_Scancode::SDL_SCANCODE_RIGHT, + Button::Left => SDL_Scancode::SDL_SCANCODE_LEFT + } + } + } +} + +use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +use sdl2::sys::*; use std::{fs, env, result::Result, vec::Vec}; use log::info; -use sdl2::sys::*; const SCREEN_SCALE:usize = 4; const TURBO_MUL:u8 = 1; @@ -39,19 +112,6 @@ fn init_logger(debug:bool)->Result<(), fern::InitError>{ Ok(()) } -fn buttons_mapper(button:Button)->SDL_Scancode{ - match button{ - Button::A => SDL_Scancode::SDL_SCANCODE_X, - Button::B => SDL_Scancode::SDL_SCANCODE_Z, - Button::Start => SDL_Scancode::SDL_SCANCODE_S, - Button::Select => SDL_Scancode::SDL_SCANCODE_A, - Button::Up => SDL_Scancode::SDL_SCANCODE_UP, - Button::Down => SDL_Scancode::SDL_SCANCODE_DOWN, - Button::Right => SDL_Scancode::SDL_SCANCODE_RIGHT, - Button::Left => SDL_Scancode::SDL_SCANCODE_LEFT - } -} - fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ args.len() >= 3 && args.contains(&String::from(flag)) } @@ -66,7 +126,7 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); @@ -107,7 +167,7 @@ fn main() { // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { - let audio_device = audio::ChosenAudioDevice::::new(44100, TURBO_MUL); + let audio_device = sdl::ChosenAudioDevice::::new(44100, TURBO_MUL); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); @@ -118,8 +178,10 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic } let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); - // let joypad_provider = SdlJoypadProvider::new(buttons_mapper); - let joypad_provider = gpio_joypad_provider::GpioJoypadProvider::new(); + #[cfg(not(feature = "gpio"))] + let joypad_provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); + #[cfg(feature = "gpio")] + let joypad_provider = GpioJoypadProvider::new(buttons_mapper); let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ let index = args.iter().position(|v| *v == String::from("--bootrom")).unwrap(); args.get(index + 1).expect("Error! you must specify a value for the --bootrom parameter").clone() diff --git a/gb/src/audio/sdl_audio_resampler.rs b/gb/src/sdl/sdl_audio_resampler.rs similarity index 97% rename from gb/src/audio/sdl_audio_resampler.rs rename to gb/src/sdl/sdl_audio_resampler.rs index 00e1c339..3117920d 100644 --- a/gb/src/audio/sdl_audio_resampler.rs +++ b/gb/src/sdl/sdl_audio_resampler.rs @@ -1,7 +1,8 @@ use std::mem::MaybeUninit; use lib_gb::apu::audio_device::{BUFFER_SIZE, StereoSample}; use sdl2::sys::*; -use super::AudioResampler; + +use crate::audio::audio_resampler::AudioResampler; pub struct SdlAudioResampler{ cvt: SDL_AudioCVT diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs similarity index 90% rename from gb/src/sdl_gfx_device.rs rename to gb/src/sdl/sdl_gfx_device.rs index 4ccf5ef1..0d91ec44 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -1,10 +1,9 @@ use std::ffi::{CString, c_void}; -use std::{ffi::CStr, mem::MaybeUninit}; -use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; -use sdl2::{libc::c_char, sys::*}; +use sdl2::sys::*; use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; -use sdl2::sys::*; + +use crate::sdl::utils::get_sdl_error_message; pub struct SdlGfxDevice{ _window_name: CString, @@ -22,7 +21,7 @@ impl SdlGfxDevice{ let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ if SDL_Init(SDL_INIT_VIDEO) != 0{ - std::panic!("Init error: {}", Self::get_sdl_error_message()); + std::panic!("Init error: {}", get_sdl_error_message()); } let window_flags = if full_screen{ @@ -60,7 +59,7 @@ impl SdlGfxDevice{ } else{ if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{ - std::panic!("Error while setting logical rendering\nError:{}", Self::get_sdl_error_message()); + std::panic!("Error while setting logical rendering\nError:{}", get_sdl_error_message()); } texture_height = SCREEN_HEIGHT as i32; texture_width = SCREEN_WIDTH as i32; @@ -85,14 +84,6 @@ impl SdlGfxDevice{ } } - fn get_sdl_error_message()->&'static str{ - unsafe{ - let error_message:*const c_char = SDL_GetError(); - - return CStr::from_ptr(error_message).to_str().unwrap(); - } - } - #[cfg(feature = "static-scale")] fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ let mut new_vec = vec![0;vec.len()*scale*scale]; diff --git a/gb/src/sdl_joypad_provider.rs b/gb/src/sdl/sdl_joypad_provider.rs similarity index 100% rename from gb/src/sdl_joypad_provider.rs rename to gb/src/sdl/sdl_joypad_provider.rs diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/sdl/sdl_pull_audio_device.rs similarity index 97% rename from gb/src/audio/sdl_pull_audio_device.rs rename to gb/src/sdl/sdl_pull_audio_device.rs index efacca24..e0d8f2a8 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/sdl/sdl_pull_audio_device.rs @@ -1,9 +1,9 @@ use std::ffi::c_void; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; -use super::{AudioResampler, ResampledAudioDevice, init_sdl_audio_device}; - use sdl2::sys::*; use crossbeam_channel::{Receiver, Sender, bounded}; +use crate::audio::audio_resampler::{AudioResampler, ResampledAudioDevice}; +use super::utils::init_sdl_audio_device; const BUFFERS_NUMBER:usize = 3; diff --git a/gb/src/audio/sdl_push_audio_device.rs b/gb/src/sdl/sdl_push_audio_device.rs similarity index 94% rename from gb/src/audio/sdl_push_audio_device.rs rename to gb/src/sdl/sdl_push_audio_device.rs index dbe1ec08..522ea09d 100644 --- a/gb/src/audio/sdl_push_audio_device.rs +++ b/gb/src/sdl/sdl_push_audio_device.rs @@ -1,7 +1,10 @@ use std::{ffi::c_void, str::FromStr}; use lib_gb::{GB_FREQUENCY, apu::audio_device::{AudioDevice, BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}}; use sdl2::sys::*; -use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message, init_sdl_audio_device}; + +use crate::audio::audio_resampler::{AudioResampler, ResampledAudioDevice}; + +use super::utils::{init_sdl_audio_device, get_sdl_error_message}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; diff --git a/gb/src/sdl/utils.rs b/gb/src/sdl/utils.rs new file mode 100644 index 00000000..f8f41bb7 --- /dev/null +++ b/gb/src/sdl/utils.rs @@ -0,0 +1,34 @@ +use std::{ffi::CStr, mem::MaybeUninit}; +use sdl2::{libc::c_char, sys::*}; + +pub fn get_sdl_error_message()->&'static str{ + unsafe{ + let error_message:*const c_char = SDL_GetError(); + + return CStr::from_ptr(error_message).to_str().unwrap(); + } +} + +pub fn init_sdl_audio_device(audio_spec:&SDL_AudioSpec)->SDL_AudioDeviceID{ + let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + + unsafe{ + SDL_Init(SDL_INIT_AUDIO); + SDL_ClearError(); + let id = SDL_OpenAudioDevice(std::ptr::null(), 0, audio_spec, uninit_audio_spec.as_mut_ptr() , 0); + + if id == 0{ + std::panic!("{}", get_sdl_error_message()); + } + + let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); + + if init_audio_spec.freq != audio_spec.freq { + std::panic!("Error initializing audio could not use the frequency: {}", audio_spec.freq); + } + + //This will start the audio processing + SDL_PauseAudioDevice(id, 0); + return id; + } +} \ No newline at end of file From c76c68a56957eef11bfd91f447d1078bb4fa6abd Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 15 Jan 2022 22:27:21 +0200 Subject: [PATCH 08/70] add --- gb/src/sdl/sdl_audio_resampler.rs | 1 - gb/src/sdl/sdl_gfx_device.rs | 2 -- gb/src/sdl/sdl_push_audio_device.rs | 2 -- 3 files changed, 5 deletions(-) diff --git a/gb/src/sdl/sdl_audio_resampler.rs b/gb/src/sdl/sdl_audio_resampler.rs index 3117920d..01b287fa 100644 --- a/gb/src/sdl/sdl_audio_resampler.rs +++ b/gb/src/sdl/sdl_audio_resampler.rs @@ -1,7 +1,6 @@ use std::mem::MaybeUninit; use lib_gb::apu::audio_device::{BUFFER_SIZE, StereoSample}; use sdl2::sys::*; - use crate::audio::audio_resampler::AudioResampler; pub struct SdlAudioResampler{ diff --git a/gb/src/sdl/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs index 0d91ec44..18efa0cf 100644 --- a/gb/src/sdl/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -1,8 +1,6 @@ use std::ffi::{CString, c_void}; use sdl2::sys::*; - use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; - use crate::sdl::utils::get_sdl_error_message; pub struct SdlGfxDevice{ diff --git a/gb/src/sdl/sdl_push_audio_device.rs b/gb/src/sdl/sdl_push_audio_device.rs index 522ea09d..c9eac6c0 100644 --- a/gb/src/sdl/sdl_push_audio_device.rs +++ b/gb/src/sdl/sdl_push_audio_device.rs @@ -1,9 +1,7 @@ use std::{ffi::c_void, str::FromStr}; use lib_gb::{GB_FREQUENCY, apu::audio_device::{AudioDevice, BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}}; use sdl2::sys::*; - use crate::audio::audio_resampler::{AudioResampler, ResampledAudioDevice}; - use super::utils::{init_sdl_audio_device, get_sdl_error_message}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing From fc9dd2debffd0cc172f4aa3004fd527c235d6cff Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sun, 16 Jan 2022 01:02:40 +0200 Subject: [PATCH 09/70] starting to implement linux framebuffer gfx device --- gb/src/linuxfb_gfx_device.rs | 33 +++++++++++++++++++++++++++++++++ gb/src/main.rs | 7 ++++--- 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 gb/src/linuxfb_gfx_device.rs diff --git a/gb/src/linuxfb_gfx_device.rs b/gb/src/linuxfb_gfx_device.rs new file mode 100644 index 00000000..e35a0764 --- /dev/null +++ b/gb/src/linuxfb_gfx_device.rs @@ -0,0 +1,33 @@ +use framebuffer::{Framebuffer, KdMode}; +use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}}; + +pub struct LinuxFbGfxDevice{ + framebuffer: Framebuffer, + buffer:[u8;1228800] +} + +impl LinuxFbGfxDevice{ + pub fn new(framebuffer_path:&str)->Self{ + let mut fb = Framebuffer::new(framebuffer_path).unwrap(); + + fb.var_screen_info.yres_virtual = SCREEN_HEIGHT as u32; + fb.var_screen_info.xres_virtual = SCREEN_WIDTH as u32; + fb.var_screen_info.yres = SCREEN_HEIGHT as u32; + fb.var_screen_info.xres = SCREEN_WIDTH as u32; + + Self{ + buffer:[0;1228800], + framebuffer:fb + } + } +} + +impl GfxDevice for LinuxFbGfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + unsafe{ + let slice = std::slice::from_raw_parts(buffer as *const u32 as *const u8,SCREEN_HEIGHT*SCREEN_WIDTH*4); + std::ptr::copy_nonoverlapping(slice.as_ptr(), self.buffer.as_mut_ptr(), slice.len()); + self.framebuffer.write_frame(&self.buffer); + } + } +} \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index b53c037f..28369fea 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,6 +1,6 @@ mod mbc_handler; mod mpmc_gfx_device; -// mod linuxfb_gfx_device; +mod linuxfb_gfx_device; mod audio{ pub mod audio_resampler; @@ -126,8 +126,9 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, - check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); + // let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + // check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); + let mut sdl_gfx_device = linuxfb_gfx_device::LinuxFbGfxDevice::new("/dev/fb0"); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); From 7af4d85516a47d1b9448d8f4cbedaebf4c3ac911 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 22 Jan 2022 01:45:36 +0200 Subject: [PATCH 10/70] Add quit with escape key --- Cargo.lock | 43 ------------------------------------------- gb/Cargo.toml | 2 +- gb/src/main.rs | 13 +++++++++---- 3 files changed, 10 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f593f92..76f01e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,27 +299,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fern" version = "0.6.0" @@ -384,17 +363,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "framebuffer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878caaaf1bb92c9f707dc6eef90933e07e913dac4bb8e11e145eaabaa94ef149" -dependencies = [ - "errno", - "libc", - "memmap", -] - [[package]] name = "futures-channel" version = "0.3.17" @@ -452,7 +420,6 @@ dependencies = [ "chrono", "crossbeam-channel", "fern", - "framebuffer", "lib_gb", "log", "rppal", @@ -676,16 +643,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "memoffset" version = "0.6.4" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index a3c354b4..511b17f8 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -19,7 +19,7 @@ wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" rppal = {version = "0.13", optional = true} -framebuffer = "0.3" +# framebuffer = "0.3" [features] default = ["static-sdl"] diff --git a/gb/src/main.rs b/gb/src/main.rs index 28369fea..7609814c 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,6 +1,6 @@ mod mbc_handler; mod mpmc_gfx_device; -mod linuxfb_gfx_device; +// mod linuxfb_gfx_device; mod audio{ pub mod audio_resampler; @@ -126,9 +126,9 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - // let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, - // check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); - let mut sdl_gfx_device = linuxfb_gfx_device::LinuxFbGfxDevice::new("/dev/fb0"); + let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); + // let mut sdl_gfx_device = linuxfb_gfx_device::LinuxFbGfxDevice::new("/dev/fb0"); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); @@ -152,6 +152,11 @@ fn main() { if event.type_ == SDL_EventType::SDL_QUIT as u32{ break; } + else if event.type_ == SDL_EventType::SDL_KEYDOWN as u32{ + if event.key.keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE{ + break; + } + } } let buffer = r.recv().unwrap(); From 1aafcfed51b203ddf8dec23814232e5981a6b9dc Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 31 Jan 2022 22:12:39 +0200 Subject: [PATCH 11/70] Change the pixel format from argb to rgb After a lot of work and investigation I have kind of understand why the project did not display properly on the rpi with the SDL KMD/DRM backend. for more info see: https://github.com/libsdl-org/SDL/issues/5273 --- gb/src/sdl/sdl_gfx_device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gb/src/sdl/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs index 18efa0cf..130a2b8a 100644 --- a/gb/src/sdl/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -65,7 +65,7 @@ impl SdlGfxDevice{ } let tex: *mut SDL_Texture = SDL_CreateTexture(rend, - SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, + SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGB888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, texture_width, texture_height); (wind, rend, tex) From 421d9f9bb553a5776d9dcfb443d578bd03aa842f Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 31 Jan 2022 22:41:30 +0200 Subject: [PATCH 12/70] Update dependencies --- Cargo.lock | 107 +++++++++++--------------------------------------- gb/Cargo.toml | 5 +-- 2 files changed, 24 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76f01e0e..50b03b9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - [[package]] name = "atty" version = "0.2.14" @@ -112,12 +103,6 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -179,7 +164,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -224,7 +209,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -234,7 +219,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -245,7 +230,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", @@ -258,7 +243,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -296,7 +281,7 @@ version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -308,25 +293,13 @@ dependencies = [ "log", ] -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "winapi", -] - [[package]] name = "flate2" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80edafed416a46fb378521624fab1cfa2eb514784fd8921adbe8a8d8321da811" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "miniz_oxide", @@ -416,7 +389,7 @@ dependencies = [ name = "gb" version = "1.0.0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "chrono", "crossbeam-channel", "fern", @@ -433,7 +406,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -628,7 +601,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -765,7 +738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "foreign-types", "libc", "once_cell", @@ -947,8 +920,6 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] @@ -1065,9 +1036,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sdl2" -version = "0.34.5" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" +checksum = "f035f8e87735fa3a8437292be49fe6056450f7cbb13c230b4bcd1bdd7279421f" dependencies = [ "bitflags", "lazy_static", @@ -1077,16 +1048,13 @@ dependencies = [ [[package]] name = "sdl2-sys" -version = "0.34.5" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" +checksum = "94cb479353c0603785c834e2307440d83d196bf255f204f7f6741358de8d6a2f" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "cmake", - "flate2", "libc", - "tar", - "unidiff", "version-compare", ] @@ -1196,24 +1164,13 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tar" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "tempfile" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "rand", "redox_syscall", @@ -1338,7 +1295,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "pin-project-lite", "tracing-core", ] @@ -1385,17 +1342,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "unidiff" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a62719acf1933bfdbeb73a657ecd9ecece70b405125267dd549e2e2edc232c" -dependencies = [ - "encoding_rs", - "lazy_static", - "regex", -] - [[package]] name = "url" version = "2.2.2" @@ -1416,9 +1362,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.0.10" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" [[package]] name = "walkdir" @@ -1453,7 +1399,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "serde", "serde_json", "wasm-bindgen-macro", @@ -1480,7 +1426,7 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -1574,15 +1520,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] - [[package]] name = "zip" version = "0.5.13" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 511b17f8..8c2bc753 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -12,14 +12,13 @@ path = "src/main.rs" [dependencies] lib_gb = {path = "../lib_gb/"} log = "0.4" -fern = "0.6.0" +fern = "0.6" chrono = "0.4" -sdl2 = "0.34" +sdl2 = "0.35" wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" rppal = {version = "0.13", optional = true} -# framebuffer = "0.3" [features] default = ["static-sdl"] From 8db5fcf9a629545f005a5d164323052b607f3423 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 31 Jan 2022 22:43:10 +0200 Subject: [PATCH 13/70] Remove traces of linuxfb --- gb/src/linuxfb_gfx_device.rs | 33 --------------------------------- gb/src/main.rs | 2 -- 2 files changed, 35 deletions(-) delete mode 100644 gb/src/linuxfb_gfx_device.rs diff --git a/gb/src/linuxfb_gfx_device.rs b/gb/src/linuxfb_gfx_device.rs deleted file mode 100644 index e35a0764..00000000 --- a/gb/src/linuxfb_gfx_device.rs +++ /dev/null @@ -1,33 +0,0 @@ -use framebuffer::{Framebuffer, KdMode}; -use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}}; - -pub struct LinuxFbGfxDevice{ - framebuffer: Framebuffer, - buffer:[u8;1228800] -} - -impl LinuxFbGfxDevice{ - pub fn new(framebuffer_path:&str)->Self{ - let mut fb = Framebuffer::new(framebuffer_path).unwrap(); - - fb.var_screen_info.yres_virtual = SCREEN_HEIGHT as u32; - fb.var_screen_info.xres_virtual = SCREEN_WIDTH as u32; - fb.var_screen_info.yres = SCREEN_HEIGHT as u32; - fb.var_screen_info.xres = SCREEN_WIDTH as u32; - - Self{ - buffer:[0;1228800], - framebuffer:fb - } - } -} - -impl GfxDevice for LinuxFbGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - unsafe{ - let slice = std::slice::from_raw_parts(buffer as *const u32 as *const u8,SCREEN_HEIGHT*SCREEN_WIDTH*4); - std::ptr::copy_nonoverlapping(slice.as_ptr(), self.buffer.as_mut_ptr(), slice.len()); - self.framebuffer.write_frame(&self.buffer); - } - } -} \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 7609814c..74231e9f 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,6 +1,5 @@ mod mbc_handler; mod mpmc_gfx_device; -// mod linuxfb_gfx_device; mod audio{ pub mod audio_resampler; @@ -128,7 +127,6 @@ fn main() { let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); - // let mut sdl_gfx_device = linuxfb_gfx_device::LinuxFbGfxDevice::new("/dev/fb0"); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); From 069791761da5650190d8df8c902dce4e3a3a81b7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 31 Jan 2022 22:47:33 +0200 Subject: [PATCH 14/70] More refactoring * Arrange the mod code in main * rename file --- ...resampler.rs => manual_audio_resampler.rs} | 8 +++--- gb/src/main.rs | 25 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) rename gb/src/audio/{magen_audio_resampler.rs => manual_audio_resampler.rs} (95%) diff --git a/gb/src/audio/magen_audio_resampler.rs b/gb/src/audio/manual_audio_resampler.rs similarity index 95% rename from gb/src/audio/magen_audio_resampler.rs rename to gb/src/audio/manual_audio_resampler.rs index 06b49571..623daf5b 100644 --- a/gb/src/audio/magen_audio_resampler.rs +++ b/gb/src/audio/manual_audio_resampler.rs @@ -2,7 +2,7 @@ use lib_gb::apu::audio_device::{BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSamp use super::audio_resampler::AudioResampler; -pub struct MagenAudioResampler{ +pub struct ManualAudioResampler{ to_skip:u32, sampling_buffer:Vec, sampling_counter:u32, @@ -12,7 +12,7 @@ pub struct MagenAudioResampler{ skip_to_use:u32, } -impl MagenAudioResampler{ +impl ManualAudioResampler{ fn interpolate_sample(samples:&[StereoSample])->StereoSample{ let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; @@ -21,7 +21,7 @@ impl MagenAudioResampler{ } } -impl AudioResampler for MagenAudioResampler{ +impl AudioResampler for ManualAudioResampler{ fn new(original_frequency:u32, target_frequency:u32)->Self{ // Calling round in order to get the nearest integer and resample as precise as possible let div = original_frequency as f32 / target_frequency as f32; @@ -41,7 +41,7 @@ impl AudioResampler for MagenAudioResampler{ std::panic!("target freqency is too high: {}", target_frequency); } - MagenAudioResampler{ + ManualAudioResampler{ to_skip:to_skip, sampling_buffer:Vec::with_capacity(upper_to_skip as usize), sampling_counter: 0, diff --git a/gb/src/main.rs b/gb/src/main.rs index 74231e9f..0dbb7aa5 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,12 +1,14 @@ mod mbc_handler; mod mpmc_gfx_device; +#[cfg(feature = "gpio")] +mod gpio_joypad_provider; mod audio{ pub mod audio_resampler; pub mod multi_device_audio; pub mod wav_file_audio_device; #[cfg(not(feature = "sdl-resample"))] - pub mod magen_audio_resampler; + pub mod manual_audio_resampler; } mod sdl{ @@ -38,13 +40,21 @@ cfg_if::cfg_if!{ pub type ChosenResampler = sdl::sdl_audio_resampler::SdlAudioResampler; } else{ - pub type ChosenResampler = audio::magen_audio_resampler::MagenAudioResampler; + pub type ChosenResampler = audio::manual_audio_resampler::ManualAudioResampler; } } +use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +use sdl2::sys::*; +use std::{fs, env, result::Result, vec::Vec}; +use log::info; + +const SCREEN_SCALE:usize = 4; +const TURBO_MUL:u8 = 1; + cfg_if::cfg_if!{ if #[cfg(feature = "gpio")]{ - mod gpio_joypad_provider; use crate::gpio_joypad_provider::*; fn buttons_mapper(button:Button)->GpioPin{ match button{ @@ -77,15 +87,6 @@ cfg_if::cfg_if!{ } } -use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; -use lib_gb::{GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; -use sdl2::sys::*; -use std::{fs, env, result::Result, vec::Vec}; -use log::info; - -const SCREEN_SCALE:usize = 4; -const TURBO_MUL:u8 = 1; - fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; let mut fern_logger = fern::Dispatch::new() From 992b9fdb1b86602191144e8b985f4ae17db8d118 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 31 Jan 2022 22:50:45 +0200 Subject: [PATCH 15/70] Move to cfg-if the joypad controller main code --- gb/src/main.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 0dbb7aa5..4afee503 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -183,10 +183,14 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic } let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); - #[cfg(not(feature = "gpio"))] - let joypad_provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); - #[cfg(feature = "gpio")] - let joypad_provider = GpioJoypadProvider::new(buttons_mapper); + cfg_if::cfg_if!{ + if #[cfg(feature = "gpio")]{ + let joypad_provider = GpioJoypadProvider::new(buttons_mapper); + } + else{ + let joypad_provider = sdl::sdl_joypad_provider::SdlJoypadProvider::new(buttons_mapper); + } + } let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ let index = args.iter().position(|v| *v == String::from("--bootrom")).unwrap(); args.get(index + 1).expect("Error! you must specify a value for the --bootrom parameter").clone() From 0b8e9fe15b50a3cf45002d4352f68dd2139ac918 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 26 Feb 2022 18:39:56 +0200 Subject: [PATCH 16/70] Fix using in the gpio feature --- gb/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 4afee503..c661b15d 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -45,7 +45,7 @@ cfg_if::cfg_if!{ } use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; -use lib_gb::{GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +use lib_gb::{keypad::button::Button, GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; use sdl2::sys::*; use std::{fs, env, result::Result, vec::Vec}; use log::info; @@ -70,7 +70,6 @@ cfg_if::cfg_if!{ } } else{ - use lib_gb::keypad::button::Button; use sdl2::sys::SDL_Scancode; fn buttons_mapper(button:Button)->SDL_Scancode{ match button{ From bc4d62eba17076b750f6486472e45c56cd34e90f Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 25 Mar 2022 01:02:00 +0200 Subject: [PATCH 17/70] Add support for start and select gpio buttons --- gb/src/gpio_joypad_provider.rs | 20 ++++++++------------ gb/src/main.rs | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index ad639466..45fdd197 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -16,20 +16,16 @@ impl GpioJoypadProvider{ pub fn newGpioPin>(mapper:F)->Self{ let gpio = Gpio::new().unwrap(); - let a_pin = gpio.get(mapper(Button::A)).unwrap().into_input(); - let b_pin = gpio.get(mapper(Button::B)).unwrap().into_input(); - let up_pin = gpio.get(mapper(Button::Up)).unwrap().into_input(); - let down_pin = gpio.get(mapper(Button::Down)).unwrap().into_input(); - let right_pin = gpio.get(mapper(Button::Right)).unwrap().into_input(); - let left_pin = gpio.get(mapper(Button::Left)).unwrap().into_input(); let mut pins:[Option;NUM_OF_KEYS] = create_default_array(); - pins[Button::A as usize] = Some(a_pin); - pins[Button::B as usize] = Some(b_pin); - pins[Button::Up as usize] = Some(up_pin); - pins[Button::Down as usize] = Some(down_pin); - pins[Button::Right as usize] = Some(right_pin); - pins[Button::Left as usize] = Some(left_pin); + pins[Button::A as usize] = Some(gpio.get(mapper(Button::A)).unwrap().into_input()); + pins[Button::B as usize] = Some(gpio.get(mapper(Button::B)).unwrap().into_input()); + pins[Button::Start as usize] = Some(gpio.get(mapper(Button::Start)).unwrap().into_input()); + pins[Button::Select as usize] = Some(gpio.get(mapper(Button::Select)).unwrap().into_input()); + pins[Button::Up as usize] = Some(gpio.get(mapper(Button::Up)).unwrap().into_input()); + pins[Button::Down as usize] = Some(gpio.get(mapper(Button::Down)).unwrap().into_input()); + pins[Button::Right as usize] = Some(gpio.get(mapper(Button::Right)).unwrap().into_input()); + pins[Button::Left as usize] = Some(gpio.get(mapper(Button::Left)).unwrap().into_input()); Self{ input_pins:pins diff --git a/gb/src/main.rs b/gb/src/main.rs index c661b15d..5d63ea9a 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -60,8 +60,8 @@ cfg_if::cfg_if!{ match button{ Button::A => 18, Button::B => 17, - Button::Start => 0, - Button::Select => 0, + Button::Start => 22, + Button::Select => 23, Button::Up => 16, Button::Down => 20, Button::Right => 21, From be6cf1b631b172aef34c1b0b45eb9e3662c12c28 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 22 Apr 2022 16:24:24 +0300 Subject: [PATCH 18/70] Remove the use of option in the gpio joypad --- gb/src/gpio_joypad_provider.rs | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index 45fdd197..929e96a6 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -1,15 +1,11 @@ -use lib_gb::keypad::{ - joypad::{Joypad, NUM_OF_KEYS}, - joypad_provider::JoypadProvider, - button::Button -}; +use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvider,button::Button}; use lib_gb::utils::create_default_array; use rppal::gpio::{Gpio, InputPin}; pub type GpioPin = u8; pub struct GpioJoypadProvider{ - input_pins:[Option;NUM_OF_KEYS] + input_pins:[InputPin;NUM_OF_KEYS] } impl GpioJoypadProvider{ @@ -17,15 +13,15 @@ impl GpioJoypadProvider{ let gpio = Gpio::new().unwrap(); - let mut pins:[Option;NUM_OF_KEYS] = create_default_array(); - pins[Button::A as usize] = Some(gpio.get(mapper(Button::A)).unwrap().into_input()); - pins[Button::B as usize] = Some(gpio.get(mapper(Button::B)).unwrap().into_input()); - pins[Button::Start as usize] = Some(gpio.get(mapper(Button::Start)).unwrap().into_input()); - pins[Button::Select as usize] = Some(gpio.get(mapper(Button::Select)).unwrap().into_input()); - pins[Button::Up as usize] = Some(gpio.get(mapper(Button::Up)).unwrap().into_input()); - pins[Button::Down as usize] = Some(gpio.get(mapper(Button::Down)).unwrap().into_input()); - pins[Button::Right as usize] = Some(gpio.get(mapper(Button::Right)).unwrap().into_input()); - pins[Button::Left as usize] = Some(gpio.get(mapper(Button::Left)).unwrap().into_input()); + let mut pins:[InputPin;NUM_OF_KEYS] = create_default_array(); + pins[Button::A as usize] = gpio.get(mapper(Button::A)).unwrap().into_input(); + pins[Button::B as usize] = gpio.get(mapper(Button::B)).unwrap().into_input(); + pins[Button::Start as usize] = gpio.get(mapper(Button::Start)).unwrap().into_input(); + pins[Button::Select as usize] = gpio.get(mapper(Button::Select)).unwrap().into_input(); + pins[Button::Up as usize] = gpio.get(mapper(Button::Up)).unwrap().into_input(); + pins[Button::Down as usize] = gpio.get(mapper(Button::Down)).unwrap().into_input(); + pins[Button::Right as usize] = gpio.get(mapper(Button::Right)).unwrap().into_input(); + pins[Button::Left as usize] = gpio.get(mapper(Button::Left)).unwrap().into_input(); Self{ input_pins:pins @@ -36,9 +32,7 @@ impl GpioJoypadProvider{ impl JoypadProvider for GpioJoypadProvider{ fn provide(&mut self, joypad:&mut Joypad){ for i in 0..NUM_OF_KEYS{ - if let Some(pin) = &self.input_pins[i] { - joypad.buttons[i] = pin.is_high(); - } + joypad.buttons[i] = pin.is_high(); } } } \ No newline at end of file From 2b88bef6a144db222837372fdf1187e14a954bc2 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 23 Apr 2022 14:51:20 +0300 Subject: [PATCH 19/70] Fix the gpio feature Read from the joypad is way better now with the gpio pins --- gb/src/gpio_joypad_provider.rs | 30 +++++++++++++----------------- gb/src/main.rs | 2 +- lib_gb/src/utils/mod.rs | 2 +- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/gb/src/gpio_joypad_provider.rs b/gb/src/gpio_joypad_provider.rs index 929e96a6..06035ebf 100644 --- a/gb/src/gpio_joypad_provider.rs +++ b/gb/src/gpio_joypad_provider.rs @@ -1,5 +1,5 @@ use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvider,button::Button}; -use lib_gb::utils::create_default_array; +use lib_gb::utils::create_array; use rppal::gpio::{Gpio, InputPin}; pub type GpioPin = u8; @@ -9,30 +9,26 @@ pub struct GpioJoypadProvider{ } impl GpioJoypadProvider{ - pub fn newGpioPin>(mapper:F)->Self{ - + pub fn newGpioPin>(mapper:F)->Self{ let gpio = Gpio::new().unwrap(); - - let mut pins:[InputPin;NUM_OF_KEYS] = create_default_array(); - pins[Button::A as usize] = gpio.get(mapper(Button::A)).unwrap().into_input(); - pins[Button::B as usize] = gpio.get(mapper(Button::B)).unwrap().into_input(); - pins[Button::Start as usize] = gpio.get(mapper(Button::Start)).unwrap().into_input(); - pins[Button::Select as usize] = gpio.get(mapper(Button::Select)).unwrap().into_input(); - pins[Button::Up as usize] = gpio.get(mapper(Button::Up)).unwrap().into_input(); - pins[Button::Down as usize] = gpio.get(mapper(Button::Down)).unwrap().into_input(); - pins[Button::Right as usize] = gpio.get(mapper(Button::Right)).unwrap().into_input(); - pins[Button::Left as usize] = gpio.get(mapper(Button::Left)).unwrap().into_input(); + let buttons = [Button::A,Button::B,Button::Start,Button::Select,Button::Up,Button::Down,Button::Right,Button::Left]; + let mut counter = 0; + let generator_lambda = ||{ + let button = &buttons[counter]; + let result = gpio.get(mapper(button)).unwrap().into_input(); + counter += 1; + return result; + }; + let pins:[InputPin;NUM_OF_KEYS] = create_array(generator_lambda); - Self{ - input_pins:pins - } + return Self{input_pins:pins}; } } impl JoypadProvider for GpioJoypadProvider{ fn provide(&mut self, joypad:&mut Joypad){ for i in 0..NUM_OF_KEYS{ - joypad.buttons[i] = pin.is_high(); + joypad.buttons[i] = self.input_pins[i].is_high(); } } } \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 64769229..23786ef2 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -58,7 +58,7 @@ const TURBO_MUL:u8 = 1; cfg_if::cfg_if!{ if #[cfg(feature = "gpio")]{ use crate::gpio_joypad_provider::*; - fn buttons_mapper(button:Button)->GpioPin{ + fn buttons_mapper(button:&Button)->GpioPin{ match button{ Button::A => 18, Button::B => 17, diff --git a/lib_gb/src/utils/mod.rs b/lib_gb/src/utils/mod.rs index 722f0216..7f40c400 100644 --- a/lib_gb/src/utils/mod.rs +++ b/lib_gb/src/utils/mod.rs @@ -12,7 +12,7 @@ pub fn create_default_array()->[T;SIZE]{ create_array(||T::default()) } -pub fn create_arrayT,const SIZE:usize>(func:F)->[T;SIZE]{ +pub fn create_arrayT,const SIZE:usize>(mut func:F)->[T;SIZE]{ let mut data: [MaybeUninit; SIZE] = unsafe{MaybeUninit::uninit().assume_init()}; for elem in &mut data[..]{ From 91f847d1361a01506d82c98575b3585ae755aed1 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 23 Apr 2022 20:19:24 +0300 Subject: [PATCH 20/70] Update the gpio button mapper --- gb/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 23786ef2..f16ef79f 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -64,10 +64,10 @@ cfg_if::cfg_if!{ Button::B => 17, Button::Start => 22, Button::Select => 23, - Button::Up => 16, - Button::Down => 20, - Button::Right => 21, - Button::Left => 19 + Button::Up => 19, + Button::Down => 16, + Button::Right => 20, + Button::Left => 21 } } } From 4ae6d16c36374b55ed7c324aee97ec07343ea39e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 19:44:40 +0000 Subject: [PATCH 21/70] Bump tokio from 1.12.0 to 1.16.1 Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.12.0 to 1.16.1. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.12.0...tokio-1.16.1) --- updated-dependencies: - dependency-name: tokio dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 686cca84..72a5314f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1386,11 +1386,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.12.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ - "autocfg", "bytes", "libc", "memchr", From e2643f144748b47f29ccc2dcb0e11f44a8794e3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 21:51:31 +0000 Subject: [PATCH 22/70] Bump regex from 1.5.4 to 1.5.6 Bumps [regex](https://github.com/rust-lang/regex) from 1.5.4 to 1.5.6. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.5.4...1.5.6) --- updated-dependencies: - dependency-name: regex dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 686cca84..4068eda8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,9 +1015,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.4" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -1032,9 +1032,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "remove_dir_all" From 2cb0af2da2f2e5b4de9281b38d9aaa087aefb325 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 07:19:07 +0000 Subject: [PATCH 23/70] Bump crossbeam-utils from 0.8.5 to 0.8.8 Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.5 to 0.8.8. - [Release notes](https://github.com/crossbeam-rs/crossbeam/releases) - [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md) - [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.5...crossbeam-utils-0.8.8) --- updated-dependencies: - dependency-name: crossbeam-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 686cca84..1ea328d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", From 78ee94fb82257133f10c1326b28618fabcf9ba4c Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 10 Jun 2022 12:52:41 +0300 Subject: [PATCH 24/70] Delete the old Cargo.toml in gb folder --- gb/Cargo.lock | 338 -------------------------------------------------- 1 file changed, 338 deletions(-) delete mode 100644 gb/Cargo.lock diff --git a/gb/Cargo.lock b/gb/Cargo.lock deleted file mode 100644 index 8ea8d259..00000000 --- a/gb/Cargo.lock +++ /dev/null @@ -1,338 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aho-corasick" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -dependencies = [ - "memchr", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "cc" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time", - "winapi", -] - -[[package]] -name = "cmake" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" -dependencies = [ - "cc", -] - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "encoding_rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "fern" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" -dependencies = [ - "log", -] - -[[package]] -name = "filetime" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" -dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lib_gb" -version = "1.0.0" -dependencies = [ - "log", -] - -[[package]] -name = "libc" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "magenboy" -version = "1.0.0" -dependencies = [ - "chrono", - "fern", - "lib_gb", - "log", - "sdl2", - "wav", -] - -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "redox_syscall" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" - -[[package]] -name = "riff" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" - -[[package]] -name = "sdl2" -version = "0.34.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d7a6ef5f96289a6ec50fc8b65ec75f5571f0aa94fa6ea230f6b228fa05d57" -dependencies = [ - "bitflags", - "lazy_static", - "libc", - "sdl2-sys", -] - -[[package]] -name = "sdl2-sys" -version = "0.34.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cb164f53dbcad111de976bbf1f3083d3fcdeda88da9cfa281c70822720ee3da" -dependencies = [ - "cfg-if 0.1.10", - "cmake", - "flate2", - "libc", - "tar", - "unidiff", - "version-compare", -] - -[[package]] -name = "tar" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0bcfbd6a598361fda270d82469fff3d65089dc33e175c9a131f7b4cd395f228" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi", - "winapi", -] - -[[package]] -name = "unidiff" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a62719acf1933bfdbeb73a657ecd9ecece70b405125267dd549e2e2edc232c" -dependencies = [ - "encoding_rs", - "lazy_static", - "regex", -] - -[[package]] -name = "version-compare" -version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wav" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549df97e073e1f901a489f159eb451bbe9c790e2f217394f4ce01acda0380b0c" -dependencies = [ - "riff", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] From 262dd45343553449cbc0469d9a5bd17cccfd1db0 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Wed, 4 May 2022 19:44:29 +0300 Subject: [PATCH 25/70] Add experimental ili9341 gfx device --- gb/Cargo.toml | 2 +- gb/src/main.rs | 8 ++- gb/src/spi.rs | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 gb/src/spi.rs diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 20fae90d..600144c6 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -22,7 +22,7 @@ crossterm = "0.23" rppal = {version = "0.13", optional = true} [features] -default = ["static-sdl"] +default = ["static-sdl", "gpio"] sdl-resample = [] push-audio = [] static-sdl = ["sdl2/bundled", "sdl2/static-link"] diff --git a/gb/src/main.rs b/gb/src/main.rs index f16ef79f..92634a49 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,3 +1,4 @@ +mod spi; mod mbc_handler; mod mpmc_gfx_device; mod joypad_terminal_menu; @@ -173,7 +174,10 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + #[cfg(feature = "gpio")] + let mut gfx_device = spi::Ili9341GfxDevice::new(); + #[cfg(not(feature = "gpio"))] + let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); @@ -205,7 +209,7 @@ fn main() { } let buffer = r.recv().unwrap(); - sdl_gfx_device.swap_buffer(&*(buffer as *const [u32; SCREEN_WIDTH * SCREEN_HEIGHT])); + gfx_device.swap_buffer(&*(buffer as *const [u32; SCREEN_WIDTH * SCREEN_HEIGHT])); } drop(r); diff --git a/gb/src/spi.rs b/gb/src/spi.rs new file mode 100644 index 00000000..1b2da789 --- /dev/null +++ b/gb/src/spi.rs @@ -0,0 +1,190 @@ +use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; +use rppal::gpio::OutputPin; + +pub struct Ili9341GfxDevice{ + ili9341_controller:Ili9341Contoller +} + +impl Ili9341GfxDevice{ + pub fn new()->Self{ + let ili9341_controller = Ili9341Contoller::new(); + Ili9341GfxDevice {ili9341_controller} + } +} + +impl GfxDevice for Ili9341GfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + let u16_buffer:[u16;SCREEN_HEIGHT*SCREEN_WIDTH] = buffer.map(|pixel| { + let r = pixel & 0xFF; + let g = (pixel & 0xFF00)>>8; + let b = (pixel & 0xFF0000)>>16; + let mut u16_pixel = r as u16 >> 3; + u16_pixel |= ((g >> 2) << 5) as u16; + u16_pixel |= ((b >> 3) << 11) as u16; + return u16_pixel; + }); + self.ili9341_controller.write_frame_buffer(&u16_buffer); + } +} + +struct RppalSpi{ + spi_device:rppal::spi::Spi, + dc_pin:OutputPin +} + +impl RppalSpi{ + pub fn new(dc_pin:OutputPin)->Self{ + let spi_device = rppal::spi::Spi::new( + rppal::spi::Bus::Spi0, + rppal::spi::SlaveSelect::Ss0/*pin 24*/, + 75_000_000/*In order to be able to achieve 60fps*/, + rppal::spi::Mode::Mode0 + ).expect("Error creating rppal spi device"); + + return RppalSpi { spi_device, dc_pin }; + } + + fn write(&mut self, command: Ili9341Commands, data:[u8; SIZE]) { + let error = "Error while writing to the spi device"; + let command = command as u8; + self.dc_pin.set_low(); + self.spi_device.write(&[command]).expect(error); + self.dc_pin.set_high(); + let chunks = data.chunks(4096); + for chunk in chunks{ + self.spi_device.write(&chunk).expect(std::format!("Error wrting data to spi device for command: {:#X}",command).as_str() ); + } + } +} + +enum Ili9341Commands{ + SoftwareReset = 0x01, + SleepOut = 0x11, + GammaSet = 0x26, + DisplayOff = 0x28, + DisplayOn = 0x29, + ColumnAddressSet = 0x2A, // Set curosr X value + PageAddressSet = 0x2B, // Set cursor Y value + MemoryWrite = 0x2C, + MemoryAccessControl = 0x36, + PixelFormatSet = 0x3A, + FrameRateControl = 0xB1, + DisplayFunctionControl = 0xB6, + PowerControl1 = 0xC0, + PowerControl2 = 0xC1, + VcomControl1 = 0xC5, + VcomControl2 = 0xC7, + PowerControlA = 0xCB, + PowerControlB = 0xCF, + PossitiveGammaCorrection = 0xE0, + NegativeGammaCorrection = 0xE1, + DriverTimingControlA = 0xE8, + DriverTimingControlB = 0xEA, + PowerOnSequenceControl = 0xED, + Enable3G = 0xF2, +} + +struct Ili9341Contoller{ + spi:RppalSpi, + led_pin: OutputPin, + reset_pin: OutputPin +} + +impl Ili9341Contoller{ + pub fn new()->Self{ + let gpio = rppal::gpio::Gpio::new().unwrap(); + let mut dc_pin = gpio.get(15).unwrap().into_output(); + let mut reset_pin = gpio.get(14).unwrap().into_output(); + let mut led_pin = gpio.get(25).unwrap().into_output(); + + + // toggling the reset pin to initalize the lcd + let wait_duration = std::time::Duration::from_millis(120); + reset_pin.set_high(); + std::thread::sleep(wait_duration); + reset_pin.set_low(); + std::thread::sleep(wait_duration); + reset_pin.set_high(); + std::thread::sleep(wait_duration); + + let mut spi: RppalSpi = RppalSpi::new(dc_pin); + + // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) + // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down + // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) + + // There is another implementation in rust for an ili9341 controller which is much simpler and uses those commands: + // Sleepms(5), SoftwareReset, Sleepms(120), MemoryAccessControl, PixelFormatSet, SleepOut, Sleepms(5), DisplayOn + // minimal config based on rust ili9341 lib (https://github.com/yuri91/ili9341-rs) + + // fbcp-ili9341 inspired implementation: + /*--------------------------------------------------------------------------- */ + // Reset the screen + spi.write(Ili9341Commands::SoftwareReset,[]); + Self::sleep_ms(5); + spi.write(Ili9341Commands::DisplayOff,[]); + + // Some power stuff, probably uneccessary but just for sure + spi.write(Ili9341Commands::PowerControlA, [0x39, 0x2C, 0x0, 0x34, 0x2]); + spi.write(Ili9341Commands::PowerControlB, [0x0, 0xC1, 0x30]); + spi.write(Ili9341Commands::DriverTimingControlA, [0x85, 0x0, 0x78]); + spi.write(Ili9341Commands::DriverTimingControlB, [0x0, 0x0]); + spi.write(Ili9341Commands::PowerOnSequenceControl, [0x64, 0x3, 0x12, 0x81]); + spi.write(Ili9341Commands::PowerControl1, [0x23]); + spi.write(Ili9341Commands::PowerControl2,[0x10]); + spi.write(Ili9341Commands::VcomControl1, [0xE3, 0x28]); + spi.write(Ili9341Commands::VcomControl2, [0x86]); + + // Configuring the screen + spi.write(Ili9341Commands::MemoryAccessControl, [0x20]); // this command can affect various graphics options like RGB format and screen fip + spi.write(Ili9341Commands::PixelFormatSet, [0x55]); // set pixel format to 16 bit per pixel; + spi.write(Ili9341Commands::FrameRateControl, [0x0, 0x1F /*According to the docs this is 61 hrz */]); + spi.write(Ili9341Commands::DisplayFunctionControl, [0x8, 0x82, 0x27]); + + // Gamma values - pretty sure its redundant + spi.write(Ili9341Commands::Enable3G, [0x2]); + spi.write(Ili9341Commands::GammaSet, [0x1]); + spi.write(Ili9341Commands::PossitiveGammaCorrection,[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); + spi.write(Ili9341Commands::NegativeGammaCorrection, [0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); + + // Turn screen on + spi.write(Ili9341Commands::SleepOut,[]); + Self::sleep_ms(120); + spi.write(Ili9341Commands::DisplayOn,[]); + /*------------------------------------------------------------------------------- */ + //End of fbcp-ili9341 inpired implementation + + // turn backlight on + led_pin.set_high(); + + return Ili9341Contoller { spi, led_pin, reset_pin}; + } + + pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ + self.spi.write(Ili9341Commands::ColumnAddressSet, [0x0,0x0, ((SCREEN_WIDTH - 1) >> 8) as u8, ((SCREEN_WIDTH - 1) & 0xFF) as u8 ]); + self.spi.write(Ili9341Commands::PageAddressSet, [0x0,0x0, ((SCREEN_HEIGHT - 1) >> 8) as u8, ((SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); + // let u8_buffer:&[u8; SCREEN_HEIGHT*SCREEN_WIDTH*2] = unsafe{ + // let ptr = (buffer as *const u16) as *const u8; + // &*(ptr as *const [u8; SCREEN_HEIGHT*SCREEN_WIDTH*2]) + // }; + let mut u8_buffer:[u8;SCREEN_HEIGHT*SCREEN_WIDTH*2] = [0;SCREEN_HEIGHT*SCREEN_WIDTH*2]; + for i in 0..buffer.len(){ + u8_buffer[i*2] = (buffer[i] >> 8) as u8; + u8_buffer[(i*2)+1] = (buffer[i] & 0xFF) as u8; + } + self.spi.write(Ili9341Commands::MemoryWrite, u8_buffer); + } + + fn sleep_ms(milliseconds_to_sleep:u64){ + std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); + } +} + +impl Drop for Ili9341Contoller{ + fn drop(&mut self) { + self.led_pin.set_low(); + self.reset_pin.set_high(); + Self::sleep_ms(1); + self.reset_pin.set_low(); + } +} \ No newline at end of file From b62f764556400c6d7c5eadfd3d1076797b25561b Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 6 May 2022 15:57:28 +0300 Subject: [PATCH 26/70] Add blilinear scaling for the ili9341 screen --- gb/src/spi.rs | 126 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 87 insertions(+), 39 deletions(-) diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 1b2da789..7e183fe9 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -1,6 +1,13 @@ use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; use rppal::gpio::OutputPin; +fn create_rgb565_pixel(r:u32, g: u32, b:u32)->u16{ + let mut u16_pixel = b as u16 >> 3; + u16_pixel |= ((g >> 2) << 5) as u16; + u16_pixel |= ((r >> 3) << 11) as u16; + return u16_pixel; +} + pub struct Ili9341GfxDevice{ ili9341_controller:Ili9341Contoller } @@ -18,10 +25,8 @@ impl GfxDevice for Ili9341GfxDevice{ let r = pixel & 0xFF; let g = (pixel & 0xFF00)>>8; let b = (pixel & 0xFF0000)>>16; - let mut u16_pixel = r as u16 >> 3; - u16_pixel |= ((g >> 2) << 5) as u16; - u16_pixel |= ((b >> 3) << 11) as u16; - return u16_pixel; + let mut u16_pixel = b as u16 >> 3; + return create_rgb565_pixel(r, g, b); }); self.ili9341_controller.write_frame_buffer(&u16_buffer); } @@ -44,7 +49,7 @@ impl RppalSpi{ return RppalSpi { spi_device, dc_pin }; } - fn write(&mut self, command: Ili9341Commands, data:[u8; SIZE]) { + fn write(&mut self, command: Ili9341Commands, data:&[u8; SIZE]) { let error = "Error while writing to the spi device"; let command = command as u8; self.dc_pin.set_low(); @@ -118,63 +123,106 @@ impl Ili9341Contoller{ // minimal config based on rust ili9341 lib (https://github.com/yuri91/ili9341-rs) // fbcp-ili9341 inspired implementation: - /*--------------------------------------------------------------------------- */ + /*---------------------------------------------------------------------------------------------------------------------- */ // Reset the screen - spi.write(Ili9341Commands::SoftwareReset,[]); + spi.write(Ili9341Commands::SoftwareReset,&[]); Self::sleep_ms(5); - spi.write(Ili9341Commands::DisplayOff,[]); + spi.write(Ili9341Commands::DisplayOff,&[]); // Some power stuff, probably uneccessary but just for sure - spi.write(Ili9341Commands::PowerControlA, [0x39, 0x2C, 0x0, 0x34, 0x2]); - spi.write(Ili9341Commands::PowerControlB, [0x0, 0xC1, 0x30]); - spi.write(Ili9341Commands::DriverTimingControlA, [0x85, 0x0, 0x78]); - spi.write(Ili9341Commands::DriverTimingControlB, [0x0, 0x0]); - spi.write(Ili9341Commands::PowerOnSequenceControl, [0x64, 0x3, 0x12, 0x81]); - spi.write(Ili9341Commands::PowerControl1, [0x23]); - spi.write(Ili9341Commands::PowerControl2,[0x10]); - spi.write(Ili9341Commands::VcomControl1, [0xE3, 0x28]); - spi.write(Ili9341Commands::VcomControl2, [0x86]); + spi.write(Ili9341Commands::PowerControlA, &[0x39, 0x2C, 0x0, 0x34, 0x2]); + spi.write(Ili9341Commands::PowerControlB, &[0x0, 0xC1, 0x30]); + spi.write(Ili9341Commands::DriverTimingControlA, &[0x85, 0x0, 0x78]); + spi.write(Ili9341Commands::DriverTimingControlB, &[0x0, 0x0]); + spi.write(Ili9341Commands::PowerOnSequenceControl, &[0x64, 0x3, 0x12, 0x81]); + spi.write(Ili9341Commands::PowerControl1, &[0x23]); + spi.write(Ili9341Commands::PowerControl2,&[0x10]); + spi.write(Ili9341Commands::VcomControl1, &[0xE3, 0x28]); + spi.write(Ili9341Commands::VcomControl2, &[0x86]); // Configuring the screen - spi.write(Ili9341Commands::MemoryAccessControl, [0x20]); // this command can affect various graphics options like RGB format and screen fip - spi.write(Ili9341Commands::PixelFormatSet, [0x55]); // set pixel format to 16 bit per pixel; - spi.write(Ili9341Commands::FrameRateControl, [0x0, 0x1F /*According to the docs this is 61 hrz */]); - spi.write(Ili9341Commands::DisplayFunctionControl, [0x8, 0x82, 0x27]); + spi.write(Ili9341Commands::MemoryAccessControl, &[0x20]); // This command tlit the screen 90 degree + spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel; + spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x1F /*According to the docs this is 61 hrz */]); + spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]); // Gamma values - pretty sure its redundant - spi.write(Ili9341Commands::Enable3G, [0x2]); - spi.write(Ili9341Commands::GammaSet, [0x1]); - spi.write(Ili9341Commands::PossitiveGammaCorrection,[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); - spi.write(Ili9341Commands::NegativeGammaCorrection, [0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); + spi.write(Ili9341Commands::Enable3G, &[0x2]); + spi.write(Ili9341Commands::GammaSet, &[0x1]); + spi.write(Ili9341Commands::PossitiveGammaCorrection,&[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); + spi.write(Ili9341Commands::NegativeGammaCorrection, &[0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); // Turn screen on - spi.write(Ili9341Commands::SleepOut,[]); + spi.write(Ili9341Commands::SleepOut,&[]); Self::sleep_ms(120); - spi.write(Ili9341Commands::DisplayOn,[]); - /*------------------------------------------------------------------------------- */ + spi.write(Ili9341Commands::DisplayOn,&[]); + /*---------------------------------------------------------------------------------------------------------------------- */ //End of fbcp-ili9341 inpired implementation // turn backlight on led_pin.set_high(); + log::info!("Initalizing with screen size width: {}, hight: {}", Self::TARGET_SCREEN_WIDTH, Self::TARGET_SCREEN_HEIGHT); + return Ili9341Contoller { spi, led_pin, reset_pin}; } pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ - self.spi.write(Ili9341Commands::ColumnAddressSet, [0x0,0x0, ((SCREEN_WIDTH - 1) >> 8) as u8, ((SCREEN_WIDTH - 1) & 0xFF) as u8 ]); - self.spi.write(Ili9341Commands::PageAddressSet, [0x0,0x0, ((SCREEN_HEIGHT - 1) >> 8) as u8, ((SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); - // let u8_buffer:&[u8; SCREEN_HEIGHT*SCREEN_WIDTH*2] = unsafe{ - // let ptr = (buffer as *const u16) as *const u8; - // &*(ptr as *const [u8; SCREEN_HEIGHT*SCREEN_WIDTH*2]) - // }; - let mut u8_buffer:[u8;SCREEN_HEIGHT*SCREEN_WIDTH*2] = [0;SCREEN_HEIGHT*SCREEN_WIDTH*2]; - for i in 0..buffer.len(){ - u8_buffer[i*2] = (buffer[i] >> 8) as u8; - u8_buffer[(i*2)+1] = (buffer[i] & 0xFF) as u8; - } + self.spi.write(Ili9341Commands::ColumnAddressSet, &[0x0,0x0, ((Self::TARGET_SCREEN_WIDTH - 1) >> 8) as u8, ((Self::TARGET_SCREEN_WIDTH - 1) & 0xFF) as u8 ]); + self.spi.write(Ili9341Commands::PageAddressSet, &[0x0,0x0, ((Self::TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); + + let mut scaled_buffer: [u16;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH]; + Self::scale_to_screen(buffer, &mut scaled_buffer); + + let u8_buffer = unsafe{std::mem::transmute::<&[u16;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH], &[u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]>(&scaled_buffer)}; + self.spi.write(Ili9341Commands::MemoryWrite, u8_buffer); } + const SCALE:f32 = 5.0 / 3.0; + const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * Self::SCALE) as usize; + const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * Self::SCALE) as usize; + + // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ + fn scale_to_screen(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u16;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH]){ + // not sure why the -1.0 + let x_ratio = (SCREEN_WIDTH as f32 - 1.0) / Self::TARGET_SCREEN_WIDTH as f32; + let y_ratio = (SCREEN_HEIGHT as f32 - 1.0) / Self::TARGET_SCREEN_HEIGHT as f32; + + let mut offset_counter = 0; + for y in 0..Self::TARGET_SCREEN_HEIGHT{ + for x in 0..Self::TARGET_SCREEN_WIDTH{ + let x_val = (x_ratio * x as f32) as u32; // x value of a point in this ratio between 0 and x + let y_val = (y_ratio * y as f32) as u32; // y value of a point in this ratio between o and y + let x_diff = (x_ratio * x as f32) - x_val as f32; + let y_diff = (y_ratio * y as f32) - y_val as f32; + let original_pixel_index = (y_val as usize * SCREEN_WIDTH) + x_val as usize; + + // Get the pixel and 3 surounding pixels + let pixel_a = input_buffer[original_pixel_index]; + let pixel_b = input_buffer[original_pixel_index + 1]; + let pixel_c = input_buffer[original_pixel_index + SCREEN_WIDTH]; + let pixel_d = input_buffer[original_pixel_index + SCREEN_WIDTH + 1]; + + let blue:f32 = ((pixel_a & 0x1F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + + ((pixel_b & 0x1F) as f32 * (x_diff)*(1.0-y_diff)) + + ((pixel_c & 0x1F) as f32 * y_diff * (1.0-x_diff)) + + ((pixel_d & 0x1F) as f32 * x_diff * y_diff); + let green:f32 = (((pixel_a >> 5) & 0x3F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + + (((pixel_b >> 5) & 0x3F) as f32 * (x_diff)*(1.0-y_diff)) + + (((pixel_c >> 5) & 0x3F) as f32 * y_diff * (1.0-x_diff)) + + (((pixel_d >> 5) & 0x3F) as f32 * x_diff * y_diff); + let red:f32 = (((pixel_a >> 11) & 0x1F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + + (((pixel_b >> 11) & 0x1F) as f32 * (x_diff)*(1.0-y_diff)) + + (((pixel_c >> 11) & 0x1F) as f32 * y_diff * (1.0-x_diff)) + + (((pixel_d >> 11) & 0x1F) as f32 * x_diff * y_diff); + + output_buffer[offset_counter] = create_rgb565_pixel(red as u32, green as u32, blue as u32); + offset_counter += 1; + } + } + } + fn sleep_ms(milliseconds_to_sleep:u64){ std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); } From 8bb38f2f35ff1d9cf8df3b0bd0611a291181b832 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 6 May 2022 16:36:52 +0300 Subject: [PATCH 27/70] Bug fixes in the ili9341 implementation --- gb/src/spi.rs | 58 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 7e183fe9..4f6f50cf 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -1,12 +1,6 @@ use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; use rppal::gpio::OutputPin; -fn create_rgb565_pixel(r:u32, g: u32, b:u32)->u16{ - let mut u16_pixel = b as u16 >> 3; - u16_pixel |= ((g >> 2) << 5) as u16; - u16_pixel |= ((r >> 3) << 11) as u16; - return u16_pixel; -} pub struct Ili9341GfxDevice{ ili9341_controller:Ili9341Contoller @@ -22,11 +16,13 @@ impl Ili9341GfxDevice{ impl GfxDevice for Ili9341GfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { let u16_buffer:[u16;SCREEN_HEIGHT*SCREEN_WIDTH] = buffer.map(|pixel| { - let r = pixel & 0xFF; + let b = pixel & 0xFF; let g = (pixel & 0xFF00)>>8; - let b = (pixel & 0xFF0000)>>16; + let r = (pixel & 0xFF0000)>>16; let mut u16_pixel = b as u16 >> 3; - return create_rgb565_pixel(r, g, b); + u16_pixel |= ((g >> 2) << 5) as u16; + u16_pixel |= ((r >> 3) << 11) as u16; + return u16_pixel; }); self.ili9341_controller.write_frame_buffer(&u16_buffer); } @@ -96,6 +92,16 @@ struct Ili9341Contoller{ } impl Ili9341Contoller{ + + const ILI9341_SCREEN_WIDTH:usize = 320; + const ILI9341_SCREEN_HEIGHT:usize = 240; + const SCALE:f32 = 5.0 / 3.0; // maximum scale to fit the ili9341 screen + const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * Self::SCALE) as usize; + const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * Self::SCALE) as usize; + const FRAME_BUFFER_X_OFFSET:usize = (Self::ILI9341_SCREEN_WIDTH - Self::TARGET_SCREEN_WIDTH) / 2; + + const CLEAN_BUFFER:[u8;Self::ILI9341_SCREEN_HEIGHT * Self::ILI9341_SCREEN_WIDTH * 2] = [0; Self::ILI9341_SCREEN_HEIGHT * Self::ILI9341_SCREEN_WIDTH * 2]; + pub fn new()->Self{ let gpio = rppal::gpio::Gpio::new().unwrap(); let mut dc_pin = gpio.get(15).unwrap().into_output(); @@ -159,6 +165,11 @@ impl Ili9341Contoller{ /*---------------------------------------------------------------------------------------------------------------------- */ //End of fbcp-ili9341 inpired implementation + // Clear screen + spi.write(Ili9341Commands::ColumnAddressSet, &[0,0,((Self::ILI9341_SCREEN_WIDTH -1) >> 8) as u8, ((Self::ILI9341_SCREEN_WIDTH -1) & 0xFF) as u8]); + spi.write(Ili9341Commands::PageAddressSet, &[0,0,((Self::ILI9341_SCREEN_HEIGHT -1) >> 8) as u8, ((Self::ILI9341_SCREEN_HEIGHT -1) & 0xFF) as u8]); + spi.write(Ili9341Commands::MemoryWrite, &Self::CLEAN_BUFFER); + // turn backlight on led_pin.set_high(); @@ -167,21 +178,32 @@ impl Ili9341Contoller{ return Ili9341Contoller { spi, led_pin, reset_pin}; } + pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ - self.spi.write(Ili9341Commands::ColumnAddressSet, &[0x0,0x0, ((Self::TARGET_SCREEN_WIDTH - 1) >> 8) as u8, ((Self::TARGET_SCREEN_WIDTH - 1) & 0xFF) as u8 ]); - self.spi.write(Ili9341Commands::PageAddressSet, &[0x0,0x0, ((Self::TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); + let end_x_index = Self::TARGET_SCREEN_WIDTH + Self::FRAME_BUFFER_X_OFFSET - 1; + self.spi.write(Ili9341Commands::ColumnAddressSet, &[ + (Self::FRAME_BUFFER_X_OFFSET >> 8) as u8, + (Self::FRAME_BUFFER_X_OFFSET & 0xFF) as u8, + (end_x_index >> 8) as u8, + (end_x_index & 0xFF) as u8 + ]); + self.spi.write(Ili9341Commands::PageAddressSet, &[ + 0x0, 0x0, + ((Self::TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, + ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 + ]); let mut scaled_buffer: [u16;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH]; Self::scale_to_screen(buffer, &mut scaled_buffer); + let mut u8_buffer:[u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH*2] = [0;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH*2]; + for i in 0..scaled_buffer.len(){ + u8_buffer[i*2] = (scaled_buffer[i] >> 8) as u8; + u8_buffer[(i*2)+1] = (scaled_buffer[i] & 0xFF) as u8; + } - let u8_buffer = unsafe{std::mem::transmute::<&[u16;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH], &[u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]>(&scaled_buffer)}; - - self.spi.write(Ili9341Commands::MemoryWrite, u8_buffer); + self.spi.write(Ili9341Commands::MemoryWrite, &u8_buffer); } - const SCALE:f32 = 5.0 / 3.0; - const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * Self::SCALE) as usize; - const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * Self::SCALE) as usize; // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ fn scale_to_screen(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u16;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH]){ @@ -217,7 +239,7 @@ impl Ili9341Contoller{ (((pixel_c >> 11) & 0x1F) as f32 * y_diff * (1.0-x_diff)) + (((pixel_d >> 11) & 0x1F) as f32 * x_diff * y_diff); - output_buffer[offset_counter] = create_rgb565_pixel(red as u32, green as u32, blue as u32); + output_buffer[offset_counter] = blue as u16 | ((green as u16) << 5) | ((red as u16) << 11); offset_counter += 1; } } From 65bba8cc987baca3c84f21428be29c7304399d86 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 7 May 2022 14:53:18 +0300 Subject: [PATCH 28/70] Add fps measurement to the ili9341 gfx device --- gb/src/spi.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 4f6f50cf..08afe0ca 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -1,15 +1,20 @@ +use std::ops::Add; + use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; use rppal::gpio::OutputPin; pub struct Ili9341GfxDevice{ - ili9341_controller:Ili9341Contoller + ili9341_controller:Ili9341Contoller, + frames_counter: u32, + time_counter:std::time::Duration, + last_time: std::time::Instant, } impl Ili9341GfxDevice{ pub fn new()->Self{ let ili9341_controller = Ili9341Contoller::new(); - Ili9341GfxDevice {ili9341_controller} + Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} } } @@ -25,6 +30,17 @@ impl GfxDevice for Ili9341GfxDevice{ return u16_pixel; }); self.ili9341_controller.write_frame_buffer(&u16_buffer); + + // measure fps + self.frames_counter += 1; + let time = std::time::Instant::now(); + self.time_counter = self.time_counter.add(time.duration_since(self.last_time)); + self.last_time = time; + if self.time_counter.as_millis() > 1000{ + log::info!("FPS: {}", self.frames_counter); + self.frames_counter = 0; + self.time_counter = std::time::Duration::ZERO; + } } } From 94620bc1ce849d83b95d5fe7130f86c35604b05b Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 13 May 2022 13:44:40 +0300 Subject: [PATCH 29/70] Add mmio spi interface --- Cargo.lock | 1 + gb/Cargo.toml | 1 + gb/src/spi.rs | 187 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 184 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c44dea83..2d312536 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,7 @@ dependencies = [ "crossterm", "fern", "lib_gb", + "libc", "log", "rppal", "sdl2", diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 600144c6..64e744a7 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -20,6 +20,7 @@ crossbeam-channel = "0.5" cfg-if = "1.0" crossterm = "0.23" rppal = {version = "0.13", optional = true} +libc = "0.2" [features] default = ["static-sdl", "gpio"] diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 08afe0ca..350c7e43 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -1,7 +1,173 @@ -use std::ops::Add; +use std::{ops::Add, os::unix::fs::OpenOptionsExt, os::unix::io::AsRawFd}; use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; -use rppal::gpio::OutputPin; +use rppal::gpio::{OutputPin, IoPin}; + +struct GpioRegistersAccess{ + ptr:*mut u32 +} +enum GpioRegister{ + Gpfsel0 = 0, + Gpfsel1 = 1, + Gpset0 = 6, + Gpset1 = 7, + Gpclr0 = 8, + Gpclr1 = 9 +} +impl GpioRegistersAccess{ + unsafe fn read_register(&self, register:GpioRegister)->u32{ + std::ptr::read_volatile(self.ptr.add(register as usize)) + } + unsafe fn write_register(&self, register:GpioRegister, value:u32){ + std::ptr::write_volatile(self.ptr.add(register as usize), value); + } + unsafe fn set_gpio_mode(&self, pin:u8, mode:u8){ + // there are less than 100 pins so I assume the largest one is less than 100 + let gpfsel_register = pin / 10; + let gpfsel_register_index = pin % 10; + let register_ptr = self.ptr.add(gpfsel_register as usize); + let mut register_value = std::ptr::read_volatile(register_ptr); + let mask = !(0b111 << (gpfsel_register_index * 3)); + register_value &= mask; + register_value |= (mode as u32) << (gpfsel_register_index *3); + std::ptr::write_volatile(register_ptr, register_value); + } + unsafe fn set_gpio_high(&self, pin:u8){ + if pin < 32{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset0 as usize), 1 << pin); + } + else{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset1 as usize), 1 << (pin - 32)); + } + } + unsafe fn set_gpio_low(&self, pin:u8){ + if pin < 32{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr0 as usize), 1 << pin); + } + else{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr1 as usize), 1 << (pin - 32)); + } + } +} + +struct SpiRegistersAccess{ + ptr:*mut u32 +} +enum SpiRegister{ + Cs = 0, + Fifo = 1, + Clk = 2, + Dlen = 3 +} +impl SpiRegistersAccess{ + #[inline] + unsafe fn read_register(&self, register:SpiRegister)->u32{ + std::ptr::read_volatile(self.ptr.add(register as usize)) + } + #[inline] + unsafe fn write_register(&self, register:SpiRegister, value:u32){ + std::ptr::write_volatile(self.ptr.add(register as usize), value); + } +} + +struct RawSpi{ + spi_registers: SpiRegistersAccess, + // gpio_registers: GpioRegistersAccess, + spi_pins:[IoPin;2], + spi_cs0:OutputPin, + + dc_pin:OutputPin +} + +fn libc_abort_if_err(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); +} + +impl RawSpi{ + const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; + const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; + const BCM2835_RPI4_BASE_ADDRESS:usize = 0xFE00_0000; + const BCM_RPI4_SIZE:usize = 0x180_0000; + + const SPI_CS_RXF:u32 = 1 << 20; + const SPI_CS_RXR:u32 = 1 << 19; + const SPI_CS_TXD:u32 = 1 << 18; + const SPI_CS_RXD:u32 = 1 << 17; + const SPI_CS_DONE:u32 = 1 << 16; + const SPI_CS_TA:u32 = 1 << 7; + + fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ + // let mem_fd = std::fs::OpenOptions::new() + // .read(true).write(true) + // .custom_flags(libc::O_SYNC) + // .open("/dev/mem") + // .expect("Error, cant open /dev/mem make sure user has root access") + // .as_raw_fd(); + + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; + + if mem_fd < 0{ + libc_abort_if_err("bad file descriptor"); + } + + let bcm2835 = unsafe{libc::mmap( + std::ptr::null_mut(), + Self::BCM_RPI4_SIZE, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + Self::BCM2835_RPI4_BASE_ADDRESS as libc::off_t + )}; + + if bcm2835 == libc::MAP_FAILED{ + libc_abort_if_err("FATAL: mapping /dev/mem failed!"); + } + + // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; + let spi_registers = unsafe{SpiRegistersAccess{ptr:bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut u32}}; + + unsafe{ + // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 + spi_cs0.set_low(); + spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA); + spi_registers.write_register(SpiRegister::Clk, 34); + } + + log::info!("finish ili9341 device init"); + + RawSpi { spi_registers, dc_pin, spi_pins, spi_cs0 } + } + + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + self.dc_pin.set_low(); + self.write_raw(&[command as u8]); + self.dc_pin.set_high(); + self.write_raw(data); + } + + fn write_raw(&mut self, data:&[u8;SIZE]){ + unsafe{ + let mut current_index = 0; + while current_index < SIZE { + let cs:u32 = self.spi_registers.read_register(SpiRegister::Cs); + if cs & Self::SPI_CS_TXD != 0{ + self.spi_registers.write_register(SpiRegister::Fifo, data[current_index] as u32); + current_index += 1; + } + if (cs & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0 { + self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | 0b10_0000) + } + } + + // wait for the last trasfer to finish + while (self.spi_registers.read_register(SpiRegister::Cs) & Self::SPI_CS_DONE) == 0 { + if (self.spi_registers.read_register(SpiRegister::Cs) & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ + self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | 0b10_0000) + } + } + } + } +} pub struct Ili9341GfxDevice{ @@ -29,7 +195,10 @@ impl GfxDevice for Ili9341GfxDevice{ u16_pixel |= ((r >> 3) << 11) as u16; return u16_pixel; }); - self.ili9341_controller.write_frame_buffer(&u16_buffer); + + if self.frames_counter & 1 == 0{ + self.ili9341_controller.write_frame_buffer(&u16_buffer); + } // measure fps self.frames_counter += 1; @@ -102,7 +271,7 @@ enum Ili9341Commands{ } struct Ili9341Contoller{ - spi:RppalSpi, + spi:RawSpi, led_pin: OutputPin, reset_pin: OutputPin } @@ -134,7 +303,13 @@ impl Ili9341Contoller{ reset_pin.set_high(); std::thread::sleep(wait_duration); - let mut spi: RppalSpi = RppalSpi::new(dc_pin); + let spi0_ceo_n = gpio.get(8).unwrap().into_output(); + let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); + let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); + + let mut spi = RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n); + + unsafe{spi.spi_registers.write_register(SpiRegister::Dlen, 2)}; // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down @@ -189,6 +364,8 @@ impl Ili9341Contoller{ // turn backlight on led_pin.set_high(); + unsafe{spi.spi_registers.write_register(SpiRegister::Clk, 4)}; + log::info!("Initalizing with screen size width: {}, hight: {}", Self::TARGET_SCREEN_WIDTH, Self::TARGET_SCREEN_HEIGHT); return Ili9341Contoller { spi, led_pin, reset_pin}; From b8b3c37819b1007ede4d395338568d002736c967 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 20 May 2022 18:12:04 +0300 Subject: [PATCH 30/70] Add DMA --- Cargo.lock | 17 ++- gb/Cargo.toml | 1 + gb/src/dma.rs | 311 +++++++++++++++++++++++++++++++++++++++++++++++++ gb/src/main.rs | 1 + gb/src/spi.rs | 91 ++++++++++++--- 5 files changed, 402 insertions(+), 19 deletions(-) create mode 100644 gb/src/dma.rs diff --git a/Cargo.lock b/Cargo.lock index 2d312536..4eeb46ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "lib_gb", "libc", "log", + "nix", "rppal", "sdl2", "wav", @@ -618,9 +619,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.101" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "lock_api" @@ -732,6 +733,18 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "ntapi" version = "0.3.6" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 64e744a7..31043936 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -21,6 +21,7 @@ cfg-if = "1.0" crossterm = "0.23" rppal = {version = "0.13", optional = true} libc = "0.2" +nix = {version = "0.24", features = ["ioctl"]} [features] default = ["static-sdl", "gpio"] diff --git a/gb/src/dma.rs b/gb/src/dma.rs new file mode 100644 index 00000000..af1b1480 --- /dev/null +++ b/gb/src/dma.rs @@ -0,0 +1,311 @@ +use std::ptr::{write_volatile, read_volatile}; + +use libc::{c_void, c_int}; + +fn libc_abort(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); +} + +#[repr(C, align(16))] +struct MailboxMessage{ + length:u32, + request:u32, + tag:u32, + buffer_length:u32, + data_length:u32, // not sure if neccessary + data:[u32;PAYLOAD_SIZE], + message_end_indicator:u32 +} + +impl MailboxMessage{ + fn new(tag:u32, data:[u32;PAYLOAD_SIZE])->Self{ + Self{ + length:std::mem::size_of::() as u32, + request:0, + tag, + buffer_length:(std::mem::size_of::()*PAYLOAD_SIZE) as u32, + data_length:(std::mem::size_of::()*PAYLOAD_SIZE) as u32, + data, + message_end_indicator:0 + } + } +} + +struct Mailbox{ + mbox_fd: c_int, +} + +impl Mailbox{ + const MAILBOX_IOCTL_PROPERTY:libc::c_ulong = nix::request_code_readwrite!(100, 0, std::mem::size_of::<*mut libc::c_void>()); + + fn new()->Self{ + let fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/vcio\0").unwrap().as_ptr(), 0)}; + if fd < 0{ + std::panic!("Error while opening vc mailbox"); + } + + Self { mbox_fd: fd } + } + + fn send_command(&self, tag:u32, data:[u32;SIZE])->u32{ + let mut message = MailboxMessage::::new(tag, data); + return self.send_message(&mut message); + } + + fn send_message(&self, message:&mut MailboxMessage)->u32{ + let raw_message = message as *mut MailboxMessage as *mut c_void; + let ret = unsafe{libc::ioctl(self.mbox_fd, Self::MAILBOX_IOCTL_PROPERTY, raw_message)}; + if ret < 0{ + libc_abort("Error in ioctl call"); + } + + return message.data[0]; + } +} + +struct DmaMemory{ + virtual_address_ptr:usize, + bus_address:u32, + mailbox_memory_handle:u32, + size:u32 +} + +// Not valid for DMA4 channels +#[repr(C, align(32))] +struct DmaControlBlock{ + transfer_information:u32, + source_address:u32, + destination_address:u32, + trasnfer_length:u32, + stride:u32, // Not avalibale on the lite channels + next_control_block_address:u32, + reserved:[u32;2] +} +macro_rules! write_volatile_member{ + ($name:ident, $member:ident) =>{ + fn $name(&mut self,value:u32){ + unsafe{std::ptr::write_volatile(&mut self.$member , value)}; + } + } +} +impl DmaControlBlock{ + write_volatile_member!(WriteTi, transfer_information); +} + + + +#[repr(C, align(4))] +struct DmaRegistersAccess{ + control_status:u32, + control_block_address:u32, + control_block:DmaControlBlock, + debug:u32 +} + +enum DmaRegisters{ + Cs = 0, + ConblkAd = 1, + Ti = 2, + SourceAd = 3, + DestAd = 4, + TxfrLen = 5, + Stride = 6, + Nextconbk = 7, + Debug = 8 +} + +pub struct DmaTransferer{ + tx_dma:*mut DmaRegistersAccess, + rx_dma:*mut DmaRegistersAccess, + mbox:Mailbox, + tx_control_block_memory:DmaMemory, + rx_control_block_memory:DmaMemory, + source_buffer_memory:DmaMemory, + dma_data_memory:DmaMemory, + dma_const_data_memory:DmaMemory, + tx_channel_number:u8, + rx_channel_number:u8 +} + +impl DmaTransferer{ + const BCM2835_DMA0_BASE:usize = 0x7_000; + + const DMA_CS_END:u32 = 1 << 1; + const DMA_CS_ACTIVE:u32 = 1; + + const DMA_TI_SRC_DREQ:u32 = 1 << 10; + const DMA_TI_SRC_INC:u32 = 1 << 8; + const DMA_TI_DEST_IGNORE:u32 = 1 << 7; + const DMA_TI_DEST_DREQ:u32 = 1 << 6; + const DMA_TI_DEST_INC:u32 = 1 << 4; + const DMA_TI_WAIT_RESP:u32 = 1 << 3; + + const DMA_DMA0_CB_PHYS_ADDRESS:u32 = 0x7E00_7000; + const fn dma_ti_permap(peripherial_mapping:u8)->u32{(peripherial_mapping as u32) << 16} + + pub fn new(bcm2835:*mut c_void, tx_channel_number:u8, rx_channel_number:u8, mem_fd:c_int)->Self{ + let mbox = Mailbox::new(); + let tx_registers = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess }; + let rx_registers = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess }; + let dma_tx_control_block_memory = Self::allocate_dma_memory(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, mem_fd); + let dma_rx_control_block_memory = Self::allocate_dma_memory(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, mem_fd); + let dma_source_buffer_memory = Self::allocate_dma_memory(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, mem_fd); + let dma_data_memory = Self::allocate_dma_memory(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, mem_fd); + let dma_const_data_memory = Self::allocate_dma_memory(&mbox, (std::mem::size_of::() * 2) as u32, mem_fd); + + + unsafe{ + // setup constant data + let ptr = dma_const_data_memory.virtual_address_ptr as *mut u32; + write_volatile(ptr, 0x100); // spi_dma enable + write_volatile(ptr.add(1), Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + + // memset the memory + std::ptr::write_bytes(dma_rx_control_block_memory.virtual_address_ptr as *mut u8, 0, dma_rx_control_block_memory.size as usize); + std::ptr::write_bytes(dma_tx_control_block_memory.virtual_address_ptr as *mut u8, 0, dma_tx_control_block_memory.size as usize); + std::ptr::write_bytes(dma_source_buffer_memory.virtual_address_ptr as *mut u8, 0, dma_source_buffer_memory.size as usize); + std::ptr::write_bytes(dma_data_memory.virtual_address_ptr as *mut u8, 0, dma_data_memory.size as usize); + } + + Self { + tx_dma: tx_registers, + rx_dma: rx_registers, + mbox, + rx_control_block_memory:dma_rx_control_block_memory, + tx_control_block_memory:dma_tx_control_block_memory, + source_buffer_memory:dma_source_buffer_memory, + dma_data_memory, + rx_channel_number, + tx_channel_number, + dma_const_data_memory + } + } + + + const DMA_SPI_CS_PHYS_ADDRESS:u32 = 0x7E20_4000; + + pub fn dma_transfer(&mut self, data:&[u8; SIZE], tx_peripherial_mapping:u8, tx_physical_destination_address:u32, rx_peripherial_mapping:u8, rx_physical_destination_address:u32){ + if SIZE != NUM_CHUNKS * CHUNK_SIZE{ + std::panic!("bad SIZE param"); + } + + unsafe{ + std::ptr::copy_nonoverlapping(data.as_ptr(), self.source_buffer_memory.virtual_address_ptr as *mut u8, SIZE); + + let mut rx_control_block = &mut *(self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); + write_volatile(&mut rx_control_block.transfer_information, Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); + write_volatile(&mut rx_control_block.source_address, rx_physical_destination_address); + write_volatile(&mut rx_control_block.destination_address, 0); + write_volatile(&mut rx_control_block.trasnfer_length, CHUNK_SIZE as u32 - 4); // without the 4 byte header + write_volatile(&mut rx_control_block.next_control_block_address, 0); + + let tx_control_block = &mut *(self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); + write_volatile(&mut tx_control_block.transfer_information, Self::dma_ti_permap(tx_peripherial_mapping) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); + write_volatile(&mut tx_control_block.source_address, self.source_buffer_memory.bus_address); + write_volatile(&mut tx_control_block.destination_address, tx_physical_destination_address); + write_volatile(&mut tx_control_block.trasnfer_length, CHUNK_SIZE as u32); + write_volatile(&mut tx_control_block.next_control_block_address, 0); + + for i in 1..NUM_CHUNKS{ + let tx_cb_index = i * 4; + let tx_control_block = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index)); + write_volatile(&mut tx_control_block.transfer_information, Self::dma_ti_permap(tx_peripherial_mapping) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); + write_volatile(&mut tx_control_block.source_address, self.source_buffer_memory.bus_address + (i * CHUNK_SIZE) as u32); + write_volatile(&mut tx_control_block.destination_address, tx_physical_destination_address); + write_volatile(&mut tx_control_block.trasnfer_length, CHUNK_SIZE as u32); + write_volatile(&mut tx_control_block.next_control_block_address, 0); + + let set_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 1)); + let disable_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 2)); + let start_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 3)); + + write_volatile(&mut rx_control_block.next_control_block_address, self.tx_control_block_memory.bus_address + ((tx_cb_index + 1) * std::mem::size_of::()) as u32); + + write_volatile((self.dma_data_memory.virtual_address_ptr as *mut u32).add(i), self.tx_control_block_memory.bus_address + (tx_cb_index * std::mem::size_of::()) as u32); + + write_volatile(&mut set_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + write_volatile(&mut set_dma_tx_address.source_address, self.dma_data_memory.bus_address + (i as u32 * 4)); + write_volatile(&mut set_dma_tx_address.destination_address, Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) + 4); // channel control block address register + write_volatile(&mut set_dma_tx_address.trasnfer_length, 4); + write_volatile(&mut set_dma_tx_address.next_control_block_address, self.tx_control_block_memory.bus_address + ((tx_cb_index + 2) * std::mem::size_of::()) as u32); + + + write_volatile(&mut disable_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + write_volatile(&mut disable_dma_tx_address.source_address, self.dma_const_data_memory.bus_address); + write_volatile(&mut disable_dma_tx_address.destination_address, Self::DMA_SPI_CS_PHYS_ADDRESS); + write_volatile(&mut disable_dma_tx_address.trasnfer_length, 4); + write_volatile(&mut disable_dma_tx_address.next_control_block_address, self.tx_control_block_memory.bus_address + ((tx_cb_index + 3) * std::mem::size_of::()) as u32); + + + write_volatile(&mut start_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + write_volatile(&mut start_dma_tx_address.source_address, self.dma_const_data_memory.bus_address + 4); + write_volatile(&mut start_dma_tx_address.destination_address, Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) as u32); + write_volatile(&mut start_dma_tx_address.trasnfer_length, 4); + write_volatile(&mut start_dma_tx_address.next_control_block_address, self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); + + + rx_control_block = &mut *((self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(i)); + write_volatile(&mut rx_control_block.transfer_information, Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); + write_volatile(&mut rx_control_block.source_address, rx_physical_destination_address); + write_volatile(&mut rx_control_block.destination_address, 0); + write_volatile(&mut rx_control_block.trasnfer_length, CHUNK_SIZE as u32 - 4); // without the 4 byte header + write_volatile(&mut rx_control_block.next_control_block_address, 0); + } + + + write_volatile(&mut (*self.tx_dma).control_block_address, self.tx_control_block_memory.bus_address); + write_volatile(&mut (*self.rx_dma).control_block_address, self.rx_control_block_memory.bus_address); + + // Starting the dma transfer + Self::sync_syncronize(); + write_volatile(&mut (*self.tx_dma).control_status, Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + write_volatile(&mut (*self.rx_dma).control_status, Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + Self::sync_syncronize(); + + // Wait for the last trasfer to end + while read_volatile(&mut (*self.tx_dma).control_status) & Self::DMA_CS_ACTIVE != 0 { + // Self::sleep_ms(250); + // log::info!("Waiting for the tx channel"); + } + while read_volatile(&mut (*self.rx_dma).control_status) & Self::DMA_CS_ACTIVE != 0 { + // Self::sleep_ms(250); + // log::info!("Waiting for the rx channel"); + } + } + } + + fn sleep_ms(milliseconds_to_sleep:u64){ + std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); + } + + // trying to create a __sync_syncronize() impl + #[inline] + fn sync_syncronize(){ + std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); + } + + const MEM_ALLOC_FLAG_DIRECT:usize = 1 << 2; + const MEM_ALLOC_FLAG_COHERENT:usize = 1 << 3; + // This function converts the from the bus address of the SDRAM uncached memory to the arm physical address + // Notice that supposed to work only for this type of memory + const fn bus_to_phys(bus_address:u32)->u32{bus_address & !0xC000_0000} + + fn allocate_dma_memory(mbox:&Mailbox, size:u32, mem_fd:c_int)->DmaMemory{ + let flags = (Self::MEM_ALLOC_FLAG_COHERENT | Self::MEM_ALLOC_FLAG_DIRECT) as u32; + let handle = mbox.send_command(0x3000C, [size, 4096, flags]); + + let bus_address = mbox.send_command(0x3000D, [handle]); + let virtual_address = unsafe{libc::mmap( + std::ptr::null_mut(), + size as libc::size_t, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + Self::bus_to_phys(bus_address) as libc::off_t + )}; + + return DmaMemory { virtual_address_ptr: virtual_address as usize, bus_address, mailbox_memory_handle:handle, size } + } +} + + diff --git a/gb/src/main.rs b/gb/src/main.rs index 92634a49..38b29db3 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,4 +1,5 @@ mod spi; +mod dma; mod mbc_handler; mod mpmc_gfx_device; mod joypad_terminal_menu; diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 350c7e43..4d8c1949 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -1,8 +1,10 @@ -use std::{ops::Add, os::unix::fs::OpenOptionsExt, os::unix::io::AsRawFd}; +use std::ops::Add; use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; use rppal::gpio::{OutputPin, IoPin}; +use crate::dma::DmaTransferer; + struct GpioRegistersAccess{ ptr:*mut u32 } @@ -72,11 +74,13 @@ impl SpiRegistersAccess{ struct RawSpi{ spi_registers: SpiRegistersAccess, + mem_fd:libc::c_int, // gpio_registers: GpioRegistersAccess, spi_pins:[IoPin;2], spi_cs0:OutputPin, + dc_pin:OutputPin, - dc_pin:OutputPin + dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}> } fn libc_abort_if_err(message:&str){ @@ -86,7 +90,7 @@ fn libc_abort_if_err(message:&str){ impl RawSpi{ const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; - const BCM2835_RPI4_BASE_ADDRESS:usize = 0xFE00_0000; + const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; const BCM_RPI4_SIZE:usize = 0x180_0000; const SPI_CS_RXF:u32 = 1 << 20; @@ -94,16 +98,12 @@ impl RawSpi{ const SPI_CS_TXD:u32 = 1 << 18; const SPI_CS_RXD:u32 = 1 << 17; const SPI_CS_DONE:u32 = 1 << 16; + const SPI_CS_DMAEN:u32 = 1 << 8; const SPI_CS_TA:u32 = 1 << 7; + const SPI_CS_CLEAR:u32 = 3<<4; + const SPI_CS_CLEAR_RX:u32 = 1 << 5; fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ - // let mem_fd = std::fs::OpenOptions::new() - // .read(true).write(true) - // .custom_flags(libc::O_SYNC) - // .open("/dev/mem") - // .expect("Error, cant open /dev/mem make sure user has root access") - // .as_raw_fd(); - let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; if mem_fd < 0{ @@ -116,7 +116,7 @@ impl RawSpi{ libc::PROT_READ | libc::PROT_WRITE, libc::MAP_SHARED, mem_fd, - Self::BCM2835_RPI4_BASE_ADDRESS as libc::off_t + Self::BCM2835_RPI4_BUS_ADDRESS as libc::off_t )}; if bcm2835 == libc::MAP_FAILED{ @@ -131,11 +131,15 @@ impl RawSpi{ spi_cs0.set_low(); spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA); spi_registers.write_register(SpiRegister::Clk, 34); + spi_registers.write_register(SpiRegister::Dlen, 2); } log::info!("finish ili9341 device init"); - RawSpi { spi_registers, dc_pin, spi_pins, spi_cs0 } + RawSpi { + spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, + dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd, ) + } } fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ @@ -167,6 +171,61 @@ impl RawSpi{ } } } + + const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes + const DMA_SPI_TRANSFER_SIZE:usize = Ili9341Contoller::TARGET_SCREEN_HEIGHT * Ili9341Contoller::TARGET_SCREEN_WIDTH * std::mem::size_of::(); + const DMA_SPI_NUM_CHUNKS:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((Self::DMA_SPI_TRANSFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; + const DMA_SPI_CHUNK_SIZE:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; + const DMA_TI_PERMAP_SPI_TX:u8 = 6; + const DMA_TI_PERMAP_SPI_RX:u8 = 7; + const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; + + fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + // log::info!("actual_size: {}, size: {}, num: {}", SIZE, Self::DMA_SPI_CHUNK_SIZE, Self::DMA_SPI_NUM_CHUNKS); + self.dc_pin.set_low(); + self.write_raw(&[command as u8]); + self.dc_pin.set_high(); + self.write_dma_raw(&data); + } + + + // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use + fn write_dma_raw(&mut self, data:&[u8;SIZE]){ + unsafe{ + self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); + let data_len = Self::DMA_SPI_CHUNK_SIZE - 4; // Removing the first 4 bytes from this length param + let header = [Self::SPI_CS_TA as u8, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; + + let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - 4); + let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; + let mut i = 0; + for chunk in chunks{ + unsafe{ + std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); + std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); + } + i += 1; + } + + self.dma_transferer.dma_transfer(&array, + Self::DMA_TI_PERMAP_SPI_TX, + Self::DMA_SPI_FIFO_PHYS_ADDRESS, + Self::DMA_TI_PERMAP_SPI_RX, + Self::DMA_SPI_FIFO_PHYS_ADDRESS + ); + + Self::sync_syncronize(); + self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + self.spi_registers.write_register(SpiRegister::Dlen, 2); // poll mode speed up + Self::sync_syncronize(); + } + } + + // trying to create a __sync_syncronize() impl + #[inline] + fn sync_syncronize(){ + std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); + } } @@ -196,9 +255,9 @@ impl GfxDevice for Ili9341GfxDevice{ return u16_pixel; }); - if self.frames_counter & 1 == 0{ + // if self.frames_counter & 1 == 0{ self.ili9341_controller.write_frame_buffer(&u16_buffer); - } + // } // measure fps self.frames_counter += 1; @@ -309,8 +368,6 @@ impl Ili9341Contoller{ let mut spi = RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n); - unsafe{spi.spi_registers.write_register(SpiRegister::Dlen, 2)}; - // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) @@ -394,7 +451,7 @@ impl Ili9341Contoller{ u8_buffer[(i*2)+1] = (scaled_buffer[i] & 0xFF) as u8; } - self.spi.write(Ili9341Commands::MemoryWrite, &u8_buffer); + self.spi.write_dma(Ili9341Commands::MemoryWrite, &u8_buffer); } From 688d079d3dc9db53930d44762798d55d36f3ded8 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 28 May 2022 19:34:33 +0300 Subject: [PATCH 31/70] Trying to optimize the DMA --- gb/src/dma.rs | 157 +++++++++++++++++++++++++++----------------------- gb/src/spi.rs | 101 ++++++++++++++++++-------------- 2 files changed, 143 insertions(+), 115 deletions(-) diff --git a/gb/src/dma.rs b/gb/src/dma.rs index af1b1480..c7ee6f85 100644 --- a/gb/src/dma.rs +++ b/gb/src/dma.rs @@ -1,4 +1,4 @@ -use std::ptr::{write_volatile, read_volatile}; +use std::ptr::write_volatile; use libc::{c_void, c_int}; @@ -6,6 +6,22 @@ fn libc_abort(message:&str){ std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); } +macro_rules! decl_write_volatile_field{ + ($function_name:ident, $field_name:ident) =>{ + #[inline] unsafe fn $function_name(&mut self,value:u32){ + std::ptr::write_volatile(&mut self.$field_name , value); + } + } +} + +macro_rules! decl_read_volatile_field{ + ($function_name:ident, $field_name:ident) =>{ + #[inline] unsafe fn $function_name(&mut self)->u32{ + std::ptr::read_volatile(&self.$field_name) + } + } +} + #[repr(C, align(16))] struct MailboxMessage{ length:u32, @@ -77,19 +93,17 @@ struct DmaControlBlock{ source_address:u32, destination_address:u32, trasnfer_length:u32, - stride:u32, // Not avalibale on the lite channels + _stride:u32, // Not avalibale on the lite channels next_control_block_address:u32, - reserved:[u32;2] -} -macro_rules! write_volatile_member{ - ($name:ident, $member:ident) =>{ - fn $name(&mut self,value:u32){ - unsafe{std::ptr::write_volatile(&mut self.$member , value)}; - } - } + _reserved:[u32;2] } + impl DmaControlBlock{ - write_volatile_member!(WriteTi, transfer_information); + decl_write_volatile_field!(write_ti, transfer_information); + decl_write_volatile_field!(write_source_ad, source_address); + decl_write_volatile_field!(write_dest_ad, destination_address); + decl_write_volatile_field!(write_txfr_len, trasnfer_length); + decl_write_volatile_field!(write_nextconbk, next_control_block_address); } @@ -102,16 +116,10 @@ struct DmaRegistersAccess{ debug:u32 } -enum DmaRegisters{ - Cs = 0, - ConblkAd = 1, - Ti = 2, - SourceAd = 3, - DestAd = 4, - TxfrLen = 5, - Stride = 6, - Nextconbk = 7, - Debug = 8 +impl DmaRegistersAccess{ + decl_write_volatile_field!(write_cs, control_status); + decl_read_volatile_field!(read_cs, control_status); + decl_write_volatile_field!(write_conblk_ad, control_block_address); } pub struct DmaTransferer{ @@ -130,6 +138,7 @@ pub struct DmaTransferer{ impl DmaTransferer{ const BCM2835_DMA0_BASE:usize = 0x7_000; + const DMA_CS_RESET:u32 = 1 << 31; const DMA_CS_END:u32 = 1 << 1; const DMA_CS_ACTIVE:u32 = 1; @@ -153,6 +162,7 @@ impl DmaTransferer() * NUM_CHUNKS) as u32, mem_fd); let dma_const_data_memory = Self::allocate_dma_memory(&mbox, (std::mem::size_of::() * 2) as u32, mem_fd); + let dma_enable_register = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + 0xFF0) as *mut u32}; unsafe{ // setup constant data @@ -160,6 +170,13 @@ impl DmaTransferer DmaTransferer()) as u32); + rx_control_block.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 1) * std::mem::size_of::()) as u32); write_volatile((self.dma_data_memory.virtual_address_ptr as *mut u32).add(i), self.tx_control_block_memory.bus_address + (tx_cb_index * std::mem::size_of::()) as u32); - write_volatile(&mut set_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - write_volatile(&mut set_dma_tx_address.source_address, self.dma_data_memory.bus_address + (i as u32 * 4)); - write_volatile(&mut set_dma_tx_address.destination_address, Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) + 4); // channel control block address register - write_volatile(&mut set_dma_tx_address.trasnfer_length, 4); - write_volatile(&mut set_dma_tx_address.next_control_block_address, self.tx_control_block_memory.bus_address + ((tx_cb_index + 2) * std::mem::size_of::()) as u32); + set_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + set_dma_tx_address.write_source_ad(self.dma_data_memory.bus_address + (i as u32 * 4)); + set_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) + 4); // channel control block address register + set_dma_tx_address.write_txfr_len(4); + set_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 2) * std::mem::size_of::()) as u32); - write_volatile(&mut disable_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - write_volatile(&mut disable_dma_tx_address.source_address, self.dma_const_data_memory.bus_address); - write_volatile(&mut disable_dma_tx_address.destination_address, Self::DMA_SPI_CS_PHYS_ADDRESS); - write_volatile(&mut disable_dma_tx_address.trasnfer_length, 4); - write_volatile(&mut disable_dma_tx_address.next_control_block_address, self.tx_control_block_memory.bus_address + ((tx_cb_index + 3) * std::mem::size_of::()) as u32); + disable_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + disable_dma_tx_address.write_source_ad(self.dma_const_data_memory.bus_address); + disable_dma_tx_address.write_dest_ad(Self::DMA_SPI_CS_PHYS_ADDRESS); + disable_dma_tx_address.write_txfr_len(4); + disable_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 3) * std::mem::size_of::()) as u32); - write_volatile(&mut start_dma_tx_address.transfer_information, Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - write_volatile(&mut start_dma_tx_address.source_address, self.dma_const_data_memory.bus_address + 4); - write_volatile(&mut start_dma_tx_address.destination_address, Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) as u32); - write_volatile(&mut start_dma_tx_address.trasnfer_length, 4); - write_volatile(&mut start_dma_tx_address.next_control_block_address, self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); + start_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + start_dma_tx_address.write_source_ad(self.dma_const_data_memory.bus_address + 4); + start_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) as u32); + start_dma_tx_address.write_txfr_len(4); + start_dma_tx_address.write_nextconbk(self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); rx_control_block = &mut *((self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(i)); - write_volatile(&mut rx_control_block.transfer_information, Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); - write_volatile(&mut rx_control_block.source_address, rx_physical_destination_address); - write_volatile(&mut rx_control_block.destination_address, 0); - write_volatile(&mut rx_control_block.trasnfer_length, CHUNK_SIZE as u32 - 4); // without the 4 byte header - write_volatile(&mut rx_control_block.next_control_block_address, 0); + rx_control_block.write_ti(Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); + rx_control_block.write_source_ad(rx_physical_destination_address); + rx_control_block.write_dest_ad(0); + rx_control_block.write_txfr_len(CHUNK_SIZE as u32 - 4); // without the 4 byte header + rx_control_block.write_nextconbk(0); } - write_volatile(&mut (*self.tx_dma).control_block_address, self.tx_control_block_memory.bus_address); - write_volatile(&mut (*self.rx_dma).control_block_address, self.rx_control_block_memory.bus_address); + (*self.tx_dma).write_conblk_ad(self.tx_control_block_memory.bus_address); + (*self.rx_dma).write_conblk_ad(self.rx_control_block_memory.bus_address); // Starting the dma transfer - Self::sync_syncronize(); - write_volatile(&mut (*self.tx_dma).control_status, Self::DMA_CS_ACTIVE | Self::DMA_CS_END); - write_volatile(&mut (*self.rx_dma).control_status, Self::DMA_CS_ACTIVE | Self::DMA_CS_END); - Self::sync_syncronize(); + (*self.tx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + (*self.rx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + + + } + } + pub fn wait_for_dma_transfer(&self){ + unsafe{ // Wait for the last trasfer to end - while read_volatile(&mut (*self.tx_dma).control_status) & Self::DMA_CS_ACTIVE != 0 { + while (*self.tx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { // Self::sleep_ms(250); // log::info!("Waiting for the tx channel"); } - while read_volatile(&mut (*self.rx_dma).control_status) & Self::DMA_CS_ACTIVE != 0 { + while (*self.rx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { // Self::sleep_ms(250); // log::info!("Waiting for the rx channel"); } @@ -278,12 +299,6 @@ impl DmaTransferer{ + #[inline] unsafe fn $function_name(&mut self,value:u32){ + std::ptr::write_volatile(&mut self.$field_name , value); + } + } +} + +macro_rules! decl_read_volatile_field{ + ($function_name:ident, $field_name:ident) =>{ + #[inline] unsafe fn $function_name(&mut self)->u32{ + std::ptr::read_volatile(&self.$field_name) + } + } +} + struct GpioRegistersAccess{ ptr:*mut u32 } @@ -53,34 +69,29 @@ impl GpioRegistersAccess{ } struct SpiRegistersAccess{ - ptr:*mut u32 -} -enum SpiRegister{ - Cs = 0, - Fifo = 1, - Clk = 2, - Dlen = 3 + control_status:u32, + fifo:u32, + clock:u32, + data_length:u32 } + impl SpiRegistersAccess{ - #[inline] - unsafe fn read_register(&self, register:SpiRegister)->u32{ - std::ptr::read_volatile(self.ptr.add(register as usize)) - } - #[inline] - unsafe fn write_register(&self, register:SpiRegister, value:u32){ - std::ptr::write_volatile(self.ptr.add(register as usize), value); - } + decl_write_volatile_field!(write_cs, control_status); + decl_read_volatile_field!(read_cs, control_status); + decl_write_volatile_field!(write_fifo, fifo); + decl_write_volatile_field!(write_clk, clock); + decl_write_volatile_field!(write_dlen, data_length); } struct RawSpi{ - spi_registers: SpiRegistersAccess, + spi_registers: *mut SpiRegistersAccess, mem_fd:libc::c_int, - // gpio_registers: GpioRegistersAccess, spi_pins:[IoPin;2], spi_cs0:OutputPin, dc_pin:OutputPin, - dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}> + dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, + last_transfer_was_dma:bool } fn libc_abort_if_err(message:&str){ @@ -124,49 +135,61 @@ impl RawSpi{ } // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; - let spi_registers = unsafe{SpiRegistersAccess{ptr:bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut u32}}; + let spi_registers = unsafe{bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess}; unsafe{ // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 spi_cs0.set_low(); - spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA); - spi_registers.write_register(SpiRegister::Clk, 34); - spi_registers.write_register(SpiRegister::Dlen, 2); + (*spi_registers).write_cs(Self::SPI_CS_TA); + (*spi_registers).write_clk(34); + (*spi_registers).write_dlen(2); } log::info!("finish ili9341 device init"); RawSpi { - spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, - dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd, ) + spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, last_transfer_was_dma: false, + dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd ) } } fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + self.prepare_for_transfer(); self.dc_pin.set_low(); self.write_raw(&[command as u8]); self.dc_pin.set_high(); self.write_raw(data); + self.last_transfer_was_dma = false; + } + + fn prepare_for_transfer(&mut self) { + if self.last_transfer_was_dma{ + self.dma_transferer.wait_for_dma_transfer(); + unsafe{ + (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + (*self.spi_registers).write_dlen(2); // poll mode speed up + } + } } fn write_raw(&mut self, data:&[u8;SIZE]){ unsafe{ let mut current_index = 0; while current_index < SIZE { - let cs:u32 = self.spi_registers.read_register(SpiRegister::Cs); + let cs:u32 = (*self.spi_registers).read_cs(); if cs & Self::SPI_CS_TXD != 0{ - self.spi_registers.write_register(SpiRegister::Fifo, data[current_index] as u32); + (*self.spi_registers).write_fifo(data[current_index] as u32); current_index += 1; } if (cs & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0 { - self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | 0b10_0000) + (*self.spi_registers).write_cs(Self::SPI_CS_TA | 0b10_0000); } } // wait for the last trasfer to finish - while (self.spi_registers.read_register(SpiRegister::Cs) & Self::SPI_CS_DONE) == 0 { - if (self.spi_registers.read_register(SpiRegister::Cs) & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ - self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | 0b10_0000) + while ((*self.spi_registers).read_cs() & Self::SPI_CS_DONE) == 0 { + if ((*self.spi_registers).read_cs() & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ + (*self.spi_registers).write_cs(Self::SPI_CS_TA | 0b10_0000); } } } @@ -181,18 +204,19 @@ impl RawSpi{ const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ - // log::info!("actual_size: {}, size: {}, num: {}", SIZE, Self::DMA_SPI_CHUNK_SIZE, Self::DMA_SPI_NUM_CHUNKS); + self.prepare_for_transfer(); self.dc_pin.set_low(); self.write_raw(&[command as u8]); self.dc_pin.set_high(); self.write_dma_raw(&data); + self.last_transfer_was_dma = true; } // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use fn write_dma_raw(&mut self, data:&[u8;SIZE]){ unsafe{ - self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); + (*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); let data_len = Self::DMA_SPI_CHUNK_SIZE - 4; // Removing the first 4 bytes from this length param let header = [Self::SPI_CS_TA as u8, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; @@ -213,19 +237,8 @@ impl RawSpi{ Self::DMA_TI_PERMAP_SPI_RX, Self::DMA_SPI_FIFO_PHYS_ADDRESS ); - - Self::sync_syncronize(); - self.spi_registers.write_register(SpiRegister::Cs, Self::SPI_CS_TA | Self::SPI_CS_CLEAR); - self.spi_registers.write_register(SpiRegister::Dlen, 2); // poll mode speed up - Self::sync_syncronize(); } } - - // trying to create a __sync_syncronize() impl - #[inline] - fn sync_syncronize(){ - std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); - } } @@ -421,7 +434,7 @@ impl Ili9341Contoller{ // turn backlight on led_pin.set_high(); - unsafe{spi.spi_registers.write_register(SpiRegister::Clk, 4)}; + unsafe{(*spi.spi_registers).write_clk(4)}; log::info!("Initalizing with screen size width: {}, hight: {}", Self::TARGET_SCREEN_WIDTH, Self::TARGET_SCREEN_HEIGHT); From d848c5a8a893f52cd8c3b38ec93f9cbbb69f559a Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Tue, 31 May 2022 22:27:57 +0300 Subject: [PATCH 32/70] Try to optimize the sacling --- Cargo.lock | 34 +++++++++--- Cargo.toml | 3 +- gb/Cargo.toml | 2 + gb/src/spi.rs | 66 ++++++++++++++++++----- image_inter/Cargo.toml | 20 +++++++ image_inter/benches/inter_bench.rs | 47 +++++++++++++++++ image_inter/build.rs | 12 +++++ image_inter/src/lib.rs | 84 ++++++++++++++++++++++++++++++ image_inter/src/scale.c | 47 +++++++++++++++++ 9 files changed, 295 insertions(+), 20 deletions(-) create mode 100644 image_inter/Cargo.toml create mode 100644 image_inter/benches/inter_bench.rs create mode 100644 image_inter/build.rs create mode 100644 image_inter/src/lib.rs create mode 100644 image_inter/src/scale.c diff --git a/Cargo.lock b/Cargo.lock index 4eeb46ab..004848a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "fast_image_resize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf174e621dc44fc6118d0e3289a1286c166ac28599e15651a82da23a14a3425" +dependencies = [ + "num-traits", + "thiserror", +] + [[package]] name = "fern" version = "0.6.0" @@ -418,7 +428,9 @@ dependencies = [ "chrono", "crossbeam-channel", "crossterm", + "fast_image_resize", "fern", + "image_inter", "lib_gb", "libc", "log", @@ -561,6 +573,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image_inter" +version = "0.1.0" +dependencies = [ + "cc", + "criterion", + "fast_image_resize", + "libc", +] + [[package]] name = "indexmap" version = "1.7.0" @@ -766,9 +788,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -1312,18 +1334,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5c90c705..4856036a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "gb", - "lib_gb" + "lib_gb", + "image_inter" ] \ No newline at end of file diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 31043936..5dfd05a6 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -11,6 +11,7 @@ path = "src/main.rs" [dependencies] lib_gb = {path = "../lib_gb/"} +image_inter = {path = "../image_inter"} log = "0.4" fern = "0.6" chrono = "0.4" @@ -22,6 +23,7 @@ crossterm = "0.23" rppal = {version = "0.13", optional = true} libc = "0.2" nix = {version = "0.24", features = ["ioctl"]} +fast_image_resize = "0.9.3" [features] default = ["static-sdl", "gpio"] diff --git a/gb/src/spi.rs b/gb/src/spi.rs index 95289e74..b15a36c8 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -456,33 +456,35 @@ impl Ili9341Contoller{ ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); - let mut scaled_buffer: [u16;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH]; - Self::scale_to_screen(buffer, &mut scaled_buffer); - let mut u8_buffer:[u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH*2] = [0;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH*2]; - for i in 0..scaled_buffer.len(){ - u8_buffer[i*2] = (scaled_buffer[i] >> 8) as u8; - u8_buffer[(i*2)+1] = (scaled_buffer[i] & 0xFF) as u8; - } + let mut scaled_buffer: [u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]; + // unsafe{ + // Self::scale_to_screen_fr(&*(buffer as *const [u16; SCREEN_HEIGHT * SCREEN_WIDTH] as *const [u8; SCREEN_HEIGHT * SCREEN_WIDTH * 2]), + // &mut scaled_buffer); + // } - self.spi.write_dma(Ili9341Commands::MemoryWrite, &u8_buffer); + // Self::scale_to_screen_nearest(buffer, &mut scaled_buffer); + unsafe{image_inter::scale_to_screen_c::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; + + self.spi.write_dma(Ili9341Commands::MemoryWrite, &scaled_buffer); } // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ - fn scale_to_screen(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u16;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH]){ + fn scale_to_screen(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ // not sure why the -1.0 let x_ratio = (SCREEN_WIDTH as f32 - 1.0) / Self::TARGET_SCREEN_WIDTH as f32; let y_ratio = (SCREEN_HEIGHT as f32 - 1.0) / Self::TARGET_SCREEN_HEIGHT as f32; let mut offset_counter = 0; for y in 0..Self::TARGET_SCREEN_HEIGHT{ + let y_val = (y_ratio * y as f32) as u32; // y value of a point in this ratio between o and y + let y_diff = (y_ratio * y as f32) - y_val as f32; + for x in 0..Self::TARGET_SCREEN_WIDTH{ let x_val = (x_ratio * x as f32) as u32; // x value of a point in this ratio between 0 and x - let y_val = (y_ratio * y as f32) as u32; // y value of a point in this ratio between o and y let x_diff = (x_ratio * x as f32) - x_val as f32; - let y_diff = (y_ratio * y as f32) - y_val as f32; - let original_pixel_index = (y_val as usize * SCREEN_WIDTH) + x_val as usize; + let original_pixel_index = (y_val as usize * SCREEN_WIDTH) + x_val as usize; // Get the pixel and 3 surounding pixels let pixel_a = input_buffer[original_pixel_index]; let pixel_b = input_buffer[original_pixel_index + 1]; @@ -502,12 +504,50 @@ impl Ili9341Contoller{ (((pixel_c >> 11) & 0x1F) as f32 * y_diff * (1.0-x_diff)) + (((pixel_d >> 11) & 0x1F) as f32 * x_diff * y_diff); - output_buffer[offset_counter] = blue as u16 | ((green as u16) << 5) | ((red as u16) << 11); + let pixel = blue as u16 | ((green as u16) << 5) | ((red as u16) << 11); + output_buffer[offset_counter * 2] = (pixel >> 8) as u8; + output_buffer[(offset_counter * 2) + 1] = (pixel & 0xFF) as u8; offset_counter += 1; } } } + fn scale_to_screen_fr(input_buffer:&[u8;SCREEN_HEIGHT*SCREEN_WIDTH * 2], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ + let mut buffer = input_buffer.clone(); + let mut src_buffer = fast_image_resize::Image::from_slice_u8( + std::num::NonZeroU32::new(SCREEN_WIDTH as u32).unwrap(), + std::num::NonZeroU32::new(SCREEN_HEIGHT as u32).unwrap(), + &mut buffer, + fast_image_resize::PixelType::U16 + ).unwrap(); + + let mut dst_buffer = fast_image_resize::Image::from_slice_u8( + std::num::NonZeroU32::new(Self::TARGET_SCREEN_WIDTH as u32).unwrap(), + std::num::NonZeroU32::new(Self::TARGET_SCREEN_HEIGHT as u32).unwrap(), + output_buffer, + fast_image_resize::PixelType::U16 + ).unwrap(); + + let mut resizer = fast_image_resize::Resizer::new(fast_image_resize::ResizeAlg::Convolution(fast_image_resize::FilterType::Bilinear)); + resizer.resize(&src_buffer.view(), &mut dst_buffer.view_mut()).unwrap(); + } + + // implemented based on this article - https://kwojcicki.github.io/blog/NEAREST-NEIGHBOUR + fn scale_to_screen_nearest(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ + for y in 0..Self::TARGET_SCREEN_HEIGHT{ + for x in 0..Self::TARGET_SCREEN_WIDTH{ + let proj_x = ((1.0 / Self::SCALE) * x as f32) as usize; + let proj_y = ((1.0 / Self::SCALE) * y as f32) as usize; + unsafe{ + let pixel = input_buffer.get_unchecked((proj_y * SCREEN_WIDTH) + proj_x); + let output_index = (y * Self::TARGET_SCREEN_WIDTH) + x; + *output_buffer.get_unchecked_mut(output_index * 2) = (pixel >> 8) as u8; + *output_buffer.get_unchecked_mut((output_index * 2) + 1) = (pixel & 0xFF) as u8; + } + } + } + } + fn sleep_ms(milliseconds_to_sleep:u64){ std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); } diff --git a/image_inter/Cargo.toml b/image_inter/Cargo.toml new file mode 100644 index 00000000..4158015b --- /dev/null +++ b/image_inter/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "image_inter" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "0.2" + +[build-dependencies] +cc = "1.0" + +[dev-dependencies] +criterion = "0.3" +fast_image_resize = "0.9.3" + +[[bench]] +name = "inter_bench" +harness = false \ No newline at end of file diff --git a/image_inter/benches/inter_bench.rs b/image_inter/benches/inter_bench.rs new file mode 100644 index 00000000..540aaf27 --- /dev/null +++ b/image_inter/benches/inter_bench.rs @@ -0,0 +1,47 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use image_inter::{scale_to_screen, scale_to_screen_c}; + + +pub fn interpolation_rust_bench(c: &mut Criterion){ + let input_buffer = [0_u16; 160*144]; + let mut output_buffer = [0_u8; 240*266*2]; + c.bench_function("bench rust inter", |b|b.iter(||{ + unsafe{scale_to_screen::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; + })); +} + +pub fn interpolation_c_bench(c: &mut Criterion){ + let input_buffer = [0_u16; 160*144]; + let mut output_buffer = [0_u8; 240*266*2]; + c.bench_function("bench c inter", |b|b.iter(||{ + unsafe{scale_to_screen_c::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; + })); +} + +pub fn interpolation_fir_bench(c: &mut Criterion){ + let input_buffer = [0_u8; 160*144*2]; + let mut output_buffer = [0_u8; 240*266*2]; + c.bench_function("bench fir inter", |b|b.iter(||{ + let mut buffer = input_buffer.clone(); + let src_buffer = fast_image_resize::Image::from_slice_u8( + std::num::NonZeroU32::new(160 as u32).unwrap(), + std::num::NonZeroU32::new(144 as u32).unwrap(), + &mut buffer, + fast_image_resize::PixelType::U16 + ).unwrap(); + + let mut dst_buffer = fast_image_resize::Image::from_slice_u8( + std::num::NonZeroU32::new(266 as u32).unwrap(), + std::num::NonZeroU32::new(240 as u32).unwrap(), + &mut output_buffer, + fast_image_resize::PixelType::U16 + ).unwrap(); + + let mut resizer = fast_image_resize::Resizer::new(fast_image_resize::ResizeAlg::Convolution(fast_image_resize::FilterType::Bilinear)); + resizer.resize(&src_buffer.view(), &mut dst_buffer.view_mut()).unwrap(); + + })); +} + +criterion_group!(benches, interpolation_fir_bench, interpolation_rust_bench, interpolation_c_bench); +criterion_main!(benches); \ No newline at end of file diff --git a/image_inter/build.rs b/image_inter/build.rs new file mode 100644 index 00000000..4d847e33 --- /dev/null +++ b/image_inter/build.rs @@ -0,0 +1,12 @@ +fn main() { + // Tell Cargo that if the given file changes, to rerun this build script. + println!("cargo:rerun-if-changed=src/hello.c"); + // Use the `cc` crate to build a C file and statically link it. + cc::Build::new() + .flag("-mfpu=neon") + .warnings(true) + .extra_warnings(true) + .warnings_into_errors(true) + .file("src/scale.c") + .compile("scale"); +} diff --git a/image_inter/src/lib.rs b/image_inter/src/lib.rs new file mode 100644 index 00000000..aa0af751 --- /dev/null +++ b/image_inter/src/lib.rs @@ -0,0 +1,84 @@ +use libc::c_int; + +extern "C" { + fn scale_buffer( + input_buffer:*const u16, + input_buffer_width:c_int, + input_buffer_height:c_int, + + output_buffer: *mut u8, + output_buffer_width:c_int, + output_buffer_height:c_int + ); +} + +pub unsafe fn scale_to_screen_asm(input_buffer: *const u16, output_buffer: *mut u8){ + let x_ratio = (INPUT_WIDTH as f32 - 1.0) / OUTPUT_WIDTH as f32; + let y_ratio = (INPUT_HEIGHT as f32 - 1.0) / OUTPUT_HEIGHT as f32; + + std::arch::asm!( + "push {{r0, r1, r2, r3}}", + ".height_loop", + "ldr r0, {OUTPUT_HEIGHT}", + "subs r0, r0, 1", + // "b nz " + "pop {{r0, r1, r2, r3}}", + OUTPUT_HEIGHT = in(reg) OUTPUT_HEIGHT + ); +} + +// This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ +pub unsafe fn scale_to_screen(input_buffer: *const u16, output_buffer: *mut u8){ + // not sure why the -1.0 + let x_ratio = (INPUT_WIDTH as f32 - 1.0) / OUTPUT_WIDTH as f32; + let y_ratio = (INPUT_HEIGHT as f32 - 1.0) / OUTPUT_HEIGHT as f32; + + let mut offset_counter = 0; + for y in 0..OUTPUT_HEIGHT{ + let y_val = (y_ratio * y as f32) as u32; // y value of a point in this ratio between o and y + let y_diff = (y_ratio * y as f32) - y_val as f32; + + for x in 0..OUTPUT_WIDTH{ + let x_val = (x_ratio * x as f32) as u32; // x value of a point in this ratio between 0 and x + let x_diff = (x_ratio * x as f32) - x_val as f32; + + let original_pixel_index = (y_val as usize * INPUT_WIDTH) + x_val as usize; + // Get the pixel and 3 surounding pixels + let pixel_a = *input_buffer.add(original_pixel_index); + let pixel_b = *input_buffer.add(original_pixel_index + 1); + let pixel_c = *input_buffer.add(original_pixel_index + INPUT_WIDTH); + let pixel_d = *input_buffer.add(original_pixel_index + INPUT_WIDTH + 1); + + let weights = [ (1.0-x_diff) * (1.0-y_diff), (x_diff)*(1.0-y_diff), y_diff * (1.0-x_diff), x_diff * y_diff]; + + let blue:f32 = ((pixel_a & 0x1F) as f32 * weights[0]) + + ((pixel_b & 0x1F) as f32 * weights[1]) + + ((pixel_c & 0x1F) as f32 * weights[2]) + + ((pixel_d & 0x1F) as f32 * weights[3]); + let green:f32 = (((pixel_a >> 5) & 0x3F) as f32 * weights[0]) + + (((pixel_b >> 5) & 0x3F) as f32 * weights[1]) + + (((pixel_c >> 5) & 0x3F) as f32 * weights[2]) + + (((pixel_d >> 5) & 0x3F) as f32 * weights[3]); + let red:f32 = (((pixel_a >> 11) & 0x1F) as f32 * weights[0]) + + (((pixel_b >> 11) & 0x1F) as f32 * weights[1]) + + (((pixel_c >> 11) & 0x1F) as f32 * weights[2]) + + (((pixel_d >> 11) & 0x1F) as f32 * weights[3]); + + let pixel = blue as u16 | ((green as u16) << 5) | ((red as u16) << 11); + *output_buffer.add(offset_counter * 2) = (pixel >> 8) as u8; + *output_buffer.add((offset_counter * 2) + 1) = (pixel & 0xFF) as u8; + offset_counter += 1; + } + } +} + +pub unsafe fn scale_to_screen_c(input_buffer: *const u16, output_buffer: *mut u8){ + scale_buffer( + input_buffer, + INPUT_WIDTH as c_int, + INPUT_HEIGHT as c_int, + output_buffer, + OUTPUT_WIDTH as c_int, + OUTPUT_HEIGHT as c_int + ); +} diff --git a/image_inter/src/scale.c b/image_inter/src/scale.c new file mode 100644 index 00000000..54cdac67 --- /dev/null +++ b/image_inter/src/scale.c @@ -0,0 +1,47 @@ +#include + +extern void scale_buffer(const uint16_t* input_buffer, int input_buffer_width, int input_buffer_height, uint8_t* output_buffer, int output_buffer_width, int output_buffer_height){ + const float x_ratio = ((float)input_buffer_width - 1.0) / (float)output_buffer_width; + const float y_ratio = ((float)input_buffer_height - 1.0) / (float)output_buffer_height; + + int output_offset_counter = 0; + for (int y = 0; y < output_buffer_height; y++){ + const int y_val = (int)(y_ratio * (float)y); + const float y_diff = (y_ratio * (float)y) - (float)y_val; + + for (int x = 0; x < output_buffer_width; x++){ + const int x_val = (int)(x_ratio * (float)x); + const float x_diff = (x_ratio * (float)x) - (float)x_val; + + const int original_pixel_index = (y_val * input_buffer_width) + x_val; + + const uint16_t pixel_a = input_buffer[original_pixel_index]; + const uint16_t pixel_b = input_buffer[original_pixel_index + 1]; + const uint16_t pixel_c = input_buffer[original_pixel_index + input_buffer_width]; + const uint16_t pixel_d = input_buffer[original_pixel_index + input_buffer_width + 1]; + + const float weights[4] = {(1.0-x_diff) * (1.0-y_diff), (x_diff)*(1.0-y_diff), y_diff * (1.0-x_diff), x_diff * y_diff}; + + const float blue = ((float)(pixel_a & 0x1F) * weights[0]) + + ((float)(pixel_b & 0x1F) * weights[1]) + + ((float)(pixel_c & 0x1F) * weights[2]) + + ((float)(pixel_d & 0x1F) * weights[3]); + + const float green = ((float)((pixel_a >> 5)& 0x3F) * weights[0]) + + ((float)((pixel_b >> 5)& 0x3F) * weights[1]) + + ((float)((pixel_c >> 5)& 0x3F) * weights[2]) + + ((float)((pixel_d >> 5)& 0x3F) * weights[3]); + + const float red = ((float)((pixel_a >> 11)& 0x1F) * weights[0]) + + ((float)((pixel_b >> 11)& 0x1F) * weights[1]) + + ((float)((pixel_c >> 11)& 0x1F) * weights[2]) + + ((float)((pixel_d >> 11)& 0x1F) * weights[3]); + + const uint16_t pixel = ((uint16_t)blue) | (((uint16_t)green) << 5) | (((uint16_t)red) << 11); + + output_buffer[output_offset_counter * 2] = (uint8_t)(pixel >> 8); + output_buffer[(output_offset_counter * 2) + 1] = (uint8_t)(pixel && 0xFF); + output_offset_counter++; + } + } +} \ No newline at end of file From c73d0b6049fd9fa4359b9ae542cd700c5e8156a2 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 4 Jun 2022 09:07:02 +0100 Subject: [PATCH 33/70] It is running 60 fps on the rpi4. the problem was that the interpolation was done after the address set commands, and writing to the spi will block until the last dma transfer will finish. --- gb/src/spi.rs | 12 +++--------- image_inter/src/lib.rs | 15 --------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/gb/src/spi.rs b/gb/src/spi.rs index b15a36c8..2f1073b5 100644 --- a/gb/src/spi.rs +++ b/gb/src/spi.rs @@ -443,6 +443,9 @@ impl Ili9341Contoller{ pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ + let mut scaled_buffer: [u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]; + unsafe{image_inter::scale_to_screen_c::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; + let end_x_index = Self::TARGET_SCREEN_WIDTH + Self::FRAME_BUFFER_X_OFFSET - 1; self.spi.write(Ili9341Commands::ColumnAddressSet, &[ (Self::FRAME_BUFFER_X_OFFSET >> 8) as u8, @@ -455,15 +458,6 @@ impl Ili9341Contoller{ ((Self::TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 ]); - - let mut scaled_buffer: [u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]; - // unsafe{ - // Self::scale_to_screen_fr(&*(buffer as *const [u16; SCREEN_HEIGHT * SCREEN_WIDTH] as *const [u8; SCREEN_HEIGHT * SCREEN_WIDTH * 2]), - // &mut scaled_buffer); - // } - - // Self::scale_to_screen_nearest(buffer, &mut scaled_buffer); - unsafe{image_inter::scale_to_screen_c::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; self.spi.write_dma(Ili9341Commands::MemoryWrite, &scaled_buffer); } diff --git a/image_inter/src/lib.rs b/image_inter/src/lib.rs index aa0af751..f6c2dc2a 100644 --- a/image_inter/src/lib.rs +++ b/image_inter/src/lib.rs @@ -12,21 +12,6 @@ extern "C" { ); } -pub unsafe fn scale_to_screen_asm(input_buffer: *const u16, output_buffer: *mut u8){ - let x_ratio = (INPUT_WIDTH as f32 - 1.0) / OUTPUT_WIDTH as f32; - let y_ratio = (INPUT_HEIGHT as f32 - 1.0) / OUTPUT_HEIGHT as f32; - - std::arch::asm!( - "push {{r0, r1, r2, r3}}", - ".height_loop", - "ldr r0, {OUTPUT_HEIGHT}", - "subs r0, r0, 1", - // "b nz " - "pop {{r0, r1, r2, r3}}", - OUTPUT_HEIGHT = in(reg) OUTPUT_HEIGHT - ); -} - // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ pub unsafe fn scale_to_screen(input_buffer: *const u16, output_buffer: *mut u8){ // not sure why the -1.0 From d8c656400430c24d966e81b012f39a6a78308a60 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 4 Jun 2022 11:54:44 +0300 Subject: [PATCH 34/70] Split the code to other modules --- Cargo.lock | 3 +- gb/Cargo.toml | 6 +- gb/src/main.rs | 19 +- gb/src/{ => rpi_gpio}/dma.rs | 37 +- gb/src/{ => rpi_gpio}/gpio_joypad_provider.rs | 0 gb/src/rpi_gpio/ili9341_controller.rs | 214 +++++++ gb/src/rpi_gpio/mod.rs | 35 ++ gb/src/rpi_gpio/raw_spi.rs | 241 ++++++++ gb/src/rpi_gpio/spi.rs | 43 ++ gb/src/spi.rs | 557 ------------------ image_inter/Cargo.toml | 2 +- image_inter/benches/inter_bench.rs | 16 +- image_inter/build.rs | 4 +- image_inter/src/lib.rs | 18 +- 14 files changed, 588 insertions(+), 607 deletions(-) rename gb/src/{ => rpi_gpio}/dma.rs (92%) rename gb/src/{ => rpi_gpio}/gpio_joypad_provider.rs (100%) create mode 100644 gb/src/rpi_gpio/ili9341_controller.rs create mode 100644 gb/src/rpi_gpio/mod.rs create mode 100644 gb/src/rpi_gpio/raw_spi.rs create mode 100644 gb/src/rpi_gpio/spi.rs delete mode 100644 gb/src/spi.rs diff --git a/Cargo.lock b/Cargo.lock index 004848a3..588856ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,7 +428,6 @@ dependencies = [ "chrono", "crossbeam-channel", "crossterm", - "fast_image_resize", "fern", "image_inter", "lib_gb", @@ -575,7 +574,7 @@ dependencies = [ [[package]] name = "image_inter" -version = "0.1.0" +version = "1.0.0" dependencies = [ "cc", "criterion", diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 5dfd05a6..c00ee7c1 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -23,12 +23,12 @@ crossterm = "0.23" rppal = {version = "0.13", optional = true} libc = "0.2" nix = {version = "0.24", features = ["ioctl"]} -fast_image_resize = "0.9.3" [features] -default = ["static-sdl", "gpio"] +default = ["static-sdl", "raw-spi"] sdl-resample = [] push-audio = [] static-sdl = ["sdl2/bundled", "sdl2/static-link"] static-scale = [] -gpio = ["rppal"] \ No newline at end of file +gpio = ["rppal"] +raw-spi = ["gpio"] # requires sudo \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 38b29db3..2c18da3a 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,11 +1,8 @@ -mod spi; -mod dma; mod mbc_handler; mod mpmc_gfx_device; mod joypad_terminal_menu; #[cfg(feature = "gpio")] -mod gpio_joypad_provider; - +mod rpi_gpio; mod audio{ pub mod audio_resampler; pub mod multi_device_audio; @@ -13,7 +10,6 @@ mod audio{ #[cfg(not(feature = "sdl-resample"))] pub mod manual_audio_resampler; } - mod sdl{ pub mod utils; pub mod sdl_gfx_device; @@ -30,12 +26,8 @@ mod sdl{ pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; } } - - cfg_if::cfg_if!{ - if #[cfg(not(feature = "gpio"))]{ - pub mod sdl_joypad_provider; - } - } + #[cfg(not(feature = "gpio"))] + pub mod sdl_joypad_provider; } cfg_if::cfg_if!{ @@ -59,7 +51,7 @@ const TURBO_MUL:u8 = 1; cfg_if::cfg_if!{ if #[cfg(feature = "gpio")]{ - use crate::gpio_joypad_provider::*; + use crate::rpi_gpio::gpio_joypad_provider::*; fn buttons_mapper(button:&Button)->GpioPin{ match button{ Button::A => 18, @@ -176,7 +168,8 @@ fn main() { } #[cfg(feature = "gpio")] - let mut gfx_device = spi::Ili9341GfxDevice::new(); + let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(); + #[cfg(not(feature = "gpio"))] let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); diff --git a/gb/src/dma.rs b/gb/src/rpi_gpio/dma.rs similarity index 92% rename from gb/src/dma.rs rename to gb/src/rpi_gpio/dma.rs index c7ee6f85..d54a962c 100644 --- a/gb/src/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -2,26 +2,9 @@ use std::ptr::write_volatile; use libc::{c_void, c_int}; -fn libc_abort(message:&str){ - std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); -} - -macro_rules! decl_write_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self,value:u32){ - std::ptr::write_volatile(&mut self.$field_name , value); - } - } -} - -macro_rules! decl_read_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self)->u32{ - std::ptr::read_volatile(&self.$field_name) - } - } -} +use super::*; +// Mailbox messages need to be 16 byte alligned #[repr(C, align(16))] struct MailboxMessage{ length:u32, @@ -47,6 +30,7 @@ impl MailboxMessage{ } } +// Docs - https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface struct Mailbox{ mbox_fd: c_int, } @@ -70,7 +54,12 @@ impl Mailbox{ fn send_message(&self, message:&mut MailboxMessage)->u32{ let raw_message = message as *mut MailboxMessage as *mut c_void; - let ret = unsafe{libc::ioctl(self.mbox_fd, Self::MAILBOX_IOCTL_PROPERTY, raw_message)}; + let ret = unsafe{ + // Using libc::ioctl and not nix high level abstraction over it cause Im sending a *void and not more + // concrete type and the nix macro will mess the types for us. I belive it could work with nix after some modification + // of the way Im handling this but Im leaving this as it for now. sorry! + libc::ioctl(self.mbox_fd, Self::MAILBOX_IOCTL_PROPERTY, raw_message) + }; if ret < 0{ libc_abort("Error in ioctl call"); } @@ -86,6 +75,8 @@ struct DmaMemory{ size:u32 } +// The DMA control block registers are in a 32 byte alligned addresses so the stracture mapping them needs to be as well +// in order for me to cast some bytes to this stuct (at least I think so) // Not valid for DMA4 channels #[repr(C, align(32))] struct DmaControlBlock{ @@ -107,7 +98,7 @@ impl DmaControlBlock{ } - +// Since Im casting an arbitary pointer to this struct it must be alligned by 4 bytes (with no gaps as well) #[repr(C, align(4))] struct DmaRegistersAccess{ control_status:u32, @@ -201,7 +192,7 @@ impl DmaTransferer(&mut self, data:&[u8; SIZE], tx_peripherial_mapping:u8, tx_physical_destination_address:u32, rx_peripherial_mapping:u8, rx_physical_destination_address:u32){ + pub fn start_dma_transfer(&mut self, data:&[u8; SIZE], tx_peripherial_mapping:u8, tx_physical_destination_address:u32, rx_peripherial_mapping:u8, rx_physical_destination_address:u32){ if SIZE != NUM_CHUNKS * CHUNK_SIZE{ std::panic!("bad SIZE param"); } @@ -281,7 +272,7 @@ impl DmaTransfererSelf; + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]); + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]); +} + +struct Ili9341Contoller{ + spi:SC, + led_pin: OutputPin, + reset_pin: OutputPin +} + +impl Ili9341Contoller{ + const CLEAN_BUFFER:[u8;ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * 2] = [0; ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * 2]; + + pub fn new()->Self{ + let gpio = rppal::gpio::Gpio::new().unwrap(); + // let mut dc_pin = gpio.get(15).unwrap().into_output(); + let mut reset_pin = gpio.get(14).unwrap().into_output(); + let mut led_pin = gpio.get(25).unwrap().into_output(); + drop(gpio); + + // toggling the reset pin to initalize the lcd + let wait_duration = std::time::Duration::from_millis(120); + reset_pin.set_high(); + std::thread::sleep(wait_duration); + reset_pin.set_low(); + std::thread::sleep(wait_duration); + reset_pin.set_high(); + std::thread::sleep(wait_duration); + + // let spi0_ceo_n = gpio.get(8).unwrap().into_output(); + // let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); + // let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); + + // let mut spi = RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n); + let mut spi:SC = SpiController::new(15); + // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) + // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down + // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) + + // There is another implementation in rust for an ili9341 controller which is much simpler and uses those commands: + // Sleepms(5), SoftwareReset, Sleepms(120), MemoryAccessControl, PixelFormatSet, SleepOut, Sleepms(5), DisplayOn + // minimal config based on rust ili9341 lib (https://github.com/yuri91/ili9341-rs) + + // fbcp-ili9341 inspired implementation: + /*---------------------------------------------------------------------------------------------------------------------- */ + // Reset the screen + spi.write(Ili9341Commands::SoftwareReset,&[]); + Self::sleep_ms(5); + spi.write(Ili9341Commands::DisplayOff,&[]); + + // Some power stuff, probably uneccessary but just for sure + spi.write(Ili9341Commands::PowerControlA, &[0x39, 0x2C, 0x0, 0x34, 0x2]); + spi.write(Ili9341Commands::PowerControlB, &[0x0, 0xC1, 0x30]); + spi.write(Ili9341Commands::DriverTimingControlA, &[0x85, 0x0, 0x78]); + spi.write(Ili9341Commands::DriverTimingControlB, &[0x0, 0x0]); + spi.write(Ili9341Commands::PowerOnSequenceControl, &[0x64, 0x3, 0x12, 0x81]); + spi.write(Ili9341Commands::PowerControl1, &[0x23]); + spi.write(Ili9341Commands::PowerControl2,&[0x10]); + spi.write(Ili9341Commands::VcomControl1, &[0xE3, 0x28]); + spi.write(Ili9341Commands::VcomControl2, &[0x86]); + + // Configuring the screen + spi.write(Ili9341Commands::MemoryAccessControl, &[0x20]); // This command tlit the screen 90 degree + spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel; + spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x1F /*According to the docs this is 61 hrz */]); + spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]); + + // Gamma values - pretty sure its redundant + spi.write(Ili9341Commands::Enable3G, &[0x2]); + spi.write(Ili9341Commands::GammaSet, &[0x1]); + spi.write(Ili9341Commands::PossitiveGammaCorrection,&[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); + spi.write(Ili9341Commands::NegativeGammaCorrection, &[0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); + + // Turn screen on + spi.write(Ili9341Commands::SleepOut,&[]); + Self::sleep_ms(120); + spi.write(Ili9341Commands::DisplayOn,&[]); + /*---------------------------------------------------------------------------------------------------------------------- */ + //End of fbcp-ili9341 inpired implementation + + // Clear screen + spi.write(Ili9341Commands::ColumnAddressSet, &[0,0,((ILI9341_SCREEN_WIDTH -1) >> 8) as u8, ((ILI9341_SCREEN_WIDTH -1) & 0xFF) as u8]); + spi.write(Ili9341Commands::PageAddressSet, &[0,0,((ILI9341_SCREEN_HEIGHT -1) >> 8) as u8, ((ILI9341_SCREEN_HEIGHT -1) & 0xFF) as u8]); + // using write and not write buffer since this is not the correct size + spi.write(Ili9341Commands::MemoryWrite, &Self::CLEAN_BUFFER); + + // turn backlight on + led_pin.set_high(); + + // unsafe{(*spi.spi_registers).write_clk(4)}; + + log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); + + return Ili9341Contoller { spi, led_pin, reset_pin}; + } + + + pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ + let mut scaled_buffer: [u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2] = [0;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]; + unsafe{image_inter::scale_biliniear_c::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; + + let end_x_index = TARGET_SCREEN_WIDTH + FRAME_BUFFER_X_OFFSET - 1; + self.spi.write(Ili9341Commands::ColumnAddressSet, &[ + (FRAME_BUFFER_X_OFFSET >> 8) as u8, + (FRAME_BUFFER_X_OFFSET & 0xFF) as u8, + (end_x_index >> 8) as u8, + (end_x_index & 0xFF) as u8 + ]); + self.spi.write(Ili9341Commands::PageAddressSet, &[ + 0x0, 0x0, + ((TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, + ((TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 + ]); + + self.spi.write_buffer(Ili9341Commands::MemoryWrite, &scaled_buffer); + } + + fn sleep_ms(milliseconds_to_sleep:u64){ + std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); + } +} + +impl Drop for Ili9341Contoller{ + fn drop(&mut self) { + self.led_pin.set_low(); + self.reset_pin.set_high(); + Self::sleep_ms(1); + self.reset_pin.set_low(); + } +} + +pub struct Ili9341GfxDevice{ + ili9341_controller:Ili9341Contoller, + frames_counter: u32, + time_counter:std::time::Duration, + last_time: std::time::Instant, +} + +impl Ili9341GfxDevice{ + pub fn new()->Self{ + let ili9341_controller = Ili9341Contoller::new(); + Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} + } +} + +impl GfxDevice for Ili9341GfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + let u16_buffer:[u16;SCREEN_HEIGHT*SCREEN_WIDTH] = buffer.map(|pixel| { + let b = pixel & 0xFF; + let g = (pixel & 0xFF00)>>8; + let r = (pixel & 0xFF0000)>>16; + let mut u16_pixel = b as u16 >> 3; + u16_pixel |= ((g >> 2) << 5) as u16; + u16_pixel |= ((r >> 3) << 11) as u16; + return u16_pixel; + }); + + // if self.frames_counter & 1 == 0{ + self.ili9341_controller.write_frame_buffer(&u16_buffer); + // } + + // measure fps + self.frames_counter += 1; + let time = std::time::Instant::now(); + self.time_counter = self.time_counter.add(time.duration_since(self.last_time)); + self.last_time = time; + if self.time_counter.as_millis() > 1000{ + log::info!("FPS: {}", self.frames_counter); + self.frames_counter = 0; + self.time_counter = std::time::Duration::ZERO; + } + } +} \ No newline at end of file diff --git a/gb/src/rpi_gpio/mod.rs b/gb/src/rpi_gpio/mod.rs new file mode 100644 index 00000000..e68b824a --- /dev/null +++ b/gb/src/rpi_gpio/mod.rs @@ -0,0 +1,35 @@ +pub mod ili9341_controller; +pub mod gpio_joypad_provider; + +cfg_if::cfg_if!{if #[cfg(feature = "raw-spi")]{ + mod dma; + mod raw_spi; + pub type SpiType = raw_spi::RawSpi; +}else{ +#[cfg(not(feature = "raw-spi"))] + mod spi; + pub type SpiType = spi::RppalSpi; +}} + + +fn libc_abort(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); +} + +macro_rules! decl_write_volatile_field{ + ($function_name:ident, $field_name:ident) =>{ + #[inline] unsafe fn $function_name(&mut self,value:u32){ + std::ptr::write_volatile(&mut self.$field_name , value); + } + } +} + +macro_rules! decl_read_volatile_field{ + ($function_name:ident, $field_name:ident) =>{ + #[inline] unsafe fn $function_name(&mut self)->u32{ + std::ptr::read_volatile(&self.$field_name) + } + } +} + +pub(self) use {decl_read_volatile_field, decl_write_volatile_field}; \ No newline at end of file diff --git a/gb/src/rpi_gpio/raw_spi.rs b/gb/src/rpi_gpio/raw_spi.rs new file mode 100644 index 00000000..0b6d366e --- /dev/null +++ b/gb/src/rpi_gpio/raw_spi.rs @@ -0,0 +1,241 @@ +use rppal::gpio::{OutputPin, IoPin}; + +use crate::rpi_gpio::{dma::DmaTransferer, libc_abort}; + +use super::{ili9341_controller::{Ili9341Commands, SpiController, TARGET_SCREEN_HEIGHT, TARGET_SCREEN_WIDTH}, decl_write_volatile_field, decl_read_volatile_field}; + +struct GpioRegistersAccess{ + ptr:*mut u32 +} +enum GpioRegister{ + Gpfsel0 = 0, + Gpfsel1 = 1, + Gpset0 = 6, + Gpset1 = 7, + Gpclr0 = 8, + Gpclr1 = 9 +} +impl GpioRegistersAccess{ + unsafe fn read_register(&self, register:GpioRegister)->u32{ + std::ptr::read_volatile(self.ptr.add(register as usize)) + } + unsafe fn write_register(&self, register:GpioRegister, value:u32){ + std::ptr::write_volatile(self.ptr.add(register as usize), value); + } + unsafe fn set_gpio_mode(&self, pin:u8, mode:u8){ + // there are less than 100 pins so I assume the largest one is less than 100 + let gpfsel_register = pin / 10; + let gpfsel_register_index = pin % 10; + let register_ptr = self.ptr.add(gpfsel_register as usize); + let mut register_value = std::ptr::read_volatile(register_ptr); + let mask = !(0b111 << (gpfsel_register_index * 3)); + register_value &= mask; + register_value |= (mode as u32) << (gpfsel_register_index *3); + std::ptr::write_volatile(register_ptr, register_value); + } + unsafe fn set_gpio_high(&self, pin:u8){ + if pin < 32{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset0 as usize), 1 << pin); + } + else{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset1 as usize), 1 << (pin - 32)); + } + } + unsafe fn set_gpio_low(&self, pin:u8){ + if pin < 32{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr0 as usize), 1 << pin); + } + else{ + std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr1 as usize), 1 << (pin - 32)); + } + } +} + +// The register are 4 bytes each so making sure the allignment and padding are correct +#[repr(C, align(4))] +struct SpiRegistersAccess{ + control_status:u32, + fifo:u32, + clock:u32, + data_length:u32 +} + +impl SpiRegistersAccess{ + decl_write_volatile_field!(write_cs, control_status); + decl_read_volatile_field!(read_cs, control_status); + decl_write_volatile_field!(write_fifo, fifo); + decl_write_volatile_field!(write_clk, clock); + decl_write_volatile_field!(write_dlen, data_length); +} + +pub struct RawSpi{ + spi_registers: *mut SpiRegistersAccess, + mem_fd:libc::c_int, + spi_pins:[IoPin;2], + spi_cs0:OutputPin, + dc_pin:OutputPin, + + dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, + last_transfer_was_dma:bool +} + +impl RawSpi{ + const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; + const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; + const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; + const BCM_RPI4_SIZE:usize = 0x180_0000; + + const SPI_CS_RXF:u32 = 1 << 20; + const SPI_CS_RXR:u32 = 1 << 19; + const SPI_CS_TXD:u32 = 1 << 18; + const SPI_CS_DONE:u32 = 1 << 16; + const SPI_CS_DMAEN:u32 = 1 << 8; + const SPI_CS_TA:u32 = 1 << 7; + const SPI_CS_CLEAR:u32 = 3<<4; + const SPI_CS_CLEAR_RX:u32 = 1 << 5; + + fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; + + if mem_fd < 0{ + libc_abort("bad file descriptor"); + } + + let bcm2835 = unsafe{libc::mmap( + std::ptr::null_mut(), + Self::BCM_RPI4_SIZE, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + Self::BCM2835_RPI4_BUS_ADDRESS as libc::off_t + )}; + + if bcm2835 == libc::MAP_FAILED{ + libc_abort("FATAL: mapping /dev/mem failed!"); + } + + // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; + let spi_registers = unsafe{bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess}; + + unsafe{ + // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 + spi_cs0.set_low(); + (*spi_registers).write_cs(Self::SPI_CS_TA); + (*spi_registers).write_clk(4); + (*spi_registers).write_dlen(2); + } + + log::info!("finish ili9341 device init"); + + RawSpi { + spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, last_transfer_was_dma: false, + dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd ) + } + } + + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + self.prepare_for_transfer(); + self.dc_pin.set_low(); + self.write_raw(&[command as u8]); + self.dc_pin.set_high(); + self.write_raw(data); + self.last_transfer_was_dma = false; + } + + fn prepare_for_transfer(&mut self) { + if self.last_transfer_was_dma{ + self.dma_transferer.end_dma_transfer(); + unsafe{ + (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + (*self.spi_registers).write_dlen(2); // poll mode speed up + } + } + } + + fn write_raw(&mut self, data:&[u8;SIZE]){ + unsafe{ + let mut current_index = 0; + while current_index < SIZE { + let cs:u32 = (*self.spi_registers).read_cs(); + if cs & Self::SPI_CS_TXD != 0{ + (*self.spi_registers).write_fifo(data[current_index] as u32); + current_index += 1; + } + if (cs & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0 { + (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR_RX); + } + } + + // wait for the last trasfer to finish + while ((*self.spi_registers).read_cs() & Self::SPI_CS_DONE) == 0 { + if ((*self.spi_registers).read_cs() & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ + (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR_RX); + } + } + } + } + + const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes + const DMA_SPI_TRANSFER_SIZE:usize = TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * std::mem::size_of::(); + const DMA_SPI_NUM_CHUNKS:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((Self::DMA_SPI_TRANSFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; + const DMA_SPI_CHUNK_SIZE:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; + const DMA_TI_PERMAP_SPI_TX:u8 = 6; + const DMA_TI_PERMAP_SPI_RX:u8 = 7; + const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; + + fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + self.prepare_for_transfer(); + + self.dc_pin.set_low(); + self.write_raw(&[command as u8]); + self.dc_pin.set_high(); + self.write_dma_raw(&data); + self.last_transfer_was_dma = true; + } + + + // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use + fn write_dma_raw(&mut self, data:&[u8;SIZE]){ + unsafe{ + (*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); + let data_len = Self::DMA_SPI_CHUNK_SIZE - 4; // Removing the first 4 bytes from this length param + let header = [Self::SPI_CS_TA as u8, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; + + let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - 4); + let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; + let mut i = 0; + for chunk in chunks{ + std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); + std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); + i += 1; + } + + self.dma_transferer.start_dma_transfer(&array, + Self::DMA_TI_PERMAP_SPI_TX, + Self::DMA_SPI_FIFO_PHYS_ADDRESS, + Self::DMA_TI_PERMAP_SPI_RX, + Self::DMA_SPI_FIFO_PHYS_ADDRESS + ); + } + } +} + +impl SpiController for RawSpi{ + fn new(dc_pin_number:u8)->Self { + let gpio = rppal::gpio::Gpio::new().unwrap(); + let spi0_ceo_n = gpio.get(8).unwrap().into_output(); + let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); + let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); + let dc_pin = gpio.get(dc_pin_number).unwrap().into_output(); + + RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n) + } + + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]) { + self.write(command, data); + } + + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]) { + self.write_dma(command, data); + } +} \ No newline at end of file diff --git a/gb/src/rpi_gpio/spi.rs b/gb/src/rpi_gpio/spi.rs new file mode 100644 index 00000000..bf03ed57 --- /dev/null +++ b/gb/src/rpi_gpio/spi.rs @@ -0,0 +1,43 @@ +use rppal::gpio::{OutputPin, IoPin}; + +use super::{ili9341_controller::{Ili9341Commands, TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT, SpiController}}; + +pub struct RppalSpi{ + spi_device:rppal::spi::Spi, + dc_pin:OutputPin +} + +impl RppalSpi{ + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + let command = command as u8; + self.dc_pin.set_low(); + self.spi_device.write(&[command]).expect("Error while writing to the spi device"); + self.dc_pin.set_high(); + let chunks = data.chunks(4096); + for chunk in chunks{ + self.spi_device.write(&chunk).expect(std::format!("Error wrting data to spi device for command: {:#X}",command).as_str() ); + } + } +} + +impl SpiController for RppalSpi{ + fn new(dc_pin:u8)->Self{ + let spi_device = rppal::spi::Spi::new( + rppal::spi::Bus::Spi0, + rppal::spi::SlaveSelect::Ss0/*pin 24*/, + 75_000_000/*In order to be able to achieve higher fps*/, + rppal::spi::Mode::Mode0 + ).expect("Error creating rppal spi device"); + + let dc_pin = rppal::gpio::Gpio::new().unwrap().get(dc_pin).unwrap().into_output(); + return RppalSpi { spi_device, dc_pin }; + } + + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]) { + self.write(command, data); + } + + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]) { + self.write(command, data); + } +} \ No newline at end of file diff --git a/gb/src/spi.rs b/gb/src/spi.rs deleted file mode 100644 index 2f1073b5..00000000 --- a/gb/src/spi.rs +++ /dev/null @@ -1,557 +0,0 @@ -use std::ops::Add; - -use lib_gb::ppu::{gfx_device::GfxDevice, gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}}; -use rppal::gpio::{OutputPin, IoPin}; - -use crate::dma::DmaTransferer; - -macro_rules! decl_write_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self,value:u32){ - std::ptr::write_volatile(&mut self.$field_name , value); - } - } -} - -macro_rules! decl_read_volatile_field{ - ($function_name:ident, $field_name:ident) =>{ - #[inline] unsafe fn $function_name(&mut self)->u32{ - std::ptr::read_volatile(&self.$field_name) - } - } -} - -struct GpioRegistersAccess{ - ptr:*mut u32 -} -enum GpioRegister{ - Gpfsel0 = 0, - Gpfsel1 = 1, - Gpset0 = 6, - Gpset1 = 7, - Gpclr0 = 8, - Gpclr1 = 9 -} -impl GpioRegistersAccess{ - unsafe fn read_register(&self, register:GpioRegister)->u32{ - std::ptr::read_volatile(self.ptr.add(register as usize)) - } - unsafe fn write_register(&self, register:GpioRegister, value:u32){ - std::ptr::write_volatile(self.ptr.add(register as usize), value); - } - unsafe fn set_gpio_mode(&self, pin:u8, mode:u8){ - // there are less than 100 pins so I assume the largest one is less than 100 - let gpfsel_register = pin / 10; - let gpfsel_register_index = pin % 10; - let register_ptr = self.ptr.add(gpfsel_register as usize); - let mut register_value = std::ptr::read_volatile(register_ptr); - let mask = !(0b111 << (gpfsel_register_index * 3)); - register_value &= mask; - register_value |= (mode as u32) << (gpfsel_register_index *3); - std::ptr::write_volatile(register_ptr, register_value); - } - unsafe fn set_gpio_high(&self, pin:u8){ - if pin < 32{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset0 as usize), 1 << pin); - } - else{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset1 as usize), 1 << (pin - 32)); - } - } - unsafe fn set_gpio_low(&self, pin:u8){ - if pin < 32{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr0 as usize), 1 << pin); - } - else{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr1 as usize), 1 << (pin - 32)); - } - } -} - -struct SpiRegistersAccess{ - control_status:u32, - fifo:u32, - clock:u32, - data_length:u32 -} - -impl SpiRegistersAccess{ - decl_write_volatile_field!(write_cs, control_status); - decl_read_volatile_field!(read_cs, control_status); - decl_write_volatile_field!(write_fifo, fifo); - decl_write_volatile_field!(write_clk, clock); - decl_write_volatile_field!(write_dlen, data_length); -} - -struct RawSpi{ - spi_registers: *mut SpiRegistersAccess, - mem_fd:libc::c_int, - spi_pins:[IoPin;2], - spi_cs0:OutputPin, - dc_pin:OutputPin, - - dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, - last_transfer_was_dma:bool -} - -fn libc_abort_if_err(message:&str){ - std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); -} - -impl RawSpi{ - const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; - const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; - const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; - const BCM_RPI4_SIZE:usize = 0x180_0000; - - const SPI_CS_RXF:u32 = 1 << 20; - const SPI_CS_RXR:u32 = 1 << 19; - const SPI_CS_TXD:u32 = 1 << 18; - const SPI_CS_RXD:u32 = 1 << 17; - const SPI_CS_DONE:u32 = 1 << 16; - const SPI_CS_DMAEN:u32 = 1 << 8; - const SPI_CS_TA:u32 = 1 << 7; - const SPI_CS_CLEAR:u32 = 3<<4; - const SPI_CS_CLEAR_RX:u32 = 1 << 5; - - fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ - let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; - - if mem_fd < 0{ - libc_abort_if_err("bad file descriptor"); - } - - let bcm2835 = unsafe{libc::mmap( - std::ptr::null_mut(), - Self::BCM_RPI4_SIZE, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - Self::BCM2835_RPI4_BUS_ADDRESS as libc::off_t - )}; - - if bcm2835 == libc::MAP_FAILED{ - libc_abort_if_err("FATAL: mapping /dev/mem failed!"); - } - - // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; - let spi_registers = unsafe{bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess}; - - unsafe{ - // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 - spi_cs0.set_low(); - (*spi_registers).write_cs(Self::SPI_CS_TA); - (*spi_registers).write_clk(34); - (*spi_registers).write_dlen(2); - } - - log::info!("finish ili9341 device init"); - - RawSpi { - spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, last_transfer_was_dma: false, - dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd ) - } - } - - fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ - self.prepare_for_transfer(); - self.dc_pin.set_low(); - self.write_raw(&[command as u8]); - self.dc_pin.set_high(); - self.write_raw(data); - self.last_transfer_was_dma = false; - } - - fn prepare_for_transfer(&mut self) { - if self.last_transfer_was_dma{ - self.dma_transferer.wait_for_dma_transfer(); - unsafe{ - (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); - (*self.spi_registers).write_dlen(2); // poll mode speed up - } - } - } - - fn write_raw(&mut self, data:&[u8;SIZE]){ - unsafe{ - let mut current_index = 0; - while current_index < SIZE { - let cs:u32 = (*self.spi_registers).read_cs(); - if cs & Self::SPI_CS_TXD != 0{ - (*self.spi_registers).write_fifo(data[current_index] as u32); - current_index += 1; - } - if (cs & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0 { - (*self.spi_registers).write_cs(Self::SPI_CS_TA | 0b10_0000); - } - } - - // wait for the last trasfer to finish - while ((*self.spi_registers).read_cs() & Self::SPI_CS_DONE) == 0 { - if ((*self.spi_registers).read_cs() & (Self::SPI_CS_RXR | Self::SPI_CS_RXF)) != 0{ - (*self.spi_registers).write_cs(Self::SPI_CS_TA | 0b10_0000); - } - } - } - } - - const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes - const DMA_SPI_TRANSFER_SIZE:usize = Ili9341Contoller::TARGET_SCREEN_HEIGHT * Ili9341Contoller::TARGET_SCREEN_WIDTH * std::mem::size_of::(); - const DMA_SPI_NUM_CHUNKS:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((Self::DMA_SPI_TRANSFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; - const DMA_SPI_CHUNK_SIZE:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; - const DMA_TI_PERMAP_SPI_TX:u8 = 6; - const DMA_TI_PERMAP_SPI_RX:u8 = 7; - const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; - - fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ - self.prepare_for_transfer(); - self.dc_pin.set_low(); - self.write_raw(&[command as u8]); - self.dc_pin.set_high(); - self.write_dma_raw(&data); - self.last_transfer_was_dma = true; - } - - - // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use - fn write_dma_raw(&mut self, data:&[u8;SIZE]){ - unsafe{ - (*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); - let data_len = Self::DMA_SPI_CHUNK_SIZE - 4; // Removing the first 4 bytes from this length param - let header = [Self::SPI_CS_TA as u8, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; - - let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - 4); - let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; - let mut i = 0; - for chunk in chunks{ - unsafe{ - std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); - std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); - } - i += 1; - } - - self.dma_transferer.dma_transfer(&array, - Self::DMA_TI_PERMAP_SPI_TX, - Self::DMA_SPI_FIFO_PHYS_ADDRESS, - Self::DMA_TI_PERMAP_SPI_RX, - Self::DMA_SPI_FIFO_PHYS_ADDRESS - ); - } - } -} - - -pub struct Ili9341GfxDevice{ - ili9341_controller:Ili9341Contoller, - frames_counter: u32, - time_counter:std::time::Duration, - last_time: std::time::Instant, -} - -impl Ili9341GfxDevice{ - pub fn new()->Self{ - let ili9341_controller = Ili9341Contoller::new(); - Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} - } -} - -impl GfxDevice for Ili9341GfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - let u16_buffer:[u16;SCREEN_HEIGHT*SCREEN_WIDTH] = buffer.map(|pixel| { - let b = pixel & 0xFF; - let g = (pixel & 0xFF00)>>8; - let r = (pixel & 0xFF0000)>>16; - let mut u16_pixel = b as u16 >> 3; - u16_pixel |= ((g >> 2) << 5) as u16; - u16_pixel |= ((r >> 3) << 11) as u16; - return u16_pixel; - }); - - // if self.frames_counter & 1 == 0{ - self.ili9341_controller.write_frame_buffer(&u16_buffer); - // } - - // measure fps - self.frames_counter += 1; - let time = std::time::Instant::now(); - self.time_counter = self.time_counter.add(time.duration_since(self.last_time)); - self.last_time = time; - if self.time_counter.as_millis() > 1000{ - log::info!("FPS: {}", self.frames_counter); - self.frames_counter = 0; - self.time_counter = std::time::Duration::ZERO; - } - } -} - -struct RppalSpi{ - spi_device:rppal::spi::Spi, - dc_pin:OutputPin -} - -impl RppalSpi{ - pub fn new(dc_pin:OutputPin)->Self{ - let spi_device = rppal::spi::Spi::new( - rppal::spi::Bus::Spi0, - rppal::spi::SlaveSelect::Ss0/*pin 24*/, - 75_000_000/*In order to be able to achieve 60fps*/, - rppal::spi::Mode::Mode0 - ).expect("Error creating rppal spi device"); - - return RppalSpi { spi_device, dc_pin }; - } - - fn write(&mut self, command: Ili9341Commands, data:&[u8; SIZE]) { - let error = "Error while writing to the spi device"; - let command = command as u8; - self.dc_pin.set_low(); - self.spi_device.write(&[command]).expect(error); - self.dc_pin.set_high(); - let chunks = data.chunks(4096); - for chunk in chunks{ - self.spi_device.write(&chunk).expect(std::format!("Error wrting data to spi device for command: {:#X}",command).as_str() ); - } - } -} - -enum Ili9341Commands{ - SoftwareReset = 0x01, - SleepOut = 0x11, - GammaSet = 0x26, - DisplayOff = 0x28, - DisplayOn = 0x29, - ColumnAddressSet = 0x2A, // Set curosr X value - PageAddressSet = 0x2B, // Set cursor Y value - MemoryWrite = 0x2C, - MemoryAccessControl = 0x36, - PixelFormatSet = 0x3A, - FrameRateControl = 0xB1, - DisplayFunctionControl = 0xB6, - PowerControl1 = 0xC0, - PowerControl2 = 0xC1, - VcomControl1 = 0xC5, - VcomControl2 = 0xC7, - PowerControlA = 0xCB, - PowerControlB = 0xCF, - PossitiveGammaCorrection = 0xE0, - NegativeGammaCorrection = 0xE1, - DriverTimingControlA = 0xE8, - DriverTimingControlB = 0xEA, - PowerOnSequenceControl = 0xED, - Enable3G = 0xF2, -} - -struct Ili9341Contoller{ - spi:RawSpi, - led_pin: OutputPin, - reset_pin: OutputPin -} - -impl Ili9341Contoller{ - - const ILI9341_SCREEN_WIDTH:usize = 320; - const ILI9341_SCREEN_HEIGHT:usize = 240; - const SCALE:f32 = 5.0 / 3.0; // maximum scale to fit the ili9341 screen - const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * Self::SCALE) as usize; - const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * Self::SCALE) as usize; - const FRAME_BUFFER_X_OFFSET:usize = (Self::ILI9341_SCREEN_WIDTH - Self::TARGET_SCREEN_WIDTH) / 2; - - const CLEAN_BUFFER:[u8;Self::ILI9341_SCREEN_HEIGHT * Self::ILI9341_SCREEN_WIDTH * 2] = [0; Self::ILI9341_SCREEN_HEIGHT * Self::ILI9341_SCREEN_WIDTH * 2]; - - pub fn new()->Self{ - let gpio = rppal::gpio::Gpio::new().unwrap(); - let mut dc_pin = gpio.get(15).unwrap().into_output(); - let mut reset_pin = gpio.get(14).unwrap().into_output(); - let mut led_pin = gpio.get(25).unwrap().into_output(); - - - // toggling the reset pin to initalize the lcd - let wait_duration = std::time::Duration::from_millis(120); - reset_pin.set_high(); - std::thread::sleep(wait_duration); - reset_pin.set_low(); - std::thread::sleep(wait_duration); - reset_pin.set_high(); - std::thread::sleep(wait_duration); - - let spi0_ceo_n = gpio.get(8).unwrap().into_output(); - let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); - let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); - - let mut spi = RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n); - - // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) - // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down - // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) - - // There is another implementation in rust for an ili9341 controller which is much simpler and uses those commands: - // Sleepms(5), SoftwareReset, Sleepms(120), MemoryAccessControl, PixelFormatSet, SleepOut, Sleepms(5), DisplayOn - // minimal config based on rust ili9341 lib (https://github.com/yuri91/ili9341-rs) - - // fbcp-ili9341 inspired implementation: - /*---------------------------------------------------------------------------------------------------------------------- */ - // Reset the screen - spi.write(Ili9341Commands::SoftwareReset,&[]); - Self::sleep_ms(5); - spi.write(Ili9341Commands::DisplayOff,&[]); - - // Some power stuff, probably uneccessary but just for sure - spi.write(Ili9341Commands::PowerControlA, &[0x39, 0x2C, 0x0, 0x34, 0x2]); - spi.write(Ili9341Commands::PowerControlB, &[0x0, 0xC1, 0x30]); - spi.write(Ili9341Commands::DriverTimingControlA, &[0x85, 0x0, 0x78]); - spi.write(Ili9341Commands::DriverTimingControlB, &[0x0, 0x0]); - spi.write(Ili9341Commands::PowerOnSequenceControl, &[0x64, 0x3, 0x12, 0x81]); - spi.write(Ili9341Commands::PowerControl1, &[0x23]); - spi.write(Ili9341Commands::PowerControl2,&[0x10]); - spi.write(Ili9341Commands::VcomControl1, &[0xE3, 0x28]); - spi.write(Ili9341Commands::VcomControl2, &[0x86]); - - // Configuring the screen - spi.write(Ili9341Commands::MemoryAccessControl, &[0x20]); // This command tlit the screen 90 degree - spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel; - spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x1F /*According to the docs this is 61 hrz */]); - spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]); - - // Gamma values - pretty sure its redundant - spi.write(Ili9341Commands::Enable3G, &[0x2]); - spi.write(Ili9341Commands::GammaSet, &[0x1]); - spi.write(Ili9341Commands::PossitiveGammaCorrection,&[0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00]); - spi.write(Ili9341Commands::NegativeGammaCorrection, &[0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F]); - - // Turn screen on - spi.write(Ili9341Commands::SleepOut,&[]); - Self::sleep_ms(120); - spi.write(Ili9341Commands::DisplayOn,&[]); - /*---------------------------------------------------------------------------------------------------------------------- */ - //End of fbcp-ili9341 inpired implementation - - // Clear screen - spi.write(Ili9341Commands::ColumnAddressSet, &[0,0,((Self::ILI9341_SCREEN_WIDTH -1) >> 8) as u8, ((Self::ILI9341_SCREEN_WIDTH -1) & 0xFF) as u8]); - spi.write(Ili9341Commands::PageAddressSet, &[0,0,((Self::ILI9341_SCREEN_HEIGHT -1) >> 8) as u8, ((Self::ILI9341_SCREEN_HEIGHT -1) & 0xFF) as u8]); - spi.write(Ili9341Commands::MemoryWrite, &Self::CLEAN_BUFFER); - - // turn backlight on - led_pin.set_high(); - - unsafe{(*spi.spi_registers).write_clk(4)}; - - log::info!("Initalizing with screen size width: {}, hight: {}", Self::TARGET_SCREEN_WIDTH, Self::TARGET_SCREEN_HEIGHT); - - return Ili9341Contoller { spi, led_pin, reset_pin}; - } - - - pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){ - let mut scaled_buffer: [u8;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2] = [0;Self::TARGET_SCREEN_HEIGHT * Self::TARGET_SCREEN_WIDTH * 2]; - unsafe{image_inter::scale_to_screen_c::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; - - let end_x_index = Self::TARGET_SCREEN_WIDTH + Self::FRAME_BUFFER_X_OFFSET - 1; - self.spi.write(Ili9341Commands::ColumnAddressSet, &[ - (Self::FRAME_BUFFER_X_OFFSET >> 8) as u8, - (Self::FRAME_BUFFER_X_OFFSET & 0xFF) as u8, - (end_x_index >> 8) as u8, - (end_x_index & 0xFF) as u8 - ]); - self.spi.write(Ili9341Commands::PageAddressSet, &[ - 0x0, 0x0, - ((Self::TARGET_SCREEN_HEIGHT - 1) >> 8) as u8, - ((Self::TARGET_SCREEN_HEIGHT - 1) & 0xFF) as u8 - ]); - - self.spi.write_dma(Ili9341Commands::MemoryWrite, &scaled_buffer); - } - - - // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ - fn scale_to_screen(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ - // not sure why the -1.0 - let x_ratio = (SCREEN_WIDTH as f32 - 1.0) / Self::TARGET_SCREEN_WIDTH as f32; - let y_ratio = (SCREEN_HEIGHT as f32 - 1.0) / Self::TARGET_SCREEN_HEIGHT as f32; - - let mut offset_counter = 0; - for y in 0..Self::TARGET_SCREEN_HEIGHT{ - let y_val = (y_ratio * y as f32) as u32; // y value of a point in this ratio between o and y - let y_diff = (y_ratio * y as f32) - y_val as f32; - - for x in 0..Self::TARGET_SCREEN_WIDTH{ - let x_val = (x_ratio * x as f32) as u32; // x value of a point in this ratio between 0 and x - let x_diff = (x_ratio * x as f32) - x_val as f32; - - let original_pixel_index = (y_val as usize * SCREEN_WIDTH) + x_val as usize; - // Get the pixel and 3 surounding pixels - let pixel_a = input_buffer[original_pixel_index]; - let pixel_b = input_buffer[original_pixel_index + 1]; - let pixel_c = input_buffer[original_pixel_index + SCREEN_WIDTH]; - let pixel_d = input_buffer[original_pixel_index + SCREEN_WIDTH + 1]; - - let blue:f32 = ((pixel_a & 0x1F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + - ((pixel_b & 0x1F) as f32 * (x_diff)*(1.0-y_diff)) + - ((pixel_c & 0x1F) as f32 * y_diff * (1.0-x_diff)) + - ((pixel_d & 0x1F) as f32 * x_diff * y_diff); - let green:f32 = (((pixel_a >> 5) & 0x3F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + - (((pixel_b >> 5) & 0x3F) as f32 * (x_diff)*(1.0-y_diff)) + - (((pixel_c >> 5) & 0x3F) as f32 * y_diff * (1.0-x_diff)) + - (((pixel_d >> 5) & 0x3F) as f32 * x_diff * y_diff); - let red:f32 = (((pixel_a >> 11) & 0x1F) as f32 * (1.0-x_diff) * (1.0-y_diff)) + - (((pixel_b >> 11) & 0x1F) as f32 * (x_diff)*(1.0-y_diff)) + - (((pixel_c >> 11) & 0x1F) as f32 * y_diff * (1.0-x_diff)) + - (((pixel_d >> 11) & 0x1F) as f32 * x_diff * y_diff); - - let pixel = blue as u16 | ((green as u16) << 5) | ((red as u16) << 11); - output_buffer[offset_counter * 2] = (pixel >> 8) as u8; - output_buffer[(offset_counter * 2) + 1] = (pixel & 0xFF) as u8; - offset_counter += 1; - } - } - } - - fn scale_to_screen_fr(input_buffer:&[u8;SCREEN_HEIGHT*SCREEN_WIDTH * 2], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ - let mut buffer = input_buffer.clone(); - let mut src_buffer = fast_image_resize::Image::from_slice_u8( - std::num::NonZeroU32::new(SCREEN_WIDTH as u32).unwrap(), - std::num::NonZeroU32::new(SCREEN_HEIGHT as u32).unwrap(), - &mut buffer, - fast_image_resize::PixelType::U16 - ).unwrap(); - - let mut dst_buffer = fast_image_resize::Image::from_slice_u8( - std::num::NonZeroU32::new(Self::TARGET_SCREEN_WIDTH as u32).unwrap(), - std::num::NonZeroU32::new(Self::TARGET_SCREEN_HEIGHT as u32).unwrap(), - output_buffer, - fast_image_resize::PixelType::U16 - ).unwrap(); - - let mut resizer = fast_image_resize::Resizer::new(fast_image_resize::ResizeAlg::Convolution(fast_image_resize::FilterType::Bilinear)); - resizer.resize(&src_buffer.view(), &mut dst_buffer.view_mut()).unwrap(); - } - - // implemented based on this article - https://kwojcicki.github.io/blog/NEAREST-NEIGHBOUR - fn scale_to_screen_nearest(input_buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH], output_buffer:&mut [u8;Self::TARGET_SCREEN_HEIGHT*Self::TARGET_SCREEN_WIDTH * 2]){ - for y in 0..Self::TARGET_SCREEN_HEIGHT{ - for x in 0..Self::TARGET_SCREEN_WIDTH{ - let proj_x = ((1.0 / Self::SCALE) * x as f32) as usize; - let proj_y = ((1.0 / Self::SCALE) * y as f32) as usize; - unsafe{ - let pixel = input_buffer.get_unchecked((proj_y * SCREEN_WIDTH) + proj_x); - let output_index = (y * Self::TARGET_SCREEN_WIDTH) + x; - *output_buffer.get_unchecked_mut(output_index * 2) = (pixel >> 8) as u8; - *output_buffer.get_unchecked_mut((output_index * 2) + 1) = (pixel & 0xFF) as u8; - } - } - } - } - - fn sleep_ms(milliseconds_to_sleep:u64){ - std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); - } -} - -impl Drop for Ili9341Contoller{ - fn drop(&mut self) { - self.led_pin.set_low(); - self.reset_pin.set_high(); - Self::sleep_ms(1); - self.reset_pin.set_low(); - } -} \ No newline at end of file diff --git a/image_inter/Cargo.toml b/image_inter/Cargo.toml index 4158015b..451733d4 100644 --- a/image_inter/Cargo.toml +++ b/image_inter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "image_inter" -version = "0.1.0" +version = "1.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/image_inter/benches/inter_bench.rs b/image_inter/benches/inter_bench.rs index 540aaf27..857ebe16 100644 --- a/image_inter/benches/inter_bench.rs +++ b/image_inter/benches/inter_bench.rs @@ -1,12 +1,12 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use image_inter::{scale_to_screen, scale_to_screen_c}; +use image_inter::{scale_bilinear, scale_biliniear_c, scale_nearest}; pub fn interpolation_rust_bench(c: &mut Criterion){ let input_buffer = [0_u16; 160*144]; let mut output_buffer = [0_u8; 240*266*2]; c.bench_function("bench rust inter", |b|b.iter(||{ - unsafe{scale_to_screen::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; + unsafe{scale_bilinear::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; })); } @@ -14,7 +14,15 @@ pub fn interpolation_c_bench(c: &mut Criterion){ let input_buffer = [0_u16; 160*144]; let mut output_buffer = [0_u8; 240*266*2]; c.bench_function("bench c inter", |b|b.iter(||{ - unsafe{scale_to_screen_c::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; + unsafe{scale_biliniear_c::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())}; + })); +} + +pub fn neighbor_rust_inter(c: &mut Criterion){ + let input_buffer = [0_u16; 160*144]; + let mut output_buffer = [0_u8; 240*266*2]; + c.bench_function("bench rust neighbor", |b|b.iter(||{ + unsafe{scale_nearest::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr(), 5.0/3.0)}; })); } @@ -43,5 +51,5 @@ pub fn interpolation_fir_bench(c: &mut Criterion){ })); } -criterion_group!(benches, interpolation_fir_bench, interpolation_rust_bench, interpolation_c_bench); +criterion_group!(benches, interpolation_fir_bench, interpolation_rust_bench, interpolation_c_bench, neighbor_rust_inter); criterion_main!(benches); \ No newline at end of file diff --git a/image_inter/build.rs b/image_inter/build.rs index 4d847e33..54a4b0d6 100644 --- a/image_inter/build.rs +++ b/image_inter/build.rs @@ -1,9 +1,9 @@ fn main() { // Tell Cargo that if the given file changes, to rerun this build script. - println!("cargo:rerun-if-changed=src/hello.c"); + println!("cargo:rerun-if-changed=src/scale.c"); // Use the `cc` crate to build a C file and statically link it. + cc::Build::new() - .flag("-mfpu=neon") .warnings(true) .extra_warnings(true) .warnings_into_errors(true) diff --git a/image_inter/src/lib.rs b/image_inter/src/lib.rs index f6c2dc2a..2eb177b5 100644 --- a/image_inter/src/lib.rs +++ b/image_inter/src/lib.rs @@ -13,7 +13,7 @@ extern "C" { } // This function implements bilinear interpolation scaling according to this article - http://tech-algorithm.com/articles/bilinear-image-scaling/ -pub unsafe fn scale_to_screen(input_buffer: *const u16, output_buffer: *mut u8){ +pub unsafe fn scale_bilinear(input_buffer: *const u16, output_buffer: *mut u8){ // not sure why the -1.0 let x_ratio = (INPUT_WIDTH as f32 - 1.0) / OUTPUT_WIDTH as f32; let y_ratio = (INPUT_HEIGHT as f32 - 1.0) / OUTPUT_HEIGHT as f32; @@ -57,7 +57,21 @@ pub unsafe fn scale_to_screen(input_buffer: *const u16, output_buffer: *mut u8){ +// implemented based on this article - https://kwojcicki.github.io/blog/NEAREST-NEIGHBOUR +pub unsafe fn scale_nearest(input_buffer: *const u16, output_buffer: *mut u8, scale:f32){ + for y in 0..OUTPUT_HEIGHT{ + for x in 0..OUTPUT_WIDTH{ + let proj_x = ((1.0 / scale) * x as f32) as usize; + let proj_y = ((1.0 / scale) * y as f32) as usize; + let pixel = *input_buffer.add((proj_y * INPUT_WIDTH) + proj_x); + let output_index = (y * OUTPUT_WIDTH) + x; + *output_buffer.add(output_index * 2) = (pixel >> 8) as u8; + *output_buffer.add((output_index * 2) + 1) = (pixel & 0xFF) as u8; + } + } +} + +pub unsafe fn scale_biliniear_c(input_buffer: *const u16, output_buffer: *mut u8){ scale_buffer( input_buffer, INPUT_WIDTH as c_int, From 5e45a08f46df1d8a1b4e212b93be1c1082f2c4ab Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 4 Jun 2022 17:19:20 +0300 Subject: [PATCH 35/70] Now freeing the resources on exit --- gb/src/rpi_gpio/dma.rs | 146 ++++++++++++++++++++++++++----------- gb/src/rpi_gpio/raw_spi.rs | 100 ++++++++++++++++++------- 2 files changed, 174 insertions(+), 72 deletions(-) diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs index d54a962c..76d0cfb5 100644 --- a/gb/src/rpi_gpio/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -2,7 +2,7 @@ use std::ptr::write_volatile; use libc::{c_void, c_int}; -use super::*; +use super::{*, raw_spi::Bcm2835}; // Mailbox messages need to be 16 byte alligned #[repr(C, align(16))] @@ -68,13 +68,75 @@ impl Mailbox{ } } -struct DmaMemory{ +impl Drop for Mailbox{ + fn drop(&mut self) { + unsafe{ + let result = libc::close(self.mbox_fd); + if result != 0{ + libc_abort("Error while closing the mbox fd"); + } + } + } +} + + +// using GpuMemory cause I need a memory that is not cached by the cpu caches (L1, L2) +struct GpuMemory{ virtual_address_ptr:usize, bus_address:u32, mailbox_memory_handle:u32, size:u32 } +impl GpuMemory{ + const MEM_ALLOC_FLAG_DIRECT:usize = 1 << 2; + const MEM_ALLOC_FLAG_COHERENT:usize = 1 << 3; + const ALLOCATE_MEMORY_TAG:u32 = 0x3000C; + const LOCK_MEMORY_TAG:u32 = 0x3000D; + const UNLOCK_MEMORY_TAG:u32 = 0x3000E; + const RELEASE_MEMORY_TAG:u32 = 0x3000E; + const PAGE_SIZE:u32 = 4096; + + // This function converts the from the bus address of the SDRAM uncached memory to the arm physical address + // Notice that supposed to work only for this type of memory + const fn bus_to_phys(bus_address:u32)->u32{bus_address & !0xC000_0000} + + // Using the Mailbox interface to allocate memory on the gpu + fn allocate(mbox:&Mailbox, size:u32, mem_fd:c_int)->GpuMemory{ + let flags = (Self::MEM_ALLOC_FLAG_COHERENT | Self::MEM_ALLOC_FLAG_DIRECT) as u32; + let handle = mbox.send_command(Self::ALLOCATE_MEMORY_TAG, [size, Self::PAGE_SIZE, flags]); + + let bus_address = mbox.send_command(Self::LOCK_MEMORY_TAG, [handle]); + let virtual_address = unsafe{libc::mmap( + std::ptr::null_mut(), + size as libc::size_t, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + Self::bus_to_phys(bus_address) as libc::off_t + )}; + + return GpuMemory { virtual_address_ptr: virtual_address as usize, bus_address, mailbox_memory_handle:handle, size } + } + + fn release(&self, mbox:&Mailbox){ + unsafe{ + let result = libc::munmap(self.virtual_address_ptr as *mut c_void, self.size as libc::size_t); + if result != 0 { + libc_abort("Error while trying to un map gpu memory"); + } + } + let status = mbox.send_command(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle]); + if status != 0{ + std::panic!("Error while trying to unlock gpu memory using mailbox"); + } + let status = mbox.send_command(Self::RELEASE_MEMORY_TAG, [self.mailbox_memory_handle]); + if status != 0{ + std::panic!("Error while to release gpu memory using mailbox"); + } + } +} + // The DMA control block registers are in a 32 byte alligned addresses so the stracture mapping them needs to be as well // in order for me to cast some bytes to this stuct (at least I think so) // Not valid for DMA4 channels @@ -117,17 +179,19 @@ pub struct DmaTransferer{ tx_dma:*mut DmaRegistersAccess, rx_dma:*mut DmaRegistersAccess, mbox:Mailbox, - tx_control_block_memory:DmaMemory, - rx_control_block_memory:DmaMemory, - source_buffer_memory:DmaMemory, - dma_data_memory:DmaMemory, - dma_const_data_memory:DmaMemory, + tx_control_block_memory:GpuMemory, + rx_control_block_memory:GpuMemory, + source_buffer_memory:GpuMemory, + dma_data_memory:GpuMemory, + dma_const_data_memory:GpuMemory, tx_channel_number:u8, - rx_channel_number:u8 + rx_channel_number:u8, + dma_enable_register_ptr:*mut u32, } impl DmaTransferer{ - const BCM2835_DMA0_BASE:usize = 0x7_000; + const BCM2835_DMA0_OFFSET:usize = 0x7_000; + const BCM2835_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM2835_DMA0_OFFSET + 0xFF; const DMA_CS_RESET:u32 = 1 << 31; const DMA_CS_END:u32 = 1 << 1; @@ -143,17 +207,17 @@ impl DmaTransfereru32{(peripherial_mapping as u32) << 16} - pub fn new(bcm2835:*mut c_void, tx_channel_number:u8, rx_channel_number:u8, mem_fd:c_int)->Self{ + pub fn new(bcm2835:&Bcm2835, tx_channel_number:u8, rx_channel_number:u8)->Self{ let mbox = Mailbox::new(); - let tx_registers = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess }; - let rx_registers = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess }; - let dma_tx_control_block_memory = Self::allocate_dma_memory(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, mem_fd); - let dma_rx_control_block_memory = Self::allocate_dma_memory(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, mem_fd); - let dma_source_buffer_memory = Self::allocate_dma_memory(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, mem_fd); - let dma_data_memory = Self::allocate_dma_memory(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, mem_fd); - let dma_const_data_memory = Self::allocate_dma_memory(&mbox, (std::mem::size_of::() * 2) as u32, mem_fd); + let tx_registers = bcm2835.get_ptr(Self::BCM2835_DMA0_OFFSET + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; + let rx_registers = bcm2835.get_ptr(Self::BCM2835_DMA0_OFFSET + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; + let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, bcm2835.get_fd()); + let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, bcm2835.get_fd()); + let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, bcm2835.get_fd()); + let dma_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, bcm2835.get_fd()); + let dma_const_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * 2) as u32, bcm2835.get_fd()); - let dma_enable_register = unsafe{bcm2835.add(Self::BCM2835_DMA0_BASE + 0xFF0) as *mut u32}; + let dma_enable_register = bcm2835.get_ptr(Self::BCM2835_DMA_ENABLE_REGISTER_OFFSET) as *mut u32; unsafe{ // setup constant data @@ -185,7 +249,8 @@ impl DmaTransferer DmaTransferer DmaTransfereru32{bus_address & !0xC000_0000} - - fn allocate_dma_memory(mbox:&Mailbox, size:u32, mem_fd:c_int)->DmaMemory{ - let flags = (Self::MEM_ALLOC_FLAG_COHERENT | Self::MEM_ALLOC_FLAG_DIRECT) as u32; - let handle = mbox.send_command(0x3000C, [size, 4096, flags]); - - let bus_address = mbox.send_command(0x3000D, [handle]); - let virtual_address = unsafe{libc::mmap( - std::ptr::null_mut(), - size as libc::size_t, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - Self::bus_to_phys(bus_address) as libc::off_t - )}; - - return DmaMemory { virtual_address_ptr: virtual_address as usize, bus_address, mailbox_memory_handle:handle, size } - } } +impl Drop for DmaTransferer{ + fn drop(&mut self) { + // reset the program before releasing the memory + unsafe{ + // reset the dma channels + (*self.tx_dma).write_cs(Self::DMA_CS_RESET); + (*self.rx_dma).write_cs(Self::DMA_CS_RESET); + // disable the channels I used + let mask = !((1 << self.tx_channel_number) | (1 << self.rx_channel_number)); + *self.dma_enable_register_ptr &= mask; + } + self.dma_const_data_memory.release(&self.mbox); + self.dma_data_memory.release(&self.mbox); + self.rx_control_block_memory.release(&self.mbox); + self.source_buffer_memory.release(&self.mbox); + self.tx_control_block_memory.release(&self.mbox); + } +} \ No newline at end of file diff --git a/gb/src/rpi_gpio/raw_spi.rs b/gb/src/rpi_gpio/raw_spi.rs index 0b6d366e..76d27af3 100644 --- a/gb/src/rpi_gpio/raw_spi.rs +++ b/gb/src/rpi_gpio/raw_spi.rs @@ -1,9 +1,70 @@ +use libc::c_void; use rppal::gpio::{OutputPin, IoPin}; use crate::rpi_gpio::{dma::DmaTransferer, libc_abort}; use super::{ili9341_controller::{Ili9341Commands, SpiController, TARGET_SCREEN_HEIGHT, TARGET_SCREEN_WIDTH}, decl_write_volatile_field, decl_read_volatile_field}; + +const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; +const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; +const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; +const BCM_RPI4_MMIO_PERIPHERALS_SIZE:usize = 0x180_0000; + +pub struct Bcm2835{ + ptr:*mut c_void, + mem_fd: libc::c_int +} + +impl Bcm2835 { + fn new()->Self{ + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; + + if mem_fd < 0{ + libc_abort("bad file descriptor"); + } + + let bcm2835 = unsafe{libc::mmap( + std::ptr::null_mut(), + BCM_RPI4_MMIO_PERIPHERALS_SIZE, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + BCM2835_RPI4_BUS_ADDRESS as libc::off_t + )}; + + if bcm2835 == libc::MAP_FAILED{ + libc_abort("FATAL: mapping /dev/mem failed!"); + } + + Bcm2835 { ptr: bcm2835, mem_fd } + } + + pub fn get_ptr(&self, offset:usize)->*mut c_void{ + unsafe{self.ptr.add(offset)} + } + + pub fn get_fd(&self)->libc::c_int{ + self.mem_fd + } +} + +impl Drop for Bcm2835{ + fn drop(&mut self) { + unsafe{ + let result = libc::munmap(self.ptr, BCM_RPI4_MMIO_PERIPHERALS_SIZE); + if result != 0{ + libc_abort("Error while unmapping the mmio memory"); + } + + let result = libc::close(self.mem_fd); + if result != 0{ + libc_abort("Error while closing the mem_fd"); + } + } + } +} + struct GpioRegistersAccess{ ptr:*mut u32 } @@ -70,21 +131,20 @@ impl SpiRegistersAccess{ pub struct RawSpi{ spi_registers: *mut SpiRegistersAccess, - mem_fd:libc::c_int, spi_pins:[IoPin;2], spi_cs0:OutputPin, dc_pin:OutputPin, dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, - last_transfer_was_dma:bool + last_transfer_was_dma:bool, + + // declared last in order for it to be freed last + // rust gurantee that the order of the droped values is the order of declaration + // keeping it last so it will be freed correctly + _bcm2835:Bcm2835, } impl RawSpi{ - const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; - const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; - const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; - const BCM_RPI4_SIZE:usize = 0x180_0000; - const SPI_CS_RXF:u32 = 1 << 20; const SPI_CS_RXR:u32 = 1 << 19; const SPI_CS_TXD:u32 = 1 << 18; @@ -95,27 +155,10 @@ impl RawSpi{ const SPI_CS_CLEAR_RX:u32 = 1 << 5; fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ - let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; - - if mem_fd < 0{ - libc_abort("bad file descriptor"); - } - - let bcm2835 = unsafe{libc::mmap( - std::ptr::null_mut(), - Self::BCM_RPI4_SIZE, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - Self::BCM2835_RPI4_BUS_ADDRESS as libc::off_t - )}; - - if bcm2835 == libc::MAP_FAILED{ - libc_abort("FATAL: mapping /dev/mem failed!"); - } + let bcm2835 = Bcm2835::new(); // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; - let spi_registers = unsafe{bcm2835.add(Self::BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess}; + let spi_registers = bcm2835.get_ptr(BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess; unsafe{ // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 @@ -127,9 +170,10 @@ impl RawSpi{ log::info!("finish ili9341 device init"); + let dma_transferer = DmaTransferer::new(&bcm2835, 7, 1 ); RawSpi { - spi_registers, dc_pin, spi_pins, spi_cs0, mem_fd, last_transfer_was_dma: false, - dma_transferer:DmaTransferer::new(bcm2835, 7, 1, mem_fd ) + _bcm2835:bcm2835, spi_registers, dc_pin, spi_pins, spi_cs0, last_transfer_was_dma: false, + dma_transferer } } From 0d1c2f4607e1d09d133ac93ca781f4e506833862 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 4 Jun 2022 16:14:41 +0100 Subject: [PATCH 36/70] Sleeping in order to decrease the busy waiting without damaging the performance --- gb/src/rpi_gpio/dma.rs | 19 +++++++++++++------ gb/src/rpi_gpio/raw_spi.rs | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs index 76d0cfb5..5d5d087a 100644 --- a/gb/src/rpi_gpio/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -338,19 +338,26 @@ impl DmaTransferer 1000000{ + std::panic!("ERROR! tx dma channel is not responding, a reboot is suggested"); + } } while (*self.rx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { - // Self::sleep_ms(250); - // log::info!("Waiting for the rx channel"); + Self::sleep_us(1); + counter += 1; + if counter > 1000000{ + std::panic!("ERROR! rx dma channel is not responding, a reboot is suggested"); + } } } } - fn sleep_ms(milliseconds_to_sleep:u64){ - std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); + fn sleep_us(milliseconds_to_sleep:u64){ + std::thread::sleep(std::time::Duration::from_micros(milliseconds_to_sleep)); } } diff --git a/gb/src/rpi_gpio/raw_spi.rs b/gb/src/rpi_gpio/raw_spi.rs index 76d27af3..c47a025d 100644 --- a/gb/src/rpi_gpio/raw_spi.rs +++ b/gb/src/rpi_gpio/raw_spi.rs @@ -11,6 +11,8 @@ const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; const BCM_RPI4_MMIO_PERIPHERALS_SIZE:usize = 0x180_0000; + +// This struct is here to managed the lifetime of the bcm2835 ptr and the memory fd pub struct Bcm2835{ ptr:*mut c_void, mem_fd: libc::c_int From d8a7400e56d4eff7af45ae04beb8cb1a4bd477f5 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 4 Jun 2022 18:43:17 +0300 Subject: [PATCH 37/70] Move the bcm to another crate Init it using the system functions and not hard coded consts --- Cargo.lock | 8 ++ Cargo.toml | 3 +- bcm_host/Cargo.toml | 9 ++ bcm_host/build.rs | 3 + bcm_host/src/lib.rs | 71 ++++++++++++ gb/Cargo.toml | 1 + gb/src/rpi_gpio/dma.rs | 29 +++-- gb/src/rpi_gpio/ili9341_controller.rs | 10 +- gb/src/rpi_gpio/raw_spi.rs | 151 +++++--------------------- image_inter/Cargo.toml | 3 +- 10 files changed, 135 insertions(+), 153 deletions(-) create mode 100644 bcm_host/Cargo.toml create mode 100644 bcm_host/build.rs create mode 100644 bcm_host/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 588856ba..fcd31e6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,13 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bcm_host" +version = "0.1.0" +dependencies = [ + "libc", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -424,6 +431,7 @@ dependencies = [ name = "gb" version = "2.1.0" dependencies = [ + "bcm_host", "cfg-if", "chrono", "crossbeam-channel", diff --git a/Cargo.toml b/Cargo.toml index 4856036a..15b4c2fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "gb", "lib_gb", - "image_inter" + "image_inter", + "bcm_host" ] \ No newline at end of file diff --git a/bcm_host/Cargo.toml b/bcm_host/Cargo.toml new file mode 100644 index 00000000..762468e7 --- /dev/null +++ b/bcm_host/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "bcm_host" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "0.2" \ No newline at end of file diff --git a/bcm_host/build.rs b/bcm_host/build.rs new file mode 100644 index 00000000..f3ccafb4 --- /dev/null +++ b/bcm_host/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-lib=bcm_host"); +} \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs new file mode 100644 index 00000000..00335867 --- /dev/null +++ b/bcm_host/src/lib.rs @@ -0,0 +1,71 @@ +use libc::{c_uint, c_void}; + +// linking to - https://github.com/raspberrypi/firmware/blob/master/opt/vc/include/bcm_host.h +extern "C"{ + // There is no need to link to the init and deinit functions. it looks like they are needed only for the gpu dispnmx stuff + pub fn bcm_host_get_peripheral_address()->c_uint; // returns the bcm bus address. should be 0xFE00_0000 on RPI4 + pub fn bcm_host_get_peripheral_size()->c_uint; // returns the bcm bus size. should be 0x180_0000 on RPI4 +} + +// This struct is here to managed the lifetime of the bcm2835 ptr and the memory fd +pub struct BcmHost{ + ptr:*mut c_void, + mem_fd: libc::c_int +} + +impl BcmHost { + pub fn new()->Self{ + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; + + if mem_fd < 0{ + libc_abort("bad file descriptor"); + } + + let bus_peripherals_address = unsafe{bcm_host_get_peripheral_address()}; + let bus_peripherals_size = unsafe{bcm_host_get_peripheral_size()}; + + let bcm2835 = unsafe{libc::mmap( + std::ptr::null_mut(), + bus_peripherals_size as usize, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + bus_peripherals_address as libc::off_t + )}; + + if bcm2835 == libc::MAP_FAILED{ + libc_abort("FATAL: mapping /dev/mem failed!"); + } + + BcmHost { ptr: bcm2835, mem_fd } + } + + pub fn get_ptr(&self, offset:usize)->*mut c_void{ + unsafe{self.ptr.add(offset)} + } + + pub fn get_fd(&self)->libc::c_int{ + self.mem_fd + } +} + +impl Drop for BcmHost{ + fn drop(&mut self) { + unsafe{ + let bus_peripherals_size = bcm_host_get_peripheral_size(); + let result = libc::munmap(self.ptr, bus_peripherals_size as usize); + if result != 0{ + libc_abort("Error while unmapping the mmio memory"); + } + + let result = libc::close(self.mem_fd); + if result != 0{ + libc_abort("Error while closing the mem_fd"); + } + } + } +} + +fn libc_abort(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); +} \ No newline at end of file diff --git a/gb/Cargo.toml b/gb/Cargo.toml index c00ee7c1..36cc39ed 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -12,6 +12,7 @@ path = "src/main.rs" [dependencies] lib_gb = {path = "../lib_gb/"} image_inter = {path = "../image_inter"} +bcm_host = {path = "../bcm_host"} log = "0.4" fern = "0.6" chrono = "0.4" diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs index 5d5d087a..1b0a202c 100644 --- a/gb/src/rpi_gpio/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -1,8 +1,9 @@ use std::ptr::write_volatile; +use bcm_host::BcmHost; use libc::{c_void, c_int}; -use super::{*, raw_spi::Bcm2835}; +use super::*; // Mailbox messages need to be 16 byte alligned #[repr(C, align(16))] @@ -190,8 +191,8 @@ pub struct DmaTransferer{ } impl DmaTransferer{ - const BCM2835_DMA0_OFFSET:usize = 0x7_000; - const BCM2835_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM2835_DMA0_OFFSET + 0xFF; + const BCM_DMA0_OFFSET:usize = 0x7_000; + const BCM_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM_DMA0_OFFSET + 0xFF; const DMA_CS_RESET:u32 = 1 << 31; const DMA_CS_END:u32 = 1 << 1; @@ -207,17 +208,17 @@ impl DmaTransfereru32{(peripherial_mapping as u32) << 16} - pub fn new(bcm2835:&Bcm2835, tx_channel_number:u8, rx_channel_number:u8)->Self{ + pub fn new(bcm_host:&BcmHost, tx_channel_number:u8, rx_channel_number:u8)->Self{ let mbox = Mailbox::new(); - let tx_registers = bcm2835.get_ptr(Self::BCM2835_DMA0_OFFSET + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; - let rx_registers = bcm2835.get_ptr(Self::BCM2835_DMA0_OFFSET + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; - let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, bcm2835.get_fd()); - let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, bcm2835.get_fd()); - let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, bcm2835.get_fd()); - let dma_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, bcm2835.get_fd()); - let dma_const_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * 2) as u32, bcm2835.get_fd()); + let tx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; + let rx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; + let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, bcm_host.get_fd()); + let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, bcm_host.get_fd()); + let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, bcm_host.get_fd()); + let dma_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, bcm_host.get_fd()); + let dma_const_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * 2) as u32, bcm_host.get_fd()); - let dma_enable_register = bcm2835.get_ptr(Self::BCM2835_DMA_ENABLE_REGISTER_OFFSET) as *mut u32; + let dma_enable_register = bcm_host.get_ptr(Self::BCM_DMA_ENABLE_REGISTER_OFFSET) as *mut u32; unsafe{ // setup constant data @@ -258,10 +259,6 @@ impl DmaTransferer(&mut self, data:&[u8; SIZE], tx_peripherial_mapping:u8, tx_physical_destination_address:u32, rx_peripherial_mapping:u8, rx_physical_destination_address:u32){ - if SIZE != NUM_CHUNKS * CHUNK_SIZE{ - std::panic!("bad SIZE param"); - } - unsafe{ std::ptr::copy_nonoverlapping(data.as_ptr(), self.source_buffer_memory.virtual_address_ptr as *mut u8, SIZE); diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index 24b579bc..0d943c1a 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -54,10 +54,8 @@ impl Ili9341Contoller{ pub fn new()->Self{ let gpio = rppal::gpio::Gpio::new().unwrap(); - // let mut dc_pin = gpio.get(15).unwrap().into_output(); let mut reset_pin = gpio.get(14).unwrap().into_output(); let mut led_pin = gpio.get(25).unwrap().into_output(); - drop(gpio); // toggling the reset pin to initalize the lcd let wait_duration = std::time::Duration::from_millis(120); @@ -68,12 +66,8 @@ impl Ili9341Contoller{ reset_pin.set_high(); std::thread::sleep(wait_duration); - // let spi0_ceo_n = gpio.get(8).unwrap().into_output(); - // let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); - // let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); - - // let mut spi = RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n); let mut spi:SC = SpiController::new(15); + // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down // And later I twicked some params specific to my display (http://www.lcdwiki.com/3.2inch_SPI_Module_ILI9341_SKU:MSP3218) @@ -128,8 +122,6 @@ impl Ili9341Contoller{ // turn backlight on led_pin.set_high(); - // unsafe{(*spi.spi_registers).write_clk(4)}; - log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); return Ili9341Contoller { spi, led_pin, reset_pin}; diff --git a/gb/src/rpi_gpio/raw_spi.rs b/gb/src/rpi_gpio/raw_spi.rs index c47a025d..fb0d475f 100644 --- a/gb/src/rpi_gpio/raw_spi.rs +++ b/gb/src/rpi_gpio/raw_spi.rs @@ -1,118 +1,12 @@ -use libc::c_void; +use bcm_host::BcmHost; use rppal::gpio::{OutputPin, IoPin}; -use crate::rpi_gpio::{dma::DmaTransferer, libc_abort}; +use crate::rpi_gpio::{dma::DmaTransferer}; use super::{ili9341_controller::{Ili9341Commands, SpiController, TARGET_SCREEN_HEIGHT, TARGET_SCREEN_WIDTH}, decl_write_volatile_field, decl_read_volatile_field}; - -const BCM2835_GPIO_BASE_ADDRESS:usize = 0x20_0000; -const BCM2835_SPI0_BASE_ADDRESS:usize = 0x20_4000; -const BCM2835_RPI4_BUS_ADDRESS:usize = 0xFE00_0000; -const BCM_RPI4_MMIO_PERIPHERALS_SIZE:usize = 0x180_0000; - - -// This struct is here to managed the lifetime of the bcm2835 ptr and the memory fd -pub struct Bcm2835{ - ptr:*mut c_void, - mem_fd: libc::c_int -} - -impl Bcm2835 { - fn new()->Self{ - let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; - - if mem_fd < 0{ - libc_abort("bad file descriptor"); - } - - let bcm2835 = unsafe{libc::mmap( - std::ptr::null_mut(), - BCM_RPI4_MMIO_PERIPHERALS_SIZE, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - BCM2835_RPI4_BUS_ADDRESS as libc::off_t - )}; - - if bcm2835 == libc::MAP_FAILED{ - libc_abort("FATAL: mapping /dev/mem failed!"); - } - - Bcm2835 { ptr: bcm2835, mem_fd } - } - - pub fn get_ptr(&self, offset:usize)->*mut c_void{ - unsafe{self.ptr.add(offset)} - } - - pub fn get_fd(&self)->libc::c_int{ - self.mem_fd - } -} - -impl Drop for Bcm2835{ - fn drop(&mut self) { - unsafe{ - let result = libc::munmap(self.ptr, BCM_RPI4_MMIO_PERIPHERALS_SIZE); - if result != 0{ - libc_abort("Error while unmapping the mmio memory"); - } - - let result = libc::close(self.mem_fd); - if result != 0{ - libc_abort("Error while closing the mem_fd"); - } - } - } -} - -struct GpioRegistersAccess{ - ptr:*mut u32 -} -enum GpioRegister{ - Gpfsel0 = 0, - Gpfsel1 = 1, - Gpset0 = 6, - Gpset1 = 7, - Gpclr0 = 8, - Gpclr1 = 9 -} -impl GpioRegistersAccess{ - unsafe fn read_register(&self, register:GpioRegister)->u32{ - std::ptr::read_volatile(self.ptr.add(register as usize)) - } - unsafe fn write_register(&self, register:GpioRegister, value:u32){ - std::ptr::write_volatile(self.ptr.add(register as usize), value); - } - unsafe fn set_gpio_mode(&self, pin:u8, mode:u8){ - // there are less than 100 pins so I assume the largest one is less than 100 - let gpfsel_register = pin / 10; - let gpfsel_register_index = pin % 10; - let register_ptr = self.ptr.add(gpfsel_register as usize); - let mut register_value = std::ptr::read_volatile(register_ptr); - let mask = !(0b111 << (gpfsel_register_index * 3)); - register_value &= mask; - register_value |= (mode as u32) << (gpfsel_register_index *3); - std::ptr::write_volatile(register_ptr, register_value); - } - unsafe fn set_gpio_high(&self, pin:u8){ - if pin < 32{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset0 as usize), 1 << pin); - } - else{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpset1 as usize), 1 << (pin - 32)); - } - } - unsafe fn set_gpio_low(&self, pin:u8){ - if pin < 32{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr0 as usize), 1 << pin); - } - else{ - std::ptr::write_volatile(self.ptr.add(GpioRegister::Gpclr1 as usize), 1 << (pin - 32)); - } - } -} +const BCM_SPI0_BASE_ADDRESS:usize = 0x20_4000; +const SPI_CLOCK_DIVISOR:u32 = 4; // the smaller the faster (on my system below 4 there are currptions) // The register are 4 bytes each so making sure the allignment and padding are correct #[repr(C, align(4))] @@ -133,17 +27,19 @@ impl SpiRegistersAccess{ pub struct RawSpi{ spi_registers: *mut SpiRegistersAccess, - spi_pins:[IoPin;2], - spi_cs0:OutputPin, dc_pin:OutputPin, - dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, last_transfer_was_dma:bool, + + // holding those pins in order to make sure they are configured correctly + // the state resets upon drop + _spi_pins:[IoPin;2], + _spi_cs0:OutputPin, // declared last in order for it to be freed last // rust gurantee that the order of the droped values is the order of declaration // keeping it last so it will be freed correctly - _bcm2835:Bcm2835, + _bcm:BcmHost, } impl RawSpi{ @@ -157,24 +53,22 @@ impl RawSpi{ const SPI_CS_CLEAR_RX:u32 = 1 << 5; fn new (dc_pin:OutputPin, spi_pins:[IoPin;2], mut spi_cs0: OutputPin)->Self{ - let bcm2835 = Bcm2835::new(); + let bcm_host = BcmHost::new(); - // let gpio_registers = unsafe{GpioRegistersAccess{ptr:bcm2835.add(Self::BCM2835_GPIO_BASE_ADDRESS) as *mut u32}}; - let spi_registers = bcm2835.get_ptr(BCM2835_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess; + let spi_registers = bcm_host.get_ptr(BCM_SPI0_BASE_ADDRESS) as *mut SpiRegistersAccess; unsafe{ // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 spi_cs0.set_low(); - (*spi_registers).write_cs(Self::SPI_CS_TA); - (*spi_registers).write_clk(4); - (*spi_registers).write_dlen(2); + Self::setup_poll_fast_transfer(&mut *spi_registers); + (*spi_registers).write_clk(SPI_CLOCK_DIVISOR); } log::info!("finish ili9341 device init"); - let dma_transferer = DmaTransferer::new(&bcm2835, 7, 1 ); + let dma_transferer = DmaTransferer::new(&bcm_host, 7, 1 ); RawSpi { - _bcm2835:bcm2835, spi_registers, dc_pin, spi_pins, spi_cs0, last_transfer_was_dma: false, + _bcm:bcm_host, spi_registers, dc_pin, _spi_pins: spi_pins, _spi_cs0: spi_cs0, last_transfer_was_dma: false, dma_transferer } } @@ -191,10 +85,15 @@ impl RawSpi{ fn prepare_for_transfer(&mut self) { if self.last_transfer_was_dma{ self.dma_transferer.end_dma_transfer(); - unsafe{ - (*self.spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); - (*self.spi_registers).write_dlen(2); // poll mode speed up - } + unsafe{Self::setup_poll_fast_transfer(&mut *self.spi_registers)}; + } + } + + fn setup_poll_fast_transfer(spi_registers:&mut SpiRegistersAccess){ + unsafe{ + spi_registers.write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + // poll mode speed up according to this forum post - https://forums.raspberrypi.com/viewtopic.php?f=44&t=181154 + spi_registers.write_dlen(2); } } diff --git a/image_inter/Cargo.toml b/image_inter/Cargo.toml index 451733d4..83986ad9 100644 --- a/image_inter/Cargo.toml +++ b/image_inter/Cargo.toml @@ -2,6 +2,7 @@ name = "image_inter" version = "1.0.0" edition = "2021" +description = "A small crate for the image interpolation, I sperated it cause I wanted to benchmark in properly" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -13,7 +14,7 @@ cc = "1.0" [dev-dependencies] criterion = "0.3" -fast_image_resize = "0.9.3" +fast_image_resize = "0.9.3" # to benchmark my results [[bench]] name = "inter_bench" From 2bc6837b149688488db5251915d715220c9d5ddb Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sun, 5 Jun 2022 14:56:01 +0300 Subject: [PATCH 38/70] Implemented option for compact-pixel a pixel of u16 bytes --- gb/Cargo.toml | 5 +++-- gb/src/main.rs | 5 +++-- gb/src/mpmc_gfx_device.rs | 4 ++-- gb/src/rpi_gpio/ili9341_controller.rs | 19 ++++++------------- gb/src/sdl/sdl_gfx_device.rs | 7 +++++-- lib_gb/Cargo.toml | 3 +++ lib_gb/src/ppu/color.rs | 13 +++++++++++-- lib_gb/src/ppu/gb_ppu.rs | 5 +++-- lib_gb/src/ppu/gfx_device.rs | 8 +++++++- lib_gb/tests/integration_tests.rs | 5 +++-- 10 files changed, 46 insertions(+), 28 deletions(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 36cc39ed..a38d23d9 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -31,5 +31,6 @@ sdl-resample = [] push-audio = [] static-sdl = ["sdl2/bundled", "sdl2/static-link"] static-scale = [] -gpio = ["rppal"] -raw-spi = ["gpio"] # requires sudo \ No newline at end of file +gpio = ["rppal", "compact-pixel"] +raw-spi = ["gpio"] # requires sudo +compact-pixel = ["lib_gb/compact-pixel"] \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 2c18da3a..d28e92ec 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -12,6 +12,7 @@ mod audio{ } mod sdl{ pub mod utils; + #[cfg(not(feature = "compact-pixel"))] pub mod sdl_gfx_device; #[cfg(feature = "sdl-resample")] pub mod sdl_audio_resampler; @@ -41,7 +42,7 @@ cfg_if::cfg_if!{ use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; use joypad_terminal_menu::{MenuOption, JoypadTerminalMenu, TerminalRawModeJoypadProvider}; -use lib_gb::{keypad::button::Button, GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +use lib_gb::{keypad::button::Button, GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}}; use sdl2::sys::*; use std::{fs, env, result::Result, vec::Vec}; use log::info; @@ -203,7 +204,7 @@ fn main() { } let buffer = r.recv().unwrap(); - gfx_device.swap_buffer(&*(buffer as *const [u32; SCREEN_WIDTH * SCREEN_HEIGHT])); + gfx_device.swap_buffer(&*(buffer as *const [Pixel; SCREEN_WIDTH * SCREEN_HEIGHT])); } drop(r); diff --git a/gb/src/mpmc_gfx_device.rs b/gb/src/mpmc_gfx_device.rs index a42aca95..9f1c8249 100644 --- a/gb/src/mpmc_gfx_device.rs +++ b/gb/src/mpmc_gfx_device.rs @@ -1,4 +1,4 @@ -use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; +use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}; pub struct MpmcGfxDevice{ sender: crossbeam_channel::Sender @@ -11,7 +11,7 @@ impl MpmcGfxDevice{ } impl GfxDevice for MpmcGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { if self.sender.send(buffer.as_ptr() as usize).is_err(){ log::debug!("The receiver endpoint has been closed"); } diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index 0d943c1a..7eb1bf72 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -1,6 +1,6 @@ use std::ops::Add; -use lib_gb::ppu::{gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::GfxDevice}; +use lib_gb::ppu::{gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}; use rppal::gpio::OutputPin; pub enum Ili9341Commands{ @@ -171,25 +171,18 @@ pub struct Ili9341GfxDevice{ impl Ili9341GfxDevice{ pub fn new()->Self{ + #[cfg(not(feature = "compact-pixel"))] + std::compile_error("ili9341 gfx device must have Pixel type = u16"); + let ili9341_controller = Ili9341Contoller::new(); Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} } } impl GfxDevice for Ili9341GfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - let u16_buffer:[u16;SCREEN_HEIGHT*SCREEN_WIDTH] = buffer.map(|pixel| { - let b = pixel & 0xFF; - let g = (pixel & 0xFF00)>>8; - let r = (pixel & 0xFF0000)>>16; - let mut u16_pixel = b as u16 >> 3; - u16_pixel |= ((g >> 2) << 5) as u16; - u16_pixel |= ((r >> 3) << 11) as u16; - return u16_pixel; - }); - + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { // if self.frames_counter & 1 == 0{ - self.ili9341_controller.write_frame_buffer(&u16_buffer); + self.ili9341_controller.write_frame_buffer(&buffer); // } // measure fps diff --git a/gb/src/sdl/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs index 130a2b8a..48fb095a 100644 --- a/gb/src/sdl/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -1,6 +1,6 @@ use std::ffi::{CString, c_void}; use sdl2::sys::*; -use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; +use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}; use crate::sdl::utils::get_sdl_error_message; pub struct SdlGfxDevice{ @@ -15,6 +15,9 @@ pub struct SdlGfxDevice{ impl SdlGfxDevice{ pub fn new(window_name:&str, screen_scale: usize, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{ + #[cfg(feature = "compact-pixel")] + std::compile_error("Sdl gfx device must have Pixel type = u32"); + let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ @@ -101,7 +104,7 @@ impl SdlGfxDevice{ } impl GfxDevice for SdlGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { self.discard = (self.discard + 1) % self.turbo_mul; if self.discard != 0{ return; diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 37851a92..fd4cfa9e 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -7,6 +7,9 @@ edition = "2018" [dependencies] log = "0.4" +[features] +compact-pixel = [] + [dev-dependencies] criterion = "0.3" reqwest = { version = "0.11", features = ["blocking"] } diff --git a/lib_gb/src/ppu/color.rs b/lib_gb/src/ppu/color.rs index 26c8fb8c..af23223f 100644 --- a/lib_gb/src/ppu/color.rs +++ b/lib_gb/src/ppu/color.rs @@ -1,3 +1,5 @@ +use super::gfx_device::Pixel; + pub struct Color{ pub r:u8, pub g:u8, @@ -34,8 +36,15 @@ impl PartialEq for Color{ } } -impl From for u32{ +impl From for Pixel{ fn from(color: Color) -> Self { - ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) + #[cfg(not(feature = "compact-pixel"))] + { + ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) + } + #[cfg(feature = "compact-pixel")] + { + (((color.r >> 3) as u16) << 11) | (((color.g >> 2) as u16) << 5) | ((color.b >> 3) as u16) + } } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 03988fa1..e6cf0fcd 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -3,6 +3,7 @@ use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::ppu::{gfx_device::GfxDevice, ppu_state::PpuState, sprite_attribute::SpriteAttribute, colors::*, color::*}; use super::fifo::{FIFO_SIZE, sprite_fetcher::*, background_fetcher::BackgroundFetcher}; +use super::gfx_device::Pixel; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; @@ -40,7 +41,7 @@ pub struct GbPpu{ gfx_device: GFX, t_cycles_passed:u16, - screen_buffers: [[u32; SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], + screen_buffers: [[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], current_screen_buffer_index:usize, push_lcd_buffer:Vec, screen_buffer_index:usize, @@ -122,7 +123,7 @@ impl GbPpu{ let cycles = std::cmp::min(fethcer_m_cycles_to_next_event, stat_m_cycles_to_next_event); for i in 0..self.push_lcd_buffer.len(){ - self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = u32::from(self.push_lcd_buffer[i]); + self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = Color::into(self.push_lcd_buffer[i]); self.screen_buffer_index += 1; if self.screen_buffer_index == SCREEN_WIDTH * SCREEN_HEIGHT{ self.swap_buffer(); diff --git a/lib_gb/src/ppu/gfx_device.rs b/lib_gb/src/ppu/gfx_device.rs index 017acacf..5ffa1646 100644 --- a/lib_gb/src/ppu/gfx_device.rs +++ b/lib_gb/src/ppu/gfx_device.rs @@ -1,5 +1,11 @@ use super::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; +#[cfg(not(feature = "compact-pixel"))] +pub type Pixel = u32; +#[cfg(feature = "compact-pixel")] +pub type Pixel = u16; + + pub trait GfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]); + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]); } \ No newline at end of file diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index d65436ee..d6f82f7a 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -3,6 +3,7 @@ use std::hash::{Hash, Hasher}; use std::io::Read; use lib_gb::apu::audio_device::BUFFER_SIZE; use lib_gb::ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; +use lib_gb::ppu::gfx_device::Pixel; use lib_gb::{ apu::audio_device::AudioDevice, keypad::joypad_provider::JoypadProvider, machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, ppu::gfx_device::GfxDevice @@ -14,7 +15,7 @@ struct CheckHashGfxDevice{ found_p:*mut bool, } impl GfxDevice for CheckHashGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { let mut s = DefaultHasher::new(); buffer.hash(&mut s); let hash = s.finish(); @@ -134,7 +135,7 @@ fn calc_hash(rom_path:&str){ static mut LAST_HASH:u64 = 0; struct GetHashGfxDevice; impl GfxDevice for GetHashGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { unsafe{ if FRAMES_COUNTER < 700{ FRAMES_COUNTER += 1; From 67e46d24f69119c0c8d4a7ce97e55a1498e6dcc2 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sun, 5 Jun 2022 17:49:09 +0300 Subject: [PATCH 39/70] Made dma code readble Refactor the responsibility between the dma and the spi --- Cargo.lock | 3 +- bcm_host/Cargo.toml | 5 +- bcm_host/src/lib.rs | 2 + gb/Cargo.toml | 16 +- gb/src/main.rs | 16 +- gb/src/rpi_gpio/dma.rs | 234 ++++++++++++-------- gb/src/rpi_gpio/ili9341_controller.rs | 32 +-- gb/src/rpi_gpio/{raw_spi.rs => mmio_spi.rs} | 76 +++---- gb/src/rpi_gpio/mod.rs | 11 +- gb/src/rpi_gpio/{spi.rs => rppal_spi.rs} | 12 +- gb/src/sdl/sdl_gfx_device.rs | 2 +- lib_gb/Cargo.toml | 2 +- lib_gb/src/ppu/color.rs | 4 +- lib_gb/src/ppu/gfx_device.rs | 4 +- 14 files changed, 224 insertions(+), 195 deletions(-) rename gb/src/rpi_gpio/{raw_spi.rs => mmio_spi.rs} (60%) rename gb/src/rpi_gpio/{spi.rs => rppal_spi.rs} (75%) diff --git a/Cargo.lock b/Cargo.lock index fcd31e6e..48738ab2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,9 +33,10 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bcm_host" -version = "0.1.0" +version = "1.0.0" dependencies = [ "libc", + "log", ] [[package]] diff --git a/bcm_host/Cargo.toml b/bcm_host/Cargo.toml index 762468e7..b9ae8bfd 100644 --- a/bcm_host/Cargo.toml +++ b/bcm_host/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "bcm_host" -version = "0.1.0" +version = "1.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc = "0.2" \ No newline at end of file +libc = "0.2" +log = "0.4" \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs index 00335867..9e737213 100644 --- a/bcm_host/src/lib.rs +++ b/bcm_host/src/lib.rs @@ -23,6 +23,8 @@ impl BcmHost { let bus_peripherals_address = unsafe{bcm_host_get_peripheral_address()}; let bus_peripherals_size = unsafe{bcm_host_get_peripheral_size()}; + + log::info!("BCM host peripherals address: {:#X}, size: {:#X}", bus_peripherals_address, bus_peripherals_size); let bcm2835 = unsafe{libc::mmap( std::ptr::null_mut(), diff --git a/gb/Cargo.toml b/gb/Cargo.toml index a38d23d9..9a0dec89 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -11,8 +11,8 @@ path = "src/main.rs" [dependencies] lib_gb = {path = "../lib_gb/"} -image_inter = {path = "../image_inter"} -bcm_host = {path = "../bcm_host"} +image_inter = {path = "../image_inter", optional = true} +bcm_host = {path = "../bcm_host", optional = true} log = "0.4" fern = "0.6" chrono = "0.4" @@ -22,15 +22,15 @@ crossbeam-channel = "0.5" cfg-if = "1.0" crossterm = "0.23" rppal = {version = "0.13", optional = true} -libc = "0.2" -nix = {version = "0.24", features = ["ioctl"]} +libc = {version = "0.2", optional = true} +nix = {version = "0.24", features = ["ioctl"], optional = true} [features] -default = ["static-sdl", "raw-spi"] +default = ["static-sdl"] sdl-resample = [] push-audio = [] static-sdl = ["sdl2/bundled", "sdl2/static-link"] static-scale = [] -gpio = ["rppal", "compact-pixel"] -raw-spi = ["gpio"] # requires sudo -compact-pixel = ["lib_gb/compact-pixel"] \ No newline at end of file +u16pixel = ["lib_gb/u16pixel"] +rpi = ["rppal", "u16pixel", "image_inter"] +mmio = ["rpi", "nix", "libc", "bcm_host"] # requires sudo \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index d28e92ec..f5a4dd31 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,7 +1,7 @@ mod mbc_handler; mod mpmc_gfx_device; mod joypad_terminal_menu; -#[cfg(feature = "gpio")] +#[cfg(feature = "rpi")] mod rpi_gpio; mod audio{ pub mod audio_resampler; @@ -12,7 +12,7 @@ mod audio{ } mod sdl{ pub mod utils; - #[cfg(not(feature = "compact-pixel"))] + #[cfg(not(feature = "u16pixel"))] pub mod sdl_gfx_device; #[cfg(feature = "sdl-resample")] pub mod sdl_audio_resampler; @@ -27,7 +27,7 @@ mod sdl{ pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; } } - #[cfg(not(feature = "gpio"))] + #[cfg(not(feature = "rpi"))] pub mod sdl_joypad_provider; } @@ -51,7 +51,7 @@ const SCREEN_SCALE:usize = 4; const TURBO_MUL:u8 = 1; cfg_if::cfg_if!{ - if #[cfg(feature = "gpio")]{ + if #[cfg(feature = "rpi")]{ use crate::rpi_gpio::gpio_joypad_provider::*; fn buttons_mapper(button:&Button)->GpioPin{ match button{ @@ -168,10 +168,10 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - #[cfg(feature = "gpio")] - let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(); + #[cfg(feature = "rpi")] + let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(14, 15, 25); - #[cfg(not(feature = "gpio"))] + #[cfg(not(feature = "rpi"))] let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); @@ -229,7 +229,7 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); cfg_if::cfg_if!{ - if #[cfg(feature = "gpio")]{ + if #[cfg(feature = "rpi")]{ let joypad_provider = GpioJoypadProvider::new(buttons_mapper); } else{ diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs index 1b0a202c..4a819808 100644 --- a/gb/src/rpi_gpio/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -1,9 +1,9 @@ -use std::ptr::write_volatile; +use std::ptr::{write_volatile, read_volatile}; use bcm_host::BcmHost; use libc::{c_void, c_int}; -use super::*; +use super::{*, ili9341_controller::SPI_BUFFER_SIZE}; // Mailbox messages need to be 16 byte alligned #[repr(C, align(16))] @@ -65,6 +65,7 @@ impl Mailbox{ libc_abort("Error in ioctl call"); } + // The return value of the command is located at the first int in the data section (for more info see the Mailbox docs) return message.data[0]; } } @@ -176,23 +177,22 @@ impl DmaRegistersAccess{ decl_write_volatile_field!(write_conblk_ad, control_block_address); } -pub struct DmaTransferer{ +pub struct DmaSpiTransferer{ tx_dma:*mut DmaRegistersAccess, rx_dma:*mut DmaRegistersAccess, mbox:Mailbox, tx_control_block_memory:GpuMemory, rx_control_block_memory:GpuMemory, source_buffer_memory:GpuMemory, - dma_data_memory:GpuMemory, - dma_const_data_memory:GpuMemory, - tx_channel_number:u8, - rx_channel_number:u8, + dma_dynamic_memory:GpuMemory, + dma_constant_memory:GpuMemory, dma_enable_register_ptr:*mut u32, } -impl DmaTransferer{ +impl DmaSpiTransferer{ const BCM_DMA0_OFFSET:usize = 0x7_000; const BCM_DMA_ENABLE_REGISTER_OFFSET:usize = Self::BCM_DMA0_OFFSET + 0xFF; + const DMA_CHANNEL_REGISTERS_SIZE:usize = 0x100; const DMA_CS_RESET:u32 = 1 << 31; const DMA_CS_END:u32 = 1 << 1; @@ -205,29 +205,53 @@ impl DmaTransfereru32{(peripherial_mapping as u32) << 16} - pub fn new(bcm_host:&BcmHost, tx_channel_number:u8, rx_channel_number:u8)->Self{ + const DMA_CONTROL_BLOCKS_PER_TRANSFER:u32 = 4; + const DMA_CONSTANT_MEMORY_SIZE:u32 = (std::mem::size_of::() * 2) as u32; + const DMA_SPI_HEADER_SIZE:u32 = std::mem::size_of::() as u32; + const RX_CHANNEL_NUMBER:u8 = 7; + const TX_CHANNEL_NUMBER:u8 = 1; + + const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes + const DMA_SPI_NUM_CHUNKS:usize = (SPI_BUFFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((SPI_BUFFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; + const DMA_SPI_CHUNK_SIZE:usize = (SPI_BUFFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; + const DMA_SPI_TRANSFER_SIZE:usize = Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS; + const DMA_TI_PERMAP_SPI_TX:u8 = 6; + const DMA_TI_PERMAP_SPI_RX:u8 = 7; + + const DMA_SPI_CS_PHYS_ADDRESS:u32 = 0x7E20_4000; + const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; + + pub fn new(bcm_host:&BcmHost, spi_enable_dma_flag:u32)->Self{ let mbox = Mailbox::new(); - let tx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (tx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; - let rx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (rx_channel_number as usize * 0x100)) as *mut DmaRegistersAccess; - let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * 4 * NUM_CHUNKS as u32, bcm_host.get_fd()); - let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, std::mem::size_of::() as u32 * NUM_CHUNKS as u32, bcm_host.get_fd()); - let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (NUM_CHUNKS * CHUNK_SIZE) as u32, bcm_host.get_fd()); - let dma_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * NUM_CHUNKS) as u32, bcm_host.get_fd()); - let dma_const_data_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * 2) as u32, bcm_host.get_fd()); + let tx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (Self::TX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess; + let rx_registers = bcm_host.get_ptr(Self::BCM_DMA0_OFFSET + (Self::RX_CHANNEL_NUMBER as usize * Self::DMA_CHANNEL_REGISTERS_SIZE)) as *mut DmaRegistersAccess; + + let dma_tx_control_block_memory = GpuMemory::allocate(&mbox, + std::mem::size_of::() as u32 * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER * Self::DMA_SPI_CHUNK_SIZE as u32, + bcm_host.get_fd() + ); + let dma_rx_control_block_memory = GpuMemory::allocate(&mbox, + std::mem::size_of::() as u32 * Self::DMA_SPI_NUM_CHUNKS as u32, + bcm_host.get_fd() + ); + let dma_source_buffer_memory = GpuMemory::allocate(&mbox, (Self::DMA_SPI_TRANSFER_SIZE) as u32, bcm_host.get_fd()); + let dma_dynamic_memory = GpuMemory::allocate(&mbox, (std::mem::size_of::() * Self::DMA_SPI_NUM_CHUNKS) as u32, bcm_host.get_fd()); + let dma_constant_memory = GpuMemory::allocate(&mbox, Self::DMA_CONSTANT_MEMORY_SIZE, bcm_host.get_fd()); let dma_enable_register = bcm_host.get_ptr(Self::BCM_DMA_ENABLE_REGISTER_OFFSET) as *mut u32; unsafe{ // setup constant data - let ptr = dma_const_data_memory.virtual_address_ptr as *mut u32; - write_volatile(ptr, 0x100); // spi_dma enable - write_volatile(ptr.add(1), Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + let ptr = dma_constant_memory.virtual_address_ptr as *mut u32; + write_volatile(ptr, spi_enable_dma_flag); // this int enable spi with dma + write_volatile(ptr.add(1), Self::DMA_CS_ACTIVE | Self::DMA_CS_END); // this int starts the dma (set active and wrtie to end to reset it) // enable the rx & tx dma channels - write_volatile(dma_enable_register, *dma_enable_register | 1 << tx_channel_number | 1<< rx_channel_number); + write_volatile(dma_enable_register, *dma_enable_register | 1 << Self::TX_CHANNEL_NUMBER | 1<< Self::RX_CHANNEL_NUMBER); //reset the dma channels (*tx_registers).write_cs(Self::DMA_CS_RESET); @@ -237,91 +261,106 @@ impl DmaTransferer(&mut self, data:&[u8; SIZE], tx_peripherial_mapping:u8, tx_physical_destination_address:u32, rx_peripherial_mapping:u8, rx_physical_destination_address:u32){ - unsafe{ - std::ptr::copy_nonoverlapping(data.as_ptr(), self.source_buffer_memory.virtual_address_ptr as *mut u8, SIZE); + rx_control_block.write_nextconbk(self.tx_control_block_memory.bus_address + (set_tx_cb_index * std::mem::size_of::()) as u32); - let mut rx_control_block = &mut *(self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); - rx_control_block.write_ti(Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); - rx_control_block.write_source_ad(rx_physical_destination_address); + write_volatile((self.dma_dynamic_memory.virtual_address_ptr as *mut u32).add(i), self.tx_control_block_memory.bus_address + (tx_cb_index * std::mem::size_of::()) as u32); + + set_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + set_dma_tx_address.write_source_ad(self.dma_dynamic_memory.bus_address + (i as u32 * Self::DMA_CONTROL_BLOCKS_PER_TRANSFER)); + set_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CONBLK_AD_PHYS_ADDRESS + (Self::TX_CHANNEL_NUMBER as u32 * Self::DMA_CHANNEL_REGISTERS_SIZE as u32)); // channel control block address register + set_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); + set_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + (disable_tx_cb_index * std::mem::size_of::()) as u32); + + + disable_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + disable_dma_tx_address.write_source_ad(self.dma_constant_memory.bus_address); + disable_dma_tx_address.write_dest_ad(Self::DMA_SPI_CS_PHYS_ADDRESS); + disable_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); + disable_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + (start_tx_cb_index * std::mem::size_of::()) as u32); + + + start_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); + start_dma_tx_address.write_source_ad(self.dma_constant_memory.bus_address + (i * std::mem::size_of::()) as u32); + start_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CS_PHYS_ADDRESS + (Self::TX_CHANNEL_NUMBER as u32 * Self::DMA_CHANNEL_REGISTERS_SIZE as u32)); + start_dma_tx_address.write_txfr_len(std::mem::size_of::() as u32); + start_dma_tx_address.write_nextconbk(self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); + + + rx_control_block = &mut *((self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(i)); + rx_control_block.write_ti(Self::dma_ti_permap(Self::DMA_TI_PERMAP_SPI_RX) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); + rx_control_block.write_source_ad(Self::DMA_SPI_FIFO_PHYS_ADDRESS); rx_control_block.write_dest_ad(0); - rx_control_block.write_txfr_len(CHUNK_SIZE as u32 - 4); // without the 4 byte header + rx_control_block.write_txfr_len(Self::DMA_SPI_CHUNK_SIZE as u32 - Self::DMA_SPI_HEADER_SIZE); // without the 4 byte header rx_control_block.write_nextconbk(0); + } + } - let tx_control_block = &mut *(self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock); - tx_control_block.write_ti(Self::dma_ti_permap(tx_peripherial_mapping) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); - tx_control_block.write_source_ad(self.source_buffer_memory.bus_address); - tx_control_block.write_dest_ad(tx_physical_destination_address); - tx_control_block.write_txfr_len(CHUNK_SIZE as u32); - tx_control_block.write_nextconbk(0); - - for i in 1..NUM_CHUNKS{ - let tx_cb_index = i * 4; - let tx_control_block = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index)); - tx_control_block.write_ti(Self::dma_ti_permap(tx_peripherial_mapping) | Self::DMA_TI_DEST_DREQ | Self::DMA_TI_SRC_INC | Self::DMA_TI_WAIT_RESP); - tx_control_block.write_source_ad(self.source_buffer_memory.bus_address + (i * CHUNK_SIZE) as u32); - tx_control_block.write_dest_ad(tx_physical_destination_address); - tx_control_block.write_txfr_len(CHUNK_SIZE as u32); - tx_control_block.write_nextconbk(0); - - let set_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 1)); - let disable_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 2)); - let start_dma_tx_address = &mut *((self.tx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(tx_cb_index + 3)); - - rx_control_block.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 1) * std::mem::size_of::()) as u32); - - write_volatile((self.dma_data_memory.virtual_address_ptr as *mut u32).add(i), self.tx_control_block_memory.bus_address + (tx_cb_index * std::mem::size_of::()) as u32); - - set_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - set_dma_tx_address.write_source_ad(self.dma_data_memory.bus_address + (i as u32 * 4)); - set_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) + 4); // channel control block address register - set_dma_tx_address.write_txfr_len(4); - set_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 2) * std::mem::size_of::()) as u32); - - - disable_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - disable_dma_tx_address.write_source_ad(self.dma_const_data_memory.bus_address); - disable_dma_tx_address.write_dest_ad(Self::DMA_SPI_CS_PHYS_ADDRESS); - disable_dma_tx_address.write_txfr_len(4); - disable_dma_tx_address.write_nextconbk(self.tx_control_block_memory.bus_address + ((tx_cb_index + 3) * std::mem::size_of::()) as u32); - - - start_dma_tx_address.write_ti(Self::DMA_TI_SRC_INC | Self::DMA_TI_DEST_INC | Self::DMA_TI_WAIT_RESP); - start_dma_tx_address.write_source_ad(self.dma_const_data_memory.bus_address + 4); - start_dma_tx_address.write_dest_ad(Self::DMA_DMA0_CB_PHYS_ADDRESS + (self.tx_channel_number as u32 * 0x100) as u32); - start_dma_tx_address.write_txfr_len(4); - start_dma_tx_address.write_nextconbk(self.rx_control_block_memory.bus_address + (i * std::mem::size_of::()) as u32); - - - rx_control_block = &mut *((self.rx_control_block_memory.virtual_address_ptr as *mut DmaControlBlock).add(i)); - rx_control_block.write_ti(Self::dma_ti_permap(rx_peripherial_mapping) | Self::DMA_TI_SRC_DREQ | Self::DMA_TI_DEST_IGNORE); - rx_control_block.write_source_ad(rx_physical_destination_address); - rx_control_block.write_dest_ad(0); - rx_control_block.write_txfr_len(CHUNK_SIZE as u32 - 4); // without the 4 byte header - rx_control_block.write_nextconbk(0); + pub fn start_dma_transfer(&mut self, data:&[u8; SPI_BUFFER_SIZE], transfer_active_flag:u8){ + unsafe{ + let data_len = Self::DMA_SPI_CHUNK_SIZE - Self::DMA_SPI_HEADER_SIZE as usize; // Removing the first 4 bytes from this length param + let header = [transfer_active_flag, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; + + let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - Self::DMA_SPI_HEADER_SIZE as usize); + let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; + let mut i = 0; + for chunk in chunks{ + std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); + std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); + i += 1; } + std::ptr::copy_nonoverlapping(array.as_ptr(), self.source_buffer_memory.virtual_address_ptr as *mut u8, array.len()); (*self.tx_dma).write_conblk_ad(self.tx_control_block_memory.bus_address); (*self.rx_dma).write_conblk_ad(self.rx_control_block_memory.bus_address); @@ -333,45 +372,46 @@ impl DmaTransferer 1000000{ + if counter > TIME_TO_ABORT_AS_MICRO{ std::panic!("ERROR! tx dma channel is not responding, a reboot is suggested"); } } while (*self.rx_dma).read_cs() & Self::DMA_CS_ACTIVE != 0 { Self::sleep_us(1); counter += 1; - if counter > 1000000{ + if counter > TIME_TO_ABORT_AS_MICRO{ std::panic!("ERROR! rx dma channel is not responding, a reboot is suggested"); } } } } - fn sleep_us(milliseconds_to_sleep:u64){ - std::thread::sleep(std::time::Duration::from_micros(milliseconds_to_sleep)); + fn sleep_us(microseconds_to_sleep:u64){ + std::thread::sleep(std::time::Duration::from_micros(microseconds_to_sleep)); } } -impl Drop for DmaTransferer{ +impl Drop for DmaSpiTransferer{ fn drop(&mut self) { - // reset the program before releasing the memory + // reset the dma channels before releasing the memory unsafe{ // reset the dma channels (*self.tx_dma).write_cs(Self::DMA_CS_RESET); (*self.rx_dma).write_cs(Self::DMA_CS_RESET); // disable the channels I used - let mask = !((1 << self.tx_channel_number) | (1 << self.rx_channel_number)); - *self.dma_enable_register_ptr &= mask; + let mask = !((1 << Self::TX_CHANNEL_NUMBER) | (1 << Self::RX_CHANNEL_NUMBER)); + write_volatile(self.dma_enable_register_ptr, read_volatile(self.dma_enable_register_ptr) & mask); } - self.dma_const_data_memory.release(&self.mbox); - self.dma_data_memory.release(&self.mbox); + self.dma_constant_memory.release(&self.mbox); + self.dma_dynamic_memory.release(&self.mbox); self.rx_control_block_memory.release(&self.mbox); self.source_buffer_memory.release(&self.mbox); self.tx_control_block_memory.release(&self.mbox); diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index 7eb1bf72..ff8de2bb 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -37,10 +37,12 @@ pub (super) const TARGET_SCREEN_WIDTH:usize = (SCREEN_WIDTH as f32 * SCALE) as u pub (super) const TARGET_SCREEN_HEIGHT:usize = (SCREEN_HEIGHT as f32 * SCALE) as usize; const FRAME_BUFFER_X_OFFSET:usize = (ILI9341_SCREEN_WIDTH - TARGET_SCREEN_WIDTH) / 2; +pub const SPI_BUFFER_SIZE:usize = TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * std::mem::size_of::(); + pub trait SpiController { fn new(dc_pin_number:u8)->Self; fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]); - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]); + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]); } struct Ili9341Contoller{ @@ -50,23 +52,25 @@ struct Ili9341Contoller{ } impl Ili9341Contoller{ - const CLEAN_BUFFER:[u8;ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * 2] = [0; ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * 2]; + const CLEAN_BUFFER:[u8;ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * std::mem::size_of::()] = [0; ILI9341_SCREEN_HEIGHT * ILI9341_SCREEN_WIDTH * std::mem::size_of::()]; + + pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8)->Self{ + + log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); - pub fn new()->Self{ let gpio = rppal::gpio::Gpio::new().unwrap(); - let mut reset_pin = gpio.get(14).unwrap().into_output(); - let mut led_pin = gpio.get(25).unwrap().into_output(); + let mut reset_pin = gpio.get(reset_pin_bcm).unwrap().into_output(); + let mut led_pin = gpio.get(led_pin_bcm).unwrap().into_output(); // toggling the reset pin to initalize the lcd - let wait_duration = std::time::Duration::from_millis(120); reset_pin.set_high(); - std::thread::sleep(wait_duration); + Self::sleep_ms(120); reset_pin.set_low(); - std::thread::sleep(wait_duration); + Self::sleep_ms(120); reset_pin.set_high(); - std::thread::sleep(wait_duration); + Self::sleep_ms(120); - let mut spi:SC = SpiController::new(15); + let mut spi:SC = SpiController::new(dc_pin_bcm); // This code snippets is ofcourse wrriten by me but took heavy insperation from fbcp-ili9341 (https://github.com/juj/fbcp-ili9341) // I used the ili9341 application notes and the fbcp-ili9341 implementation in order to write it all down @@ -122,7 +126,7 @@ impl Ili9341Contoller{ // turn backlight on led_pin.set_high(); - log::info!("Initalizing with screen size width: {}, hight: {}", TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT); + log::info!("finish ili9341 device init"); return Ili9341Contoller { spi, led_pin, reset_pin}; } @@ -170,11 +174,11 @@ pub struct Ili9341GfxDevice{ } impl Ili9341GfxDevice{ - pub fn new()->Self{ - #[cfg(not(feature = "compact-pixel"))] + pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8)->Self{ + #[cfg(not(feature = "u16pixel"))] std::compile_error("ili9341 gfx device must have Pixel type = u16"); - let ili9341_controller = Ili9341Contoller::new(); + let ili9341_controller = Ili9341Contoller::new(reset_pin_bcm, dc_pin_bcm, led_pin_bcm); Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} } } diff --git a/gb/src/rpi_gpio/raw_spi.rs b/gb/src/rpi_gpio/mmio_spi.rs similarity index 60% rename from gb/src/rpi_gpio/raw_spi.rs rename to gb/src/rpi_gpio/mmio_spi.rs index fb0d475f..358a6a45 100644 --- a/gb/src/rpi_gpio/raw_spi.rs +++ b/gb/src/rpi_gpio/mmio_spi.rs @@ -1,9 +1,9 @@ use bcm_host::BcmHost; use rppal::gpio::{OutputPin, IoPin}; -use crate::rpi_gpio::{dma::DmaTransferer}; +use crate::rpi_gpio::dma::DmaSpiTransferer; -use super::{ili9341_controller::{Ili9341Commands, SpiController, TARGET_SCREEN_HEIGHT, TARGET_SCREEN_WIDTH}, decl_write_volatile_field, decl_read_volatile_field}; +use super::{ili9341_controller::{Ili9341Commands, SpiController, SPI_BUFFER_SIZE}, decl_write_volatile_field, decl_read_volatile_field}; const BCM_SPI0_BASE_ADDRESS:usize = 0x20_4000; const SPI_CLOCK_DIVISOR:u32 = 4; // the smaller the faster (on my system below 4 there are currptions) @@ -25,10 +25,10 @@ impl SpiRegistersAccess{ decl_write_volatile_field!(write_dlen, data_length); } -pub struct RawSpi{ +pub struct MmioSpi{ spi_registers: *mut SpiRegistersAccess, dc_pin:OutputPin, - dma_transferer:DmaTransferer<{Self::DMA_SPI_CHUNK_SIZE}, {Self::DMA_SPI_NUM_CHUNKS}>, + dma_transferer:DmaSpiTransferer, last_transfer_was_dma:bool, // holding those pins in order to make sure they are configured correctly @@ -42,7 +42,11 @@ pub struct RawSpi{ _bcm:BcmHost, } -impl RawSpi{ +impl MmioSpi{ + const SPI0_CE0_N_BCM_PIN:u8 = 8; + const SPI0_MOSI_BCM_PIN:u8 = 10; + const SPI0_SCLK_BCM_PIN:u8 = 11; + const SPI_CS_RXF:u32 = 1 << 20; const SPI_CS_RXR:u32 = 1 << 19; const SPI_CS_TXD:u32 = 1 << 18; @@ -64,10 +68,10 @@ impl RawSpi{ (*spi_registers).write_clk(SPI_CLOCK_DIVISOR); } - log::info!("finish ili9341 device init"); - let dma_transferer = DmaTransferer::new(&bcm_host, 7, 1 ); - RawSpi { + let dma_transferer = DmaSpiTransferer::new(&bcm_host, Self::SPI_CS_DMAEN); + + MmioSpi { _bcm:bcm_host, spi_registers, dc_pin, _spi_pins: spi_pins, _spi_cs0: spi_cs0, last_transfer_was_dma: false, dma_transferer } @@ -85,15 +89,16 @@ impl RawSpi{ fn prepare_for_transfer(&mut self) { if self.last_transfer_was_dma{ self.dma_transferer.end_dma_transfer(); - unsafe{Self::setup_poll_fast_transfer(&mut *self.spi_registers)}; + Self::setup_poll_fast_transfer(self.spi_registers); } } - fn setup_poll_fast_transfer(spi_registers:&mut SpiRegistersAccess){ + fn setup_poll_fast_transfer(spi_registers:*mut SpiRegistersAccess){ unsafe{ - spi_registers.write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + (*spi_registers).write_cs(Self::SPI_CS_TA | Self::SPI_CS_CLEAR); + // poll mode speed up according to this forum post - https://forums.raspberrypi.com/viewtopic.php?f=44&t=181154 - spi_registers.write_dlen(2); + (*spi_registers).write_dlen(2); } } @@ -120,15 +125,7 @@ impl RawSpi{ } } - const MAX_DMA_SPI_TRANSFER:usize = 0xFFE0; // must be smaller than max u16 and better be alligned for 32 bytes - const DMA_SPI_TRANSFER_SIZE:usize = TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * std::mem::size_of::(); - const DMA_SPI_NUM_CHUNKS:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::MAX_DMA_SPI_TRANSFER) + ((Self::DMA_SPI_TRANSFER_SIZE % Self::MAX_DMA_SPI_TRANSFER) != 0) as usize; - const DMA_SPI_CHUNK_SIZE:usize = (Self::DMA_SPI_TRANSFER_SIZE / Self::DMA_SPI_NUM_CHUNKS) + 4; - const DMA_TI_PERMAP_SPI_TX:u8 = 6; - const DMA_TI_PERMAP_SPI_RX:u8 = 7; - const DMA_SPI_FIFO_PHYS_ADDRESS:u32 = 0x7E20_4004; - - fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ + fn write_dma(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]){ self.prepare_for_transfer(); self.dc_pin.set_low(); @@ -140,47 +137,28 @@ impl RawSpi{ // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use - fn write_dma_raw(&mut self, data:&[u8;SIZE]){ - unsafe{ - (*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR); - let data_len = Self::DMA_SPI_CHUNK_SIZE - 4; // Removing the first 4 bytes from this length param - let header = [Self::SPI_CS_TA as u8, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; - - let chunks = data.chunks_exact(Self::DMA_SPI_CHUNK_SIZE - 4); - let mut array:[u8;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS] = [0;Self::DMA_SPI_CHUNK_SIZE * Self::DMA_SPI_NUM_CHUNKS]; - let mut i = 0; - for chunk in chunks{ - std::ptr::copy_nonoverlapping(header.as_ptr(), array.as_mut_ptr().add(i * Self::DMA_SPI_CHUNK_SIZE), 4); - std::ptr::copy_nonoverlapping(chunk.as_ptr(), array.as_mut_ptr().add(4 + (i * Self::DMA_SPI_CHUNK_SIZE)), Self::DMA_SPI_CHUNK_SIZE - 4); - i += 1; - } - - self.dma_transferer.start_dma_transfer(&array, - Self::DMA_TI_PERMAP_SPI_TX, - Self::DMA_SPI_FIFO_PHYS_ADDRESS, - Self::DMA_TI_PERMAP_SPI_RX, - Self::DMA_SPI_FIFO_PHYS_ADDRESS - ); - } + fn write_dma_raw(&mut self, data:&[u8;SPI_BUFFER_SIZE]){ + unsafe{(*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR)}; + self.dma_transferer.start_dma_transfer(data, Self::SPI_CS_TA as u8); } } -impl SpiController for RawSpi{ +impl SpiController for MmioSpi{ fn new(dc_pin_number:u8)->Self { let gpio = rppal::gpio::Gpio::new().unwrap(); - let spi0_ceo_n = gpio.get(8).unwrap().into_output(); - let spi0_mosi = gpio.get(10).unwrap().into_io(rppal::gpio::Mode::Alt0); - let spi0_sclk = gpio.get(11).unwrap().into_io(rppal::gpio::Mode::Alt0); + let spi0_ceo_n = gpio.get(Self::SPI0_CE0_N_BCM_PIN).unwrap().into_output(); + let spi0_mosi = gpio.get(Self::SPI0_MOSI_BCM_PIN).unwrap().into_io(rppal::gpio::Mode::Alt0); + let spi0_sclk = gpio.get(Self::SPI0_SCLK_BCM_PIN).unwrap().into_io(rppal::gpio::Mode::Alt0); let dc_pin = gpio.get(dc_pin_number).unwrap().into_output(); - RawSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n) + MmioSpi::new(dc_pin, [spi0_mosi, spi0_sclk], spi0_ceo_n) } fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]) { self.write(command, data); } - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]) { + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { self.write_dma(command, data); } } \ No newline at end of file diff --git a/gb/src/rpi_gpio/mod.rs b/gb/src/rpi_gpio/mod.rs index e68b824a..3e258458 100644 --- a/gb/src/rpi_gpio/mod.rs +++ b/gb/src/rpi_gpio/mod.rs @@ -1,14 +1,13 @@ pub mod ili9341_controller; pub mod gpio_joypad_provider; -cfg_if::cfg_if!{if #[cfg(feature = "raw-spi")]{ +cfg_if::cfg_if!{if #[cfg(feature = "mmio")]{ mod dma; - mod raw_spi; - pub type SpiType = raw_spi::RawSpi; + mod mmio_spi; + pub type SpiType = mmio_spi::MmioSpi; }else{ -#[cfg(not(feature = "raw-spi"))] - mod spi; - pub type SpiType = spi::RppalSpi; + mod rppal_spi; + pub type SpiType = rppal_spi::RppalSpi; }} diff --git a/gb/src/rpi_gpio/spi.rs b/gb/src/rpi_gpio/rppal_spi.rs similarity index 75% rename from gb/src/rpi_gpio/spi.rs rename to gb/src/rpi_gpio/rppal_spi.rs index bf03ed57..ca1e0e05 100644 --- a/gb/src/rpi_gpio/spi.rs +++ b/gb/src/rpi_gpio/rppal_spi.rs @@ -1,19 +1,23 @@ use rppal::gpio::{OutputPin, IoPin}; -use super::{ili9341_controller::{Ili9341Commands, TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT, SpiController}}; +use super::{ili9341_controller::{Ili9341Commands, SPI_BUFFER_SIZE, SpiController}}; +// This will work only if spi is enabled at /boot/config.txt (dtparam=spi=on) pub struct RppalSpi{ spi_device:rppal::spi::Spi, dc_pin:OutputPin } impl RppalSpi{ + const CLOCK_SPEED:u32 = 75_000_000; // chose based on trial and error + const SPI_TRANSFER_MAX_SIZE:usize = 4096; + fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]){ let command = command as u8; self.dc_pin.set_low(); self.spi_device.write(&[command]).expect("Error while writing to the spi device"); self.dc_pin.set_high(); - let chunks = data.chunks(4096); + let chunks = data.chunks(Self::SPI_TRANSFER_MAX_SIZE); for chunk in chunks{ self.spi_device.write(&chunk).expect(std::format!("Error wrting data to spi device for command: {:#X}",command).as_str() ); } @@ -25,7 +29,7 @@ impl SpiController for RppalSpi{ let spi_device = rppal::spi::Spi::new( rppal::spi::Bus::Spi0, rppal::spi::SlaveSelect::Ss0/*pin 24*/, - 75_000_000/*In order to be able to achieve higher fps*/, + Self::CLOCK_SPEED, rppal::spi::Mode::Mode0 ).expect("Error creating rppal spi device"); @@ -37,7 +41,7 @@ impl SpiController for RppalSpi{ self.write(command, data); } - fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2]) { + fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { self.write(command, data); } } \ No newline at end of file diff --git a/gb/src/sdl/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs index 48fb095a..5e3f1bf3 100644 --- a/gb/src/sdl/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -15,7 +15,7 @@ pub struct SdlGfxDevice{ impl SdlGfxDevice{ pub fn new(window_name:&str, screen_scale: usize, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{ - #[cfg(feature = "compact-pixel")] + #[cfg(feature = "u16pixel")] std::compile_error("Sdl gfx device must have Pixel type = u32"); let cs_wnd_name = CString::new(window_name).unwrap(); diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index fd4cfa9e..123debcb 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" log = "0.4" [features] -compact-pixel = [] +u16pixel = [] [dev-dependencies] criterion = "0.3" diff --git a/lib_gb/src/ppu/color.rs b/lib_gb/src/ppu/color.rs index af23223f..44472dc2 100644 --- a/lib_gb/src/ppu/color.rs +++ b/lib_gb/src/ppu/color.rs @@ -38,11 +38,11 @@ impl PartialEq for Color{ impl From for Pixel{ fn from(color: Color) -> Self { - #[cfg(not(feature = "compact-pixel"))] + #[cfg(not(feature = "u16pixel"))] { ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) } - #[cfg(feature = "compact-pixel")] + #[cfg(feature = "u16pixel")] { (((color.r >> 3) as u16) << 11) | (((color.g >> 2) as u16) << 5) | ((color.b >> 3) as u16) } diff --git a/lib_gb/src/ppu/gfx_device.rs b/lib_gb/src/ppu/gfx_device.rs index 5ffa1646..8eaae28d 100644 --- a/lib_gb/src/ppu/gfx_device.rs +++ b/lib_gb/src/ppu/gfx_device.rs @@ -1,8 +1,8 @@ use super::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; -#[cfg(not(feature = "compact-pixel"))] +#[cfg(not(feature = "u16pixel"))] pub type Pixel = u32; -#[cfg(feature = "compact-pixel")] +#[cfg(feature = "u16pixel")] pub type Pixel = u16; From a2aa8b7b5eca63559ce3c7f31e1d79b73f828324 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 10 Jun 2022 11:40:04 +0300 Subject: [PATCH 40/70] Fix compilation errors Add platform conditional compilation exclude bcm_host from the test command --- .github/workflows/rust.yml | 2 +- bcm_host/build.rs | 1 + bcm_host/src/bcm.rs | 74 ++++++++++++++++++++++++++++++++++++ bcm_host/src/lib.rs | 77 ++------------------------------------ image_inter/src/scale.c | 6 +-- 5 files changed, 83 insertions(+), 77 deletions(-) create mode 100644 bcm_host/src/bcm.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9fd17576..4ed899d6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,4 +19,4 @@ jobs: - name: Build run: cargo build --verbose - name: Run tests - run: cargo test --verbose + run: cargo test --verbose --workspace --exclude bcm_host diff --git a/bcm_host/build.rs b/bcm_host/build.rs index f3ccafb4..b71829d5 100644 --- a/bcm_host/build.rs +++ b/bcm_host/build.rs @@ -1,3 +1,4 @@ fn main() { + #[cfg(target_os = "linux")] println!("cargo:rustc-link-lib=bcm_host"); } \ No newline at end of file diff --git a/bcm_host/src/bcm.rs b/bcm_host/src/bcm.rs new file mode 100644 index 00000000..4d54a4bb --- /dev/null +++ b/bcm_host/src/bcm.rs @@ -0,0 +1,74 @@ + +use libc::{c_uint, c_void}; + +// linking to - https://github.com/raspberrypi/firmware/blob/master/opt/vc/include/bcm_host.h +extern "C"{ + // There is no need to link to the init and deinit functions. it looks like they are needed only for the gpu dispnmx stuff + pub fn bcm_host_get_peripheral_address()->c_uint; // returns the bcm bus address. should be 0xFE00_0000 on RPI4 + pub fn bcm_host_get_peripheral_size()->c_uint; // returns the bcm bus size. should be 0x180_0000 on RPI4 +} + +// This struct is here to managed the lifetime of the bcm2835 ptr and the memory fd +pub struct BcmHost{ + ptr:*mut c_void, + mem_fd: libc::c_int +} + +impl BcmHost { + pub fn new()->Self{ + let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; + + if mem_fd < 0{ + libc_abort("bad file descriptor"); + } + + let bus_peripherals_address = unsafe{bcm_host_get_peripheral_address()}; + let bus_peripherals_size = unsafe{bcm_host_get_peripheral_size()}; + + log::info!("BCM host peripherals address: {:#X}, size: {:#X}", bus_peripherals_address, bus_peripherals_size); + + let bcm2835 = unsafe{libc::mmap( + std::ptr::null_mut(), + bus_peripherals_size as usize, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_SHARED, + mem_fd, + bus_peripherals_address as libc::off_t + )}; + + if bcm2835 == libc::MAP_FAILED{ + libc_abort("FATAL: mapping /dev/mem failed!"); + } + + BcmHost { ptr: bcm2835, mem_fd } + } + + pub fn get_ptr(&self, offset:usize)->*mut c_void{ + unsafe{self.ptr.add(offset)} + } + + pub fn get_fd(&self)->libc::c_int{ + self.mem_fd + } +} + +impl Drop for BcmHost{ + fn drop(&mut self) { + unsafe{ + let bus_peripherals_size = bcm_host_get_peripheral_size(); + let result = libc::munmap(self.ptr, bus_peripherals_size as usize); + if result != 0{ + libc_abort("Error while unmapping the mmio memory"); + } + + let result = libc::close(self.mem_fd); + if result != 0{ + libc_abort("Error while closing the mem_fd"); + } + } + } +} + +fn libc_abort(message:&str){ + std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); +} \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs index 9e737213..0a6d7d02 100644 --- a/bcm_host/src/lib.rs +++ b/bcm_host/src/lib.rs @@ -1,73 +1,4 @@ -use libc::{c_uint, c_void}; - -// linking to - https://github.com/raspberrypi/firmware/blob/master/opt/vc/include/bcm_host.h -extern "C"{ - // There is no need to link to the init and deinit functions. it looks like they are needed only for the gpu dispnmx stuff - pub fn bcm_host_get_peripheral_address()->c_uint; // returns the bcm bus address. should be 0xFE00_0000 on RPI4 - pub fn bcm_host_get_peripheral_size()->c_uint; // returns the bcm bus size. should be 0x180_0000 on RPI4 -} - -// This struct is here to managed the lifetime of the bcm2835 ptr and the memory fd -pub struct BcmHost{ - ptr:*mut c_void, - mem_fd: libc::c_int -} - -impl BcmHost { - pub fn new()->Self{ - let mem_fd = unsafe{libc::open(std::ffi::CStr::from_bytes_with_nul(b"/dev/mem\0").unwrap().as_ptr(), libc::O_RDWR | libc::O_SYNC)}; - - if mem_fd < 0{ - libc_abort("bad file descriptor"); - } - - let bus_peripherals_address = unsafe{bcm_host_get_peripheral_address()}; - let bus_peripherals_size = unsafe{bcm_host_get_peripheral_size()}; - - log::info!("BCM host peripherals address: {:#X}, size: {:#X}", bus_peripherals_address, bus_peripherals_size); - - let bcm2835 = unsafe{libc::mmap( - std::ptr::null_mut(), - bus_peripherals_size as usize, - libc::PROT_READ | libc::PROT_WRITE, - libc::MAP_SHARED, - mem_fd, - bus_peripherals_address as libc::off_t - )}; - - if bcm2835 == libc::MAP_FAILED{ - libc_abort("FATAL: mapping /dev/mem failed!"); - } - - BcmHost { ptr: bcm2835, mem_fd } - } - - pub fn get_ptr(&self, offset:usize)->*mut c_void{ - unsafe{self.ptr.add(offset)} - } - - pub fn get_fd(&self)->libc::c_int{ - self.mem_fd - } -} - -impl Drop for BcmHost{ - fn drop(&mut self) { - unsafe{ - let bus_peripherals_size = bcm_host_get_peripheral_size(); - let result = libc::munmap(self.ptr, bus_peripherals_size as usize); - if result != 0{ - libc_abort("Error while unmapping the mmio memory"); - } - - let result = libc::close(self.mem_fd); - if result != 0{ - libc_abort("Error while closing the mem_fd"); - } - } - } -} - -fn libc_abort(message:&str){ - std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); -} \ No newline at end of file +#[cfg(feature_os = "linux")] +pub mod bcm; +#[cfg(feature_os = "linux")] +pub use bcm; \ No newline at end of file diff --git a/image_inter/src/scale.c b/image_inter/src/scale.c index 54cdac67..1b1d50ff 100644 --- a/image_inter/src/scale.c +++ b/image_inter/src/scale.c @@ -1,8 +1,8 @@ #include extern void scale_buffer(const uint16_t* input_buffer, int input_buffer_width, int input_buffer_height, uint8_t* output_buffer, int output_buffer_width, int output_buffer_height){ - const float x_ratio = ((float)input_buffer_width - 1.0) / (float)output_buffer_width; - const float y_ratio = ((float)input_buffer_height - 1.0) / (float)output_buffer_height; + const float x_ratio = ((float)input_buffer_width - 1.0f) / (float)output_buffer_width; + const float y_ratio = ((float)input_buffer_height - 1.0f) / (float)output_buffer_height; int output_offset_counter = 0; for (int y = 0; y < output_buffer_height; y++){ @@ -20,7 +20,7 @@ extern void scale_buffer(const uint16_t* input_buffer, int input_buffer_width, i const uint16_t pixel_c = input_buffer[original_pixel_index + input_buffer_width]; const uint16_t pixel_d = input_buffer[original_pixel_index + input_buffer_width + 1]; - const float weights[4] = {(1.0-x_diff) * (1.0-y_diff), (x_diff)*(1.0-y_diff), y_diff * (1.0-x_diff), x_diff * y_diff}; + const float weights[4] = {(1.0f-x_diff) * (1.0f-y_diff), (x_diff)*(1.0f-y_diff), y_diff * (1.0f-x_diff), x_diff * y_diff}; const float blue = ((float)(pixel_a & 0x1F) * weights[0]) + ((float)(pixel_b & 0x1F) * weights[1]) + From 3872729d232944352232dfc1ce6212230a275cc1 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 11 Jun 2022 11:33:43 +0300 Subject: [PATCH 41/70] Fix the conditional compilation of the bcm_host --- bcm_host/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs index 0a6d7d02..7bd9c669 100644 --- a/bcm_host/src/lib.rs +++ b/bcm_host/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(feature_os = "linux")] +#[cfg(target_os = "linux")] pub mod bcm; -#[cfg(feature_os = "linux")] -pub use bcm; \ No newline at end of file +#[cfg(target_os = "linux")] +pub use bcm::BcmHost; \ No newline at end of file From 0b5fdf78e5c3f9ea1a3d777909e47176a54406fe Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Jun 2022 15:22:09 +0300 Subject: [PATCH 42/70] Add the new features to the README.md --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f906f2f..bde3dfcd 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,14 @@ magenboy [path_to_rom] [other_optional_flags] cargo build --release --features [optional_features] ``` #### Optional features: -* static-sdl - will link statically to sdl2. +* `static-sdl` - will link statically to sdl2. On by default (to turn off pass `--no-default-features`) -* sdl-resample - Use the audio resampler from sdl2 library and a manual one I wrote -* push-audio - Use a push methododlogy instead of pull for the delivery of the sound samples to sdl2 -* static-scale - Will use a fixed scale values for the renderer instead of addapting to the screen size +* `sdl-resample` - Use the audio resampler from sdl2 library and a manual one I wrote +* `push-audio` - Use a push methododlogy instead of pull for the delivery of the sound samples to sdl2 +* `static-scale` - Will use a fixed scale values for the renderer instead of addapting to the screen size +* `u16pixel` - pixels are represented by 16 bits and not 32 bits - neccessary for interfacing the ili9341 spi lcd +* `rpi` - Input is from the RPI GPIO pins and output is to an ili9341 spi lcd connected to the RPI GPIO pins, activates the `u16pixel` feature. +* `mmio` - Will interface the spi lcd screen using the Memory Mapped IO interface of the RPI for better performance (uses the DMA peripherals as well, activates the `rpi` feature. ## GameBoy From 4e985504656074e0d69db3e380baf7aaa6443500 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Jun 2022 15:37:58 +0300 Subject: [PATCH 43/70] Add fps limiter to the ili9341 gfx device --- gb/src/main.rs | 15 +++++++++------ gb/src/rpi_gpio/ili9341_controller.rs | 22 ++++++++++++++++++---- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index f5a4dd31..c3e71965 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -168,12 +168,15 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - #[cfg(feature = "rpi")] - let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(14, 15, 25); - - #[cfg(not(feature = "rpi"))] - let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, - check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); + cfg_if::cfg_if!{ if #[cfg(feature = "rpi")]{ + let reset_pin = 14; + let dc_pin = 15; + let led_pin = 25; + let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(reset_pin, dc_pin, led_pin, TURBO_MUL, 0); + }else{ + let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); + }} let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index ff8de2bb..2cce635f 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -168,26 +168,40 @@ impl Drop for Ili9341Contoller{ pub struct Ili9341GfxDevice{ ili9341_controller:Ili9341Contoller, + turbo_mul:u8, + turbo_frame_counter:u8, + + frame_limiter:u32, frames_counter: u32, time_counter:std::time::Duration, last_time: std::time::Instant, } impl Ili9341GfxDevice{ - pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8)->Self{ + pub fn new(reset_pin_bcm:u8, dc_pin_bcm:u8, led_pin_bcm:u8, turbo_mul:u8, frame_limiter:u32)->Self{ #[cfg(not(feature = "u16pixel"))] std::compile_error("ili9341 gfx device must have Pixel type = u16"); let ili9341_controller = Ili9341Contoller::new(reset_pin_bcm, dc_pin_bcm, led_pin_bcm); - Ili9341GfxDevice {ili9341_controller,frames_counter:0, time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now()} + + Ili9341GfxDevice { + ili9341_controller,frames_counter:0, + time_counter: std::time::Duration::ZERO, last_time:std::time::Instant::now(), + turbo_mul, turbo_frame_counter:0, frame_limiter + } } } impl GfxDevice for Ili9341GfxDevice{ fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { - // if self.frames_counter & 1 == 0{ + self.turbo_frame_counter = (self.turbo_frame_counter + 1) % self.turbo_mul; + if self.turbo_frame_counter != 0{ + return; + } + + if self.frames_counter & self.frame_limiter == 0{ self.ili9341_controller.write_frame_buffer(&buffer); - // } + } // measure fps self.frames_counter += 1; From a55729a0c59d2ec3bf4700903197b5bd3ec3ce72 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Jun 2022 16:14:25 +0300 Subject: [PATCH 44/70] Remove exit by escape --- gb/src/main.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index c3e71965..770dfcfd 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -199,11 +199,6 @@ fn main() { if event.type_ == SDL_EventType::SDL_QUIT as u32{ break; } - else if event.type_ == SDL_EventType::SDL_KEYDOWN as u32{ - if event.key.keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE{ - break; - } - } } let buffer = r.recv().unwrap(); From 1dfa46665170f99980d32edf3de5b66cb4986324 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Jun 2022 16:20:04 +0300 Subject: [PATCH 45/70] Use cfg_if in the bcm_host lib --- Cargo.lock | 1 + bcm_host/Cargo.toml | 3 ++- bcm_host/src/lib.rs | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48738ab2..75d946cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" name = "bcm_host" version = "1.0.0" dependencies = [ + "cfg-if", "libc", "log", ] diff --git a/bcm_host/Cargo.toml b/bcm_host/Cargo.toml index b9ae8bfd..b6d3e615 100644 --- a/bcm_host/Cargo.toml +++ b/bcm_host/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] libc = "0.2" -log = "0.4" \ No newline at end of file +log = "0.4" +cfg-if = "1.0" \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs index 7bd9c669..80944ad5 100644 --- a/bcm_host/src/lib.rs +++ b/bcm_host/src/lib.rs @@ -1,4 +1,4 @@ -#[cfg(target_os = "linux")] -pub mod bcm; -#[cfg(target_os = "linux")] -pub use bcm::BcmHost; \ No newline at end of file +cfg_if::cfg_if!{ if #[cfg(target_os = "linux")]{ + pub mod bcm; + pub use bcm::BcmHost; +}} \ No newline at end of file From f9ebec2bafdf9b500898f0b8ce4c80b5c2943b90 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 17 Jun 2022 16:51:20 +0300 Subject: [PATCH 46/70] Improve joypad_handler performace improve the flip_bit logic and inlined it --- lib_gb/benches/lib_gb_bench.rs | 25 +++++++++++++++++--- lib_gb/src/utils/bit_masks.rs | 43 +++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/lib_gb/benches/lib_gb_bench.rs b/lib_gb/benches/lib_gb_bench.rs index 0fa41513..d9711bac 100644 --- a/lib_gb/benches/lib_gb_bench.rs +++ b/lib_gb/benches/lib_gb_bench.rs @@ -1,9 +1,9 @@ use criterion::*; -use lib_gb::apu::{ +use lib_gb::{apu::{ audio_device::*, channel::Channel, gb_apu::*, sound_terminal::SoundTerminal, square_sample_producer::SquareSampleProducer -}; +}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_handler::JoypadHandler}}; pub fn criterion_bench(c: &mut Criterion){ struct StubApu; @@ -47,5 +47,24 @@ pub fn apu_sound_terminal(c:&mut Criterion){ })); } -criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal); +pub fn keypad_joypad_handler(c:&mut Criterion){ + struct StubJoypadProvider{ + set:bool + } + impl JoypadProvider for StubJoypadProvider{ + fn provide(&mut self, joypad:&mut Joypad) { + joypad.buttons.fill(self.set); + self.set = !self.set; + } + } + + // let mut joypad = Joypad::default(); + let mut joypad_handler = JoypadHandler::new(StubJoypadProvider{set:true}); + + c.bench_function("Joypad handler", |b|b.iter(||{ + joypad_handler.poll_joypad_state(); + })); +} + +criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal, keypad_joypad_handler); criterion_main!(benches); \ No newline at end of file diff --git a/lib_gb/src/utils/bit_masks.rs b/lib_gb/src/utils/bit_masks.rs index 9afab06f..50f3d34b 100644 --- a/lib_gb/src/utils/bit_masks.rs +++ b/lib_gb/src/utils/bit_masks.rs @@ -9,15 +9,12 @@ pub const BIT_7_MASK:u8 = 1 << 7; pub const BIT_9_MASK:u16 = 1 << 9; +#[inline] pub fn flip_bit_u8(value:&mut u8, bit_number:u8, set:bool){ - let mask = 1 << bit_number; - if set{ - *value |= mask; - } - else{ - let inverse_mask = !mask; - *value &= inverse_mask; - } + let mask = !(1 << bit_number); + let bit_value = (set as u8) << bit_number; + *value |= bit_value; // setting the bit if set or do nothing if unset + *value &= mask | bit_value; // masking value with the mask and the bit (if set) } pub fn flip_bit_u16(value:&mut u16, bit_number:u8, set:bool){ @@ -30,3 +27,33 @@ pub fn flip_bit_u16(value:&mut u16, bit_number:u8, set:bool){ *value &= inverse_mask; } } + + +#[cfg(test)] +mod tests{ + use super::flip_bit_u8; + + #[test] + fn test_flip_bit_u8_set(){ + let mut values = [0, 0x01, 0x02, 0x03, 0x14]; + let expected = [0x10, 0x11, 0x12, 0x13, 0x14]; + + for v in &mut values{ + flip_bit_u8(v, 4, true); + } + + assert_eq!(values, expected); + } + + #[test] + fn test_flip_bit_u8_unset(){ + let mut values = [0x10, 0x11, 0x12, 0x13, 0x04]; + let expected = [0, 0x01, 0x02, 0x03, 0x04]; + + for v in &mut values{ + flip_bit_u8(v, 4, false); + } + + assert_eq!(values, expected); + } +} \ No newline at end of file From b319f0b81dbec2c9682032c51dcad043dc818835 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 17 Jun 2022 16:52:29 +0300 Subject: [PATCH 47/70] Fix interrupts priority the interrupt priority was wrong and was the opposite priority --- lib_gb/src/mmu/interrupts_handler.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/mmu/interrupts_handler.rs b/lib_gb/src/mmu/interrupts_handler.rs index f382ff91..28f4b873 100644 --- a/lib_gb/src/mmu/interrupts_handler.rs +++ b/lib_gb/src/mmu/interrupts_handler.rs @@ -35,20 +35,22 @@ impl InterruptsHandler{ let mut interrupt_request = InterruptRequest::None; if master_interrupt_enable && self.ei_triggered{ + // The order is the interrupt priority of the interrupts + if self.interrupt_flag & BIT_0_MASK != 0 && self.interrupt_enable_flag & BIT_0_MASK != 0{ interrupt_request = self.prepare_for_interrupt( BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS); } // Checking those STAT register bits for the STAT interrupts requests - if self.interrupt_flag & BIT_1_MASK != 0 && self.interrupt_enable_flag & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ + else if self.interrupt_flag & BIT_1_MASK != 0 && self.interrupt_enable_flag & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ interrupt_request = self.prepare_for_interrupt(BIT_1_MASK, LCD_STAT_INTERRUPT_ADDERESS); } - if self.interrupt_flag & BIT_2_MASK != 0 && self.interrupt_enable_flag & BIT_2_MASK != 0{ + else if self.interrupt_flag & BIT_2_MASK != 0 && self.interrupt_enable_flag & BIT_2_MASK != 0{ interrupt_request = self.prepare_for_interrupt(BIT_2_MASK, TIMER_INTERRUPT_ADDERESS); } - if self.interrupt_flag & BIT_3_MASK != 0 && self.interrupt_enable_flag & BIT_3_MASK != 0{ + else if self.interrupt_flag & BIT_3_MASK != 0 && self.interrupt_enable_flag & BIT_3_MASK != 0{ interrupt_request = self.prepare_for_interrupt(BIT_3_MASK, SRIAL_INTERRUPT_ADDERESS); } - if self.interrupt_flag & BIT_4_MASK != 0 && self.interrupt_enable_flag & BIT_4_MASK != 0{ + else if self.interrupt_flag & BIT_4_MASK != 0 && self.interrupt_enable_flag & BIT_4_MASK != 0{ interrupt_request = self.prepare_for_interrupt(BIT_4_MASK, JOYPAD_INTERRUPT_ADDERESS); } } From 54691c0e73903ba32f65d9d9630ceaba76ce13c7 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 17 Jun 2022 17:19:00 +0300 Subject: [PATCH 48/70] Improve performance of the interrupt handler no need for a for, just checking once for all the interrupt in the unhalt section --- lib_gb/benches/lib_gb_bench.rs | 43 ++++++++++++++++++++++++++-- lib_gb/src/mmu/interrupts_handler.rs | 16 ++++------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/lib_gb/benches/lib_gb_bench.rs b/lib_gb/benches/lib_gb_bench.rs index d9711bac..6cd3e4c1 100644 --- a/lib_gb/benches/lib_gb_bench.rs +++ b/lib_gb/benches/lib_gb_bench.rs @@ -3,7 +3,7 @@ use lib_gb::{apu::{ audio_device::*, channel::Channel, gb_apu::*, sound_terminal::SoundTerminal, square_sample_producer::SquareSampleProducer -}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_handler::JoypadHandler}}; +}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_handler::JoypadHandler}, mmu::interrupts_handler::InterruptsHandler}; pub fn criterion_bench(c: &mut Criterion){ struct StubApu; @@ -58,7 +58,6 @@ pub fn keypad_joypad_handler(c:&mut Criterion){ } } - // let mut joypad = Joypad::default(); let mut joypad_handler = JoypadHandler::new(StubJoypadProvider{set:true}); c.bench_function("Joypad handler", |b|b.iter(||{ @@ -66,5 +65,43 @@ pub fn keypad_joypad_handler(c:&mut Criterion){ })); } -criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal, keypad_joypad_handler); +pub fn mmu_interrupt_handler_irq(c:&mut Criterion){ + let mut irh = InterruptsHandler::default(); + irh.interrupt_enable_flag = 1; + irh.interrupt_flag = 1; + + c.bench_function("Interrupt handler irq", |b|b.iter(||{ + irh.interrupt_enable_flag = irh.interrupt_enable_flag.rotate_left(1); + irh.interrupt_flag = irh.interrupt_flag.rotate_left(1); + + irh.handle_interrupts(true, 0); + })); +} + + +pub fn mmu_interrupt_handler_unhalt(c:&mut Criterion){ + let mut irh = InterruptsHandler::default(); + irh.interrupt_enable_flag = 1; + irh.interrupt_flag = 1; + + c.bench_function("Interrupt handler unhalt", |b|b.iter(||{ + irh.interrupt_enable_flag = irh.interrupt_enable_flag.rotate_left(1); + irh.interrupt_flag = irh.interrupt_flag.rotate_left(1); + + irh.handle_interrupts(false, 0); + })); +} + + +pub fn mmu_interrupt_handler_early(c:&mut Criterion){ + let mut irh = InterruptsHandler::default(); + irh.interrupt_enable_flag = 1; + irh.interrupt_flag = 0; + + c.bench_function("Interrupt handler early", |b|b.iter(||{ + std::mem::swap(&mut irh.interrupt_enable_flag, &mut irh.interrupt_flag); + irh.handle_interrupts(false, 0); + })); +} +criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal, keypad_joypad_handler, mmu_interrupt_handler_irq, mmu_interrupt_handler_unhalt, mmu_interrupt_handler_early); criterion_main!(benches); \ No newline at end of file diff --git a/lib_gb/src/mmu/interrupts_handler.rs b/lib_gb/src/mmu/interrupts_handler.rs index 28f4b873..8a3585a8 100644 --- a/lib_gb/src/mmu/interrupts_handler.rs +++ b/lib_gb/src/mmu/interrupts_handler.rs @@ -30,13 +30,12 @@ impl Default for InterruptsHandler{ impl InterruptsHandler{ pub fn handle_interrupts(&mut self, master_interrupt_enable:bool, stat_register:u8)->InterruptRequest{ - //there is a delay of one instruction cause there is this delay since EI opcode is called untill the interrupt could happen - let mut interrupt_request = InterruptRequest::None; - if master_interrupt_enable && self.ei_triggered{ + //there is a delay of one instruction cause there is this delay since EI opcode is called untill the interrupt could happen + if master_interrupt_enable && self.ei_triggered { // The order is the interrupt priority of the interrupts - + if self.interrupt_flag & BIT_0_MASK != 0 && self.interrupt_enable_flag & BIT_0_MASK != 0{ interrupt_request = self.prepare_for_interrupt( BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS); } @@ -55,16 +54,13 @@ impl InterruptsHandler{ } } else { - for i in 0..5{ - let mask = 1 << i; - if self.interrupt_flag & mask != 0 && self.interrupt_enable_flag & mask != 0{ - interrupt_request = InterruptRequest::Unhalt; - } + // if anding them is not zero there is at least one interrupt pending + if (self.interrupt_enable_flag & self.interrupt_flag) & 0b1_1111 != 0{ + interrupt_request = InterruptRequest::Unhalt; } } self.ei_triggered = master_interrupt_enable; - return interrupt_request; } From 61e94570b18e51cb2b1b9ad5542507d2cbb717eb Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 17 Jun 2022 18:02:15 +0300 Subject: [PATCH 49/70] Minor improve to the ppu performance --- lib_gb/benches/lib_gb_bench.rs | 34 ++++++++++++++++++++++++++++++++-- lib_gb/src/ppu/color.rs | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib_gb/benches/lib_gb_bench.rs b/lib_gb/benches/lib_gb_bench.rs index 6cd3e4c1..37538c7f 100644 --- a/lib_gb/benches/lib_gb_bench.rs +++ b/lib_gb/benches/lib_gb_bench.rs @@ -3,7 +3,7 @@ use lib_gb::{apu::{ audio_device::*, channel::Channel, gb_apu::*, sound_terminal::SoundTerminal, square_sample_producer::SquareSampleProducer -}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_handler::JoypadHandler}, mmu::interrupts_handler::InterruptsHandler}; +}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_handler::JoypadHandler}, mmu::interrupts_handler::InterruptsHandler, ppu::{gb_ppu::GbPpu, gfx_device::GfxDevice}}; pub fn criterion_bench(c: &mut Criterion){ struct StubApu; @@ -103,5 +103,35 @@ pub fn mmu_interrupt_handler_early(c:&mut Criterion){ irh.handle_interrupts(false, 0); })); } -criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal, keypad_joypad_handler, mmu_interrupt_handler_irq, mmu_interrupt_handler_unhalt, mmu_interrupt_handler_early); + +pub fn ppu_gb_ppu(c:&mut Criterion){ + struct StubGfxDevice; + impl GfxDevice for StubGfxDevice{ + fn swap_buffer(&mut self, _:&[lib_gb::ppu::gfx_device::Pixel; lib_gb::ppu::gb_ppu::SCREEN_HEIGHT * lib_gb::ppu::gb_ppu::SCREEN_WIDTH]) {} + } + + let mut ppu = GbPpu::new(StubGfxDevice{}); + ppu.lcd_control = 0xFF; + ppu.stat_register = 0b111_1000; + for i in 0..4{ + let y = 16 * (i + 1); + for j in 0..10{ + let x = 8 * (j + 1); + ppu.oam[(i*j)] = y as u8; + ppu.oam[(i*j) + 1] = x as u8; + ppu.oam[(i*j) + 2] = 0; + ppu.oam[(i*j) + 3] = 0b111_0000; + + } + } + + c.bench_function("Ppu", |b|b.iter(||{ + let mut if_register = 0; + for _ in 0..100{ + ppu.cycle(10, &mut if_register); + } + })); + +} +criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal, keypad_joypad_handler, mmu_interrupt_handler_irq, mmu_interrupt_handler_unhalt, mmu_interrupt_handler_early, ppu_gb_ppu); criterion_main!(benches); \ No newline at end of file diff --git a/lib_gb/src/ppu/color.rs b/lib_gb/src/ppu/color.rs index 44472dc2..6b84b448 100644 --- a/lib_gb/src/ppu/color.rs +++ b/lib_gb/src/ppu/color.rs @@ -37,6 +37,7 @@ impl PartialEq for Color{ } impl From for Pixel{ + #[inline] fn from(color: Color) -> Self { #[cfg(not(feature = "u16pixel"))] { From eefd05efbfc8cdeff1032cc68a7be5839bae36e6 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 25 Jun 2022 18:32:59 +0300 Subject: [PATCH 50/70] Remove the ppu lcd push buffer vector for perf --- lib_gb/src/ppu/gb_ppu.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index e6cf0fcd..00db0310 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -43,7 +43,6 @@ pub struct GbPpu{ t_cycles_passed:u16, screen_buffers: [[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], current_screen_buffer_index:usize, - push_lcd_buffer:Vec, screen_buffer_index:usize, pixel_x_pos:u8, scanline_started:bool, @@ -85,7 +84,6 @@ impl GbPpu{ trigger_stat_interrupt:false, bg_fetcher:BackgroundFetcher::new(), sprite_fetcher:SpriteFetcher::new(), - push_lcd_buffer:Vec::::new(), pixel_x_pos:0, scanline_started:false } @@ -122,16 +120,6 @@ impl GbPpu{ let cycles = std::cmp::min(fethcer_m_cycles_to_next_event, stat_m_cycles_to_next_event); - for i in 0..self.push_lcd_buffer.len(){ - self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = Color::into(self.push_lcd_buffer[i]); - self.screen_buffer_index += 1; - if self.screen_buffer_index == SCREEN_WIDTH * SCREEN_HEIGHT{ - self.swap_buffer(); - } - } - - self.push_lcd_buffer.clear(); - return Some(cycles); } @@ -340,8 +328,16 @@ impl GbPpu{ bg_pixel }; - self.push_lcd_buffer.push(pixel); + self.push_pixel(Color::into(pixel)); self.pixel_x_pos += 1; } } + + fn push_pixel(&mut self, pixel: Pixel) { + self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = pixel; + self.screen_buffer_index += 1; + if self.screen_buffer_index == SCREEN_WIDTH * SCREEN_HEIGHT{ + self.swap_buffer(); + } + } } \ No newline at end of file From 16253d81e9ff33e1e8b5fc3b1276f10c8393ea68 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 25 Jun 2022 19:02:50 +0300 Subject: [PATCH 51/70] Fix the gameboy programming manual link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bde3dfcd..3c8d1937 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ Curerently there is no Support (support is planned in the future) ## Resources - [The Pandocs](https://gbdev.io/pandocs/) - [gbops](https://izik1.github.io/gbops/index.html) -- [The GameBoy Programming Manual](http://index-of.es/Varios-2/Game%20Boy%20Programming%20Manual.pdf) +- [The GameBoy Programming Manual](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwi2muaT98j4AhWwhc4BHRaxAaEQFnoECAcQAQ&url=https%3A%2F%2Farchive.org%2Fdownload%2FGameBoyProgManVer1.1%2FGameBoyProgManVer1.1.pdf&usg=AOvVaw3LoEvXhZRBH7r68qdXIhiP) - [gbdev gameboy sound hardware](https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware) - [Hactix's awsome blog post](https://hacktix.github.io/GBEDG/) - [Nightshade's awsome blog post](https://nightshade256.github.io/2021/03/27/gb-sound-emulation.html) -- [The Ultimate GameBoy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI) \ No newline at end of file +- [The Ultimate GameBoy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI) From 5056670e49499c53798c239a574f50c487020c00 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 25 Jun 2022 19:16:00 +0300 Subject: [PATCH 52/70] Fix the mem error bug used a pointer to an stack alllocated struct for the sdl audio user data, the memory got currupted and the program would freeze on exit (x86) or crash randomaly (arm) now using heap allocated memory using box --- gb/src/sdl/sdl_pull_audio_device.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/gb/src/sdl/sdl_pull_audio_device.rs b/gb/src/sdl/sdl_pull_audio_device.rs index e0d8f2a8..630a9089 100644 --- a/gb/src/sdl/sdl_pull_audio_device.rs +++ b/gb/src/sdl/sdl_pull_audio_device.rs @@ -20,7 +20,7 @@ pub struct SdlPullAudioDevice{ buffer_index:usize, tarnsmiter: Sender, - userdata: UserData, + userdata_ptr: *mut UserData, device_id:SDL_AudioDeviceID, } @@ -29,11 +29,11 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ // cap of less than 2 hurts the fps let(s,r) = bounded(BUFFERS_NUMBER - 1); - let data = UserData{ + let data = Box::new(UserData{ current_buf:Option::None, current_buf_index:0, rx:r - }; + }); let mut device = SdlPullAudioDevice{ buffers:[[DEFAULT_SAPMPLE;BUFFER_SIZE];BUFFERS_NUMBER], @@ -41,7 +41,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ buffer_number_index:0, resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), tarnsmiter:s, - userdata:data, + userdata_ptr:Box::into_raw(data), device_id:0 }; @@ -54,7 +54,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ padding: 0, size: 0, callback: Option::Some(audio_callback), - userdata: (&mut device.userdata) as *mut UserData as *mut c_void + userdata: device.userdata_ptr as *mut c_void }; // Ignore device id @@ -89,6 +89,7 @@ impl Drop for SdlPullAudioDevice{ fn drop(&mut self) { unsafe{ SDL_CloseAudioDevice(self.device_id); + Box::from_raw(self.userdata_ptr); } } } From ccbc0141c43a9478ccf0f10382df80c69b621ae3 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 25 Jun 2022 21:10:58 +0300 Subject: [PATCH 53/70] Improve performance in the ppu state machine Improve the oam search by a lot and the pixel fetching by a bit --- lib_gb/src/ppu/gb_ppu.rs | 89 ++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 00db0310..8da4c68d 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -174,28 +174,36 @@ impl GbPpu{ while t_cycles_counter < t_cycles{ match self.state{ PpuState::OamSearch=>{ - let oam_index = self.t_cycles_passed / 2; - let oam_entry_address = (oam_index * OAM_ENTRY_SIZE) as usize; - let end_y = self.oam[oam_entry_address]; - let end_x = self.oam[oam_entry_address + 1]; - - if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < MAX_SPRITES_PER_LINE as u8{ - let tile_number = self.oam[oam_entry_address + 2]; - let attributes = self.oam[oam_entry_address + 3]; - self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); - self.sprite_fetcher.oam_entries_len += 1; + // first iteration + if self.t_cycles_passed == 0{ + for oam_index in 0..(OAM_MEMORY_SIZE as u16 / OAM_ENTRY_SIZE){ + let oam_entry_address = (oam_index * OAM_ENTRY_SIZE) as usize; + let end_y = self.oam[oam_entry_address]; + let end_x = self.oam[oam_entry_address + 1]; + + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height { + let tile_number = self.oam[oam_entry_address + 2]; + let attributes = self.oam[oam_entry_address + 3]; + self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.sprite_fetcher.oam_entries_len += 1; + if self.sprite_fetcher.oam_entries_len == MAX_SPRITES_PER_LINE as u8{ + break; + } + } + } + + self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize] + .sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); } - self.t_cycles_passed += 2; //half a m_cycle + let scope_t_cycles_passed = std::cmp::min(t_cycles as u16, OAM_SEARCH_T_CYCLES_LENGTH - self.t_cycles_passed); + self.t_cycles_passed += scope_t_cycles_passed; + t_cycles_counter += scope_t_cycles_passed as u32; if self.t_cycles_passed == OAM_SEARCH_T_CYCLES_LENGTH{ - let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); - slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); self.state = PpuState::PixelTransfer; self.scanline_started = false; } - - t_cycles_counter += 2; } PpuState::Hblank=>{ let t_cycles_to_add = std::cmp::min((t_cycles - t_cycles_counter) as u16, HBLANK_T_CYCLES_LENGTH - self.t_cycles_passed); @@ -245,36 +253,35 @@ impl GbPpu{ } PpuState::PixelTransfer=>{ - for _ in 0..2{ - if self.pixel_x_pos < SCREEN_WIDTH as u8{ - if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); - } - if self.sprite_fetcher.rendering{ - self.bg_fetcher.pause(); - } - else{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - self.try_push_to_lcd(); - if self.pixel_x_pos == SCREEN_WIDTH as u8{ - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.bg_fetcher.try_increment_window_counter(self.ly_register, self.window_pos.y); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - - // If im on the first iteration and finished the 160 pixels break; - // In this case the number of t_cycles should be eneven but it will break - // my code way too much for now so Im leaving this as it is... (maybe in the future) - break; + while t_cycles_counter < t_cycles && self.pixel_x_pos < SCREEN_WIDTH as u8{ + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); + } + if self.sprite_fetcher.rendering{ + self.bg_fetcher.pause(); + } + else{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + self.try_push_to_lcd(); + if self.pixel_x_pos == SCREEN_WIDTH as u8{ + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; } + self.bg_fetcher.try_increment_window_counter(self.ly_register, self.window_pos.y); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; } } + + self.t_cycles_passed += 1; + t_cycles_counter += 1; } - self.t_cycles_passed += 2; - t_cycles_counter += 2; } } } From c8b449c34fcfdc227572d66f1ff034fbe2790715 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 8 Jul 2022 13:16:44 +0300 Subject: [PATCH 54/70] The ppu mostly ticks my m_cycles and not t_cycles This improves ppu peroformance by about 10% --- lib_gb/src/ppu/gb_ppu.rs | 120 +++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 8da4c68d..a49ba95c 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -12,9 +12,9 @@ pub const BUFFERS_NUMBER:usize = 2; const OAM_ENTRY_SIZE:u16 = 4; const OAM_MEMORY_SIZE:usize = 0xA0; -const OAM_SEARCH_T_CYCLES_LENGTH: u16 = 80; -const HBLANK_T_CYCLES_LENGTH: u16 = 456; -const VBLANK_T_CYCLES_LENGTH: u16 = 4560; +const OAM_SEARCH_M_CYCLES_LENGTH: u16 = 80 / 4; +const HBLANK_M_CYCLES_LENGTH: u16 = 456 / 4; +const VBLANK_M_CYCLES_LENGTH: u16 = 4560 / 4; pub struct GbPpu{ pub vram: VRam, @@ -40,7 +40,7 @@ pub struct GbPpu{ pub coincidence_interrupt_request:bool, gfx_device: GFX, - t_cycles_passed:u16, + m_cycles_passed:u16, screen_buffers: [[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], current_screen_buffer_index:usize, screen_buffer_index:usize, @@ -79,7 +79,7 @@ impl GbPpu{ oam_search_interrupt_request:false, coincidence_interrupt_request:false, screen_buffer_index:0, - t_cycles_passed:0, + m_cycles_passed:0, stat_triggered:false, trigger_stat_interrupt:false, bg_fetcher:BackgroundFetcher::new(), @@ -90,7 +90,7 @@ impl GbPpu{ } pub fn turn_off(&mut self){ - self.t_cycles_passed = 0; + self.m_cycles_passed = 0; //This is an expensive operation! unsafe{std::ptr::write_bytes(self.screen_buffers[self.current_screen_buffer_index].as_mut_ptr(), 0xFF, SCREEN_HEIGHT * SCREEN_WIDTH)}; self.swap_buffer(); @@ -153,13 +153,13 @@ impl GbPpu{ self.trigger_stat_interrupt = false; let t_cycles_to_next_stat_change = if self.lyc_register < self.ly_register{ - ((self.ly_register - self.lyc_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32 + ((self.ly_register - self.lyc_register) as u32 * HBLANK_M_CYCLES_LENGTH as u32) - self.m_cycles_passed as u32 } else if self.lyc_register == self.ly_register{ - (HBLANK_T_CYCLES_LENGTH as u32 * 154 ) - self.t_cycles_passed as u32 + (HBLANK_M_CYCLES_LENGTH as u32 * 154 ) - self.m_cycles_passed as u32 } else{ - ((self.lyc_register - self.ly_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32 + ((self.lyc_register - self.ly_register) as u32 * HBLANK_M_CYCLES_LENGTH as u32) - self.m_cycles_passed as u32 }; // Divide by 4 to transform the t_cycles to m_cycles @@ -167,15 +167,14 @@ impl GbPpu{ } fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u16{ - let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; - let t_cycles = m_cycles * 4; - let mut t_cycles_counter = 0; + let mut m_cycles_counter = 0; - while t_cycles_counter < t_cycles{ + while m_cycles_counter < m_cycles{ match self.state{ PpuState::OamSearch=>{ // first iteration - if self.t_cycles_passed == 0{ + if self.m_cycles_passed == 0{ + let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; for oam_index in 0..(OAM_MEMORY_SIZE as u16 / OAM_ENTRY_SIZE){ let oam_entry_address = (oam_index * OAM_ENTRY_SIZE) as usize; let end_y = self.oam[oam_entry_address]; @@ -196,23 +195,23 @@ impl GbPpu{ .sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); } - let scope_t_cycles_passed = std::cmp::min(t_cycles as u16, OAM_SEARCH_T_CYCLES_LENGTH - self.t_cycles_passed); - self.t_cycles_passed += scope_t_cycles_passed; - t_cycles_counter += scope_t_cycles_passed as u32; + let scope_m_cycles_passed = std::cmp::min(m_cycles as u16, OAM_SEARCH_M_CYCLES_LENGTH - self.m_cycles_passed); + self.m_cycles_passed += scope_m_cycles_passed; + m_cycles_counter += scope_m_cycles_passed as u32; - if self.t_cycles_passed == OAM_SEARCH_T_CYCLES_LENGTH{ + if self.m_cycles_passed == OAM_SEARCH_M_CYCLES_LENGTH{ self.state = PpuState::PixelTransfer; self.scanline_started = false; } } PpuState::Hblank=>{ - let t_cycles_to_add = std::cmp::min((t_cycles - t_cycles_counter) as u16, HBLANK_T_CYCLES_LENGTH - self.t_cycles_passed); - self.t_cycles_passed += t_cycles_to_add; - t_cycles_counter += t_cycles_to_add as u32; + let m_cycles_to_add = std::cmp::min((m_cycles - m_cycles_counter) as u16, HBLANK_M_CYCLES_LENGTH - self.m_cycles_passed); + self.m_cycles_passed += m_cycles_to_add; + m_cycles_counter += m_cycles_to_add as u32; - if self.t_cycles_passed == HBLANK_T_CYCLES_LENGTH{ + if self.m_cycles_passed == HBLANK_M_CYCLES_LENGTH{ self.pixel_x_pos = 0; - self.t_cycles_passed = 0; + self.m_cycles_passed = 0; self.ly_register += 1; if self.ly_register == SCREEN_HEIGHT as u8{ self.state = PpuState::Vblank; @@ -233,68 +232,69 @@ impl GbPpu{ } } PpuState::Vblank=>{ - let t_cycles_to_add = std::cmp::min((t_cycles - t_cycles_counter) as u16, VBLANK_T_CYCLES_LENGTH - self.t_cycles_passed); - self.t_cycles_passed += t_cycles_to_add; - t_cycles_counter += t_cycles_to_add as u32; + let m_cycles_to_add = std::cmp::min((m_cycles - m_cycles_counter) as u16, VBLANK_M_CYCLES_LENGTH - self.m_cycles_passed); + self.m_cycles_passed += m_cycles_to_add; + m_cycles_counter += m_cycles_to_add as u32; - if self.t_cycles_passed == VBLANK_T_CYCLES_LENGTH{ + if self.m_cycles_passed == VBLANK_M_CYCLES_LENGTH{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ self.trigger_stat_interrupt = true; } self.pixel_x_pos = 0; - self.t_cycles_passed = 0; + self.m_cycles_passed = 0; self.ly_register = 0; } else{ //VBlank is technically 10 HBlank combined - self.ly_register = SCREEN_HEIGHT as u8 + (self.t_cycles_passed / HBLANK_T_CYCLES_LENGTH) as u8; + self.ly_register = SCREEN_HEIGHT as u8 + (self.m_cycles_passed / HBLANK_M_CYCLES_LENGTH) as u8; } } PpuState::PixelTransfer=>{ - while t_cycles_counter < t_cycles && self.pixel_x_pos < SCREEN_WIDTH as u8{ - if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); - } - if self.sprite_fetcher.rendering{ - self.bg_fetcher.pause(); - } - else{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - self.try_push_to_lcd(); - if self.pixel_x_pos == SCREEN_WIDTH as u8{ - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; + while m_cycles_counter < m_cycles && self.pixel_x_pos < SCREEN_WIDTH as u8{ + for _ in 0..4{ + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); + } + if self.sprite_fetcher.rendering{ + self.bg_fetcher.pause(); + } + else{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + self.try_push_to_lcd(); + if self.pixel_x_pos == SCREEN_WIDTH as u8{ + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; + } + self.bg_fetcher.try_increment_window_counter(self.ly_register, self.window_pos.y); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; } - self.bg_fetcher.try_increment_window_counter(self.ly_register, self.window_pos.y); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - - // If im on the first iteration and finished the 160 pixels break; - // In this case the number of t_cycles should be eneven but it will break - // my code way too much for now so Im leaving this as it is... (maybe in the future) - break; } } - self.t_cycles_passed += 1; - t_cycles_counter += 1; + self.m_cycles_passed += 1; + m_cycles_counter += 1; } } } } - let t_cycles = match self.state{ - PpuState::Vblank => ((self.t_cycles_passed / HBLANK_T_CYCLES_LENGTH)+1) * HBLANK_T_CYCLES_LENGTH, - PpuState::Hblank => HBLANK_T_CYCLES_LENGTH, - PpuState::OamSearch => OAM_SEARCH_T_CYCLES_LENGTH, - PpuState::PixelTransfer => self.t_cycles_passed + let m_cycles = match self.state{ + PpuState::Vblank => ((self.m_cycles_passed / HBLANK_M_CYCLES_LENGTH)+1) * HBLANK_M_CYCLES_LENGTH, + PpuState::Hblank => HBLANK_M_CYCLES_LENGTH, + PpuState::OamSearch => OAM_SEARCH_M_CYCLES_LENGTH, + PpuState::PixelTransfer => self.m_cycles_passed }; - // Subtract by 4 in order to cast the t_cycles to m_cycles - return (t_cycles - self.t_cycles_passed) >> 2; + return m_cycles - self.m_cycles_passed; } fn try_push_to_lcd(&mut self){ From b8654b1e50429506efee50da97d220b883b65364 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 9 Jul 2022 15:04:24 +0300 Subject: [PATCH 55/70] Minor refactoring that may help perf --- lib_gb/src/ppu/fifo/background_fetcher.rs | 32 ++++++----- lib_gb/src/ppu/fifo/fetching_state.rs | 1 + lib_gb/src/ppu/fifo/sprite_fetcher.rs | 2 +- lib_gb/src/ppu/gb_ppu.rs | 65 ++++++++++++----------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 42e633c6..6a0df004 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -83,29 +83,27 @@ impl BackgroundFetcher{ self.fetcher_state_machine.data.high_tile_data = Some(high_data); } - FetchingState::Push=>{ + FetchingState::Push if self.fifo.len() == 0 => { let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); - if self.fifo.len() == 0{ - if lcd_control & BIT_0_MASK == 0{ - for _ in 0..SPRITE_WIDTH{ - //When the baclkground is off pushes 0 - self.fifo.push(0); - self.current_x_pos += 1; - } + if lcd_control & BIT_0_MASK == 0{ + for _ in 0..SPRITE_WIDTH{ + //When the baclkground is off pushes 0 + self.fifo.push(0); + self.current_x_pos += 1; } - else{ - for i in (0..SPRITE_WIDTH).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push(pixel); - self.current_x_pos += 1; - } + } + else{ + for i in (0..SPRITE_WIDTH).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push(pixel); + self.current_x_pos += 1; } } } - FetchingState::Sleep=>{} + _ => {} } self.fetcher_state_machine.advance(); diff --git a/lib_gb/src/ppu/fifo/fetching_state.rs b/lib_gb/src/ppu/fifo/fetching_state.rs index 68b6c777..6846b036 100644 --- a/lib_gb/src/ppu/fifo/fetching_state.rs +++ b/lib_gb/src/ppu/fifo/fetching_state.rs @@ -1,3 +1,4 @@ +// Since each operation takes 2 t_cycles I pad them with sleep for my implementation pub enum FetchingState{ FetchTileNumber, FetchLowTile, diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index fe6a3625..347087cf 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -18,7 +18,7 @@ pub struct SpriteFetcher{ impl SpriteFetcher{ pub fn new()->Self{ let oam_entries:[SpriteAttribute; MAX_SPRITES_PER_LINE] = utils::create_array(|| SpriteAttribute::new(0,0,0,0)); - let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; + let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; SpriteFetcher{ fetcher_state_machine:FetcherStateMachine::new(state_machine), diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index a49ba95c..a9b6f946 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -298,46 +298,47 @@ impl GbPpu{ } fn try_push_to_lcd(&mut self){ - if !(self.bg_fetcher.fifo.len() == 0){ - if !self.scanline_started{ - // discard the next pixel in the bg fifo - // the bg fifo should start with 8 pixels and not push more untill its empty again - if FIFO_SIZE as usize - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % FIFO_SIZE as usize{ - self.scanline_started = true; - } - else{ - self.bg_fetcher.fifo.remove(); - return; - } + if self.bg_fetcher.fifo.len() == 0{ + return; + } + if !self.scanline_started{ + // discard the next pixel in the bg fifo + // the bg fifo should start with 8 pixels and not push more untill its empty again + if FIFO_SIZE as usize - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % FIFO_SIZE as usize{ + self.scanline_started = true; + } + else{ + self.bg_fetcher.fifo.remove(); + return; } + } - let bg_pixel_color_num = self.bg_fetcher.fifo.remove(); - let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; - let pixel = if !(self.sprite_fetcher.fifo.len() == 0){ - let sprite_color_num = self.sprite_fetcher.fifo.remove(); - let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; + let bg_pixel_color_num = self.bg_fetcher.fifo.remove(); + let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; + let pixel = if !(self.sprite_fetcher.fifo.len() == 0){ + let sprite_color_num = self.sprite_fetcher.fifo.remove(); + let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; - if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ - bg_pixel + if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ + bg_pixel + } + else{ + let sprite_pixel = if pixel_oam_attribute.palette_number{ + self.obj_color_mapping1[sprite_color_num.0 as usize] } else{ - let sprite_pixel = if pixel_oam_attribute.palette_number{ - self.obj_color_mapping1[sprite_color_num.0 as usize] - } - else{ - self.obj_color_mapping0[sprite_color_num.0 as usize] - }; + self.obj_color_mapping0[sprite_color_num.0 as usize] + }; - sprite_pixel.expect("Corruption in the object color pallete") - } + sprite_pixel.expect("Corruption in the object color pallete") } - else{ - bg_pixel - }; - - self.push_pixel(Color::into(pixel)); - self.pixel_x_pos += 1; } + else{ + bg_pixel + }; + + self.push_pixel(Color::into(pixel)); + self.pixel_x_pos += 1; } fn push_pixel(&mut self, pixel: Pixel) { From 8bc5b9e3c4a4a024de6e7e3e29a816f4efa60b40 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 9 Jul 2022 15:29:16 +0300 Subject: [PATCH 56/70] Remove the Option in the fetchers state This boosts the performance a bit --- lib_gb/src/ppu/fifo/background_fetcher.rs | 15 +++++++-------- lib_gb/src/ppu/fifo/fetcher_state_machine.rs | 2 +- lib_gb/src/ppu/fifo/fetching_state.rs | 14 ++++++++------ lib_gb/src/ppu/fifo/sprite_fetcher.rs | 14 +++++++------- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 6a0df004..b336f655 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -67,25 +67,25 @@ impl BackgroundFetcher{ }; self.fetcher_state_machine.data.reset(); - self.fetcher_state_machine.data.tile_data = Some(tile_num); + self.fetcher_state_machine.data.tile_data = tile_num; } FetchingState::FetchLowTile=>{ - let tile_num = self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchLowTIle"); + let tile_num = self.fetcher_state_machine.data.tile_data; let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let low_data = vram.read_current_bank(address); - self.fetcher_state_machine.data.low_tile_data = Some(low_data); + self.fetcher_state_machine.data.low_tile_data = low_data; } FetchingState::FetchHighTile=>{ - let tile_num= self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchHighTIle"); + let tile_num= self.fetcher_state_machine.data.tile_data; let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let high_data = vram.read_current_bank(address + 1); - self.fetcher_state_machine.data.high_tile_data = Some(high_data); + self.fetcher_state_machine.data.high_tile_data = high_data; } FetchingState::Push if self.fifo.len() == 0 => { - let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); - let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); + let low_data = self.fetcher_state_machine.data.low_tile_data; + let high_data = self.fetcher_state_machine.data.high_tile_data; if lcd_control & BIT_0_MASK == 0{ for _ in 0..SPRITE_WIDTH{ //When the baclkground is off pushes 0 @@ -105,7 +105,6 @@ impl BackgroundFetcher{ } _ => {} } - self.fetcher_state_machine.advance(); } diff --git a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs index e051c30b..595237bf 100644 --- a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs +++ b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs @@ -13,7 +13,7 @@ impl FetcherStateMachine{ pub fn new(state_machine:[FetchingState;8])->Self{ Self{ - data:FetchingStateData{high_tile_data:None, low_tile_data:None, tile_data:None}, + data:FetchingStateData{high_tile_data:0, low_tile_data:0, tile_data:0}, state:0, state_machine } diff --git a/lib_gb/src/ppu/fifo/fetching_state.rs b/lib_gb/src/ppu/fifo/fetching_state.rs index 6846b036..589c217c 100644 --- a/lib_gb/src/ppu/fifo/fetching_state.rs +++ b/lib_gb/src/ppu/fifo/fetching_state.rs @@ -7,16 +7,18 @@ pub enum FetchingState{ Sleep } + + pub struct FetchingStateData{ - pub tile_data:Option, - pub low_tile_data:Option, - pub high_tile_data:Option, + pub tile_data:u8, + pub low_tile_data:u8, + pub high_tile_data:u8, } impl FetchingStateData{ pub fn reset(&mut self){ - self.high_tile_data = None; - self.low_tile_data = None; - self.tile_data = None; + self.high_tile_data = 0; + self.low_tile_data = 0; + self.tile_data = 0; } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 347087cf..16422270 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -46,24 +46,24 @@ impl SpriteFetcher{ self.try_fetch_tile_number(current_x_pos, lcd_control); } FetchingState::FetchLowTile=>{ - let tile_num = self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchLowTIle"); + let tile_num = self.fetcher_state_machine.data.tile_data; let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let low_data = vram.read_current_bank(current_tile_data_address); - self.fetcher_state_machine.data.low_tile_data = Some(low_data); + self.fetcher_state_machine.data.low_tile_data = low_data; self.fetcher_state_machine.advance(); } FetchingState::FetchHighTile=>{ - let tile_num= self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchHighTIle"); + let tile_num= self.fetcher_state_machine.data.tile_data; let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let high_data = vram.read_current_bank(current_tile_data_address + 1); - self.fetcher_state_machine.data.high_tile_data = Some(high_data); + self.fetcher_state_machine.data.high_tile_data = high_data; self.fetcher_state_machine.advance(); } FetchingState::Push=>{ - let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); - let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); + let low_data = self.fetcher_state_machine.data.low_tile_data; + let high_data = self.fetcher_state_machine.data.high_tile_data; let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let start_x = self.fifo.len(); let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; @@ -110,7 +110,7 @@ impl SpriteFetcher{ } self.rendering = true; self.fetcher_state_machine.data.reset(); - self.fetcher_state_machine.data.tile_data = Some(tile_number); + self.fetcher_state_machine.data.tile_data = tile_number; self.fetcher_state_machine.advance(); return; } From a98b2f451eff57eb0f8bda8c7ebe5dd2df2b3dd2 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 9 Jul 2022 17:30:04 +0300 Subject: [PATCH 57/70] Fix some error in the t vs m cycle in the ppu --- lib_gb/src/ppu/gb_ppu.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index a9b6f946..65e06907 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -162,8 +162,7 @@ impl GbPpu{ ((self.lyc_register - self.ly_register) as u32 * HBLANK_M_CYCLES_LENGTH as u32) - self.m_cycles_passed as u32 }; - // Divide by 4 to transform the t_cycles to m_cycles - return t_cycles_to_next_stat_change >> 2; + return t_cycles_to_next_stat_change; } fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u16{ @@ -287,14 +286,14 @@ impl GbPpu{ } } - let m_cycles = match self.state{ + let m_cycles_for_state = match self.state{ PpuState::Vblank => ((self.m_cycles_passed / HBLANK_M_CYCLES_LENGTH)+1) * HBLANK_M_CYCLES_LENGTH, PpuState::Hblank => HBLANK_M_CYCLES_LENGTH, PpuState::OamSearch => OAM_SEARCH_M_CYCLES_LENGTH, PpuState::PixelTransfer => self.m_cycles_passed }; - return m_cycles - self.m_cycles_passed; + return m_cycles_for_state - self.m_cycles_passed; } fn try_push_to_lcd(&mut self){ From 4330d069043aa985a5ac618ab60d3e075aba9c92 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 9 Jul 2022 18:54:36 +0300 Subject: [PATCH 58/70] Add a cycles to wait for the PixelTransfer state --- lib_gb/src/ppu/gb_ppu.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 65e06907..5e4cbe62 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -290,7 +290,10 @@ impl GbPpu{ PpuState::Vblank => ((self.m_cycles_passed / HBLANK_M_CYCLES_LENGTH)+1) * HBLANK_M_CYCLES_LENGTH, PpuState::Hblank => HBLANK_M_CYCLES_LENGTH, PpuState::OamSearch => OAM_SEARCH_M_CYCLES_LENGTH, - PpuState::PixelTransfer => self.m_cycles_passed + + // taking the pixels that left to draw and divide by 4 (usually pushing 4 pixels per m_cycle) + // to try and calculate how much cycles left for the pixel transfer state + PpuState::PixelTransfer => self.m_cycles_passed + ((SCREEN_WIDTH - self.pixel_x_pos as usize) as u16 >> 2) }; return m_cycles_for_state - self.m_cycles_passed; From 817094cf52ddcc6bee8328834c765f9b8908a6ee Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 15 Jul 2022 17:27:41 +0300 Subject: [PATCH 59/70] Add fill fucntion to the fifo --- lib_gb/src/ppu/fifo/background_fetcher.rs | 20 ++++++++++---------- lib_gb/src/utils/fixed_size_queue.rs | 8 ++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index b336f655..944632a5 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -1,6 +1,8 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, fixed_size_queue::FixedSizeQueue, vec2::Vec2}}; use super::{FIFO_SIZE, SPRITE_WIDTH, fetcher_state_machine::FetcherStateMachine, fetching_state::*}; +const EMPTY_FIFO_BUFFER:[u8;FIFO_SIZE] = [0;FIFO_SIZE]; + pub struct BackgroundFetcher{ pub fifo:FixedSizeQueue, pub window_line_counter:u8, @@ -84,24 +86,22 @@ impl BackgroundFetcher{ self.fetcher_state_machine.data.high_tile_data = high_data; } FetchingState::Push if self.fifo.len() == 0 => { - let low_data = self.fetcher_state_machine.data.low_tile_data; - let high_data = self.fetcher_state_machine.data.high_tile_data; if lcd_control & BIT_0_MASK == 0{ - for _ in 0..SPRITE_WIDTH{ - //When the baclkground is off pushes 0 - self.fifo.push(0); - self.current_x_pos += 1; - } + self.fifo.fill(&EMPTY_FIFO_BUFFER); } else{ - for i in (0..SPRITE_WIDTH).rev(){ + let low_data = self.fetcher_state_machine.data.low_tile_data; + let high_data = self.fetcher_state_machine.data.high_tile_data; + let mut buffer:[u8;SPRITE_WIDTH as usize] = [0;SPRITE_WIDTH as usize]; + for i in 0..buffer.len(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push(pixel); - self.current_x_pos += 1; + buffer[(buffer.len() - 1 - i) as usize] = pixel; } + self.fifo.fill(&buffer); } + self.current_x_pos += SPRITE_WIDTH; } _ => {} } diff --git a/lib_gb/src/utils/fixed_size_queue.rs b/lib_gb/src/utils/fixed_size_queue.rs index 95be450b..4b1b1046 100644 --- a/lib_gb/src/utils/fixed_size_queue.rs +++ b/lib_gb/src/utils/fixed_size_queue.rs @@ -72,6 +72,14 @@ impl FixedSizeQueue{ pub fn len(&self)->usize{ self.length } + + pub fn fill(&mut self, value:&[T;SIZE]){ + unsafe{ + std::ptr::copy_nonoverlapping(value.as_ptr(), self.base_data_pointer, SIZE); + self.length = SIZE; + self.data_pointer = self.end_data_pointer.sub(1) as *mut T; + } + } } impl std::ops::Index for FixedSizeQueue{ From 278e73c7939954e98dfb7f2e4f549698e97dccf4 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 13 Aug 2022 18:57:26 +0300 Subject: [PATCH 60/70] Fix some bugs causing the program to panic mbc1 - casting to u16 when the program length in mbc1 carts can be greater than u16. read or write to the ram when there is not ram read or writes are ignored Add support for ignoring and not crashing gb_ppu - when calculating the next ppu state change did not count the vblank state when the counter get greater than 114 m_cycles. Now Im using modolu of 114 on the cycles to handle this sprite_fetcher - a bug where the flipx calculation was off by the amount of pixels needs to skip causing panic. It seems to be unecessary so I reomved it. --- lib_gb/src/mmu/carts/mbc1.rs | 11 ++++++++--- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 6 +++--- lib_gb/src/ppu/gb_ppu.rs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib_gb/src/mmu/carts/mbc1.rs b/lib_gb/src/mmu/carts/mbc1.rs index 61e5dfc4..e00cbb60 100644 --- a/lib_gb/src/mmu/carts/mbc1.rs +++ b/lib_gb/src/mmu/carts/mbc1.rs @@ -27,7 +27,7 @@ impl Mbc for Mbc1{ fn read_current_bank(&self, address:u16)->u8{ let bank:u16 = self.get_current_rom_bank() as u16; - return self.program[(ROM_BANK_SIZE * bank + address) as usize]; + return self.program[ROM_BANK_SIZE as usize * bank as usize + address as usize]; } fn write_rom(&mut self, address: u16, value: u8){ @@ -41,13 +41,18 @@ impl Mbc for Mbc1{ } fn read_external_ram(&self, address: u16)->u8{ + if self.ram.is_empty(){ + return 0xFF; + } let bank:u16 = self.get_current_ram_bank() as u16; return self.ram[(bank * RAM_BANK_SIZE + address) as usize]; } fn write_external_ram(&mut self, address: u16, value: u8){ - let bank:u16 = self.get_current_ram_bank() as u16; - self.ram[(bank * RAM_BANK_SIZE + address) as usize] = value; + if self.ram.len() > 0{ + let bank:u16 = self.get_current_ram_bank() as u16; + self.ram[(bank * RAM_BANK_SIZE + address) as usize] = value; + } } } diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 16422270..f88e8ccb 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -71,11 +71,11 @@ impl SpriteFetcher{ if oam_attribute.flip_x{ for i in (0 + skip_x)..SPRITE_WIDTH as usize{ let pixel = Self::get_decoded_pixel(i, low_data, high_data); - if i + skip_x >= start_x { + if i >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } - else if self.fifo[i + skip_x].0 == 0{ - self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); + else if self.fifo[i].0 == 0{ + self.fifo[i] = (pixel, self.current_oam_entry); } } } diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 5e4cbe62..9d12f435 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -159,7 +159,7 @@ impl GbPpu{ (HBLANK_M_CYCLES_LENGTH as u32 * 154 ) - self.m_cycles_passed as u32 } else{ - ((self.lyc_register - self.ly_register) as u32 * HBLANK_M_CYCLES_LENGTH as u32) - self.m_cycles_passed as u32 + ((self.lyc_register - self.ly_register) as u32 * HBLANK_M_CYCLES_LENGTH as u32) - (self.m_cycles_passed as u32 % HBLANK_M_CYCLES_LENGTH as u32) }; return t_cycles_to_next_stat_change; From cbdd53c18093bd4e8cf5cd6337a430d583ecb6ca Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 13 Aug 2022 19:04:47 +0300 Subject: [PATCH 61/70] Fix bug - window wasnt offsetted by `wx % 8 != 0` --- lib_gb/src/ppu/fifo/background_fetcher.rs | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 944632a5..a34c5bf2 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -45,14 +45,7 @@ impl BackgroundFetcher{ pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ self.has_wy_reached_ly = self.has_wy_reached_ly || ly_register == window_pos.y; - let last_rendering_status = self.rendering_window; self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos); - - // In case I was rendering a background pixel need to reset the state of the fetcher - // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) - if self.rendering_window && !last_rendering_status{ - self.fetcher_state_machine.reset(); - } match self.fetcher_state_machine.current_state(){ FetchingState::FetchTileNumber=>{ @@ -88,20 +81,28 @@ impl BackgroundFetcher{ FetchingState::Push if self.fifo.len() == 0 => { if lcd_control & BIT_0_MASK == 0{ self.fifo.fill(&EMPTY_FIFO_BUFFER); + self.current_x_pos += SPRITE_WIDTH; } else{ let low_data = self.fetcher_state_machine.data.low_tile_data; let high_data = self.fetcher_state_machine.data.high_tile_data; - let mut buffer:[u8;SPRITE_WIDTH as usize] = [0;SPRITE_WIDTH as usize]; - for i in 0..buffer.len(){ + for i in (0..FIFO_SIZE).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - buffer[(buffer.len() - 1 - i) as usize] = pixel; + self.fifo.push(pixel); + self.current_x_pos += 1; + + let last_rendering_status = self.rendering_window; + self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos); + // In case I was rendering a background pixel need to reset the state of the fetcher + // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) + if self.rendering_window && !last_rendering_status{ + self.fetcher_state_machine.reset(); + return; + } } - self.fifo.fill(&buffer); } - self.current_x_pos += SPRITE_WIDTH; } _ => {} } From 60603d52879dfc2eef13a67a185040cc86bb01c3 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 20 Aug 2022 17:19:06 +0300 Subject: [PATCH 62/70] Fix memory corruption bugs Fixed 2 bugs: sdl_pull_audio_device - aside from naming, fixed a bug where memseting outside of bounds cause heap corruption fixed_size_queue - aside from naming, fixed a bug where index and index_mut caused accessing memory outside of bounds. Funny thing is that when allocating with vec![] and with with_capacity, the corruption did not happen, my guess is that since vec![] macro simply allocates a bigger buffer and the out of bounds writes did not corrupt anything. --- gb/src/sdl/sdl_pull_audio_device.rs | 19 ++++--- lib_gb/src/utils/fixed_size_queue.rs | 69 +++++++++++++------------- lib_gb/tests/fixed_size_queue_tests.rs | 39 +++++++++++++++ 3 files changed, 82 insertions(+), 45 deletions(-) diff --git a/gb/src/sdl/sdl_pull_audio_device.rs b/gb/src/sdl/sdl_pull_audio_device.rs index 630a9089..56cb911e 100644 --- a/gb/src/sdl/sdl_pull_audio_device.rs +++ b/gb/src/sdl/sdl_pull_audio_device.rs @@ -10,7 +10,7 @@ const BUFFERS_NUMBER:usize = 3; struct UserData{ rx: Receiver, current_buf: Option, - current_buf_index:usize, + current_buf_byte_index:usize, } pub struct SdlPullAudioDevice{ @@ -31,7 +31,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ let(s,r) = bounded(BUFFERS_NUMBER - 1); let data = Box::new(UserData{ current_buf:Option::None, - current_buf_index:0, + current_buf_byte_index:0, rx:r }); @@ -103,22 +103,21 @@ unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length } let samples = &*((safe_userdata.current_buf.unwrap()) as *const [Sample;BUFFER_SIZE]); - let samples_size = (samples.len() * std::mem::size_of::()) - safe_userdata.current_buf_index; - let samples_ptr = (samples.as_ptr() as *mut u8).add(safe_userdata.current_buf_index); + let samples_size = (samples.len() * std::mem::size_of::()) - safe_userdata.current_buf_byte_index; + let samples_ptr = (samples.as_ptr() as *mut u8).add(safe_userdata.current_buf_byte_index); std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); if length > samples_size && safe_userdata.rx.is_empty(){ safe_userdata.current_buf = Option::None; - safe_userdata.current_buf_index = 0; - std::ptr::write_bytes(buffer.add(samples.len() as usize), 0, length - samples_size); + safe_userdata.current_buf_byte_index = 0; + std::ptr::write_bytes(buffer.add(samples_size), 0, length - samples_size); } else if length > samples_size{ safe_userdata.current_buf = Option::None; - safe_userdata.current_buf_index = 0; + safe_userdata.current_buf_byte_index = 0; audio_callback(userdata, buffer.add(samples_size), (length - samples_size) as i32); } else{ - safe_userdata.current_buf_index = length; + safe_userdata.current_buf_byte_index = length; } -} - +} \ No newline at end of file diff --git a/lib_gb/src/utils/fixed_size_queue.rs b/lib_gb/src/utils/fixed_size_queue.rs index 4b1b1046..b135db16 100644 --- a/lib_gb/src/utils/fixed_size_queue.rs +++ b/lib_gb/src/utils/fixed_size_queue.rs @@ -3,14 +3,14 @@ pub struct FixedSizeQueue{ // Im modifing it but not increasing its allocated size once its allocated so I hope this will work for me // and I wont get weird memory issues _data: Vec, // This field is not use directly only through pointers aquired in the new() function - end_data_pointer: *const T, - start_data_pointer: *const T, + end_alloc_pointer: *const T, + start_alloc_pointer: *const T, data_pointer: *mut T, base_data_pointer: *mut T, length: usize, } -impl FixedSizeQueue{ +impl FixedSizeQueue{ pub fn new()->Self{ let data = Vec::with_capacity(SIZE); let mut s = Self{ @@ -18,14 +18,14 @@ impl FixedSizeQueue{ length:0, base_data_pointer: std::ptr::null_mut(), data_pointer: std::ptr::null_mut(), - end_data_pointer: std::ptr::null_mut(), - start_data_pointer: std::ptr::null_mut(), + end_alloc_pointer: std::ptr::null_mut(), + start_alloc_pointer: std::ptr::null_mut(), }; s.base_data_pointer = s._data.as_mut_ptr(); s.data_pointer = s._data.as_mut_ptr(); - s.start_data_pointer = s._data.as_ptr(); - unsafe{s.end_data_pointer = s._data.as_ptr().add(SIZE)}; + s.start_alloc_pointer = s._data.as_ptr(); + unsafe{s.end_alloc_pointer = s._data.as_ptr().add(SIZE)}; return s; } @@ -33,8 +33,8 @@ impl FixedSizeQueue{ pub fn push(&mut self, t:T){ if self.length < SIZE{ unsafe{ - if self.data_pointer == self.end_data_pointer as *mut T{ - self.data_pointer = self.start_data_pointer as *mut T; + if self.data_pointer == self.end_alloc_pointer as *mut T{ + self.data_pointer = self.start_alloc_pointer as *mut T; } *self.data_pointer = t; self.data_pointer = self.data_pointer.add(1); @@ -51,8 +51,8 @@ impl FixedSizeQueue{ unsafe{ let t = *self.base_data_pointer; self.base_data_pointer = self.base_data_pointer.add(1); - if self.base_data_pointer == self.end_data_pointer as *mut T{ - self.base_data_pointer = self.start_data_pointer as *mut T; + if self.base_data_pointer == self.end_alloc_pointer as *mut T{ + self.base_data_pointer = self.start_alloc_pointer as *mut T; } self.length -= 1; @@ -65,8 +65,8 @@ impl FixedSizeQueue{ pub fn clear(&mut self){ self.length = 0; - self.data_pointer = self.start_data_pointer as *mut T; - self.base_data_pointer = self.start_data_pointer as *mut T; + self.data_pointer = self.start_alloc_pointer as *mut T; + self.base_data_pointer = self.start_alloc_pointer as *mut T; } pub fn len(&self)->usize{ @@ -75,43 +75,42 @@ impl FixedSizeQueue{ pub fn fill(&mut self, value:&[T;SIZE]){ unsafe{ + self.base_data_pointer = self.start_alloc_pointer as *mut T; std::ptr::copy_nonoverlapping(value.as_ptr(), self.base_data_pointer, SIZE); self.length = SIZE; - self.data_pointer = self.end_data_pointer.sub(1) as *mut T; + self.data_pointer = self.end_alloc_pointer as *mut T; } } } -impl std::ops::Index for FixedSizeQueue{ - type Output = T; - - fn index(&self, mut index: usize) -> &Self::Output { +impl FixedSizeQueue{ + #[inline] + fn get_index_ptr(&self, index:usize)->*const T{ if index < self.length{ unsafe{ - if self.base_data_pointer.add(index) >= self.end_data_pointer as *mut T{ - index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; + if self.base_data_pointer.add(index) >= self.end_alloc_pointer as *mut T{ + let wrap_offset = self.end_alloc_pointer.offset_from(self.base_data_pointer) as usize; + return self.start_alloc_pointer.add(index - wrap_offset); + } + else{ + return self.base_data_pointer.add(index); } - // casting a *mut T to a &T - return &*(self.base_data_pointer.add(index)); } } - std::panic!("Index is out of range"); } } -impl std::ops::IndexMut for FixedSizeQueue{ - fn index_mut(&mut self, mut index: usize) -> &mut Self::Output { - if index < self.length{ - unsafe{ - if self.base_data_pointer.add(index) >= self.end_data_pointer as *mut T{ - index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; - } - // casting a *mut T to a &mut T - return &mut *(self.base_data_pointer.add(index)); - } - } +impl std::ops::Index for FixedSizeQueue{ + type Output = T; - std::panic!("Index is out of range"); + fn index(&self, index: usize) -> &Self::Output { + unsafe{&*(self.get_index_ptr(index))} + } +} + +impl std::ops::IndexMut for FixedSizeQueue{ + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + unsafe{&mut *(self.get_index_ptr(index) as *mut T)} } } \ No newline at end of file diff --git a/lib_gb/tests/fixed_size_queue_tests.rs b/lib_gb/tests/fixed_size_queue_tests.rs index d3e9e16e..bdec9e0b 100644 --- a/lib_gb/tests/fixed_size_queue_tests.rs +++ b/lib_gb/tests/fixed_size_queue_tests.rs @@ -84,4 +84,43 @@ fn panic_on_fifo_set_index_out_of_range(){ //should panic fifo[2] = 4; +} + +#[test] +fn fill_fills_the_fifo(){ + let mut fifo = FixedSizeQueue::::new(); + fifo.push(1); + fifo.push(1); + fifo.push(1); + + fifo.remove(); + fifo.remove(); + + fifo.fill(&[0;8]); + + assert_eq!(fifo.len(), 8); + for i in 0..8{ + assert_eq!(fifo[i], 0); + } +} + +#[test] +fn fifo_index_check_happyflow(){ + let mut fifo = FixedSizeQueue::::new(); + for i in 0..8{ + fifo.push(i); + } + for _ in 0..6{ + fifo.remove(); + } + for i in 0..6{ + fifo.push(i); + } + + assert_eq!(fifo[0], 6); + assert_eq!(fifo[1], 7); + assert_eq!(fifo[2], 0); + assert_eq!(fifo[3], 1); + assert_eq!(fifo[4], 2); + assert_eq!(fifo[5], 3); } \ No newline at end of file From 48f68b4dd82cedc1db9f6744e7d40244312d85c8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 18 Sep 2022 00:51:16 +0300 Subject: [PATCH 63/70] Improve the PPU accuracy * Allow the PPU to be m_cycle accurate by cycling the mmu each time an operation on the mmu is executed. * Add the initial delay in the PPU background fifo that resets it after 6 t_cycles at the beginning of each scanline. * Add a delay in the PPU state changing mechanism. the state should change at the m_cycle the new state begins and not the m_cycle the old state ends. --- lib_gb/src/cpu/gb_cpu.rs | 4 +- lib_gb/src/cpu/opcode_runner.rs | 6 +- .../opcodes/arithmetic_16bit_instructions.rs | 16 +- .../opcodes/arithmetic_8bit_instructions.rs | 145 +++++++++--------- .../cpu/opcodes/cpu_control_instructions.rs | 26 ++-- lib_gb/src/cpu/opcodes/jump_instructions.rs | 67 ++++---- .../cpu/opcodes/load_16bit_instructions.rs | 28 ++-- .../src/cpu/opcodes/load_8bit_instructions.rs | 110 ++++++------- lib_gb/src/cpu/opcodes/opcodes_utils.rs | 8 +- .../cpu/opcodes/rotate_shift_instructions.rs | 113 +++++++------- .../cpu/opcodes/single_bit_sintructions.rs | 34 ++-- lib_gb/src/machine/gameboy.rs | 17 +- lib_gb/src/mmu/gb_mmu.rs | 16 +- lib_gb/src/mmu/io_bus.rs | 8 +- lib_gb/src/mmu/memory.rs | 4 +- lib_gb/src/ppu/fifo/background_fetcher.rs | 19 ++- lib_gb/src/ppu/gb_ppu.rs | 27 +++- lib_gb/src/ppu/ppu_state.rs | 6 + lib_gb/tests/memory_stub.rs | 4 +- lib_gb/tests/rotate_shift_tests.rs | 18 +-- 20 files changed, 345 insertions(+), 331 deletions(-) diff --git a/lib_gb/src/cpu/gb_cpu.rs b/lib_gb/src/cpu/gb_cpu.rs index b04bf9a8..41aed929 100644 --- a/lib_gb/src/cpu/gb_cpu.rs +++ b/lib_gb/src/cpu/gb_cpu.rs @@ -57,8 +57,8 @@ impl GbCpu { //unhalting the CPU self.halt = false; - //cycles passed - return 5; + // 5 cycles - 2 pushing pc to memory, 3 internal operation + return 3; } pub fn set_flag(&mut self, flag:Flag){ diff --git a/lib_gb/src/cpu/opcode_runner.rs b/lib_gb/src/cpu/opcode_runner.rs index 463e484c..3c2a3caa 100644 --- a/lib_gb/src/cpu/opcode_runner.rs +++ b/lib_gb/src/cpu/opcode_runner.rs @@ -24,6 +24,7 @@ impl GbCpu{ match opcode{ //Stop 0x10=>{ + // TODO: verify if stop is 1 byte or 2 bytes let next_byte = self.fetch_next_byte(memory); if next_byte == 0{ stop(self, memory) @@ -34,7 +35,7 @@ impl GbCpu{ } //just cpu - 0x00=>1, + 0x00=>0, // 1 cycles - 1 reading opcode 0x07=>rlca(self), 0x0F=>rrca(self), 0x17=>rla(self), @@ -124,7 +125,6 @@ impl GbCpu{ 0xF0=>run_u16_memory_opcode(self, memory, opcode, ld_a_ioport_n), //Memory u32 opcodes - 0x08=>run_u32_memory_opcode(self, memory, opcode, ld_nn_sp), 0xC4|0xCC|0xD4|0xDC=>run_u32_memory_opcode(self, memory, opcode, call_cc), 0xCD=>run_u32_memory_opcode(self, memory, opcode, call), @@ -172,7 +172,7 @@ impl GbCpu{ fn fetch_next_byte(&mut self, memory: &mut impl Memory)->u8{ - let byte:u8 = memory.read(self.program_counter); + let byte:u8 = memory.read(self.program_counter, 1); self.program_counter+=1; return byte; } diff --git a/lib_gb/src/cpu/opcodes/arithmetic_16bit_instructions.rs b/lib_gb/src/cpu/opcodes/arithmetic_16bit_instructions.rs index 66162edb..cdd67fb5 100644 --- a/lib_gb/src/cpu/opcodes/arithmetic_16bit_instructions.rs +++ b/lib_gb/src/cpu/opcodes/arithmetic_16bit_instructions.rs @@ -19,8 +19,8 @@ pub fn add_hl_rr(cpu:&mut GbCpu, opcode:u8)->u8{ *cpu.hl.value() = value; - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 internal operation + return 1; } pub fn add_sp_dd(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -34,8 +34,8 @@ pub fn add_sp_dd(cpu:&mut GbCpu, opcode:u16)->u8{ cpu.set_by_value(Flag::Carry, signed_check_for_carry_first_nible_add(temp as i16, dd)); cpu.set_by_value(Flag::HalfCarry, signed_check_for_half_carry_first_nible_add(temp as i16, dd)); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 2 internal operation + return 2; } pub fn inc_rr(cpu:&mut GbCpu, opcode:u8)->u8{ @@ -43,8 +43,8 @@ pub fn inc_rr(cpu:&mut GbCpu, opcode:u8)->u8{ let reg = get_arithmetic_16reg(cpu, reg); *reg = (*reg).wrapping_add(1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 internal operation + return 1; } @@ -53,6 +53,6 @@ pub fn dec_rr(cpu:&mut GbCpu, opcode:u8)->u8{ let reg = get_arithmetic_16reg(cpu, reg); *reg = (*reg).wrapping_sub(1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 internal operation + return 1; } \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/arithmetic_8bit_instructions.rs b/lib_gb/src/cpu/opcodes/arithmetic_8bit_instructions.rs index 57a3ca0f..89df1043 100644 --- a/lib_gb/src/cpu/opcodes/arithmetic_8bit_instructions.rs +++ b/lib_gb/src/cpu/opcodes/arithmetic_8bit_instructions.rs @@ -98,8 +98,8 @@ pub fn add_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = add(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //add A and nn @@ -108,18 +108,18 @@ pub fn add_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = add(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //add A and (hl) pub fn add_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = add(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //add A and r + carry flag @@ -128,8 +128,8 @@ pub fn adc_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = adc(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //add A and nn + carry @@ -138,18 +138,18 @@ pub fn adc_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = adc(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //add A and (hl) + Scarry pub fn adc_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = adc(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //sub r from A @@ -158,8 +158,8 @@ pub fn sub_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = sub(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //sub A and nn @@ -168,18 +168,18 @@ pub fn sub_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = sub(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //sub A and (hl) pub fn sub_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = sub(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } @@ -189,8 +189,8 @@ pub fn sbc_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = subc(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //sub A and nn @@ -199,18 +199,18 @@ pub fn sbc_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = subc(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //sub A and (hl) pub fn sbc_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = subc(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //and A and r @@ -219,8 +219,8 @@ pub fn and_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = and(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //and A and nn @@ -229,18 +229,18 @@ pub fn and_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = and(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //and A and (hl) pub fn and_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = and(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //xor A and r @@ -249,8 +249,8 @@ pub fn xor_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = xor(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //xor A and nn @@ -259,29 +259,28 @@ pub fn xor_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = xor(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //xor A and (hl) pub fn xor_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = xor(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } - //or A and r pub fn or_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let src_reg = *get_src_register(cpu, opcode); let dest = *cpu.af.high(); *cpu.af.high() = or(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //or A and nn @@ -290,18 +289,18 @@ pub fn or_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); *cpu.af.high() = or(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //or A and (hl) pub fn or_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); *cpu.af.high() = or(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //cp A and r @@ -310,8 +309,8 @@ pub fn cp_a_r(cpu:&mut GbCpu, opcode:u8)->u8{ let dest = *cpu.af.high(); sub(cpu, dest, src_reg); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //cp A and nn @@ -320,18 +319,18 @@ pub fn cp_a_nn(cpu:&mut GbCpu, opcode:u16)->u8{ let dest = *cpu.af.high(); sub(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //or A and (hl) pub fn cp_a_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let src = memory.read(*cpu.hl.value()); + let src = memory.read(*cpu.hl.value(), 1); let dest = *cpu.af.high(); sub(cpu, dest, src); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } pub fn inc_r(cpu:&mut GbCpu, opcode:u8)->u8{ @@ -347,21 +346,21 @@ pub fn inc_r(cpu:&mut GbCpu, opcode:u8)->u8{ cpu.set_by_value(Flag::HalfCarry, check_for_half_carry_first_nible_add(original_reg, 1)); cpu.unset_flag(Flag::Subtraction); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn inc_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let value = memory.read(*cpu.hl.value()); + let value = memory.read(*cpu.hl.value(), 1); let altered_value = value.wrapping_add(1); - memory.write(*cpu.hl.value(), altered_value); + memory.write(*cpu.hl.value(), altered_value, 1); cpu.set_by_value(Flag::Zero, altered_value == 0); cpu.set_by_value(Flag::HalfCarry, check_for_half_carry_first_nible_add(value, 1)); cpu.unset_flag(Flag::Subtraction); - //cycles - return 3; + // 3 cycles - 1 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn dec_r(cpu:&mut GbCpu, opcode:u8)->u8{ @@ -377,21 +376,21 @@ pub fn dec_r(cpu:&mut GbCpu, opcode:u8)->u8{ cpu.set_by_value(Flag::HalfCarry, check_for_half_carry_first_nible_sub(original_reg, finished_reg)); cpu.set_flag(Flag::Subtraction); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn dec_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let value = memory.read(*cpu.hl.value()); + let value = memory.read(*cpu.hl.value(), 1); let altered_value = value.wrapping_sub(1); - memory.write(*cpu.hl.value(), altered_value); + memory.write(*cpu.hl.value(), altered_value, 1); cpu.set_by_value(Flag::Zero, altered_value == 0); cpu.set_by_value(Flag::HalfCarry, check_for_half_carry_first_nible_sub(value, altered_value)); cpu.set_flag(Flag::Subtraction); - //cycles - return 3; + // 3 cycles - 1 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn cpl(cpu:&mut GbCpu)->u8{ @@ -399,8 +398,8 @@ pub fn cpl(cpu:&mut GbCpu)->u8{ cpu.set_flag(Flag::HalfCarry); cpu.set_flag(Flag::Subtraction); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn daa(cpu:&mut GbCpu)->u8{ @@ -434,6 +433,6 @@ pub fn daa(cpu:&mut GbCpu)->u8{ cpu.set_by_value(Flag::Zero, zero); cpu.unset_flag(Flag::HalfCarry); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/cpu_control_instructions.rs b/lib_gb/src/cpu/opcodes/cpu_control_instructions.rs index ab922d05..ba0a0d25 100644 --- a/lib_gb/src/cpu/opcodes/cpu_control_instructions.rs +++ b/lib_gb/src/cpu/opcodes/cpu_control_instructions.rs @@ -8,8 +8,8 @@ pub fn ccf(cpu:&mut GbCpu)->u8{ cpu.unset_flag(Flag::HalfCarry); cpu.unset_flag(Flag::Subtraction); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn scf(cpu:&mut GbCpu)->u8{ @@ -17,36 +17,36 @@ pub fn scf(cpu:&mut GbCpu)->u8{ cpu.unset_flag(Flag::HalfCarry); cpu.unset_flag(Flag::Subtraction); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn halt(cpu:&mut GbCpu)->u8{ cpu.halt = true; - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn stop(cpu:&mut GbCpu, memory: &mut impl Memory)->u8{ - if (memory.read(IE_REGISTER_ADDRESS) & 0b11111 == 0) && (memory.read(JOYP_REGISTER_ADDRESS) & 0b1111 == 0){ + if (memory.read(IE_REGISTER_ADDRESS, 0) & 0b11111 == 0) && (memory.read(JOYP_REGISTER_ADDRESS, 0) & 0b1111 == 0){ cpu.stop = true; } - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn di(cpu:&mut GbCpu)->u8{ cpu.mie = false; - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn ei(cpu:&mut GbCpu)->u8{ cpu.mie = true; - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/jump_instructions.rs b/lib_gb/src/cpu/opcodes/jump_instructions.rs index 6fc10e10..be4872c0 100644 --- a/lib_gb/src/cpu/opcodes/jump_instructions.rs +++ b/lib_gb/src/cpu/opcodes/jump_instructions.rs @@ -15,20 +15,18 @@ pub fn call(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ push_pc(cpu, memory); cpu.program_counter = address_to_jump; - //cycles - return 6; + // 6 cycles - 3 reading opcode, 2 writing pc to sp address, 1 internal operation + return 1; } fn call_if_true(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u32, flag:bool)->u8{ if flag{ - call(cpu, memory, opcode); - - //cycles - return 6; + // 6 cycles - 6 as call opcode + return call(cpu, memory, opcode); } - //cycles - return 3; + // 3 cycles - 3 reading opcode (no call executed) + return 0; } pub fn call_cc(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ @@ -47,20 +45,20 @@ pub fn call_cc(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ pub fn ret(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ cpu.program_counter = pop(cpu, memory); - //cycles - return 4; + // 4 cycles - 1 reading opcode, 2 writing pc to sp address, 1 internal operation + return 1; } fn ret_if_true(cpu:&mut GbCpu, memory:&mut impl Memory, flag:bool)->u8{ if flag{ - ret(cpu, memory); + let cycles = ret(cpu, memory); - //cycles - return 5; + // 5 cycles - 4 as ret opcode, 1 internal operation + return cycles+1; } - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 internal operation + return 1; } pub fn ret_cc(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ @@ -92,35 +90,34 @@ pub fn rst(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ push_pc(cpu, memory); cpu.program_counter = value as u16; - //cycles - return 4; + // 4 cycles - 1 reading opcode, 2 writing pc to sp address, 1 internal operation + return 1; } pub fn reti(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ let cycles = ret(cpu, memory); cpu.mie = true; - cycles + // 4 cycles - 4 as ret opcode + return cycles; } fn jump_if_true(cpu:&mut GbCpu, opcode:u32, flag:bool)->u8{ if flag{ - jump(cpu, opcode); - - //cycles - return 4; + // 4 cycles - 4 as jump opcode + return jump(cpu, opcode); } - //cycles - return 3; + // 3 cycles - 3 reading opcode + return 0; } pub fn jump(cpu:&mut GbCpu, opcode:u32)->u8{ let address = (((opcode & 0xFF) as u16)<<8) | (((opcode & 0xFF00)as u16)>>8); cpu.program_counter = address; - //cycles - return 4; + // 4 cycles - 3 reading opcode, 1 internal operation + return 1; } pub fn jump_cc(cpu:&mut GbCpu, opcode:u32)->u8{ @@ -139,20 +136,18 @@ pub fn jump_cc(cpu:&mut GbCpu, opcode:u32)->u8{ pub fn jump_hl(cpu:&mut GbCpu)->u8{ cpu.program_counter = *cpu.hl.value(); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } fn jump_r_if_true(cpu:&mut GbCpu, opcode:u16, flag:bool)->u8{ if flag{ - jump_r(cpu, opcode); - - //cycles for jump - return 3; + // 3 cycles - 3 as jump_r opcode + return jump_r(cpu, opcode); } - //cycles for no jump - return 2; + // 2 cycles - 2 reading opcode (no jump) + return 0; } pub fn jump_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -160,8 +155,8 @@ pub fn jump_r(cpu:&mut GbCpu, opcode:u16)->u8{ let address = address as i8; cpu.program_counter = cpu.program_counter.wrapping_add(address as u16); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 internal operation + return 1; } pub fn jump_r_cc(cpu:&mut GbCpu, opcode:u16)->u8{ diff --git a/lib_gb/src/cpu/opcodes/load_16bit_instructions.rs b/lib_gb/src/cpu/opcodes/load_16bit_instructions.rs index c829beb3..f93ed412 100644 --- a/lib_gb/src/cpu/opcodes/load_16bit_instructions.rs +++ b/lib_gb/src/cpu/opcodes/load_16bit_instructions.rs @@ -18,16 +18,16 @@ pub fn load_rr_nn(cpu:&mut GbCpu, opcode:u32)->u8{ *reg = nn; - //cycles - return 3; + // 3 cycles - 3 reading opcode + return 0; } //loads register HL into the SP pub fn load_sp_hl(cpu:&mut GbCpu)->u8{ cpu.stack_pointer = *cpu.hl.value(); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 internal operation + return 1; } //pop from the stack pointer to one register @@ -44,8 +44,8 @@ pub fn pop(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ *reg.value() = poped_value; - //cycles - return 3; + // 3 cycles - 1 reading opcode, 2 reading sp address and sp+1 address + return 0; } //push to stack the register @@ -61,8 +61,8 @@ pub fn push(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ opcodes_utils::push(cpu, memory, value); - //cycles - return 4; + // 4 cycles - 1 reading opcode, 2 writing to sp address and sp+1 address, 1 internal operation + return 1; } //load into hl sp + rr @@ -84,17 +84,17 @@ pub fn ld_hl_spdd(cpu:&mut GbCpu, opcode:u16)->u8{ cpu.unset_flag(Flag::Zero); cpu.unset_flag(Flag::Subtraction); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 internal operation + return 1; } //load sp into memory pub fn ld_nn_sp(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ let address = opcode_to_u16_value((opcode & 0xFFFF) as u16); let (high, low):(u8, u8) = u16_to_high_and_low(cpu.stack_pointer); - memory.write(address, low); - memory.write(address+1, high); + memory.write(address, low, 1); + memory.write(address+1, high, 1); - //cycles - return 5; + // 5 cycles - 3 reading opcode, 2 writing sp to nn address + return 0; } \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/load_8bit_instructions.rs b/lib_gb/src/cpu/opcodes/load_8bit_instructions.rs index d38f3c7b..3054138b 100644 --- a/lib_gb/src/cpu/opcodes/load_8bit_instructions.rs +++ b/lib_gb/src/cpu/opcodes/load_8bit_instructions.rs @@ -29,8 +29,8 @@ pub fn ld_r_r(cpu: &mut GbCpu, opcode:u8)->u8{ let dest_register = get_dest_register(cpu, opcode); *dest_register = src_register_value; - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } //load src value into dest register @@ -39,8 +39,8 @@ pub fn ld_r_n(cpu: &mut GbCpu, opcode:u16)->u8 { let n = (opcode&0xFF) as u8; *reg = n; - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } //load the value in address of HL into dest register @@ -58,146 +58,146 @@ pub fn ld_r_hl(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ _=>panic!("no register") }; - *reg = memory.read(hl_value); + *reg = memory.read(hl_value, 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //load the value in reg_src into the address of HL in memory pub fn ld_hl_r(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u8{ - memory.write(*cpu.hl.value(), *get_src_register(cpu, opcode)); + memory.write(*cpu.hl.value(), *get_src_register(cpu, opcode), 1); - //cycles - return 2; + // 2 cycles, 1 reading opcode, 1 writing hl address + return 0; } //load the valie src into the address HL in memory pub fn ld_hl_n(cpu: &mut GbCpu, memory:&mut impl Memory, opcode:u16)->u8{ let src = (0xFF & opcode) as u8; - memory.write(*cpu.hl.value(), src); + memory.write(*cpu.hl.value(), src, 1); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 writing hl address + return 0; } //load the value in address of BC into register A pub fn ld_a_bc(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - *cpu.af.high() = memory.read(*cpu.bc.value()); + *cpu.af.high() = memory.read(*cpu.bc.value(), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading bc address + return 0; } //load the value in address of DE into register A pub fn ld_a_de(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - *cpu.af.high() = memory.read(*cpu.de.value()); + *cpu.af.high() = memory.read(*cpu.de.value(), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading de address + return 0; } //load the value at address NN into register A pub fn ld_a_nn(cpu: &mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ let mut address = ((0xFF & opcode) as u16)<<8; address |= ((0xFF00&opcode) as u16)>>8; - *cpu.af.high() = memory.read(address); + *cpu.af.high() = memory.read(address, 1); - //cycles - return 4; + // 4 cycles - 3 reading opcode, 1 reading nn address + return 0; } //load the value in register A into the address of BC pub fn ld_bc_a(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - memory.write(*cpu.bc.value(), *cpu.af.high()); + memory.write(*cpu.bc.value(), *cpu.af.high(), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 writing bc address + return 0; } //load the value in register A into the address of DE pub fn ld_de_a(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - memory.write(*cpu.de.value(), *cpu.af.high()); + memory.write(*cpu.de.value(), *cpu.af.high(), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 writing de address + return 0; } //load the value in register A into the address of NN pub fn ld_nn_a(cpu: &mut GbCpu, memory:&mut impl Memory, opcode:u32)->u8{ let mut address = ((0xFF & opcode) as u16)<<8; address |= ((0xFF00&opcode) as u16)>>8; - memory.write(address, *cpu.af.high()); + memory.write(address, *cpu.af.high(), 1); - //cycles - return 4; + // 4 cycles - 3 reading opcode, 1 writing nn address + return 0; } //load value in register A into address HL and then increment register HL value pub fn ldi_hl_a(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - memory.write(*cpu.hl.value(), *cpu.af.high()); + memory.write(*cpu.hl.value(), *cpu.af.high(), 1); cpu.inc_hl(); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 writing hl address + return 0; } //load into register A the value in address HL and then increment register HL value pub fn ldi_a_hl(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - *cpu.af.high() = memory.read(*cpu.hl.value()); + *cpu.af.high() = memory.read(*cpu.hl.value(), 1); cpu.inc_hl(); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //load value in register A into address HL and then decrement register HL value pub fn ldd_hl_a(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - memory.write(*cpu.hl.value(), *cpu.af.high()); + memory.write(*cpu.hl.value(), *cpu.af.high(), 1); cpu.dec_hl(); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 writing hl address + return 0; } //load into register A the value in address HL and then decrement register HL value pub fn ldd_a_hl(cpu: &mut GbCpu, memory:&mut impl Memory)->u8{ - *cpu.af.high() = memory.read(*cpu.hl.value()); + *cpu.af.high() = memory.read(*cpu.hl.value(), 1); cpu.dec_hl(); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading hl address + return 0; } //load into register A the value in io port N pub fn ld_a_ioport_n(cpu: &mut GbCpu, memory:&mut impl Memory, opcode:u16)->u8{ let io_port = 0x00FF & opcode; - *cpu.af.high() = memory.read(IO_PORTS_ADDRESS + (io_port as u16)); + *cpu.af.high() = memory.read(IO_PORTS_ADDRESS + (io_port as u16), 1); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 reading io port address + return 0; } //load into io port N the value in register A pub fn ld_ioport_n_a(cpu: &mut GbCpu, memory: &mut impl Memory, opcode:u16)->u8{ let io_port = 0x00FF & opcode; - memory.write(IO_PORTS_ADDRESS + (io_port as u16), *cpu.af.high()); + memory.write(IO_PORTS_ADDRESS + (io_port as u16), *cpu.af.high(), 1); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 writing io port address + return 0; } //load into io port C the value in register A pub fn ld_ioport_c_a(cpu: &mut GbCpu, memory: &mut impl Memory)->u8{ - memory.write(IO_PORTS_ADDRESS + (*cpu.bc.low() as u16), *cpu.af.high()); + memory.write(IO_PORTS_ADDRESS + (*cpu.bc.low() as u16), *cpu.af.high(), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 writing io port address + return 0; } pub fn ld_a_ioport_c(cpu: &mut GbCpu, memory: &mut impl Memory)->u8{ - *cpu.af.high() = memory.read(IO_PORTS_ADDRESS + (*cpu.bc.low() as u16)); + *cpu.af.high() = memory.read(IO_PORTS_ADDRESS + (*cpu.bc.low() as u16), 1); - //cycles - return 2; + // 2 cycles - 1 reading opcode, 1 reading io port address + return 0; } \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/opcodes_utils.rs b/lib_gb/src/cpu/opcodes/opcodes_utils.rs index 28be4ffc..88ea998d 100644 --- a/lib_gb/src/cpu/opcodes/opcodes_utils.rs +++ b/lib_gb/src/cpu/opcodes/opcodes_utils.rs @@ -73,14 +73,14 @@ pub fn push(cpu:&mut GbCpu,memory:&mut impl Memory, value:u16){ let high = ((value & 0xFF00) >> 8) as u8; let low = (value & 0xFF) as u8; - memory.write(cpu.stack_pointer-1, high); - memory.write(cpu.stack_pointer-2, low); + memory.write(cpu.stack_pointer-1, high, 1); + memory.write(cpu.stack_pointer-2, low, 1); cpu.stack_pointer-=2; } pub fn pop(cpu:&mut GbCpu,memory:&mut impl Memory)->u16{ - let mut value:u16 = memory.read(cpu.stack_pointer) as u16; - value |= (memory.read(cpu.stack_pointer+1) as u16)<<8; + let mut value:u16 = memory.read(cpu.stack_pointer, 1) as u16; + value |= (memory.read(cpu.stack_pointer+1, 1) as u16)<<8; cpu.stack_pointer+=2; return value; diff --git a/lib_gb/src/cpu/opcodes/rotate_shift_instructions.rs b/lib_gb/src/cpu/opcodes/rotate_shift_instructions.rs index bc1dfa44..24795dd7 100644 --- a/lib_gb/src/cpu/opcodes/rotate_shift_instructions.rs +++ b/lib_gb/src/cpu/opcodes/rotate_shift_instructions.rs @@ -50,8 +50,8 @@ pub fn rlca(cpu:&mut GbCpu)->u8{ a_rotate_flags(cpu, carry); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn rla(cpu:&mut GbCpu)->u8{ @@ -60,8 +60,8 @@ pub fn rla(cpu:&mut GbCpu)->u8{ a_rotate_flags(cpu, carry); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } pub fn rrca(cpu:&mut GbCpu)->u8{ @@ -69,19 +69,18 @@ pub fn rrca(cpu:&mut GbCpu)->u8{ a_rotate_flags(cpu, carry); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } - pub fn rra(cpu:&mut GbCpu)->u8{ let carry_flag = cpu.get_flag(Flag::Carry); let carry:bool = rotate_right_carry(cpu.af.high(), carry_flag); a_rotate_flags(cpu, carry); - //cycles - return 1; + // 1 cycles - 1 reading opcode + return 0; } fn rotate_shift_flags(cpu:&mut GbCpu, carry:bool, zero:bool){ @@ -104,18 +103,18 @@ pub fn rlc_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn rlc_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = rotate_left(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn rl_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -132,18 +131,18 @@ pub fn rl_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn rl_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = rotate_left_carry(&mut byte, cpu.get_flag(Flag::Carry)); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn rrc_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -159,18 +158,18 @@ pub fn rrc_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn rrc_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = rotate_right(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn rr_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -187,19 +186,19 @@ pub fn rr_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn rr_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry_flag = cpu.get_flag(Flag::Carry); let carry:bool = rotate_right_carry(&mut byte, carry_flag); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } fn shift_left(r:&mut u8)->bool{ @@ -234,18 +233,18 @@ pub fn sla_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn sla_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = shift_left(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } fn swap_nibbles(r:&mut u8){ @@ -273,18 +272,18 @@ pub fn swap_r(cpu:&mut GbCpu, opcode:u16)->u8{ set_swap_flags(cpu, value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn swap_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); swap_nibbles(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); set_swap_flags(cpu, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn sra_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -299,18 +298,18 @@ pub fn sra_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn sra_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = arithmetic_shift_right(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn srl_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -325,16 +324,16 @@ pub fn srl_r(cpu:&mut GbCpu, opcode:u16)->u8{ rotate_shift_flags(cpu, carry, register_value == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn srl_hl(cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - let mut byte: u8 = memory.read(*cpu.hl.value()); + let mut byte: u8 = memory.read(*cpu.hl.value(), 1); let carry:bool = logical_shift_right(&mut byte); - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); rotate_shift_flags(cpu, carry, byte == 0); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } diff --git a/lib_gb/src/cpu/opcodes/single_bit_sintructions.rs b/lib_gb/src/cpu/opcodes/single_bit_sintructions.rs index ea9b786f..c236a2dd 100644 --- a/lib_gb/src/cpu/opcodes/single_bit_sintructions.rs +++ b/lib_gb/src/cpu/opcodes/single_bit_sintructions.rs @@ -22,19 +22,19 @@ pub fn bit_r(cpu:&mut GbCpu, opcode:u16)->u8{ let bit = *register & bit_number; set_flags_bit(cpu, bit == 0); - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn bit_hl(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u16)->u8{ let opcode = get_cb_opcode(opcode); - let byte = memory.read(*cpu.hl.value()); + let byte = memory.read(*cpu.hl.value(), 1); let bit_number = get_bit_number(opcode); let bit = byte & bit_number; set_flags_bit(cpu, bit == 0); - //cycles - return 3; + // 3 cycles - 2 reading opcode, 1 reading hl address + return 0; } pub fn set_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -43,19 +43,19 @@ pub fn set_r(cpu:&mut GbCpu, opcode:u16)->u8{ let bit_number = get_bit_number(opcode); *register |= bit_number; - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn set_hl(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u16)->u8{ let opcode = get_cb_opcode(opcode); - let mut byte = memory.read(*cpu.hl.value()); + let mut byte = memory.read(*cpu.hl.value(), 1); let bit_number = get_bit_number(opcode); byte |= bit_number; - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } pub fn res_r(cpu:&mut GbCpu, opcode:u16)->u8{ @@ -65,18 +65,18 @@ pub fn res_r(cpu:&mut GbCpu, opcode:u16)->u8{ let bit_mask:u8 = 0xFF ^ bit_number; *register &= bit_mask; - //cycles - return 2; + // 2 cycles - 2 reading opcode + return 0; } pub fn res_hl(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u16)->u8{ let opcode = get_cb_opcode(opcode); - let mut byte = memory.read(*cpu.hl.value()); + let mut byte = memory.read(*cpu.hl.value(), 1); let bit_number = get_bit_number(opcode); let bit_mask:u8 = 0xFF ^ bit_number; byte &= bit_mask; - memory.write(*cpu.hl.value(), byte); + memory.write(*cpu.hl.value(), byte, 1); - //cycles - return 4; + // 4 cycles - 2 reading opcode, 1 reading hl address, 1 writing hl address + return 0; } \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 7449d5db..dc67996d 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -41,9 +41,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G } pub fn cycle_frame(&mut self){ - let mut cycles_counter = 0; - - while cycles_counter < CYCLES_PER_FRAME{ + while self.mmu.m_cycle_counter < CYCLES_PER_FRAME{ self.mmu.poll_joypad_state(); //CPU @@ -51,18 +49,19 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G if !self.cpu.halt{ cpu_cycles_passed = self.execute_opcode(); } - - self.mmu.cycle(cpu_cycles_passed); + if cpu_cycles_passed != 0{ + self.mmu.cycle(cpu_cycles_passed); + } //interrupts let interrupt_request = self.mmu.handle_interrupts(self.cpu.mie); let interrupt_cycles = self.cpu.execute_interrupt_request(&mut self.mmu, interrupt_request); - if interrupt_cycles != 0{ + if interrupt_cycles != 0{ self.mmu.cycle(interrupt_cycles); } - - cycles_counter += cpu_cycles_passed as u32 + interrupt_cycles as u32; } + + self.mmu.m_cycle_counter = 0; } fn execute_opcode(&mut self)->u8{ @@ -79,7 +78,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G let h = *self.cpu.hl.high(); let l = *self.cpu.hl.low(); debug!("A: {:02X} F: {:02X} B: {:02X} C: {:02X} D: {:02X} E: {:02X} H: {:02X} L: {:02X} SP: {:04X} PC: 00:{:04X} ({:02X} {:02X} {:02X} {:02X})", - a,f,b,c,d,e,h,l, self.cpu.stack_pointer, pc, self.mmu.read(pc), self.mmu.read(pc+1), self.mmu.read(pc+2), self.mmu.read(pc+3)); + a,f,b,c,d,e,h,l, self.cpu.stack_pointer, pc, self.mmu.read(pc,0), self.mmu.read(pc+1,0), self.mmu.read(pc+2,0), self.mmu.read(pc+3,0)); } self.cpu.run_opcode(&mut self.mmu) diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index f7c127d9..67c64035 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -16,6 +16,7 @@ const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider>{ pub io_bus: IoBus, + pub m_cycle_counter:u32, boot_rom:[u8;BOOT_ROM_SIZE], external_memory_bus:ExternalMemoryBus<'a>, oucupied_access_bus:Option, @@ -26,7 +27,8 @@ pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G, J>{ - fn read(&mut self, address:u16)->u8{ + fn read(&mut self, address:u16, m_cycles:u8)->u8{ + self.cycle(m_cycles); if let Some (bus) = &self.oucupied_access_bus{ return match address{ 0xFF00..=0xFF7F => self.io_bus.read(address - 0xFF00), @@ -61,7 +63,8 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G }; } - fn write(&mut self, address:u16, value:u8){ + fn write(&mut self, address:u16, value:u8, m_cycles:u8){ + self.cycle(m_cycles); if let Some(bus) = &self.oucupied_access_bus{ match address{ 0xFF00..=0xFF7F => self.io_bus.write(address- 0xFF00, value), @@ -136,6 +139,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G, joypad_proider:J)->Self{ GbMmu{ io_bus:IoBus::new(apu, gfx_device, joypad_proider), + m_cycle_counter:0, external_memory_bus: ExternalMemoryBus::new(mbc), oucupied_access_bus:None, hram:[0;HRAM_SIZE], @@ -148,7 +152,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ let mut mmu = GbMmu::new_with_bootrom(mbc, [0;BOOT_ROM_SIZE], apu, gfx_device, joypad_proider); //Setting the bootrom register to be set (the boot sequence has over) - mmu.write(BOOT_REGISTER_ADDRESS, 1); + mmu.write(BOOT_REGISTER_ADDRESS, 1, 0); return mmu; } @@ -156,6 +160,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ pub fn cycle(&mut self, m_cycles:u8){ self.oucupied_access_bus = self.io_bus.dma_controller.cycle(m_cycles as u32, &mut self.external_memory_bus, &mut self.io_bus.ppu); self.io_bus.cycle(m_cycles as u32); + self.m_cycle_counter += m_cycles as u32; } pub fn handle_interrupts(&mut self, master_interrupt_enable:bool)->InterruptRequest{ @@ -167,12 +172,11 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ } fn is_oam_ready_for_io(&self)->bool{ - let ppu_state = self.io_bus.ppu.state as u8; - return ppu_state != PpuState::OamSearch as u8 && ppu_state != PpuState::PixelTransfer as u8 + return self.io_bus.ppu.state != PpuState::OamSearch && self.io_bus.ppu.state != PpuState::PixelTransfer } fn is_vram_ready_for_io(&self)->bool{ - return self.io_bus.ppu.state as u8 != PpuState::PixelTransfer as u8; + return self.io_bus.ppu.state != PpuState::PixelTransfer; } fn bad_dma_read(address:u16)->u8{ diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 735a3b9c..5f127906 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -4,7 +4,7 @@ use crate::{ timer::{timer_register_updater::*, gb_timer::GbTimer}, keypad::{joypad_provider::JoypadProvider, joypad_handler::JoypadHandler} }; -use super::{interrupts_handler::*, memory::*,io_ports::*, oam_dma_controller::OamDmaController}; +use super::{interrupts_handler::*, io_ports::*, oam_dma_controller::OamDmaController}; pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; @@ -31,8 +31,8 @@ pub struct IoBus{ ppu_event:Option, } -impl Memory for IoBus{ - fn read(&mut self, address:u16)->u8 { +impl IoBus{ + pub fn read(&mut self, address:u16)->u8 { match address{ TAC_REGISTER_INDEX | DIV_REGISTER_INDEX | TIMA_REGISTER_INDEX=> self.cycle_timer(), @@ -95,7 +95,7 @@ impl Memory for IoBus self.cycle_timer(), NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_apu(), diff --git a/lib_gb/src/mmu/memory.rs b/lib_gb/src/mmu/memory.rs index d86f05b6..369aa2e5 100644 --- a/lib_gb/src/mmu/memory.rs +++ b/lib_gb/src/mmu/memory.rs @@ -1,5 +1,5 @@ pub trait Memory{ - fn read(&mut self, address:u16)->u8; - fn write(&mut self, address:u16, value:u8); + fn read(&mut self, address:u16, m_cycles:u8)->u8; + fn write(&mut self, address:u16, value:u8, m_cycles:u8); } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index a34c5bf2..5f0accc6 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -11,11 +11,12 @@ pub struct BackgroundFetcher{ current_x_pos:u8, rendering_window:bool, fetcher_state_machine:FetcherStateMachine, + scanline_rendering_started:bool, } impl BackgroundFetcher{ pub fn new()->Self{ - let state_machine = [FetchingState::Sleep, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; + let state_machine = [FetchingState::Sleep, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Push, FetchingState::Sleep]; BackgroundFetcher{ fetcher_state_machine:FetcherStateMachine::new(state_machine), current_x_pos:0, @@ -23,6 +24,7 @@ impl BackgroundFetcher{ window_line_counter:0, rendering_window:false, has_wy_reached_ly:false, + scanline_rendering_started:false } } @@ -31,6 +33,7 @@ impl BackgroundFetcher{ self.current_x_pos = 0; self.fetcher_state_machine.reset(); self.rendering_window = false; + self.scanline_rendering_started = false; } pub fn pause(&mut self){ @@ -77,8 +80,18 @@ impl BackgroundFetcher{ let high_data = vram.read_current_bank(address + 1); self.fetcher_state_machine.data.high_tile_data = high_data; + + // The gameboy has this quirk that in the first fetch of the scanline it reset itself after reaching the fetch high tile step + if !self.scanline_rendering_started{ + self.reset(); + self.scanline_rendering_started = true; + } } - FetchingState::Push if self.fifo.len() == 0 => { + FetchingState::Push => { + if self.fifo.len() != 0{ + // wait until the fifo is empty, dont advance the state machine either + return; + } if lcd_control & BIT_0_MASK == 0{ self.fifo.fill(&EMPTY_FIFO_BUFFER); self.current_x_pos += SPRITE_WIDTH; @@ -104,7 +117,7 @@ impl BackgroundFetcher{ } } } - _ => {} + FetchingState::Sleep => {} } self.fetcher_state_machine.advance(); } diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 9d12f435..7bc3cc30 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -50,6 +50,7 @@ pub struct GbPpu{ sprite_fetcher:SpriteFetcher, stat_triggered:bool, trigger_stat_interrupt:bool, + next_state:PpuState } impl GbPpu{ @@ -85,7 +86,8 @@ impl GbPpu{ bg_fetcher:BackgroundFetcher::new(), sprite_fetcher:SpriteFetcher::new(), pixel_x_pos:0, - scanline_started:false + scanline_started:false, + next_state:PpuState::OamSearch } } @@ -169,8 +171,9 @@ impl GbPpu{ let mut m_cycles_counter = 0; while m_cycles_counter < m_cycles{ - match self.state{ + match self.next_state{ PpuState::OamSearch=>{ + self.state = PpuState::OamSearch; // first iteration if self.m_cycles_passed == 0{ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; @@ -199,11 +202,12 @@ impl GbPpu{ m_cycles_counter += scope_m_cycles_passed as u32; if self.m_cycles_passed == OAM_SEARCH_M_CYCLES_LENGTH{ - self.state = PpuState::PixelTransfer; + self.next_state = PpuState::PixelTransfer; self.scanline_started = false; } } PpuState::Hblank=>{ + self.state = PpuState::Hblank; let m_cycles_to_add = std::cmp::min((m_cycles - m_cycles_counter) as u16, HBLANK_M_CYCLES_LENGTH - self.m_cycles_passed); self.m_cycles_passed += m_cycles_to_add; m_cycles_counter += m_cycles_to_add as u32; @@ -213,7 +217,7 @@ impl GbPpu{ self.m_cycles_passed = 0; self.ly_register += 1; if self.ly_register == SCREEN_HEIGHT as u8{ - self.state = PpuState::Vblank; + self.next_state = PpuState::Vblank; //reseting the window counter on vblank self.bg_fetcher.window_line_counter = 0; self.bg_fetcher.has_wy_reached_ly = false; @@ -223,7 +227,7 @@ impl GbPpu{ } } else{ - self.state = PpuState::OamSearch; + self.next_state = PpuState::OamSearch; if self.oam_search_interrupt_request{ self.trigger_stat_interrupt = true; } @@ -231,12 +235,13 @@ impl GbPpu{ } } PpuState::Vblank=>{ + self.state = PpuState::Vblank; let m_cycles_to_add = std::cmp::min((m_cycles - m_cycles_counter) as u16, VBLANK_M_CYCLES_LENGTH - self.m_cycles_passed); self.m_cycles_passed += m_cycles_to_add; m_cycles_counter += m_cycles_to_add as u32; if self.m_cycles_passed == VBLANK_M_CYCLES_LENGTH{ - self.state = PpuState::OamSearch; + self.next_state = PpuState::OamSearch; if self.oam_search_interrupt_request{ self.trigger_stat_interrupt = true; } @@ -251,6 +256,7 @@ impl GbPpu{ } PpuState::PixelTransfer=>{ + self.state = PpuState::PixelTransfer; while m_cycles_counter < m_cycles && self.pixel_x_pos < SCREEN_WIDTH as u8{ for _ in 0..4{ if self.lcd_control & BIT_1_MASK != 0{ @@ -263,7 +269,7 @@ impl GbPpu{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); self.try_push_to_lcd(); if self.pixel_x_pos == SCREEN_WIDTH as u8{ - self.state = PpuState::Hblank; + self.next_state = PpuState::Hblank; if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; } @@ -286,7 +292,12 @@ impl GbPpu{ } } - let m_cycles_for_state = match self.state{ + // If there was a state change I want to run the state machine another m_cycle + // in order to sync the stat register containing the ppu state (by allowing state and next_state to sync) + if self.next_state as u8 != self.state as u8{ + return 1; + } + let m_cycles_for_state = match self.next_state{ PpuState::Vblank => ((self.m_cycles_passed / HBLANK_M_CYCLES_LENGTH)+1) * HBLANK_M_CYCLES_LENGTH, PpuState::Hblank => HBLANK_M_CYCLES_LENGTH, PpuState::OamSearch => OAM_SEARCH_M_CYCLES_LENGTH, diff --git a/lib_gb/src/ppu/ppu_state.rs b/lib_gb/src/ppu/ppu_state.rs index 31bfb996..3034e3f4 100644 --- a/lib_gb/src/ppu/ppu_state.rs +++ b/lib_gb/src/ppu/ppu_state.rs @@ -14,6 +14,12 @@ impl Clone for PpuState{ } } +impl PartialEq for PpuState{ + fn eq(&self, other: &Self) -> bool { + core::mem::discriminant(self) == core::mem::discriminant(other) + } +} + impl PpuState{ pub fn from_u8(mut value:u8)->Self{ value = value & 0b0000_0011; diff --git a/lib_gb/tests/memory_stub.rs b/lib_gb/tests/memory_stub.rs index 9fd1f157..8f00f440 100644 --- a/lib_gb/tests/memory_stub.rs +++ b/lib_gb/tests/memory_stub.rs @@ -5,11 +5,11 @@ pub struct MemoryStub{ } impl Memory for MemoryStub{ - fn read(&mut self, address:u16)->u8{ + fn read(&mut self, address:u16, _m_cycles:u8)->u8{ self.data[address as usize] } - fn write(&mut self, address:u16, value:u8){ + fn write(&mut self, address:u16, value:u8, _m_cycles:u8){ self.data[address as usize] = value; } } \ No newline at end of file diff --git a/lib_gb/tests/rotate_shift_tests.rs b/lib_gb/tests/rotate_shift_tests.rs index 0520a265..7b1b4043 100644 --- a/lib_gb/tests/rotate_shift_tests.rs +++ b/lib_gb/tests/rotate_shift_tests.rs @@ -1,21 +1,9 @@ +mod memory_stub; + +use memory_stub::MemoryStub; use lib_gb::cpu::opcodes::rotate_shift_instructions::*; use lib_gb::cpu::gb_cpu::*; use lib_gb::cpu::flag::Flag; -use lib_gb::mmu::memory::Memory; - -struct MemoryStub{ - pub data:[u8;0xFFFF] -} - -impl Memory for MemoryStub{ - fn read(&mut self, address:u16)->u8{ - self.data[address as usize] - } - - fn write(&mut self, address:u16, value:u8){ - self.data[address as usize] = value; - } -} #[test] fn test_rlc_r(){ From 2362e4e7c6f64d5914806398a1c0741e12df05d9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 18 Sep 2022 00:55:47 +0300 Subject: [PATCH 64/70] Add some of mooneye tests to the integration tests * Add some of the PPU acceptane tests to the integration tests to be run each time. The tests I added are: - intr_2_0_timing - intr_2_mode0_timing - intr_2_mode3_timing - intr_2_oam_ok_timing The tests can be found here - https://github.com/Gekkio/mooneye-test-suite --- .gitignore | 1 + Cargo.lock | 207 +++++++++++++++++++++++++++++- lib_gb/Cargo.toml | 1 + lib_gb/tests/integration_tests.rs | 113 +++++++++++----- 4 files changed, 285 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 2f57324e..df49abca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ lib_*/Cargo.lock Dependencies/ *.wav +*.bmp ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/Cargo.lock b/Cargo.lock index c058dd90..626f41da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,12 @@ dependencies = [ "log", ] +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + [[package]] name = "bitflags" version = "1.3.2" @@ -64,6 +70,12 @@ version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" +[[package]] +name = "bytemuck" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" + [[package]] name = "byteorder" version = "1.4.3" @@ -151,6 +163,12 @@ dependencies = [ "cc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "core-foundation" version = "0.9.1" @@ -318,6 +336,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "exr" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c26a90d9dd411a3d119d6f55752fb4c134ca243250c32fb9cab7b2561638d2" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide 0.5.4", + "smallvec", + "threadpool", +] + [[package]] name = "fast_image_resize" version = "0.9.3" @@ -346,7 +379,20 @@ dependencies = [ "cfg-if", "crc32fast", "libc", - "miniz_oxide", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", ] [[package]] @@ -451,13 +497,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -481,9 +539,9 @@ dependencies = [ [[package]] name = "half" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5956d4e63858efaec57e0d6c1c2f6a41e1487f830314a324ccd7e2223a7ca0" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" @@ -582,6 +640,25 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "image_inter" version = "1.0.0" @@ -623,6 +700,15 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "jpeg-decoder" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.55" @@ -638,11 +724,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "lib_gb" version = "2.1.0" dependencies = [ "criterion", + "image", "log", "reqwest", "zip", @@ -710,6 +803,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.7.13" @@ -746,6 +848,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "native-tls" version = "0.2.8" @@ -795,6 +906,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -888,6 +1010,26 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -934,6 +1076,18 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "png" +version = "0.17.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide 0.5.4", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1146,6 +1300,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1307,6 +1467,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" +dependencies = [ + "lock_api", +] + [[package]] name = "syn" version = "1.0.77" @@ -1361,6 +1530,26 @@ dependencies = [ "syn", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiff" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7259662e32d1e219321eb309d5f9d898b779769d81b76e762c07c8e5d38fcb65" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.1.44" @@ -1639,6 +1828,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "winapi" version = "0.3.9" diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 123debcb..23670310 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -14,6 +14,7 @@ u16pixel = [] criterion = "0.3" reqwest = { version = "0.11", features = ["blocking"] } zip = "0.5" +image = "0.24" [[bench]] name = "lib_gb_bench" diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index d6f82f7a..87bf30aa 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -1,13 +1,12 @@ use std::collections::hash_map::DefaultHasher; +use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::io::Read; -use lib_gb::apu::audio_device::BUFFER_SIZE; -use lib_gb::ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; -use lib_gb::ppu::gfx_device::Pixel; -use lib_gb::{ - apu::audio_device::AudioDevice, keypad::joypad_provider::JoypadProvider, - machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, ppu::gfx_device::GfxDevice -}; +use lib_gb::mmu::gb_mmu::BOOT_ROM_SIZE; +use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{Pixel, GfxDevice}}; +use lib_gb::apu::audio_device::{BUFFER_SIZE, AudioDevice}; +use lib_gb::keypad::joypad_provider::JoypadProvider; +use lib_gb::machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}; struct CheckHashGfxDevice{ hash:u64, @@ -67,40 +66,78 @@ fn test_turtle_window_y_trigger_wx_offscreen(){ run_turtle_integration_test("window_y_trigger_wx_offscreen.gb", 15592061677463553443); } +#[test] +fn test_mooneye_acceptance_ppu_intr_2_0_timing(){ + run_mooneye_test_suite_test("acceptance/ppu/intr_2_0_timing.gb", 10509154465546589481); +} + +#[test] +fn test_mooneye_acceptance_ppu_intr_2_mode0_timing(){ + run_mooneye_test_suite_test("acceptance/ppu/intr_2_mode0_timing.gb", 13181382744438017604); +} + +#[test] +fn test_mooneye_acceptance_ppu_intr_2_mode3_timing(){ + run_mooneye_test_suite_test("acceptance/ppu/intr_2_mode3_timing.gb", 6495990171031472337); +} + +#[test] +fn test_mooneye_acceptance_ppu_intr_2_oam_ok_timing(){ + run_mooneye_test_suite_test("acceptance/ppu/intr_2_oam_ok_timing.gb", 1784377789505089325); +} + fn run_turtle_integration_test(program_name:&str, hash:u64){ let zip_url = "https://github.com/Powerlated/TurtleTests/releases/download/v1.0/release.zip"; - - let file = reqwest::blocking::get(zip_url).unwrap() - .bytes().unwrap(); + let program = get_ziped_program(zip_url, program_name); + run_integration_test(program, None, 100, hash, format!("The program: {} has failed", program_name)); +} - let cursor = std::io::Cursor::new(file.as_ref()); +fn run_mooneye_test_suite_test(program_name:&str, hash:u64){ + let zip_url = "https://gekkio.fi/files/mooneye-test-suite/mts-20220522-1522-55c535c/mts-20220522-1522-55c535c.zip"; + let boot_rom_url = "https://github.com/alloncm/MagenBoot/releases/download/0.1.1/dmg_boot.bin"; + let program_zip_path = format!("{}/{program_name}", "mts-20220522-1522-55c535c"); + let program = get_ziped_program(zip_url, program_zip_path.as_str()); + let boot_rom = reqwest::blocking::get(boot_rom_url).unwrap().bytes().unwrap().to_vec(); + run_integration_test(program, Some(boot_rom.try_into().unwrap()), 300, hash, format!("The program: {} has failed", program_zip_path)); +} +fn get_ziped_program(zip_url:&str, program_zip_path:&str)->Vec{ + let zip_file = reqwest::blocking::get(zip_url).unwrap().bytes().unwrap(); + let cursor = std::io::Cursor::new(zip_file.as_ref()); let mut programs = zip::ZipArchive::new(cursor).unwrap(); - let zip_file = programs.by_name(program_name).unwrap(); + let zip_file = programs.by_name(program_zip_path).unwrap(); let program = zip_file.bytes().map(|x|x.unwrap()).collect::>(); - - run_integration_test(program, 100, hash, format!("The program: {} has failed", program_name)); + return program; } fn run_integration_test_from_url(program_url:&str, frames_to_execute:u32, expected_hash:u64){ - let file = reqwest::blocking::get(program_url).unwrap() - .bytes().unwrap(); - + let file = reqwest::blocking::get(program_url).unwrap().bytes().unwrap(); let program = Vec::from(file.as_ref()); let fail_message = format!("The program {} has failed", program_url); - run_integration_test(program, frames_to_execute, expected_hash, fail_message); + run_integration_test(program, None, frames_to_execute, expected_hash, fail_message); } -fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u64, fail_message:String){ +fn run_integration_test(program:Vec, boot_rom:Option<[u8;BOOT_ROM_SIZE]>, frames_to_execute:u32, expected_hash:u64, fail_message:String){ let mut mbc = initialize_mbc(program, None); let mut last_hash:u64 = 0; let mut found = false; - let mut gameboy = GameBoy::new( - &mut mbc, - StubJoypadProvider{}, - StubAudioDevice{}, - CheckHashGfxDevice{hash:expected_hash,last_hash_p:&mut last_hash, found_p:&mut found} - ); + let mut gameboy = if let Some(boot_rom) = boot_rom { + GameBoy::new_with_bootrom( + &mut mbc, + StubJoypadProvider{}, + StubAudioDevice{}, + CheckHashGfxDevice{hash:expected_hash,last_hash_p:&mut last_hash, found_p:&mut found}, + boot_rom.try_into().expect("Error bootrom is not the correct size") + ) + } + else{ + GameBoy::new( + &mut mbc, + StubJoypadProvider{}, + StubAudioDevice{}, + CheckHashGfxDevice{hash:expected_hash,last_hash_p:&mut last_hash, found_p:&mut found} + ) + }; for _ in 0..frames_to_execute { gameboy.cycle_frame(); @@ -108,18 +145,18 @@ fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u6 return; } } - assert!(false, "{}", fail_message); + assert!(false, "{}", fail_message); } -// This function is for clcualting the hash of a new test rom +// This function is for calculating the hash of a new test rom /// # Examples /// ///``` ///#[test] ///fn calc_custom_rom_hash(){ -/// calc_hash("path_to_rom"); +/// calc_hash("path_to_rom", None /*in case no bootrom needed*/); ///} ///``` @@ -127,10 +164,11 @@ fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u6 #[ignore] fn generate_hash(){ let path = "path to rom"; - calc_hash(path); + let boot_rom_path = None; + calc_hash(path, boot_rom_path); } -fn calc_hash(rom_path:&str){ +fn calc_hash(rom_path:&str, boot_rom_path:Option<&str>){ static mut FRAMES_COUNTER:u32 = 0; static mut LAST_HASH:u64 = 0; struct GetHashGfxDevice; @@ -148,6 +186,13 @@ fn calc_hash(rom_path:&str){ unsafe{ if LAST_HASH == hash{ println!("{}", hash); + let mut vec_buffer = Vec::::new(); + for i in 0..buffer.len(){ + vec_buffer.push((buffer[i] & 0xFF) as u8); + vec_buffer.push((buffer[i] >> 8 & 0xFF) as u8); + vec_buffer.push((buffer[i] >> 16 & 0xFF) as u8); + } + image::save_buffer("output.bmp", &vec_buffer, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, image::ColorType::Rgb8).unwrap(); std::process::exit(0); } LAST_HASH = hash; @@ -162,7 +207,13 @@ fn calc_hash(rom_path:&str){ let mut mbc = initialize_mbc(program, None); - let mut gameboy = GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, GetHashGfxDevice{}); + let mut gameboy = if let Some(boot_rom_path) = boot_rom_path{ + let boot_rom = std::fs::read(boot_rom_path).expect("Cant find bootrom"); + GameBoy::new_with_bootrom(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, GetHashGfxDevice{}, boot_rom.try_into().unwrap()) + } + else{ + GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, GetHashGfxDevice{}) + }; loop {gameboy.cycle_frame();} } \ No newline at end of file From 76a76f93385fcbf77344ad198973a38aef1c78f0 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 18 Sep 2022 00:57:32 +0300 Subject: [PATCH 65/70] Add more resources and tests to the README * Add the new mooneye tests * Add the Nitty gritty gameboy timing and the mgba gbdoc to the resources in the README --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c8d1937..439a5cc3 100644 --- a/README.md +++ b/README.md @@ -48,18 +48,24 @@ On by default (to turn off pass `--no-default-features`) - CPU - Cycle accurate CPU - PPU - Cycle accurate fifo PPU - Timer - Mostly accurate timer -- APU - Cycle mostly accurate APU +- APU - mostly accurate APU - Tests - [Blargg's cpu_instrs](https://github.com/retrio/gb-test-roms/tree/master/cpu_instrs) - :thumbsup: - [dmg-acid2](https://github.com/mattcurrie/dmg-acid2) - :thumbsup: - [TurtleTests](https://github.com/Powerlated/TurtleTests) - :thumbsup: - [CPU cycle accurate](https://github.com/retrio/gb-test-roms/tree/master/instr_timing) - :thumbsup: + - [mooneye-test-suite](https://github.com/Gekkio/mooneye-test-suite) + - acceptance/ppu/intr_2_0_timing - :thumbsup: + - acceptance/ppu/intr_2_mode0_timing - :thumbsup: + - acceptance/ppu/intr_2_mode3_timing - :thumbsup: + - acceptance/ppu/intr_2_oam_ok_timing - :thumbsup: - APU passes some of [blargs dmg_sound tests](https://github.com/retrio/gb-test-roms/tree/master/dmg_sound)- :thumbsup: - - Timer passes most of [mooneye-gb tests](https://github.com/Gekkio/mooneye-gb/tree/master/tests/acceptance/timer) - :thumbsup: + - Timer passes most of [mooneye-test-suite](https://github.com/Gekkio/mooneye-test-suite/tree/main/acceptance/timer) - :thumbsup: ### Games Tested - Pokemon Red - :thumbsup: - Tetris - :thumbsup: +- Super Mario World - :thumbsup: ## GameBoy Color @@ -73,3 +79,5 @@ Curerently there is no Support (support is planned in the future) - [Hactix's awsome blog post](https://hacktix.github.io/GBEDG/) - [Nightshade's awsome blog post](https://nightshade256.github.io/2021/03/27/gb-sound-emulation.html) - [The Ultimate GameBoy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI) +- [Nitty gritty Gameboy timing](http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.xt) +- [mgba gbdoc](https://mgba-emu.github.io/gbdoc/) From c492f59e6f05754059328ae578951e0d2ee4547b Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Sep 2022 18:02:29 +0300 Subject: [PATCH 66/70] Improve RPI detection in bcm host lib --- bcm_host/build.rs | 3 ++- bcm_host/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bcm_host/build.rs b/bcm_host/build.rs index b71829d5..974e86a6 100644 --- a/bcm_host/build.rs +++ b/bcm_host/build.rs @@ -1,4 +1,5 @@ fn main() { - #[cfg(target_os = "linux")] + // Checking for rpi + #[cfg(all(target_os = "linux", target_arch = "arm"))] println!("cargo:rustc-link-lib=bcm_host"); } \ No newline at end of file diff --git a/bcm_host/src/lib.rs b/bcm_host/src/lib.rs index 80944ad5..68fb6980 100644 --- a/bcm_host/src/lib.rs +++ b/bcm_host/src/lib.rs @@ -1,4 +1,5 @@ -cfg_if::cfg_if!{ if #[cfg(target_os = "linux")]{ +// Checking for rpi +cfg_if::cfg_if!{ if #[cfg(all(target_os = "linux", target_arch = "arm"))]{ pub mod bcm; pub use bcm::BcmHost; }} \ No newline at end of file From f4139f1154c8b51b1d443174b963bc7485efa942 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Sep 2022 18:10:32 +0300 Subject: [PATCH 67/70] Add apu and sdl features and graceful exit on rpi * sdl feature will enable sdl2 linking (on by defualt) * apu feature enable the apu. Exsists cause right now on the rpi zero2 i dont want apu (too slow). * Add SIGINT signal handler to exit without sdl Also arrange main a bit --- gb/Cargo.toml | 22 ++++--- gb/src/main.rs | 85 ++++++++++++++++--------- gb/src/rpi_gpio/gpio_joypad_provider.rs | 4 +- gb/src/sdl/utils.rs | 5 +- lib_gb/Cargo.toml | 1 + lib_gb/src/mmu/io_bus.rs | 5 +- 6 files changed, 79 insertions(+), 43 deletions(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 9a0dec89..0bc37bf3 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -16,21 +16,23 @@ bcm_host = {path = "../bcm_host", optional = true} log = "0.4" fern = "0.6" chrono = "0.4" -sdl2 = "0.35" -wav = "1.0" +sdl2 = {version = "0.35", optional = true} +wav = {version = "1.0", optional = true} crossbeam-channel = "0.5" cfg-if = "1.0" crossterm = "0.23" rppal = {version = "0.13", optional = true} libc = {version = "0.2", optional = true} -nix = {version = "0.24", features = ["ioctl"], optional = true} +nix = {version = "0.24", optional = true} [features] -default = ["static-sdl"] -sdl-resample = [] -push-audio = [] -static-sdl = ["sdl2/bundled", "sdl2/static-link"] -static-scale = [] +default = ["static-sdl", "apu"] +sdl = ["sdl2"] +sdl-resample = ["apu"] +push-audio = ["apu"] +static-sdl = ["sdl", "sdl2/bundled", "sdl2/static-link"] +static-scale = ["sdl"] u16pixel = ["lib_gb/u16pixel"] -rpi = ["rppal", "u16pixel", "image_inter"] -mmio = ["rpi", "nix", "libc", "bcm_host"] # requires sudo \ No newline at end of file +apu = ["lib_gb/apu", "sdl", "wav"] +rpi = ["rppal", "u16pixel", "image_inter", "nix/signal"] +mmio = ["rpi", "nix/ioctl", "libc", "bcm_host"] # requires sudo \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 770dfcfd..339d130d 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -6,10 +6,12 @@ mod rpi_gpio; mod audio{ pub mod audio_resampler; pub mod multi_device_audio; + #[cfg(feature = "apu")] pub mod wav_file_audio_device; #[cfg(not(feature = "sdl-resample"))] pub mod manual_audio_resampler; } +#[cfg(feature = "sdl")] mod sdl{ pub mod utils; #[cfg(not(feature = "u16pixel"))] @@ -17,6 +19,7 @@ mod sdl{ #[cfg(feature = "sdl-resample")] pub mod sdl_audio_resampler; + #[cfg(feature = "apu")] cfg_if::cfg_if!{ if #[cfg(feature = "push-audio")]{ pub mod sdl_push_audio_device; @@ -40,20 +43,27 @@ cfg_if::cfg_if!{ } } -use crate::{audio::multi_device_audio::*, audio::audio_resampler::ResampledAudioDevice, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; +use crate::{audio::multi_device_audio::*, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice}; use joypad_terminal_menu::{MenuOption, JoypadTerminalMenu, TerminalRawModeJoypadProvider}; -use lib_gb::{keypad::button::Button, GB_FREQUENCY, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}}; -use sdl2::sys::*; +use lib_gb::{keypad::button::Button, apu::audio_device::*, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}}; use std::{fs, env, result::Result, vec::Vec}; use log::info; +cfg_if::cfg_if! {if #[cfg(feature = "apu")]{ + use lib_gb::GB_FREQUENCY; + use crate::audio::audio_resampler::ResampledAudioDevice; +}} +#[cfg(feature = "sdl")] +use sdl2::sys::*; -const SCREEN_SCALE:usize = 4; const TURBO_MUL:u8 = 1; cfg_if::cfg_if!{ if #[cfg(feature = "rpi")]{ + const RESET_PIN_BCM:u8 = 14; + const DC_PIN_BCM:u8 = 15; + const LED_PIN_BCM:u8 = 25; use crate::rpi_gpio::gpio_joypad_provider::*; - fn buttons_mapper(button:&Button)->GpioPin{ + fn buttons_mapper(button:&Button)->GpioBcmPin{ match button{ Button::A => 18, Button::B => 17, @@ -67,6 +77,7 @@ cfg_if::cfg_if!{ } } else{ + const SCREEN_SCALE:usize = 4; use sdl2::sys::SDL_Scancode; fn buttons_mapper(button:Button)->SDL_Scancode{ match button{ @@ -150,6 +161,8 @@ fn get_rom_selection(roms_path:&str)->String{ return result; } +static mut RUNNING:bool = true; + fn main() { let args: Vec = env::args().collect(); @@ -169,10 +182,7 @@ fn main() { } cfg_if::cfg_if!{ if #[cfg(feature = "rpi")]{ - let reset_pin = 14; - let dc_pin = 15; - let led_pin = 25; - let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(reset_pin, dc_pin, led_pin, TURBO_MUL, 0); + let mut gfx_device:rpi_gpio::ili9341_controller::Ili9341GfxDevice = rpi_gpio::ili9341_controller::Ili9341GfxDevice::new(RESET_PIN_BCM, DC_PIN_BCM, LED_PIN_BCM, TURBO_MUL, 0); }else{ let mut gfx_device = sdl::sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); @@ -180,20 +190,21 @@ fn main() { let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); - - - let mut running = true; - // Casting to ptr cause you cant pass a raw ptr (*const/mut T) to another thread - let running_ptr:usize = (&running as *const bool) as usize; let emualation_thread = std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn( - move || emulation_thread_main(args, program_name, mpmc_device, running_ptr) + move || emulation_thread_main(args, program_name, mpmc_device) ).unwrap(); unsafe{ + #[cfg(feature = "rpi")]{ + let handler = nix::sys::signal::SigHandler::Handler(sigint_handler); + nix::sys::signal::signal(nix::sys::signal::Signal::SIGINT, handler).unwrap(); + } + + #[cfg(feature = "sdl")] let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); loop{ - + #[cfg(feature = "sdl")] if SDL_PollEvent(event.as_mut_ptr()) != 0{ let event: SDL_Event = event.assume_init(); if event.type_ == SDL_EventType::SDL_QUIT as u32{ @@ -201,28 +212,44 @@ fn main() { } } - let buffer = r.recv().unwrap(); - gfx_device.swap_buffer(&*(buffer as *const [Pixel; SCREEN_WIDTH * SCREEN_HEIGHT])); + match r.recv() { + Result::Ok(buffer) => gfx_device.swap_buffer(&*(buffer as *const [Pixel; SCREEN_WIDTH * SCREEN_HEIGHT])), + Result::Err(_) => break, + } + } drop(r); - std::ptr::write_volatile(&mut running as *mut bool, false); + RUNNING = false; emualation_thread.join().unwrap(); + #[cfg(feature = "sdl")] SDL_Quit(); } } +#[cfg(feature = "rpi")] +extern "C" fn sigint_handler(_:std::os::raw::c_int){ + unsafe {RUNNING = false}; +} + // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread -fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { - let audio_device = sdl::ChosenAudioDevice::::new(44100, TURBO_MUL); - - let mut devices: Vec::> = Vec::new(); - devices.push(Box::new(audio_device)); - if check_for_terminal_feature_flag(&args, "--file-audio"){ - let wav_ad = audio::wav_file_audio_device::WavfileAudioDevice::::new(44100, GB_FREQUENCY, "output.wav"); - devices.push(Box::new(wav_ad)); - log::info!("Writing audio to file: output.wav"); +fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice) { + cfg_if::cfg_if!{ + if #[cfg(feature = "apu")]{ + let mut devices: Vec::> = Vec::new(); + let audio_device = sdl::ChosenAudioDevice::::new(44100, TURBO_MUL); + devices.push(Box::new(audio_device)); + + if check_for_terminal_feature_flag(&args, "--file-audio"){ + let wav_ad = audio::wav_file_audio_device::WavfileAudioDevice::::new(44100, GB_FREQUENCY, "output.wav"); + devices.push(Box::new(wav_ad)); + log::info!("Writing audio to file: output.wav"); + } + } + else{ + let devices: Vec::> = Vec::new(); + } } let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); @@ -260,7 +287,7 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic info!("initialized gameboy successfully!"); unsafe{ - while std::ptr::read_volatile(running_ptr as *const bool){ + while RUNNING{ gameboy.cycle_frame(); } } diff --git a/gb/src/rpi_gpio/gpio_joypad_provider.rs b/gb/src/rpi_gpio/gpio_joypad_provider.rs index 06035ebf..4b63f1b3 100644 --- a/gb/src/rpi_gpio/gpio_joypad_provider.rs +++ b/gb/src/rpi_gpio/gpio_joypad_provider.rs @@ -2,14 +2,14 @@ use lib_gb::keypad::{joypad::{Joypad, NUM_OF_KEYS},joypad_provider::JoypadProvid use lib_gb::utils::create_array; use rppal::gpio::{Gpio, InputPin}; -pub type GpioPin = u8; +pub type GpioBcmPin = u8; pub struct GpioJoypadProvider{ input_pins:[InputPin;NUM_OF_KEYS] } impl GpioJoypadProvider{ - pub fn newGpioPin>(mapper:F)->Self{ + pub fn newGpioBcmPin>(mapper:F)->Self{ let gpio = Gpio::new().unwrap(); let buttons = [Button::A,Button::B,Button::Start,Button::Select,Button::Up,Button::Down,Button::Right,Button::Left]; let mut counter = 0; diff --git a/gb/src/sdl/utils.rs b/gb/src/sdl/utils.rs index f8f41bb7..537ff218 100644 --- a/gb/src/sdl/utils.rs +++ b/gb/src/sdl/utils.rs @@ -1,5 +1,7 @@ -use std::{ffi::CStr, mem::MaybeUninit}; +use std::ffi::CStr; use sdl2::{libc::c_char, sys::*}; +#[cfg(feature = "apu")] +use std::mem::MaybeUninit; pub fn get_sdl_error_message()->&'static str{ unsafe{ @@ -9,6 +11,7 @@ pub fn get_sdl_error_message()->&'static str{ } } +#[cfg(feature = "apu")] pub fn init_sdl_audio_device(audio_spec:&SDL_AudioSpec)->SDL_AudioDeviceID{ let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 23670310..b0463b67 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -9,6 +9,7 @@ log = "0.4" [features] u16pixel = [] +apu = [] [dev-dependencies] criterion = "0.3" diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 5f127906..a5c7687f 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -204,7 +204,10 @@ impl IoBus{ self.ppu_cycles = 0; } fn cycle_apu(&mut self){ - self.apu_event_cycles = self.apu.cycle(self.apu_cycles_counter); + #[cfg(feature = "apu")]{ + self.apu_event_cycles = self.apu.cycle(self.apu_cycles_counter); + } + self.apu_cycles_counter = 0; } fn cycle_timer(&mut self){ From ab42753bb68ce239b5bb1d311bb8e49d7c25c16f Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Sep 2022 18:17:25 +0300 Subject: [PATCH 68/70] Fix dma and mmio and gracefull shutdown * DMA - Fix a GPU memory leak, add memory barriers and shutdown gracefully * SPI - Add option to start on slow clock and then move to fast clock and add memory barriers * ili9341 - Add slow initialization and shutdown gracefully --- gb/src/rpi_gpio/dma.rs | 40 +++++++++++++++++++++------ gb/src/rpi_gpio/ili9341_controller.rs | 17 +++++++++--- gb/src/rpi_gpio/mmio_spi.rs | 25 ++++++++++++++--- gb/src/rpi_gpio/mod.rs | 6 ++++ gb/src/rpi_gpio/rppal_spi.rs | 5 ++++ 5 files changed, 76 insertions(+), 17 deletions(-) diff --git a/gb/src/rpi_gpio/dma.rs b/gb/src/rpi_gpio/dma.rs index 4a819808..e191a006 100644 --- a/gb/src/rpi_gpio/dma.rs +++ b/gb/src/rpi_gpio/dma.rs @@ -96,7 +96,7 @@ impl GpuMemory{ const ALLOCATE_MEMORY_TAG:u32 = 0x3000C; const LOCK_MEMORY_TAG:u32 = 0x3000D; const UNLOCK_MEMORY_TAG:u32 = 0x3000E; - const RELEASE_MEMORY_TAG:u32 = 0x3000E; + const RELEASE_MEMORY_TAG:u32 = 0x3000F; const PAGE_SIZE:u32 = 4096; // This function converts the from the bus address of the SDRAM uncached memory to the arm physical address @@ -107,8 +107,16 @@ impl GpuMemory{ fn allocate(mbox:&Mailbox, size:u32, mem_fd:c_int)->GpuMemory{ let flags = (Self::MEM_ALLOC_FLAG_COHERENT | Self::MEM_ALLOC_FLAG_DIRECT) as u32; let handle = mbox.send_command(Self::ALLOCATE_MEMORY_TAG, [size, Self::PAGE_SIZE, flags]); - + // This is not documented well but after testing - on out of Gpu memory mailbox returns handle = 0 + if handle == 0{ + std::panic!("Error allocating Gpu memory! perhaps there is not enough free Gpu memory"); + } let bus_address = mbox.send_command(Self::LOCK_MEMORY_TAG, [handle]); + // This is not documented well but after testing - on invalid handle mailbox returns bus_address = 0 + if bus_address == 0{ + std::panic!("Error locking Gpu memory!"); + } + let virtual_address = unsafe{libc::mmap( std::ptr::null_mut(), size as libc::size_t, @@ -123,18 +131,15 @@ impl GpuMemory{ fn release(&self, mbox:&Mailbox){ unsafe{ - let result = libc::munmap(self.virtual_address_ptr as *mut c_void, self.size as libc::size_t); - if result != 0 { + if libc::munmap(self.virtual_address_ptr as *mut c_void, self.size as libc::size_t) != 0 { libc_abort("Error while trying to un map gpu memory"); } } - let status = mbox.send_command(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle]); - if status != 0{ + if mbox.send_command(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle]) != 0{ std::panic!("Error while trying to unlock gpu memory using mailbox"); } - let status = mbox.send_command(Self::RELEASE_MEMORY_TAG, [self.mailbox_memory_handle]); - if status != 0{ - std::panic!("Error while to release gpu memory using mailbox"); + if mbox.send_command(Self::RELEASE_MEMORY_TAG, [self.mailbox_memory_handle]) != 0{ + std::panic!("Error while trying to release gpu memory using mailbox"); } } } @@ -278,6 +283,8 @@ impl DmaSpiTransferer{ unsafe{dma_controller.init_dma_control_blocks()}; + log::info!("Initialized dma contorller"); + return dma_controller; } @@ -348,6 +355,10 @@ impl DmaSpiTransferer{ pub fn start_dma_transfer(&mut self, data:&[u8; SPI_BUFFER_SIZE], transfer_active_flag:u8){ unsafe{ + if (*self.tx_dma).read_cs() & 0x100 != 0{ + log::error!("Error in the tx dma"); + } + let data_len = Self::DMA_SPI_CHUNK_SIZE - Self::DMA_SPI_HEADER_SIZE as usize; // Removing the first 4 bytes from this length param let header = [transfer_active_flag, 0, (data_len & 0xFF) as u8, /*making sure this is little endian order*/ (data_len >> 8) as u8]; @@ -365,9 +376,12 @@ impl DmaSpiTransferer{ (*self.tx_dma).write_conblk_ad(self.tx_control_block_memory.bus_address); (*self.rx_dma).write_conblk_ad(self.rx_control_block_memory.bus_address); + memory_barrier(); // Sync all the memory operations happened in this function // Starting the dma transfer (*self.tx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); (*self.rx_dma).write_cs(Self::DMA_CS_ACTIVE | Self::DMA_CS_END); + // Since the DMA controller writes to the SPI registers adding a barrier (even though it wrties afterwards to the DMA registers) + memory_barrier(); // Change DMA to SPI } } @@ -400,11 +414,17 @@ impl DmaSpiTransferer{ impl Drop for DmaSpiTransferer{ fn drop(&mut self) { + // Finish current dma operation + self.end_dma_transfer(); + // reset the dma channels before releasing the memory unsafe{ // reset the dma channels (*self.tx_dma).write_cs(Self::DMA_CS_RESET); (*self.rx_dma).write_cs(Self::DMA_CS_RESET); + // clear the permaps for the channels + (*self.tx_dma).control_block.write_ti(0); + (*self.rx_dma).control_block.write_ti(0); // disable the channels I used let mask = !((1 << Self::TX_CHANNEL_NUMBER) | (1 << Self::RX_CHANNEL_NUMBER)); write_volatile(self.dma_enable_register_ptr, read_volatile(self.dma_enable_register_ptr) & mask); @@ -415,5 +435,7 @@ impl Drop for DmaSpiTransferer{ self.rx_control_block_memory.release(&self.mbox); self.source_buffer_memory.release(&self.mbox); self.tx_control_block_memory.release(&self.mbox); + + log::info!("Successfuly release dma resources"); } } \ No newline at end of file diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index 2cce635f..9bcbc7ff 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -41,6 +41,7 @@ pub const SPI_BUFFER_SIZE:usize = TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * s pub trait SpiController { fn new(dc_pin_number:u8)->Self; + fn fast_mode(&mut self); fn write(&mut self, command:Ili9341Commands, data:&[u8;SIZE]); fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]); } @@ -101,7 +102,7 @@ impl Ili9341Contoller{ // Configuring the screen spi.write(Ili9341Commands::MemoryAccessControl, &[0x20]); // This command tlit the screen 90 degree spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel; - spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x1F /*According to the docs this is 61 hrz */]); + spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x10 /*According to the docs this is 119 hrz, setting this option in order to avoid screen tearing on rpi zero2 */]); spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]); // Gamma values - pretty sure its redundant @@ -117,14 +118,21 @@ impl Ili9341Contoller{ /*---------------------------------------------------------------------------------------------------------------------- */ //End of fbcp-ili9341 inpired implementation + log::info!("Finish configuring ili9341"); + + // turn backlight on + led_pin.set_high(); + // Clear screen spi.write(Ili9341Commands::ColumnAddressSet, &[0,0,((ILI9341_SCREEN_WIDTH -1) >> 8) as u8, ((ILI9341_SCREEN_WIDTH -1) & 0xFF) as u8]); spi.write(Ili9341Commands::PageAddressSet, &[0,0,((ILI9341_SCREEN_HEIGHT -1) >> 8) as u8, ((ILI9341_SCREEN_HEIGHT -1) & 0xFF) as u8]); // using write and not write buffer since this is not the correct size spi.write(Ili9341Commands::MemoryWrite, &Self::CLEAN_BUFFER); - // turn backlight on - led_pin.set_high(); + // need to sleep before changing the clock after transferring pixels to the lcd + Self::sleep_ms(120); + + spi.fast_mode(); log::info!("finish ili9341 device init"); @@ -159,6 +167,7 @@ impl Ili9341Contoller{ impl Drop for Ili9341Contoller{ fn drop(&mut self) { + self.spi.write(Ili9341Commands::DisplayOff, &[]); self.led_pin.set_low(); self.reset_pin.set_high(); Self::sleep_ms(1); @@ -209,7 +218,7 @@ impl GfxDevice for Ili9341GfxDevice{ self.time_counter = self.time_counter.add(time.duration_since(self.last_time)); self.last_time = time; if self.time_counter.as_millis() > 1000{ - log::info!("FPS: {}", self.frames_counter); + log::debug!("FPS: {}", self.frames_counter); self.frames_counter = 0; self.time_counter = std::time::Duration::ZERO; } diff --git a/gb/src/rpi_gpio/mmio_spi.rs b/gb/src/rpi_gpio/mmio_spi.rs index 358a6a45..f1a49abd 100644 --- a/gb/src/rpi_gpio/mmio_spi.rs +++ b/gb/src/rpi_gpio/mmio_spi.rs @@ -3,10 +3,11 @@ use rppal::gpio::{OutputPin, IoPin}; use crate::rpi_gpio::dma::DmaSpiTransferer; -use super::{ili9341_controller::{Ili9341Commands, SpiController, SPI_BUFFER_SIZE}, decl_write_volatile_field, decl_read_volatile_field}; +use super::{ili9341_controller::{Ili9341Commands, SpiController, SPI_BUFFER_SIZE}, decl_write_volatile_field, decl_read_volatile_field, memory_barrier}; const BCM_SPI0_BASE_ADDRESS:usize = 0x20_4000; -const SPI_CLOCK_DIVISOR:u32 = 4; // the smaller the faster (on my system below 4 there are currptions) +const FAST_SPI_CLOCK_DIVISOR:u32 = 4; // the smaller the faster (on my ili9341 screen below 4 there are currptions) +const INIT_SPI_CLOCK_DIVISOR:u32 = 34; // slow clock for verifiying the initialization goes smooth with no corruptions // The register are 4 bytes each so making sure the allignment and padding are correct #[repr(C, align(4))] @@ -65,12 +66,14 @@ impl MmioSpi{ // ChipSelect = 0, ClockPhase = 0, ClockPolarity = 0 spi_cs0.set_low(); Self::setup_poll_fast_transfer(&mut *spi_registers); - (*spi_registers).write_clk(SPI_CLOCK_DIVISOR); + (*spi_registers).write_clk(INIT_SPI_CLOCK_DIVISOR); } - + memory_barrier(); // Change SPI to DMA let dma_transferer = DmaSpiTransferer::new(&bcm_host, Self::SPI_CS_DMAEN); + memory_barrier(); // Change DMA to SPI + log::info!("Finish initializing spi mmio interface"); MmioSpi { _bcm:bcm_host, spi_registers, dc_pin, _spi_pins: spi_pins, _spi_cs0: spi_cs0, last_transfer_was_dma: false, dma_transferer @@ -88,7 +91,9 @@ impl MmioSpi{ fn prepare_for_transfer(&mut self) { if self.last_transfer_was_dma{ + memory_barrier(); // Change SPI to DMA self.dma_transferer.end_dma_transfer(); + memory_barrier(); // Change DMA to SPI Self::setup_poll_fast_transfer(self.spi_registers); } } @@ -139,7 +144,9 @@ impl MmioSpi{ // Since generic_const_exprs is not stable yet Im reserving the first 4 bytes of the data variable for internal use fn write_dma_raw(&mut self, data:&[u8;SPI_BUFFER_SIZE]){ unsafe{(*self.spi_registers).write_cs(Self::SPI_CS_DMAEN | Self::SPI_CS_CLEAR)}; + memory_barrier(); // Change SPI to DMA self.dma_transferer.start_dma_transfer(data, Self::SPI_CS_TA as u8); + memory_barrier(); // Change DMA to SPI } } @@ -161,4 +168,14 @@ impl SpiController for MmioSpi{ fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { self.write_dma(command, data); } + + fn fast_mode(&mut self) { + unsafe{(*self.spi_registers).write_clk(FAST_SPI_CLOCK_DIVISOR)}; + } +} + +impl Drop for MmioSpi{ + fn drop(&mut self) { + unsafe{(*self.spi_registers).write_cs(Self::SPI_CS_CLEAR)}; + } } \ No newline at end of file diff --git a/gb/src/rpi_gpio/mod.rs b/gb/src/rpi_gpio/mod.rs index 3e258458..b317aafd 100644 --- a/gb/src/rpi_gpio/mod.rs +++ b/gb/src/rpi_gpio/mod.rs @@ -15,6 +15,12 @@ fn libc_abort(message:&str){ std::io::Result::<&str>::Err(std::io::Error::last_os_error()).expect(message); } +// According to the docs the raspberrypi requires memory barrier between reads and writes to differnet peripherals +#[inline] +pub(self) fn memory_barrier(){ + std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst); +} + macro_rules! decl_write_volatile_field{ ($function_name:ident, $field_name:ident) =>{ #[inline] unsafe fn $function_name(&mut self,value:u32){ diff --git a/gb/src/rpi_gpio/rppal_spi.rs b/gb/src/rpi_gpio/rppal_spi.rs index ca1e0e05..0042908f 100644 --- a/gb/src/rpi_gpio/rppal_spi.rs +++ b/gb/src/rpi_gpio/rppal_spi.rs @@ -44,4 +44,9 @@ impl SpiController for RppalSpi{ fn write_buffer(&mut self, command:Ili9341Commands, data:&[u8;SPI_BUFFER_SIZE]) { self.write(command, data); } + + fn fast_mode(&mut self) { + // No need to change the clock here, + // since we have no access directly to the clock register we cant set it high enough for it to matter :() + } } \ No newline at end of file From 05305ee3169496cf7d0c793860d4a8a451d995e3 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Sep 2022 18:17:49 +0300 Subject: [PATCH 69/70] Add RPI docs to the readme --- README.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 439a5cc3..db0f0a13 100644 --- a/README.md +++ b/README.md @@ -13,19 +13,6 @@ The main goal of this project is to be able to play Pokemon on my own emulator. ## How to use -```shell -magenboy [path_to_rom] [other_optional_flags] -``` - -### Optional flags - -* `--log` - Print logs in debug mode to a file -* `--file-audio` - Saves the audio to a file -* `--full-screen` - Full screen mode -* `--no-vsync` - Disable vsync -* `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look for `dmg_boot.bin` at the cwd) -* `--rom_menu [path to roms folder]` - Opens an interactive dialog uopn start to choose the rom from the folder - ### Building ```shell @@ -41,6 +28,25 @@ On by default (to turn off pass `--no-default-features`) * `rpi` - Input is from the RPI GPIO pins and output is to an ili9341 spi lcd connected to the RPI GPIO pins, activates the `u16pixel` feature. * `mmio` - Will interface the spi lcd screen using the Memory Mapped IO interface of the RPI for better performance (uses the DMA peripherals as well, activates the `rpi` feature. +### Running + +#### Desktop +```sh +magenboy [path_to_rom] [other_optional_flags] +``` + +#### Raspberry Pi +Coming soon! + +### Optional flags + +* `--log` - Print logs in debug mode to a file +* `--file-audio` - Saves the audio to a file +* `--full-screen` - Full screen mode +* `--no-vsync` - Disable vsync +* `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look for `dmg_boot.bin` at the cwd) +* `--rom_menu [path to roms folder]` - Opens an interactive dialog uopn start to choose the rom from the folder + ## GameBoy ### Development Status @@ -72,6 +78,7 @@ On by default (to turn off pass `--no-default-features`) Curerently there is no Support (support is planned in the future) ## Resources +### Gameboy - [The Pandocs](https://gbdev.io/pandocs/) - [gbops](https://izik1.github.io/gbops/index.html) - [The GameBoy Programming Manual](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwi2muaT98j4AhWwhc4BHRaxAaEQFnoECAcQAQ&url=https%3A%2F%2Farchive.org%2Fdownload%2FGameBoyProgManVer1.1%2FGameBoyProgManVer1.1.pdf&usg=AOvVaw3LoEvXhZRBH7r68qdXIhiP) @@ -81,3 +88,8 @@ Curerently there is no Support (support is planned in the future) - [The Ultimate GameBoy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI) - [Nitty gritty Gameboy timing](http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.xt) - [mgba gbdoc](https://mgba-emu.github.io/gbdoc/) + +### RPI +- [Raspberry Pi docs](https://www.raspberrypi.com/documentation/computers/processors.html) +- [juj/fbcp-ili9341 as a refference](https://github.com/juj/fbcp-ili9341) +- [Raspberry Pi DMA programming in C](https://iosoft.blog/2020/05/25/raspberry-pi-dma-programming/) \ No newline at end of file From 981bf77a2e8d2df3e40aa854dd60d443bdf77aec Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Sep 2022 18:35:38 +0300 Subject: [PATCH 70/70] Up versions to 2.2.0 --- Cargo.lock | 4 ++-- gb/Cargo.toml | 2 +- lib_gb/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 626f41da..2df29634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,7 +477,7 @@ dependencies = [ [[package]] name = "gb" -version = "2.1.0" +version = "2.2.0" dependencies = [ "bcm_host", "cfg-if", @@ -732,7 +732,7 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "lib_gb" -version = "2.1.0" +version = "2.2.0" dependencies = [ "criterion", "image", diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 0bc37bf3..0523f3eb 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gb" -version = "2.1.0" +version = "2.2.0" authors = ["alloncm "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index b0463b67..ac5bb76c 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lib_gb" -version = "2.1.0" +version = "2.2.0" authors = ["alloncm "] edition = "2018"