Skip to content

Latest commit

 

History

History
111 lines (76 loc) · 3.76 KB

NOTES.md

File metadata and controls

111 lines (76 loc) · 3.76 KB

Useful References

NOTES

CPU Architecture

Registers

  • 8-bit each, 16-bit combined
    • A + F
    • B + C
    • D + E
    • H + L
  • 16-bit
    • SP
    • PC

Memory

Cartridge

Instruction Handling

  • Look into abstracting out all arg types to simplify instruction handling
    • e.g., Operand can be Reg8, Reg16, Imm8u, Imm8i, Imm16, Mem, MemImm16

Joypad/Gamepad

Register 0xFF00 contains the joypad data. The 5th and 6th bits are used to select between directions and buttons. Whenever a keyboard event comes in from the window loop, we pass that down to the CPU to update the joypad register. Games typically snoop this register to determine whether or not a button is currently pressed.

For now, we only pass in the last pressed button in each frame -- so, 1 joypad input per frame.

A 2D GUI in Rust

SDL2 is the best option. You get 2D graphics (SW and HW rendering), keyboard events, and sound -- on all platforms. Also, the emulator can statically link against the SDL library.

However, the texture needs to passed into the main emulator loop. The reason is that the LCD can be updated mid-frame (heck, even mid-scanline!). So, we will define a trait within the gbc crate and implement it for the SDL Texture type. Then we can pass in a trait object to the inner GB frame handler.

PPU (GPU) Architecture

  • Basic registers, LY updates, and LCD STAT interrupts are fairly straightforward.
  • Some of the registers are latched during a scanline. In other words, writing to these registers has no effect until the next scanline. The registers are: SCY, SCX, LYC, WX, and WY.

APU Architecture

TBD...

SDL2

Example of streaming texture:

// Create texture in streaming mode

// .. in frame loop
canvas.clear();

texture.with_lock(None, |pixels, _| {
    // Draw the rendered frame
    for x in 0..LCD_WIDTH {
        for y in 0..LCD_HEIGHT {
            let color = frame_buffer.data[y * LCD_WIDTH + x];
            let i = y * LCD_WIDTH * 4 + x * 4;

            // LE representation of ARGB pixel
            // Caveat: need to be aware of pixel format and platform endianness (?)
            pixels[i+3] = color.alpha;
            pixels[i+2] = color.red;
            pixels[i+1] = color.green;
            pixels[i] = color.blue;
        }
    }
}).unwrap();

canvas.copy(&texture, None, None).unwrap();
canvas.present();

From worst to best:

  • HW and SW rendering w/ texture target: ~5 ms per frame
    • With HW, copy time decreases, but render time increases (makes sense)
  • SW w/ streaming texture: ~4 ms, render time goes way down (50 us), but copy overhead remains
  • HW w/ streaming texture: ~2 ms, both render and copy time decrease

WASM Support

Cartridge

  • Build a Cartridge from raw ROM bytes
    • Remove rom_path and rom_file fields
    • Figure out how to handle battery-backed RAM and RTC

In JS:

  • Use FileReader to read the file in JS and pass that in (or underlying buffer) to WASM
  • Pass the buffer to the Gameboy to initialize it

Save States

  • Use Vec<u8> for save state methods. Allow the upper layer to figure out how to handle the raw data.