feat: integrate zig-clap with ZBA
This commit is contained in:
parent
3e786d02ac
commit
17b91db2ef
|
@ -1,3 +1,6 @@
|
||||||
[submodule "lib/SDL.zig"]
|
[submodule "lib/SDL.zig"]
|
||||||
path = lib/SDL.zig
|
path = lib/SDL.zig
|
||||||
url = https://github.com/MasterQ32/SDL.zig
|
url = https://github.com/MasterQ32/SDL.zig
|
||||||
|
[submodule "lib/zig-clap"]
|
||||||
|
path = lib/zig-clap
|
||||||
|
url = https://github.com/Hejsil/zig-clap
|
||||||
|
|
|
@ -15,7 +15,11 @@ pub fn build(b: *std.build.Builder) void {
|
||||||
const exe = b.addExecutable("zba", "src/main.zig");
|
const exe = b.addExecutable("zba", "src/main.zig");
|
||||||
|
|
||||||
// Bitfield type from FlorenceOS: https://github.com/FlorenceOS/
|
// Bitfield type from FlorenceOS: https://github.com/FlorenceOS/
|
||||||
exe.addPackage(.{ .name = "bitfield", .path = .{ .path = "lib/util/bitfield.zig" } });
|
// exe.addPackage(.{ .name = "bitfield", .path = .{ .path = "lib/util/bitfield.zig" } });
|
||||||
|
exe.addPackagePath("bitfield", "lib/util/bitfield.zig");
|
||||||
|
|
||||||
|
// Argument Parsing Library
|
||||||
|
exe.addPackagePath("clap", "lib/zig-clap/clap.zig");
|
||||||
|
|
||||||
// Zig SDL Bindings: https://github.com/MasterQ32/SDL.zig
|
// Zig SDL Bindings: https://github.com/MasterQ32/SDL.zig
|
||||||
const sdk = Sdk.init(b);
|
const sdk = Sdk.init(b);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit a2af4a9267c547ec0f91ea1a84b2a50760ca5483
|
|
@ -18,10 +18,10 @@ iwram: Iwram,
|
||||||
ewram: Ewram,
|
ewram: Ewram,
|
||||||
io: Io,
|
io: Io,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, sched: *Scheduler, path: []const u8) !Self {
|
pub fn init(alloc: Allocator, sched: *Scheduler, rom_path: []const u8, maybe_bios: ?[]const u8) !Self {
|
||||||
return Self{
|
return Self{
|
||||||
.pak = try GamePak.init(alloc, path),
|
.pak = try GamePak.init(alloc, rom_path),
|
||||||
.bios = try Bios.init(alloc, "./bin/gba_bios.bin"), // TODO: don't hardcode this + bundle open-sorce Boot ROM
|
.bios = try Bios.init(alloc, maybe_bios),
|
||||||
.ppu = try Ppu.init(alloc, sched),
|
.ppu = try Ppu.init(alloc, sched),
|
||||||
.iwram = try Iwram.init(alloc),
|
.iwram = try Iwram.init(alloc),
|
||||||
.ewram = try Ewram.init(alloc),
|
.ewram = try Ewram.init(alloc),
|
||||||
|
|
|
@ -3,33 +3,46 @@ const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
buf: []u8,
|
buf: ?[]u8,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, path: []const u8) !Self {
|
pub fn init(alloc: Allocator, maybe_path: ?[]const u8) !Self {
|
||||||
|
var buf: ?[]u8 = null;
|
||||||
|
if (maybe_path) |path| {
|
||||||
const file = try std.fs.cwd().openFile(path, .{});
|
const file = try std.fs.cwd().openFile(path, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
const len = try file.getEndPos();
|
const len = try file.getEndPos();
|
||||||
|
buf = try file.readToEndAlloc(alloc, len);
|
||||||
|
}
|
||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.buf = try file.readToEndAlloc(alloc, len),
|
.buf = buf,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: Self) void {
|
||||||
self.alloc.free(self.buf);
|
if (self.buf) |buf| self.alloc.free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get32(self: *const Self, idx: usize) u32 {
|
pub fn get32(self: *const Self, idx: usize) u32 {
|
||||||
return (@as(u32, self.buf[idx + 3]) << 24) | (@as(u32, self.buf[idx + 2]) << 16) | (@as(u32, self.buf[idx + 1]) << 8) | (@as(u32, self.buf[idx]));
|
if (self.buf) |buf|
|
||||||
|
return (@as(u32, buf[idx + 3]) << 24) | (@as(u32, buf[idx + 2]) << 16) | (@as(u32, buf[idx + 1]) << 8) | (@as(u32, buf[idx]));
|
||||||
|
|
||||||
|
std.debug.panic("[CPU|BIOS:32] ZBA tried to read from 0x{X:0>8} but no BIOS was provided.", .{idx});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get16(self: *const Self, idx: usize) u16 {
|
pub fn get16(self: *const Self, idx: usize) u16 {
|
||||||
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
if (self.buf) |buf|
|
||||||
|
return (@as(u16, buf[idx + 1]) << 8) | @as(u16, buf[idx]);
|
||||||
|
|
||||||
|
std.debug.panic("[CPU|BIOS:16] ZBA tried to read from 0x{X:0>8} but no BIOS was provided.", .{idx});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get8(self: *const Self, idx: usize) u8 {
|
pub fn get8(self: *const Self, idx: usize) u8 {
|
||||||
return self.buf[idx];
|
if (self.buf) |buf|
|
||||||
|
return buf[idx];
|
||||||
|
|
||||||
|
std.debug.panic("[CPU|BIOS:8] ZBA tried to read from 0x{X:0>8} but no BIOS was provided.", .{idx});
|
||||||
}
|
}
|
||||||
|
|
49
src/main.zig
49
src/main.zig
|
@ -1,5 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const SDL = @import("sdl2");
|
const SDL = @import("sdl2");
|
||||||
|
const clap = @import("clap");
|
||||||
|
|
||||||
const emu = @import("emu.zig");
|
const emu = @import("emu.zig");
|
||||||
const Bus = @import("Bus.zig");
|
const Bus = @import("Bus.zig");
|
||||||
|
@ -25,25 +26,46 @@ pub fn main() anyerror!void {
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
defer std.debug.assert(!gpa.deinit());
|
defer std.debug.assert(!gpa.deinit());
|
||||||
|
|
||||||
// Handle CLI Arguments
|
// Parse CLI Arguments
|
||||||
const args = try std.process.argsAlloc(alloc);
|
const params = comptime [_]clap.Param(clap.Help){
|
||||||
defer std.process.argsFree(alloc, args);
|
clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
|
||||||
|
clap.parseParam("-b, --bios <PATH> Optional Path to GBA BIOS ROM. ") catch unreachable,
|
||||||
|
clap.parseParam("<PATH> Path to GBA GamePak ROM ") catch unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
const zba_args: []const []const u8 = args[1..];
|
var args = try clap.parse(clap.Help, ¶ms, .{});
|
||||||
|
defer args.deinit();
|
||||||
|
|
||||||
if (zba_args.len == 0) {
|
if (args.flag("--help")) {
|
||||||
std.log.err("Expected PATH to Gameboy Advance ROM as a CLI argument", .{});
|
return clap.help(std.io.getStdErr().writer(), ¶ms);
|
||||||
return;
|
|
||||||
} else if (zba_args.len > 1) {
|
|
||||||
std.log.err("Too many CLI arguments were provided", .{});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var maybe_bios: ?[]const u8 = null;
|
||||||
|
if (args.option("--bios")) |path| {
|
||||||
|
maybe_bios = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionals = args.positionals();
|
||||||
|
const stderr = std.io.getStdErr();
|
||||||
|
defer stderr.close();
|
||||||
|
|
||||||
|
const rom_path = switch (positionals.len) {
|
||||||
|
1 => positionals[0],
|
||||||
|
0 => {
|
||||||
|
try stderr.writeAll("ZBA requires a positional path to a GamePak ROM.\n");
|
||||||
|
return CliError.InsufficientOptions;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
try stderr.writeAll("ZBA received too many arguments.\n");
|
||||||
|
return CliError.UnneededOptions;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize Emulator
|
// Initialize Emulator
|
||||||
var scheduler = Scheduler.init(alloc);
|
var scheduler = Scheduler.init(alloc);
|
||||||
defer scheduler.deinit();
|
defer scheduler.deinit();
|
||||||
|
|
||||||
var bus = try Bus.init(alloc, &scheduler, zba_args[0]);
|
var bus = try Bus.init(alloc, &scheduler, rom_path, maybe_bios);
|
||||||
defer bus.deinit();
|
defer bus.deinit();
|
||||||
|
|
||||||
var cpu = Arm7tdmi.init(&scheduler, &bus);
|
var cpu = Arm7tdmi.init(&scheduler, &bus);
|
||||||
|
@ -158,3 +180,8 @@ fn sdlPanic() noreturn {
|
||||||
const str = @as(?[*:0]const u8, SDL.SDL_GetError()) orelse "unknown error";
|
const str = @as(?[*:0]const u8, SDL.SDL_GetError()) orelse "unknown error";
|
||||||
@panic(std.mem.sliceTo(str, 0));
|
@panic(std.mem.sliceTo(str, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CliError = error{
|
||||||
|
InsufficientOptions,
|
||||||
|
UnneededOptions,
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue