[][src]Crate rgy

rgy is no-std cross-platform Rust GameBoy emulator library.

The users of this library only needs to implement Hardware trait, which abstracts OS-specific function. Once it's implemented, the emulator works.

The following code is the example which just implements Hardware. The implementation does nothing. You can replace the body of each function with the actual meaningful logic.

use rgy::{Config, Key, Stream, VRAM_HEIGHT, VRAM_WIDTH};

struct Hardware {
    dummy_display: Vec<Vec<u32>>,

impl Hardware {
    fn new() -> Self {
        // Create a frame buffer with the size VRAM_WIDTH * VRAM_HEIGHT.
        let dummy_display = vec![vec![0u32; VRAM_HEIGHT]; VRAM_WIDTH];

        Self { dummy_display }

impl rgy::Hardware for Hardware {
    // Called when a horizontal line in the display is updated by the emulator.
    fn vram_update(&mut self, line: usize, buffer: &[u32]) {
        // `line` corresponds to the y coordinate.
        let y = line;

        for (x, col) in buffer.iter().enumerate() {
            // TODO: Update the pixels in the actual display here.
            self.dummy_display[x][y] = *col;

    // Called when the emulator checks if a key is pressed or not.
    fn joypad_pressed(&mut self, key: Key) -> bool {
        println!("Is {:?} pressed?", key);

        // TODO: Read a keyboard device and check if the `key` is pressed or not.


    // Called when the emulator plays a sound.
    fn sound_play(&mut self, _stream: Box<dyn Stream>) {
        // TODO: Play the wave pattern provided `Stream`.

    // Provides clock for the emulator.
    fn clock(&mut self) -> u64 {
        // TODO: Return the epoch in microseconds.
        let epoch = std::time::SystemTime::now()
            .expect("Couldn't get epoch");
        epoch.as_micros() as u64

    // Called when the emulator sends a byte to the serial port.
    fn send_byte(&mut self, _b: u8) {
        // TODO: Send a byte to a serial port.

    // Called when the emulator peeks a byte from the serial port.
    fn recv_byte(&mut self) -> Option<u8> {
        // TODO: Check the status of the serial port and read a byte if any.

    // Called every time the emulator executes an instruction.
    fn sched(&mut self) -> bool {
        // TODO: Do some periodic jobs if any. Return `true` to continue, `false` to stop the emulator.
        println!("It's running!");

    // Called when the emulator stores the save data to the battery-backed RAM.
    fn load_ram(&mut self, size: usize) -> Vec<u8> {
        // TODO: Return save data.
        vec![0; size]

    // Called when the emulator loads the save data from the battery-backed RAM.
    fn save_ram(&mut self, _ram: &[u8]) {
        // TODO: Store save data.

fn main() {
    // Create the default config.
    let cfg = Config::new();

    // Create the hardware instance.
    let hw = Hardware::new();

    // TODO: The content of a ROM file, which can be downloaded from the Internet.
    let rom = vec![0u8; 1024];

    // Run the emulator.
    rgy::run(cfg, &rom, hw);



CPU state.


Debugger interface.


Adaptor to register devices to MMU.


Decoder which evaluates each CPU instructions.


Handles memory and I/O port access from the CPU.



Configuration of the emulator.


Represents the entire emulator context.



Represents a key of the joypad.



The height of the VRAM.


The width of the VRAM.



The interface to abstracts the OS-specific functions.


Sound wave stream which generates the wave to be played by the sound device.



Run the emulator with the given configuration.


Run the emulator with the given configuration and debugger.