Skip to content

Commit

Permalink
[core] Improve loading API (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcellocordeiro committed Jul 27, 2024
1 parent 28e6033 commit 9f51112
Show file tree
Hide file tree
Showing 26 changed files with 213 additions and 165 deletions.
126 changes: 63 additions & 63 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ missing_panics_doc = "allow"
[workspace.dependencies]
arrayvec = "0.7.4"
bitflags = "2.6.0"
clap = { version = "4.5.9", features = ["derive"] }
clap = { version = "4.5.11", features = ["derive"] }
console_error_panic_hook = "0.1.7"
console_log = "1.0.0"
eframe = { version = "0.28.1", default-features = false }
egui = "0.28.1"
enum_dispatch = "0.3.13"
env_logger = "0.11.3"
env_logger = "0.11.5"
gb-core = { path = "./core/gb-core" }
image = { version = "0.25.2", default-features = false }
itertools = "0.13.0"
Expand Down
4 changes: 2 additions & 2 deletions core/gb-core-c/gb-bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ enum Button {

struct GameBoy;

struct GameBoy* gameboy_new();
struct GameBoy* gameboy_new(bool is_cgb);
void gameboy_destroy(struct GameBoy* gb_ptr);
void gameboy_reset(struct GameBoy* gb_ptr);
void gameboy_insert_cartridge(struct GameBoy* gb_ptr, const uint8_t* rom, uintptr_t rom_size);
void gameboy_load(struct GameBoy* gb_ptr, const uint8_t* rom, uintptr_t rom_size, const uint8_t* bootrom, uintptr_t bootrom_size);
void gameboy_run_frame(struct GameBoy* gb_ptr);
void gameboy_set_joypad_button(struct GameBoy* gb_ptr, enum Button button, bool value);
void gameboy_joypad_button_up(struct GameBoy* gb_ptr, enum Button button);
Expand Down
23 changes: 18 additions & 5 deletions core/gb-core-c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ use gb_core::{DeviceModel, GameBoy, ScreenPixels, SCREEN_HEIGHT, SCREEN_WIDTH};

#[no_mangle]
pub extern "C" fn gameboy_new(is_cgb: bool) -> *mut GameBoy {
Box::into_raw(Box::new(GameBoy::new(if is_cgb {
let device_model = if is_cgb {
DeviceModel::Cgb
} else {
DeviceModel::Dmg
})))
};

let gb = GameBoy::new(device_model);

Box::into_raw(Box::new(gb))
}

/// # Safety
Expand Down Expand Up @@ -40,17 +44,26 @@ pub unsafe extern "C" fn gameboy_reset(gb_ptr: *mut GameBoy) {
/// 1. The Game Boy core pointer cannot be null.
/// 2. The ROM array pointer cannot be null.
/// 3. The allocated size for the ROM has to be equal to `rom_size`.
/// 4. The bootrom is optional, but if provided its allocated size has to be equal to `bootrom_size`.
#[no_mangle]
pub unsafe extern "C" fn gameboy_insert_cartridge(
pub unsafe extern "C" fn gameboy_load(
gb_ptr: *mut GameBoy,
rom: *const u8,
rom_size: usize,
bootrom: *const u8,
bootrom_size: usize,
) {
let gb = &mut *gb_ptr;

let vec = unsafe { std::slice::from_raw_parts(rom, rom_size).to_vec() };
let rom = unsafe { std::slice::from_raw_parts(rom, rom_size).to_vec() };

let bootrom = if bootrom.is_null() {
None
} else {
Some(unsafe { std::slice::from_raw_parts(bootrom, bootrom_size).to_vec() })
};

gb.insert_cartridge(vec).unwrap();
gb.load(rom, bootrom).unwrap();
}

/// # Safety
Expand Down
17 changes: 11 additions & 6 deletions core/gb-core-swift/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ let configDir = "debug"
let configDir = "release"
#endif


let packageDir = Context.packageDirectory
let rootDir = "\(packageDir)/../.."
let libsDir = "\(rootDir)/target/\(configDir)"
Expand All @@ -21,7 +20,7 @@ let linkerSettings: [PackageDescription.LinkerSetting]
#if os(macOS)
linkerSettings = [
.unsafeFlags(["-L\(libsDir)/"]),
.linkedLibrary(libName)
.linkedLibrary(libName),
]
#else
linkerSettings = [.linkedLibrary("\(libsDir)/\(staticLibFile)")]
Expand All @@ -30,19 +29,25 @@ linkerSettings = [.linkedLibrary("\(libsDir)/\(staticLibFile)")]
let package = Package(
name: "GameBoyCore",
platforms: [
.macOS(.v14)
.macOS(.v14),
],
products: [
.library(name: "GameBoyCore", targets: ["GameBoyCore"])
.library(name: "GameBoyCore", targets: ["GameBoyCore"]),
],
targets: [
.target(
name: "CGameBoyCore",
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),
],
linkerSettings: linkerSettings
),
.target(
name: "GameBoyCore",
dependencies: ["CGameBoyCore"]
)
dependencies: ["CGameBoyCore"],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),
]
),
]
)
24 changes: 13 additions & 11 deletions core/gb-core-swift/Sources/GameBoyCore/GameBoyCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,31 @@ import CGameBoyCore
import Foundation

