diff --git a/src/core/emu.zig b/src/core/emu.zig index 55d475f..0f321c2 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -75,7 +75,7 @@ pub fn loadFirm(allocator: Allocator, system: System, firm_path: []const u8) !vo const buf = try file.readToEndAlloc(allocator, try file.getEndPos()); defer allocator.free(buf); - @memcpy(system.bus7.bios[0..buf.len], buf); + try system.bus7.bios.load(allocator, buf); } { // NDS9 BIOS @@ -90,7 +90,7 @@ pub fn loadFirm(allocator: Allocator, system: System, firm_path: []const u8) !vo const buf = try file.readToEndAlloc(allocator, try file.getEndPos()); defer allocator.free(buf); - @memcpy(system.bus9.bios[0..buf.len], buf); + try system.bus9.bios.load(allocator, buf); } } diff --git a/src/core/nds7/Bios.zig b/src/core/nds7/Bios.zig new file mode 100644 index 0000000..9bff1ce --- /dev/null +++ b/src/core/nds7/Bios.zig @@ -0,0 +1,47 @@ +const std = @import("std"); + +const Allocator = std.mem.Allocator; + +const log = std.log.scoped(.nds7_bios); + +const KiB = 0x400; +const len = 16 * KiB; + +buf: ?*align(4) [len]u8 = null, + +// FIXME: Currently we dupe here, should we just take ownership? +pub fn load(self: *@This(), allocator: Allocator, data: []u8) !void { + if (data.len != len) { + const acutal_size = @as(f32, @floatFromInt(data.len)) / KiB; + log.warn("BIOS was {d:.2} KiB (should be {} KiB)", .{ acutal_size, len }); + } + + const buf = try allocator.alignedAlloc(u8, 4, len); + @memset(buf, 0); + @memcpy(buf[0..data.len], data); + + self.* = .{ .buf = buf[0..len] }; +} + +pub fn deinit(self: @This(), allocator: Allocator) void { + if (self.buf) |ptr| allocator.destroy(ptr); +} + +// Note: Parts of 16MiB addrspace that aren't mapped to BIOS are typically undefined +pub fn read(self: *const @This(), comptime T: type, address: u32) T { + const readInt = std.mem.readIntLittle; + const byte_count = @divExact(@typeInfo(T).Int.bits, 8); + + // if (address >= len) return 0x0000_0000; // TODO: What is undefined actually? + + const ptr = self.buf orelse { + log.err("read(T: {}, address: 0x{X:0>8}) from BIOS but none was found!", .{ T, address }); + @panic("TODO: ability to load in NDS7 BIOS just-in-time"); + }; + + return readInt(T, ptr[address & (len - 1) ..][0..byte_count]); +} + +pub fn write(_: *const @This(), comptime T: type, address: u32, value: T) void { + log.err("write(T: {}, address: 0x{X:0>8}, value: 0x{X:}) but we're in the BIOS!", .{ T, address, value }); +} diff --git a/src/core/nds7/Bus.zig b/src/core/nds7/Bus.zig index 338c8be..0598322 100644 --- a/src/core/nds7/Bus.zig +++ b/src/core/nds7/Bus.zig @@ -5,6 +5,7 @@ const Scheduler = @import("../Scheduler.zig"); const SharedCtx = @import("../emu.zig").SharedCtx; const Wram = @import("../emu.zig").Wram; const Vram = @import("../ppu.zig").Vram; +const Bios = @import("Bios.zig"); const forceAlign = @import("../emu.zig").forceAlign; const Allocator = std.mem.Allocator; @@ -20,18 +21,13 @@ main: *[4 * MiB]u8, shr_wram: *Wram, wram: *[64 * KiB]u8, vram: *Vram, -io: io.Io, -bios: *[16 * KiB]u8, +io: io.Io, +bios: Bios, pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() { const wram = try allocator.create([64 * KiB]u8); @memset(wram, 0); - errdefer allocator.destroy(wram); - - const bios = try allocator.create([16 * KiB]u8); - @memset(bios, 0); - errdefer allocator.destroy(bios); return .{ .main = ctx.main, @@ -41,13 +37,13 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This( .scheduler = scheduler, .io = io.Io.init(ctx.io), - .bios = bios, + .bios = .{}, }; } pub fn deinit(self: *@This(), allocator: Allocator) void { allocator.destroy(self.wram); - allocator.destroy(self.bios); + self.bios.deinit(allocator); } pub fn reset(_: *@This()) void {} @@ -73,7 +69,7 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T } return switch (aligned_addr) { - 0x0000_0000...0x01FF_FFFF => readInt(T, self.bios[address & 0x3FFF ..][0..byte_count]), + 0x0000_0000...0x01FF_FFFF => self.bios.read(T, address), 0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]), 0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) { 0b00 => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]), @@ -107,7 +103,7 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v } switch (aligned_addr) { - 0x0000_0000...0x01FF_FFFF => log.err("tried to read from NDS7 BIOS: 0x{X:0>8}", .{aligned_addr}), + 0x0000_0000...0x01FF_FFFF => self.bios.write(T, address, value), 0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count], value), 0x0300_0000...0x037F_FFFF => switch (self.io.shr.wramcnt.mode.read()) { 0b00 => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value), diff --git a/src/core/nds9/Bios.zig b/src/core/nds9/Bios.zig new file mode 100644 index 0000000..daf831b --- /dev/null +++ b/src/core/nds9/Bios.zig @@ -0,0 +1,49 @@ +const std = @import("std"); + +const Allocator = std.mem.Allocator; + +const log = std.log.scoped(.nds7_bios); + +const KiB = 0x400; + +const _len = 4 * KiB; // real size of the NDS9 BIOS +const len = 32 * KiB; // size allocated to NDS9 BIOS + +buf: ?*align(4) [len]u8 = null, + +// FIXME: Currently we dupe here, should we just take ownership? +pub fn load(self: *@This(), allocator: Allocator, data: []u8) !void { + if (data.len != _len) { + const acutal_size = @as(f32, @floatFromInt(data.len)) / KiB; + log.warn("BIOS was {d:.2} KiB (should be {} KiB)", .{ acutal_size, len }); + } + + const buf = try allocator.alignedAlloc(u8, 4, len); + @memcpy(buf[0..data.len], data); + @memset(buf[data.len..], 0); + + self.* = .{ .buf = buf[0..len] }; +} + +pub fn deinit(self: @This(), allocator: Allocator) void { + if (self.buf) |ptr| allocator.destroy(ptr); +} + +// Note: Parts of 16MiB addrspace that aren't mapped to BIOS are typically undefined +pub fn read(self: *const @This(), comptime T: type, address: u32) T { + const readInt = std.mem.readIntLittle; + const byte_count = @divExact(@typeInfo(T).Int.bits, 8); + + // if (address >= len) return 0x0000_0000; // TODO: What is undefined actually? + + const ptr = self.buf orelse { + log.err("read(T: {}, address: 0x{X:0>8}) from BIOS but none was found!", .{ T, address }); + @panic("TODO: ability to load in NDS9 BIOS just-in-time"); + }; + + return readInt(T, ptr[address & (len - 1) ..][0..byte_count]); +} + +pub fn write(_: *const @This(), comptime T: type, address: u32, value: T) void { + log.err("write(T: {}, address: 0x{X:0>8}, value: 0x{X:}) but we're in the BIOS!", .{ T, address, value }); +} diff --git a/src/core/nds9/Bus.zig b/src/core/nds9/Bus.zig index 459a21f..7611197 100644 --- a/src/core/nds9/Bus.zig +++ b/src/core/nds9/Bus.zig @@ -5,6 +5,7 @@ const Ppu = @import("../ppu.zig").Ppu; const Scheduler = @import("../Scheduler.zig"); const SharedCtx = @import("../emu.zig").SharedCtx; const Wram = @import("../emu.zig").Wram; +const Bios = @import("Bios.zig"); const forceAlign = @import("../emu.zig").forceAlign; const Allocator = std.mem.Allocator; @@ -17,21 +18,16 @@ const log = std.log.scoped(.nds9_bus); main: *[4 * MiB]u8, wram: *Wram, +scheduler: *Scheduler, + io: io.Io, ppu: Ppu, - -bios: *[32 * KiB]u8, - -scheduler: *Scheduler, +bios: Bios, pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() { const dots_per_cycle = 3; // ARM946E-S runs twice as fast as the ARM7TDMI scheduler.push(.{ .nds9 = .draw }, 256 * dots_per_cycle); - const bios = try allocator.create([32 * KiB]u8); - @memset(bios, 0); - errdefer allocator.destroy(bios); - return .{ .main = ctx.main, .wram = ctx.wram, @@ -39,13 +35,13 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This( .scheduler = scheduler, .io = io.Io.init(ctx.io), - .bios = bios, + .bios = .{}, }; } pub fn deinit(self: *@This(), allocator: Allocator) void { self.ppu.deinit(allocator); - allocator.destroy(self.bios); + self.bios.deinit(allocator); } pub fn reset(_: *@This()) void { @@ -77,7 +73,7 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T 0x0300_0000...0x03FF_FFFF => self.wram.read(T, .nds9, aligned_addr), 0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr), 0x0600_0000...0x06FF_FFFF => self.ppu.vram.read(T, .nds9, aligned_addr), - 0xFFFF_0000...0xFFFF_FFFF => readInt(T, self.bios[address & 0x0000_7FFF ..][0..byte_count]), + 0xFFFF_0000...0xFFFF_FFFF => self.bios.read(T, address), else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }), }; } @@ -107,7 +103,7 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v 0x0300_0000...0x03FF_FFFF => self.wram.write(T, .nds9, aligned_addr, value), 0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value), 0x0600_0000...0x06FF_FFFF => self.ppu.vram.write(T, .nds9, aligned_addr, value), - 0xFFFF_0000...0xFFFF_FFFF => log.err("tried to read from NDS9 BIOS: 0x{X:0>8}", .{aligned_addr}), + 0xFFFF_0000...0xFFFF_FFFF => self.bios.write(T, address, value), else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }), } }