1 unstable release
0.0.1 | May 16, 2022 |
---|
#58 in #gameboy
765KB
5K
SLoC
Padme core
Pixel As Dot-Matrix Emulator
padme-core is a Gameboy emulator engine. It itself doesn't rely on libstd or on dynamic memory, which makes it easier to use in any embedded platforms or web assembly.
Tests
For fast unit / integration / doc tests:
cargo test
For more expensive tests, you can use:
cargo test -- --ignored
or run all tests with:
cargo test -- --include-ignored
Examples
- Create your hardware components: a screen, a speaker and a serial output
These components should be specific to your platform.
use padme_core::{AudioSpeaker, Button, Pixel, Rom, Screen, SerialOutput, System};
struct MyScreen {
// ... your framebuffer implementation
}
impl Screen for MyScreen {
fn set_pixel(&mut self, pixel: &Pixel, x: u8, y: u8) {
// add pixel to your framebuffer
}
}
struct MySpeaker {
// ... your audio buffer implementation
}
impl AudioSpeaker for MySpeaker {
fn set_samples(&mut self, left: f32, right: f32) {
// add samples for left and right channels
}
}
struct MySerialConsole {
}
impl SerialOutput for MySerialConsole {
fn putchar(&mut self, ch: u8) {
// add char to
}
}
Alternatively, if you don't need / want some of these components, it's possible to use empty versions:
use padme::default::{NoScreen, NoSerial, NoSpeaker};
- Load a rom
use padme_core::Rom;
let bin: Vec<u8> = std::fs::read("some_game.gb").expect("could not find game");
let mut rom = Rom::load(bin).unwrap();
- Create your emulator and run it
use std::time::Instant;
use std::thread::sleep;
let mut emulator = System::new(rom, MyScreen, MySerialConsole, MySpeaker);
// Set the number of frame per seconds
// This also sets the number of cycles needed per frame given the fixed CPU clock frequency
emulator.set_frame_rate(60);
while running {
// We need to know how much time it took to display a frame
let t0 = Instant::now();
// This executes all the cycles needed to display one frame
emulator.update_frame();
// Deal with button inputs
emulator.set_button(Button::A, a_pressed);
emulator.set_button(Button::B, b_pressed);
emulator.set_button(Button::Start, start_pressed);
emulator.set_button(Button::Select, select_pressed);
emulator.set_button(Button::Up, up_pressed);
emulator.set_button(Button::Down, down_pressed);
emulator.set_button(Button::Left, left_pressed);
emulator.set_button(Button::Right, right_pressed);
// Now we just need to wait the remaining time before the next frame
// This is because we need to keep ~60 frames / second
let frame_time = t0.elapsed();
let min_frame_time = emulator.min_frame_time();
if frame_time < min_frame_time {
sleep(min_frame_time - frame_time);
}
}
Alternatively, you may want to execute the steps yourself:
use padme_core::{CLOCK_SPEED};
let cycles_per_frame = CLOCK_SPEED / 60;
let mut cycles = 0u32;
while cycles < cycles_per_frame {
cycles += emulator.step() as u32;
}
emulator.screen().update();
To see some implementations, check out padme-demo, a desktop demo or padme-browser, a web assembly version.
Features
- no_std
- Timer
- DMA
- CPU Dissassembler
- Pixel Processor Unit with fifo
- External Screen
- External Serial port
- Joypad
- Rom, MBC1, MBC3
- Integration tests
- Audio processor unit
Todo
- Add support for MBC2, MBC4, MBC5, MBC6, MBC7
- Add unit tests for each module