diff --git a/src/cartridge.zig b/src/cartridge.zig
index edc65d5..d565b9d 100644
--- a/src/cartridge.zig
+++ b/src/cartridge.zig
@@ -273,7 +273,6 @@ pub const MBC = struct {
rom_size: RomSize,
ram_size: RamSize,
- gpa: std.heap.GeneralPurposeAllocator(.{}),
allocator: std.mem.Allocator,
pub fn handle_register(self: *MBC, address: u16, byte: u8) void {
@@ -569,16 +568,14 @@ pub const MBC = struct {
self.banking_mode = mode & 0x01;
}
- pub fn new(filename: []u8) !MBC {
+ pub fn new(filename: []u8, alc: std.mem.Allocator) !MBC {
+ // It might be better to take a byte array with the rom already read
+ // instead of a filename to make the function easier to test and also
+ // move IO away
const file = try std.fs.cwd().openFile(filename, .{});
defer file.close();
- const size = try file.getEndPos();
-
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
- const allocator = gpa.allocator();
- const rom = try allocator.alloc(u8, size);
- _ = try file.readAll(rom);
+ const rom = try file.readToEndAlloc(alc, std.math.maxInt(usize));
std.debug.print("raw mbc {} rom size {}, ram size {}\n", .{
rom[0x147],
rom[0x148],
@@ -586,11 +583,16 @@ pub const MBC = struct {
});
const header = get_game_rom_metadata(rom);
- const ram = try allocator.alloc(u8, header.ram_size.num_bytes());
+ // The maximum size this can be is 128KiB. It might be a good idea to
+ // allocate buffer of this size globally and slice it off here instead
+ // of doing an allocation. The downside is that you can only have a
+ // single cartridge instance.
+ const ram = try alc.alloc(u8, header.ram_size.num_bytes());
+ // Use std.log. std.debug.print is for printf-debugging
std.debug.print("cartridge type: {}, size {}, rom size: {}, rom bytes: {}, rom banks: {}, ram size: {}\n", .{
header.cartridge_type,
- size,
+ rom.len,
header.rom_size,
header.rom_size.num_bytes(),
header.rom_size.num_banks(),
@@ -610,8 +612,7 @@ pub const MBC = struct {
.rom_size = header.rom_size,
.ram_size = header.ram_size,
- .gpa = gpa,
- .allocator = allocator,
+ .allocator = alc,
};
}
@@ -619,7 +620,6 @@ pub const MBC = struct {
pub fn deinit(self: *MBC) void {
self.allocator.free(self.ram);
self.allocator.free(self.rom);
- self.gpa.deinit();
}
};
pub fn get_game_rom_metadata(memory: []u8) GameBoyRomHeader {
diff --git a/src/cpu.zig b/src/cpu.zig
index c92fa2b..a503454 100644
--- a/src/cpu.zig
+++ b/src/cpu.zig
@@ -2604,7 +2604,7 @@ pub const CPU = struct {
return address;
}
- pub fn new(bus: *MemoryBus) !CPU {
+ pub fn new(bus: *MemoryBus) CPU {
const cpu: CPU = CPU{
.registers = Registers{
.A = 0x01,
diff --git a/src/gameboy.zig b/src/gameboy.zig
index 2c50dd0..167a3d4 100644
--- a/src/gameboy.zig
+++ b/src/gameboy.zig
@@ -22,8 +22,8 @@ pub const Gameboy = struct {
var joypad_ = joypad.Joypad.new();
var timer_ = timer.Timer.new();
timer_.tac.frequency = timer.Frequency.Hz4096;
- var memory_bus_ = try memory_bus.MemoryBus.new(&mbc_, &gpu_, &timer_, &joypad_);
- var cpu_ = try cpu.CPU.new(&memory_bus_);
+ var memory_bus_ = memory_bus.MemoryBus.new(&mbc_, &gpu_, &timer_, &joypad_);
+ var cpu_ = cpu.CPU.new(&memory_bus_);
return Gameboy{
.mbc = &mbc_,
diff --git a/src/memory_bus.zig b/src/memory_bus.zig
index 4c0dbd4..84529c3 100644
--- a/src/memory_bus.zig
+++ b/src/memory_bus.zig
@@ -81,7 +81,7 @@ pub const MemoryBus = struct {
interrupt_enable: IERegister,
interrupt_flag: IERegister,
- pub fn new(mbc_: *MBC, gpu_: *GPU, timer_: *timer.Timer, joypad_: *joypad.Joypad) !MemoryBus {
+ pub fn new(mbc_: *MBC, gpu_: *GPU, timer_: *timer.Timer, joypad_: *joypad.Joypad) MemoryBus {
var memory = [_]u8{0} ** 0x10000;
std.mem.copyForwards(u8, memory[0..0x7FFF], mbc_.rom[cartridge.FULL_ROM_START..cartridge.FULL_ROM_END]);
making the cartridge constructor take already read file content instead of doing IO and using a static global memory buffer instead of allocating one dynamically would remove all error handling from the entire gameboy constructor, which i find neat.