diff --git a/src/core/emu.zig b/src/core/emu.zig index d1b57dd..3b8fd7a 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -8,13 +8,16 @@ const Allocator = std.mem.Allocator; /// Load a NDS Cartridge /// /// intended to be used immediately after Emulator initialization -pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 { +pub fn load(allocator: Allocator, system: System, rom_path: []const u8) ![12]u8 { const log = std.log.scoped(.load_rom); - const rom_buf = try rom_file.readToEndAlloc(allocator, try rom_file.getEndPos()); - defer allocator.free(rom_buf); + const file = try std.fs.cwd().openFile(rom_path, .{}); + defer file.close(); - var stream = std.io.fixedBufferStream(rom_buf); + const buf = try file.readToEndAlloc(allocator, try file.getEndPos()); + defer allocator.free(buf); + + var stream = std.io.fixedBufferStream(buf); const header = try stream.reader().readStruct(Header); log.info("Title: \"{s}\"", .{std.mem.sliceTo(&header.title, 0)}); @@ -29,7 +32,7 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 log.debug("ARM9 Size: 0x{X:0>8}", .{header.arm9_size}); // Copy ARM9 Code into Main Memory - for (rom_buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| { + for (buf[header.arm9_rom_offset..][0..header.arm9_size], 0..) |value, i| { const address = header.arm9_ram_address + @as(u32, @intCast(i)); system.bus9.dbgWrite(u8, address, value); } @@ -45,7 +48,7 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 log.debug("ARM7 Size: 0x{X:0>8}", .{header.arm7_size}); // Copy ARM7 Code into Main Memory - for (rom_buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| { + for (buf[header.arm7_rom_offset..][0..header.arm7_size], 0..) |value, i| { const address = header.arm7_ram_address + @as(u32, @intCast(i)); system.bus7.dbgWrite(u8, address, value); } @@ -56,6 +59,39 @@ pub fn load(allocator: Allocator, system: System, rom_file: std.fs.File) ![12]u8 return header.title; } +/// Load NDS Firmware +pub fn loadFirm(allocator: Allocator, system: System, firm_path: []const u8) !void { + const log = std.log.scoped(.load_firm); + + { // NDS7 BIOS + const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios7.bin" }); + defer allocator.free(path); + + log.debug("bios7 path: {s}", .{path}); + + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + const buf = try file.readToEndAlloc(allocator, try file.getEndPos()); + defer allocator.free(buf); + + @memcpy(system.bus7.bios[0..buf.len], buf); + } + + { // NDS9 BIOS + const path = try std.mem.join(allocator, "/", &.{ firm_path, "bios9.bin" }); + defer allocator.free(path); + + const file = try std.fs.cwd().openFile(path, .{}); + defer file.close(); + + const buf = try file.readToEndAlloc(allocator, try file.getEndPos()); + defer allocator.free(buf); + + @memcpy(system.bus9.bios[0..buf.len], buf); + } +} + const bus_clock = 33513982; // 33.513982 Hz const dot_clock = 5585664; // 5.585664 Hz const arm7_clock = bus_clock; @@ -273,3 +309,27 @@ pub const System = struct { self.bus9.deinit(allocator); } }; + +// FIXME: Using Wram.Device here is jank. System should probably carry an Enum + some Generic Type Fns +pub fn handleInterrupt(comptime dev: Wram.Device, cpu: if (dev == .nds9) *System.Arm946es else *System.Arm7tdmi) void { + const Bus = if (dev == .nds9) System.Bus9 else System.Bus7; + const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr)); + + if (!bus_ptr.io.ime or cpu.cpsr.i.read()) return; // ensure irqs are enabled + if ((bus_ptr.io.ie.raw & bus_ptr.io.irq.raw) == 0) return; // ensure there is an irq to handle + + // TODO: Handle HALT + // HALTCNG (NDS7) and CP15 (NDS9) + + const ret_addr = cpu.r[15] - if (cpu.cpsr.t.read()) 0 else @as(u32, 4); + const spsr = cpu.cpsr; + + cpu.changeMode(.Irq); + cpu.cpsr.t.unset(); + cpu.cpsr.i.set(); + + cpu.r[14] = ret_addr; + cpu.spsr.raw = spsr.raw; + cpu.r[15] = if (dev == .nds9) 0xFFFF_0018 else 0x0000_0018; + cpu.pipe.reload(cpu); +} diff --git a/src/core/io.zig b/src/core/io.zig index acd0210..938d6a5 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -2,6 +2,8 @@ const std = @import("std"); const Bitfield = @import("bitfield").Bitfield; const Bit = @import("bitfield").Bit; +const System = @import("emu.zig").System; +const handleInterrupt = @import("emu.zig").handleInterrupt; const log = std.log.scoped(.shared_io); @@ -10,34 +12,10 @@ const log = std.log.scoped(.shared_io); // every other "shared I/O register" is just duplicated on both CPUs. So they shouldn't be here pub const Io = struct { - /// Interrupt Master Enable - /// Read/Write - ime: bool = false, - - /// Interrupt Enable - /// Read/Write - /// - /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable` - ie: u32 = 0x0000_0000, - - /// IF - Interrupt Request - /// Read/Write - /// - /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest` - irq: u32 = 0x0000_0000, - /// Inter Process Communication FIFO - ipc_fifo: IpcFifo = .{}, - - /// Post Boot Flag - /// Read/Write - /// - /// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg` - post_flg: u8 = @intFromEnum(nds7.PostFlag.in_progress), + ipc: Ipc = .{}, wramcnt: WramCnt = .{ .raw = 0x00 }, - - // TODO: DS Cartridge I/O Ports }; fn warn(comptime format: []const u8, args: anytype) u0 { @@ -45,13 +23,20 @@ fn warn(comptime format: []const u8, args: anytype) u0 { return 0; } -const IpcFifo = struct { +/// Inter-Process Communication +const Ipc = struct { const Sync = IpcSync; const Control = IpcFifoCnt; _nds7: Impl = .{}, _nds9: Impl = .{}, + // we need access to the CPUs to handle IPC IRQs + arm7tdmi: ?*System.Arm7tdmi = null, + arm946es: ?*System.Arm946es = null, + + // TODO: DS Cartridge I/O Ports + const Source = enum { nds7, nds9 }; const Impl = struct { @@ -69,6 +54,11 @@ const IpcFifo = struct { last_read: ?u32 = null, }; + pub fn configure(self: *@This(), system: System) void { + self.arm7tdmi = system.arm7tdmi; + self.arm946es = system.arm946es; + } + /// IPCSYNC /// Read/Write pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void { @@ -77,6 +67,13 @@ const IpcFifo = struct { self._nds7.sync.raw = masks.ipcFifoSync(self._nds7.sync.raw, value); self._nds9.sync.raw = masks.mask(self._nds9.sync.raw, (self._nds7.sync.raw >> 8) & 0xF, 0xF); + if (value >> 13 & 1 == 1 and self._nds9.sync.recv_irq.read()) { + const bus: *System.Bus9 = @ptrCast(@alignCast(self.arm946es.?.bus.ptr)); + + bus.io.irq.ipcsync.set(); + handleInterrupt(.nds9, self.arm946es.?); + } + if (value >> 3 & 1 == 1) { self._nds7.fifo.reset(); @@ -91,6 +88,13 @@ const IpcFifo = struct { self._nds9.sync.raw = masks.ipcFifoSync(self._nds9.sync.raw, value); self._nds7.sync.raw = masks.mask(self._nds7.sync.raw, (self._nds9.sync.raw >> 8) & 0xF, 0xF); + if (value >> 13 & 1 == 1 and self._nds7.sync.recv_irq.read()) { + const bus: *System.Bus7 = @ptrCast(@alignCast(self.arm7tdmi.?.bus.ptr)); + + bus.io.irq.ipcsync.set(); + handleInterrupt(.nds7, self.arm7tdmi.?); + } + if (value >> 3 & 1 == 1) { self._nds9.fifo.reset(); @@ -271,23 +275,10 @@ pub const masks = struct { } }; -pub const nds7 = struct { - pub const IntEnable = extern union { - raw: u32, - }; - - pub const IntRequest = IntEnable; - pub const PostFlag = enum(u8) { in_progress = 0, completed }; -}; - -pub const nds9 = struct { - pub const IntEnable = extern union { - raw: u32, - }; - - pub const IntRequest = IntEnable; - - pub const PostFlag = enum(u8) { in_progress = 0, completed }; +// FIXME: bitfields depends on NDS9 / NDS7 +pub const IntEnable = extern union { + ipcsync: Bit(u32, 16), + raw: u32, }; const Fifo = struct { diff --git a/src/core/nds7/Bus.zig b/src/core/nds7/Bus.zig index 4e7a3d9..338c8be 100644 --- a/src/core/nds7/Bus.zig +++ b/src/core/nds7/Bus.zig @@ -22,10 +22,16 @@ wram: *[64 * KiB]u8, vram: *Vram, io: io.Io, +bios: *[16 * KiB]u8, + pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This() { const wram = try allocator.create([64 * KiB]u8); - errdefer allocator.destroy(wram); @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, @@ -34,11 +40,14 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, ctx: SharedCtx) !@This( .wram = wram, .scheduler = scheduler, .io = io.Io.init(ctx.io), + + .bios = bios, }; } pub fn deinit(self: *@This(), allocator: Allocator) void { allocator.destroy(self.wram); + allocator.destroy(self.bios); } pub fn reset(_: *@This()) void {} @@ -64,12 +73,13 @@ 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]), 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]), else => self.shr_wram.read(T, .nds7, aligned_addr), }, - 0x0380_0000...0x0380_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]), + 0x0380_0000...0x03FF_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]), 0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr), 0x0600_0000...0x06FF_FFFF => self.vram.read(T, .nds7, aligned_addr), else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }), @@ -97,6 +107,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}), 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/nds7/io.zig b/src/core/nds7/io.zig index ddf3922..a29569a 100644 --- a/src/core/nds7/io.zig +++ b/src/core/nds7/io.zig @@ -7,11 +7,36 @@ const Bus = @import("Bus.zig"); const SharedCtx = @import("../emu.zig").SharedCtx; const masks = @import("../io.zig").masks; +const IntEnable = @import("../io.zig").IntEnable; +const IntRequest = @import("../io.zig").IntEnable; + const log = std.log.scoped(.nds7_io); pub const Io = struct { shr: *SharedCtx.Io, + /// Interrupt Master Enable + /// Read/Write + ime: bool = false, + + /// Interrupt Enable + /// Read/Write + /// + /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable` + ie: IntEnable = .{ .raw = 0x0000_0000 }, + + /// IF - Interrupt Request + /// Read/Write + /// + /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest` + irq: IntRequest = .{ .raw = 0x0000_0000 }, + + /// Post Boot Flag + /// Read/Write + /// + /// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg` + postflg: PostFlag = .in_progress, + pub fn init(io: *SharedCtx.Io) @This() { return .{ .shr = io }; } @@ -20,21 +45,23 @@ pub const Io = struct { pub fn read(bus: *const Bus, comptime T: type, address: u32) T { return switch (T) { u32 => switch (address) { - 0x0400_0208 => @intFromBool(bus.io.shr.ime), - 0x0400_0210 => bus.io.shr.ie, - 0x0400_0214 => bus.io.shr.irq, + 0x0400_0208 => @intFromBool(bus.io.ime), + 0x0400_0210 => bus.io.ie.raw, + 0x0400_0214 => bus.io.irq.raw, - 0x0410_0000 => bus.io.shr.ipc_fifo.recv(.nds7), + 0x0410_0000 => bus.io.shr.ipc.recv(.nds7), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u16 => switch (address) { - 0x0400_0180 => @truncate(bus.io.shr.ipc_fifo._nds7.sync.raw), - 0x0400_0184 => @truncate(bus.io.shr.ipc_fifo._nds7.cnt.raw), + 0x0400_0180 => @truncate(bus.io.shr.ipc._nds7.sync.raw), + 0x0400_0184 => @truncate(bus.io.shr.ipc._nds7.cnt.raw), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u8 => switch (address) { 0x0400_0240 => bus.vram.stat().raw, 0x0400_0241 => bus.io.shr.wramcnt.raw, + + 0x0400_0300 => @intFromEnum(bus.io.postflg), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, else => @compileError(T ++ " is an unsupported bus read type"), @@ -44,19 +71,20 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { switch (T) { u32 => switch (address) { - 0x0400_0208 => bus.io.shr.ime = value & 1 == 1, - 0x0400_0210 => bus.io.shr.ie = value, - 0x0400_0214 => bus.io.shr.irq = value, + 0x0400_0208 => bus.io.ime = value & 1 == 1, + 0x0400_0210 => bus.io.ie.raw = value, + 0x0400_0214 => bus.io.irq.raw &= ~value, - 0x0400_0188 => bus.io.shr.ipc_fifo.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}), + 0x0400_0188 => bus.io.shr.ipc.send(.nds7, value) catch |e| std.debug.panic("FIFO error: {}", .{e}), else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), }, u16 => switch (address) { - 0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds7, value), - 0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds7, value), + 0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds7, value), + 0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds7, value), else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), }, u8 => switch (address) { + 0x0400_0208 => bus.io.ime = value & 1 == 1, else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), }, else => @compileError(T ++ " is an unsupported bus write type"), @@ -73,3 +101,5 @@ pub const Vramstat = extern union { vramd_enabled: Bit(u8, 1), raw: u8, }; + +const PostFlag = enum(u8) { in_progress = 0, completed }; diff --git a/src/core/nds9/Bus.zig b/src/core/nds9/Bus.zig index 6427118..459a21f 100644 --- a/src/core/nds9/Bus.zig +++ b/src/core/nds9/Bus.zig @@ -20,23 +20,32 @@ wram: *Wram, io: io.Io, ppu: Ppu, +bios: *[32 * KiB]u8, + scheduler: *Scheduler, 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, .ppu = try Ppu.init(allocator, ctx.vram), .scheduler = scheduler, .io = io.Io.init(ctx.io), + + .bios = bios, }; } pub fn deinit(self: *@This(), allocator: Allocator) void { self.ppu.deinit(allocator); + allocator.destroy(self.bios); } pub fn reset(_: *@This()) void { @@ -68,6 +77,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]), else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }), }; } @@ -97,6 +107,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}), else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }), } } diff --git a/src/core/nds9/io.zig b/src/core/nds9/io.zig index 68df754..6475b36 100644 --- a/src/core/nds9/io.zig +++ b/src/core/nds9/io.zig @@ -7,6 +7,9 @@ const Bus = @import("Bus.zig"); const SharedCtx = @import("../emu.zig").SharedCtx; const masks = @import("../io.zig").masks; +const IntEnable = @import("../io.zig").IntEnable; +const IntRequest = @import("../io.zig").IntEnable; + const sext = @import("../../util.zig").sext; const log = std.log.scoped(.nds9_io); @@ -14,6 +17,22 @@ const log = std.log.scoped(.nds9_io); pub const Io = struct { shr: *SharedCtx.Io, + /// Interrupt Master Enable + /// Read/Write + ime: bool = false, + + /// Interrupt Enable + /// Read/Write + /// + /// Caller must cast the `u32` to either `nds7.IntEnable` or `nds9.IntEnable` + ie: IntEnable = .{ .raw = 0x0000_0000 }, + + /// IF - Interrupt Request + /// Read/Write + /// + /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest` + irq: IntRequest = .{ .raw = 0x0000_0000 }, + /// POWCNT1 - Graphics Power Control /// Read / Write powcnt: PowCnt = .{ .raw = 0x0000_0000 }, @@ -33,9 +52,9 @@ pub const Io = struct { pub fn read(bus: *const Bus, comptime T: type, address: u32) T { return switch (T) { u32 => switch (address) { - 0x0400_0208 => @intFromBool(bus.io.shr.ime), - 0x0400_0210 => bus.io.shr.ie, - 0x0400_0214 => bus.io.shr.irq, + 0x0400_0208 => @intFromBool(bus.io.ime), + 0x0400_0210 => bus.io.ie.raw, + 0x0400_0214 => bus.io.irq.raw, 0x0400_02A0 => @truncate(bus.io.div.result), 0x0400_02A4 => @truncate(bus.io.div.result >> 32), @@ -43,15 +62,15 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_02AC => @truncate(bus.io.div.remainder >> 32), 0x0400_02B4 => @truncate(bus.io.sqrt.result), - 0x0410_0000 => bus.io.shr.ipc_fifo.recv(.nds9), + 0x0410_0000 => bus.io.shr.ipc.recv(.nds9), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u16 => switch (address) { 0x0400_0004 => bus.ppu.io.dispstat.raw, 0x0400_0130 => bus.io.keyinput.load(.Monotonic), - 0x0400_0180 => @truncate(bus.io.shr.ipc_fifo._nds9.sync.raw), - 0x0400_0184 => @truncate(bus.io.shr.ipc_fifo._nds9.cnt.raw), + 0x0400_0180 => @truncate(bus.io.shr.ipc._nds9.sync.raw), + 0x0400_0184 => @truncate(bus.io.shr.ipc._nds9.cnt.raw), 0x0400_0280 => @truncate(bus.io.div.cnt.raw), 0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw), @@ -69,9 +88,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { switch (T) { u32 => switch (address) { 0x0400_0000 => bus.ppu.io.dispcnt_a.raw = value, - 0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds9, value), - 0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds9, value), - 0x0400_0188 => bus.io.shr.ipc_fifo.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}), + 0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value), + 0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value), + 0x0400_0188 => bus.io.shr.ipc.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}), 0x0400_0240 => { bus.ppu.vram.io.cnt_a.raw = @truncate(value >> 0); // 0x0400_0240 @@ -80,9 +99,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { bus.ppu.vram.io.cnt_d.raw = @truncate(value >> 24); // 0x0400_0243 }, - 0x0400_0208 => bus.io.shr.ime = value & 1 == 1, - 0x0400_0210 => bus.io.shr.ie = value, - 0x0400_0214 => bus.io.shr.irq = value, + 0x0400_0208 => bus.io.ime = value & 1 == 1, + 0x0400_0210 => bus.io.ie.raw = value, + 0x0400_0214 => bus.io.irq.raw &= ~value, 0x0400_0290 => { bus.io.div.numerator = masks.mask(bus.io.div.numerator, value, 0xFFFF_FFFF); @@ -114,9 +133,9 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), }, u16 => switch (address) { - 0x0400_0180 => bus.io.shr.ipc_fifo.setIpcSync(.nds9, value), - 0x0400_0184 => bus.io.shr.ipc_fifo.setIpcFifoCnt(.nds9, value), - 0x0400_0208 => bus.io.shr.ime = value & 1 == 1, + 0x0400_0180 => bus.io.shr.ipc.setIpcSync(.nds9, value), + 0x0400_0184 => bus.io.shr.ipc.setIpcFifoCnt(.nds9, value), + 0x0400_0208 => bus.io.ime = value & 1 == 1, 0x0400_0280 => { bus.io.div.cnt.raw = value; diff --git a/src/main.zig b/src/main.zig index 043de31..e4060bf 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,6 +13,7 @@ const ClapResult = clap.Result(clap.Help, &cli_params, clap.parsers.default); const cli_params = clap.parseParamsComptime( \\-h, --help Display this help and exit. + \\-f, --firm Path to NDS Firmware Directory \\ Path to the NDS ROM \\ ); @@ -31,10 +32,10 @@ pub fn main() !void { const rom_path = try handlePositional(result); log.debug("loading rom from: {s}", .{rom_path}); - const rom_file = try std.fs.cwd().openFile(rom_path, .{}); - defer rom_file.close(); + const firm_path = result.args.firm; + log.debug("loading firmware from from: {?s}", .{firm_path}); - const ctx = try SharedCtx.init(allocator); + var ctx = try SharedCtx.init(allocator); defer ctx.deinit(allocator); var scheduler = try Scheduler.init(allocator); @@ -56,7 +57,10 @@ pub fn main() !void { break :blk .{ .arm7tdmi = &arm7tdmi, .arm946es = &arm946es, .bus7 = &bus7, .bus9 = &bus9, .cp15 = &cp15 }; }; defer system.deinit(allocator); - const rom_title = try emu.load(allocator, system, rom_file); + + ctx.io.ipc.configure(system); // Shared I/O needs access to both CPUs (e.g. IPCSYNC) + const rom_title = try emu.load(allocator, system, rom_path); + if (firm_path) |path| try emu.loadFirm(allocator, system, path); var ui = try Ui.init(allocator); defer ui.deinit(allocator);