diff --git a/lib/arm32 b/lib/arm32 index 30cf951..a3eefa6 160000 --- a/lib/arm32 +++ b/lib/arm32 @@ -1 +1 @@ -Subproject commit 30cf951d2a4ccba3ff7ce70ceeae44780af5f1a1 +Subproject commit a3eefa643268617d10e47ca972c4ba5bd05f131e diff --git a/src/core/emu.zig b/src/core/emu.zig index 7f553ab..82c9461 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -70,25 +70,6 @@ const arm9_clock = bus_clock * 2; pub fn runFrame(nds7_group: nds7.Group, nds9_group: nds9.Group) void { // TODO: might be more efficient to run them both in the same loop? - { - const scheduler = nds7_group.scheduler; - - const cycles_per_dot = arm7_clock / dot_clock + 1; - comptime std.debug.assert(cycles_per_dot == 6); - - const cycles_per_frame = 355 * 263 * cycles_per_dot; - const frame_end = scheduler.tick + cycles_per_frame; - - const cpu = nds7_group.cpu; - const bus = nds7_group.bus; - - while (scheduler.tick < frame_end) { - cpu.step(); - - if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); - } - } - { const scheduler = nds9_group.scheduler; @@ -107,6 +88,25 @@ pub fn runFrame(nds7_group: nds7.Group, nds9_group: nds9.Group) void { if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); } } + + { + const scheduler = nds7_group.scheduler; + + const cycles_per_dot = arm7_clock / dot_clock + 1; + comptime std.debug.assert(cycles_per_dot == 6); + + const cycles_per_frame = 355 * 263 * cycles_per_dot; + const frame_end = scheduler.tick + cycles_per_frame; + + const cpu = nds7_group.cpu; + const bus = nds7_group.bus; + + while (scheduler.tick < frame_end) { + cpu.step(); + + if (scheduler.tick >= scheduler.next()) scheduler.handle(bus); + } + } } // FIXME: Perf win to allocating on the stack instead? diff --git a/src/core/io.zig b/src/core/io.zig index d52350c..51d1a37 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -22,21 +22,8 @@ pub const Io = struct { /// Caller must cast the `u32` to either `nds7.IntRequest` or `nds9.IntRequest` irq: u32 = 0x0000_0000, - /// IPC Synchronize - /// Read/Write - ipc_sync: IpcSync = .{ .raw = 0x0000_0000 }, - - /// IPC Fifo Control - /// Read/Write - ipc_fifo_cnt: IpcFifoCnt = .{ .raw = 0x0000_0000 }, - - /// IPC Send FIFO - /// Write-Only - ipc_fifo_send: u32 = 0x0000_0000, - - /// IPC Receive FIFO - /// Read-Only - ipc_fifo_recv: u32 = 0x0000_0000, + /// Inter Process Communication FIFO + ipc_fifo: IpcFifo = .{}, /// Post Boot Flag /// Read/Write @@ -121,6 +108,54 @@ pub inline fn writeToAddressOffset( }; } +const IpcFifo = struct { + const Sync = IpcSync; + const Control = IpcFifoCnt; + + /// IPC Synchronize + /// Read/Write + sync: Sync = .{ .raw = 0x0000_0000 }, + + /// IPC Fifo Control + /// Read/Write + cnt: Control = .{ .raw = 0x0000_0000 }, + + fifo: [2]Fifo = .{ Fifo{}, Fifo{} }, + + const Source = enum { arm7, arm9 }; + + /// IPC Send FIFO + /// Write-Only + pub fn send(self: *@This(), comptime src: Source, value: u32) !void { + const idx = switch (src) { + .arm7 => 0, + .arm9 => 1, + }; + + if (!self.cnt.enable_fifos.read()) return; + + try self.fifo[idx].push(value); + } + + /// IPC Receive FIFO + /// Read-Only + pub fn recv(self: *@This(), comptime src: Source) u32 { + const idx = switch (src) { + .arm7 => 1, // switched around on purpose + .arm9 => 0, + }; + + const enabled = self.cnt.enable_fifos.read(); + const val_opt = if (enabled) self.fifo[idx].pop() else self.fifo[idx].peek(); + + return val_opt orelse blk: { + self.cnt.send_fifo_empty.set(); + + break :blk 0x0000_0000; + }; + } +}; + const IpcSync = extern union { /// Data input to IPCSYNC Bit 8->11 of remote CPU /// Read-Only @@ -187,3 +222,64 @@ pub const nds9 = struct { pub const PostFlag = enum(u8) { in_progress = 0, completed }; }; + +const Fifo = struct { + const Index = u8; + const Error = error{full}; + const len = 0x10; + + read_idx: Index = 0, + write_idx: Index = 0, + + buf: [len]u32 = [_]u32{undefined} ** len, + + comptime { + const max_capacity = (@as(Index, 1) << @typeInfo(Index).Int.bits - 1) - 1; // half the range of index type + + std.debug.assert(std.math.isPowerOfTwo(len)); + std.debug.assert(len <= max_capacity); + } + + pub fn reset(self: *@This()) void { + self.read_idx = 0; + self.write_idx = 0; + } + + pub fn push(self: *@This(), value: u32) Error!void { + if (self.isFull()) return Error.full; + defer self.write_idx += 1; + + self.buf[self.mask(self.write_idx)] = value; + } + + pub fn pop(self: *@This()) ?u32 { + if (self.isEmpty()) return null; + defer self.read_idx += 1; + + return self.buf[self.mask(self.read_idx)]; + } + + pub fn peek(self: *const @This()) ?u32 { + if (self.isEmpty()) return null; + + return self.buf[self.mask(self.read_idx)]; + } + + fn _len(self: *const @This()) Index { + return self.write_idx - self.read_idx; + } + + fn isFull(self: *const @This()) bool { + return self._len() == self.buf.len; + } + + fn isEmpty(self: *const @This()) bool { + return self.read_idx == self.write_idx; + } + + inline fn mask(self: *const @This(), idx: Index) Index { + const _mask: Index = @intCast(self.buf.len - 1); + + return idx & _mask; + } +}; diff --git a/src/core/nds7/io.zig b/src/core/nds7/io.zig index b74721b..4dcbf03 100644 --- a/src/core/nds7/io.zig +++ b/src/core/nds7/io.zig @@ -24,11 +24,13 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0208 => @intFromBool(bus.io.shared.ime), 0x0400_0210 => bus.io.shared.ie, 0x0400_0214 => bus.io.shared.irq, + + 0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm7), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u16 => switch (address) { - 0x0400_0180 => @truncate(bus.io.shared.ipc_sync.raw), - 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo_cnt.raw), + 0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw), + 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u8 => switch (address) { @@ -44,11 +46,18 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0208 => bus.io.shared.ime = value & 1 == 1, 0x0400_0210 => bus.io.shared.ie = value, 0x0400_0214 => bus.io.shared.irq = value, + + 0x0400_0188 => bus.io.shared.ipc_fifo.send(.arm7, 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.shared.ipc_sync.raw = value, - 0x0400_0184 => bus.io.shared.ipc_fifo_cnt.raw = value, + 0x0400_0180 => bus.io.shared.ipc_fifo.sync.raw = blk: { + const ret = value & ~@as(u16, 0xF) | (bus.io.shared.ipc_fifo.sync.raw & 0xF); + log.debug("IPCFIFOSYNC <- 0x{X:0>8}", .{ret}); + + break :blk ret; + }, + 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = value, else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), }, u8 => switch (address) { diff --git a/src/core/nds9/io.zig b/src/core/nds9/io.zig index 7703a54..38e1bc2 100644 --- a/src/core/nds9/io.zig +++ b/src/core/nds9/io.zig @@ -31,14 +31,16 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0208 => @intFromBool(bus.io.shared.ime), 0x0400_0210 => bus.io.shared.ie, 0x0400_0214 => bus.io.shared.irq, + + 0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm9), 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.shared.ipc_sync.raw), - 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo_cnt.raw), + 0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw), + 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u8 => switch (address) { @@ -52,9 +54,14 @@ 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.shared.ipc_sync.raw = value, - 0x0400_0184 => bus.io.shared.ipc_fifo_cnt.raw = value, - 0x0400_0188 => bus.io.shared.ipc_fifo_send = value, + 0x0400_0180 => bus.io.shared.ipc_fifo.sync.raw = blk: { + const ret = value & ~@as(u32, 0xF) | (bus.io.shared.ipc_fifo.sync.raw & 0xF); + log.debug("IPCFIFOSYNC <- 0x{X:0>8}", .{ret}); + + break :blk ret; + }, + 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = value, + 0x0400_0188 => bus.io.shared.ipc_fifo.send(.arm9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}), 0x0400_0240 => { bus.ppu.io.vramcnt_a.raw = @truncate(value >> 0); // 0x0400_0240 @@ -72,8 +79,13 @@ 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.shared.ipc_sync.raw = value, - 0x0400_0184 => bus.io.shared.ipc_fifo_cnt.raw = value, + 0x0400_0180 => bus.io.shared.ipc_fifo.sync.raw = blk: { + const ret = value & ~@as(u16, 0xF) | (bus.io.shared.ipc_fifo.sync.raw & 0xF); + log.debug("IPCFIFOSYNC <- 0x{X:0>8}", .{ret}); + + break :blk ret; + }, + 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = value, 0x0400_0208 => bus.io.shared.ime = value & 1 == 1, else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),