public final class GameBoy {
let gb = gameboy_new()
private let gb: OpaquePointer!

public static let width = Int(SCREEN_WIDTH)
public static let height = Int(SCREEN_HEIGHT)

public init() {}


public init(cgb: Bool = false) {
self.gb = gameboy_new(cgb)
}

deinit {
gameboy_destroy(gb)
}
public func load(_ rom: [UInt8]) {
gameboy_insert_cartridge(gb, rom, UInt(rom.count))

public func load(rom: [UInt8], bootrom: [UInt8]?) {
gameboy_load(gb, rom, UInt(rom.count), bootrom, UInt(bootrom?.count ?? 0))
}

public func runFrame() {
gameboy_run_frame(gb)
}

public func setButton(button: JoypadButton, value: Bool) {
gameboy_set_joypad_button(gb, button.toCoreButton, value)
}

public func draw(frame: inout [UInt8]) {
gameboy_draw_into_frame_rgba8888(gb, &frame)
}
Expand Down
4 changes: 2 additions & 2 deletions core/gb-core-swift/Sources/GameBoyCore/JoypadButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public enum JoypadButton: Int, CaseIterable {
case left = 5
case up = 6
case down = 7

public var toString: String {
switch self {
case .a:
Expand All @@ -30,7 +30,7 @@ public enum JoypadButton: Int, CaseIterable {
"Down"
}
}

var toCoreButton: Button {
switch self {
case .a:
Expand Down
4 changes: 2 additions & 2 deletions core/gb-core-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ impl GameBoy {
self.gb.reset();
}

pub fn insert_cartridge(&mut self, rom: Vec<u8>) {
self.gb.insert_cartridge(rom).unwrap();
pub fn load(&mut self, rom: Vec<u8>) {
self.gb.load(rom, None).unwrap();
}

pub fn run_frame(&mut self) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ impl Cpu {
pub(super) fn opcode_0x10(&self, memory: &mut impl MemoryInterface) {
// TODO?

// check for CGB mode?
memory.speed_switch_mut().process_speed_switch();
}

Expand Down
4 changes: 2 additions & 2 deletions core/gb-core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub const ONE_MIB_TO_KIB: usize = 1024;
pub const SCREEN_WIDTH: usize = 160;
pub const SCREEN_HEIGHT: usize = 144;

pub const SCREEN_PIXELS_SIZE: usize = SCREEN_WIDTH * SCREEN_HEIGHT * std::mem::size_of::<u32>();
pub const SCREEN_PIXELS_SIZE: usize = SCREEN_WIDTH * SCREEN_HEIGHT * size_of::<u32>();

// #[cfg(not(feature = "cgb"))]
// pub const TILE_DATA_FRAME_WIDTH: usize = 128;
Expand All @@ -15,7 +15,7 @@ pub const TILE_DATA_FRAME_WIDTH: usize = 128 * 2;

pub const TILE_DATA_FRAME_HEIGHT: usize = 192;
pub const TILE_DATA_FRAME_SIZE: usize =
TILE_DATA_FRAME_WIDTH * TILE_DATA_FRAME_HEIGHT * std::mem::size_of::<u32>();
TILE_DATA_FRAME_WIDTH * TILE_DATA_FRAME_HEIGHT * size_of::<u32>();
pub const TILES_PER_LINE: usize = 16;

pub type ScreenPixels = [u8; SCREEN_PIXELS_SIZE];
Expand Down
14 changes: 12 additions & 2 deletions core/gb-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,18 @@ impl GameBoy {
}
}

pub fn load(&mut self, rom: Vec<u8>, bootrom: Option<Vec<u8>>) -> Result<(), CartridgeError> {
if let Some(bootrom) = bootrom {
self.insert_bootrom(bootrom);
}

self.insert_cartridge(rom)?;

Ok(())
}

/// Insert the bootrom before the cartridge (TODO: improve this).
pub fn insert_bootrom(&mut self, bootrom: Vec<u8>) {
fn insert_bootrom(&mut self, bootrom: Vec<u8>) {
assert!(!self.cartridge_inserted());

let bootrom = Arc::<Box<[u8]>>::from(bootrom.into_boxed_slice());
Expand All @@ -67,7 +77,7 @@ impl GameBoy {
}

/// Reset before inserting a new cartridge.
pub fn insert_cartridge(&mut self, rom: Vec<u8>) -> Result<(), CartridgeError> {
fn insert_cartridge(&mut self, rom: Vec<u8>) -> Result<(), CartridgeError> {
let cartridge = Cartridge::new(rom)?;

self.memory.load_cartridge(&cartridge);
Expand Down
6 changes: 3 additions & 3 deletions core/gb-core/tests/cgb_acid2.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use common::{runners::run_until_break, validators::validate_screenshot};
use gb_core::GameBoy;
use gb_core::{DeviceModel, GameBoy};

mod common;

Expand All @@ -8,8 +8,8 @@ fn test_cgb_acid2() {
let name = "cgb-acid2";
let rom = include_bytes!("../../../external/gameboy-test-roms/cgb-acid2.gbc");

let mut gb = GameBoy::new(gb_core::DeviceModel::Cgb);
gb.insert_cartridge(rom.to_vec()).unwrap();
let mut gb = GameBoy::new(DeviceModel::Cgb);
gb.load(rom.to_vec(), None).unwrap();

run_until_break(&mut gb).unwrap();
validate_screenshot(&gb, name).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion core/gb-core/tests/common/runners.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ where
F: FnOnce(&mut GameBoy) -> Result<(), Error>,
{
let mut gb = GameBoy::new(device_model);
gb.insert_cartridge(rom.to_vec()).unwrap();
gb.load(rom.to_vec(), None)?;

runner(&mut gb)?;

Expand Down
4 changes: 2 additions & 2 deletions core/gb-core/tests/dmg_acid2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn test_dmg_acid2_dmg() {
let rom = include_bytes!("../../../external/gameboy-test-roms/dmg-acid2.gb");

let mut gb = GameBoy::new(DeviceModel::Dmg);
gb.insert_cartridge(rom.to_vec()).unwrap();
gb.load(rom.to_vec(), None).unwrap();

run_until_break(&mut gb).unwrap();
validate_screenshot(&gb, name).unwrap();
Expand All @@ -21,7 +21,7 @@ fn test_dmg_acid2_cgb() {
let rom = include_bytes!("../../../external/gameboy-test-roms/dmg-acid2.gb");

let mut gb = GameBoy::new(DeviceModel::Cgb);
gb.insert_cartridge(rom.to_vec()).unwrap();
gb.load(rom.to_vec(), None).unwrap();

run_until_break(&mut gb).unwrap();
validate_screenshot(&gb, name).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions core/gb-opcode-info/src/globals.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::sync::OnceLock;
use std::sync::LazyLock;

use crate::opcodes::{parse_json, OpcodeTable};

static OPCODES: OnceLock<OpcodeTable> = OnceLock::new();
static OPCODES: LazyLock<OpcodeTable> = LazyLock::new(|| parse_json().unwrap());

pub fn opcodes() -> &'static OpcodeTable {
OPCODES.get_or_init(|| parse_json().unwrap())
&OPCODES
}

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions platform/browser/src/lib/game-boy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import init, { GameBoy } from "gb-core-wasm";

await init();

const gameBoy = new GameBoy();
const gameBoy = new GameBoy(false);

export const SCREEN_WIDTH = 160;
export const SCREEN_HEIGHT = 144;

export function loadCartridge(rom: Uint8Array) {
gameBoy.insert_cartridge(rom);
export function load(rom: Uint8Array) {
gameBoy.load(rom, null);
}

export function reset() {
Expand Down
11 changes: 3 additions & 8 deletions platform/eframe/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ impl App {
bootrom_path: Option<String>,
rom_path: Option<String>,
) -> Self {
let mut gb = GameBoy::new(device_model);

if let Some(path) = bootrom_path {
let bootrom = std::fs::read(path).unwrap();
gb.insert_bootrom(bootrom);
}

// Maybe let the UI handle the errors?
let rom_path = {
if let Some(path) = rom_path {
Expand All @@ -42,8 +35,10 @@ impl App {
};

let rom = std::fs::read(&rom_path).unwrap();
let bootrom = bootrom_path.map(|path| std::fs::read(path).unwrap());

gb.insert_cartridge(rom).unwrap();
let mut gb = GameBoy::new(device_model);
gb.load(rom, bootrom).unwrap();
load_battery(&mut gb, &rom_path);

Self {
Expand Down
7 changes: 3 additions & 4 deletions platform/eframe/src/cartridge.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::path::Path;

use gb_core::GameBoy;
use log::info;

pub fn load_battery<P: AsRef<Path>>(gb: &mut GameBoy, rom_path: P) {
let rom_path = rom_path.as_ref();
Expand All @@ -11,17 +10,17 @@ pub fn load_battery<P: AsRef<Path>>(gb: &mut GameBoy, rom_path: P) {
let result = path.try_exists().unwrap();

if result {
info!("Loading battery file from {}", path.display());
log::info!("Loading battery file from {}", path.display());
let file = std::fs::read(path).unwrap();
gb.load_battery(file);
} else {
info!("No battery file found.");
log::info!("No battery file found.");
}
}

pub fn save_battery<P: AsRef<Path>>(gb: &GameBoy, rom_path: P) {
if let Some(battery) = gb.get_battery() {
info!("Saving battery file...");
log::info!("Saving battery file...");

let rom_path = rom_path.as_ref();
let path = Path::new(rom_path).with_extension("srm");
Expand Down
2 changes: 1 addition & 1 deletion platform/libretro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl RetroCore for Emulator {
}
};

let result = self.gb.insert_cartridge(rom);
let result = self.gb.load(rom, None);

match result {
Ok(()) => RetroLoadGameResult::Success {
Expand Down
Loading

0 comments on commit 9f51112

Please sign in to comment.