Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rpi zero 2 #96

Merged
merged 4 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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/)
3 changes: 2 additions & 1 deletion bcm_host/build.rs
Original file line number Diff line number Diff line change
@@ -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");
}
3 changes: 2 additions & 1 deletion bcm_host/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
}}
22 changes: 12 additions & 10 deletions gb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
apu = ["lib_gb/apu", "sdl", "wav"]
rpi = ["rppal", "u16pixel", "image_inter", "nix/signal"]
mmio = ["rpi", "nix/ioctl", "libc", "bcm_host"] # requires sudo
85 changes: 56 additions & 29 deletions gb/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ 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"))]
pub mod sdl_gfx_device;
#[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;
Expand All @@ -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,
Expand All @@ -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{
Expand Down Expand Up @@ -150,6 +161,8 @@ fn get_rom_selection(roms_path:&str)->String{
return result;
}

static mut RUNNING:bool = true;

fn main() {
let args: Vec<String> = env::args().collect();

Expand All @@ -169,60 +182,74 @@ 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::SpiType> = 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::SpiType> = 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"));
}}

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<SDL_Event> = 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{
break;
}
}

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<String>, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) {
let audio_device = sdl::ChosenAudioDevice::<ChosenResampler>::new(44100, TURBO_MUL);

let mut devices: Vec::<Box::<dyn AudioDevice>> = 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::<ChosenResampler>::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<String>, program_name: String, spsc_gfx_device: MpmcGfxDevice) {
cfg_if::cfg_if!{
if #[cfg(feature = "apu")]{
let mut devices: Vec::<Box::<dyn AudioDevice>> = Vec::new();
let audio_device = sdl::ChosenAudioDevice::<ChosenResampler>::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::<ChosenResampler>::new(44100, GB_FREQUENCY, "output.wav");
devices.push(Box::new(wav_ad));
log::info!("Writing audio to file: output.wav");
}
}
else{
let devices: Vec::<Box::<dyn AudioDevice>> = Vec::new();
}
}
let audio_devices = MultiAudioDevice::new(devices);
let mut mbc = initialize_mbc(&program_name);
Expand Down Expand Up @@ -260,7 +287,7 @@ fn emulation_thread_main(args: Vec<String>, 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();
}
}
Expand Down
Loading