From eba2de39fb461a5430a878213728903e80b128ed Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 28 Sep 2023 19:02:08 -0500 Subject: [PATCH] feat(io): implement ipcsync (non-irq) and ipcfifo (non-irq) --- src/core/io.zig | 228 ++++++++++++++++++++++--------------------- src/core/nds7/io.zig | 12 +-- src/core/nds9/io.zig | 16 +-- 3 files changed, 132 insertions(+), 124 deletions(-) diff --git a/src/core/io.zig b/src/core/io.zig index 6a4d688..5f27fe1 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -5,6 +5,10 @@ const Bit = @import("bitfield").Bit; const log = std.log.scoped(.shared_io); +// FIXME: This whole thing is bad bad bad bad bad +// I think only the IPC stuff needs to be here, since they talk to each other. +// 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 @@ -39,120 +43,130 @@ fn warn(comptime format: []const u8, args: anytype) u0 { return 0; } -// TODO: Please Rename -// TODO: Figure out a way to apply masks while calling valueAtAddressOffset -// TODO: These aren't optimized well. Can we improve that? -pub inline fn valueAtAddressOffset(comptime T: type, address: u32, value: T) u8 { - const L2I = std.math.Log2Int(T); - - return @truncate(switch (T) { - u16 => value >> @as(L2I, @truncate((address & 1) << 3)), - u32 => value >> @as(L2I, @truncate((address & 3) << 3)), - else => @compileError("unsupported for " ++ @typeName(T) ++ "values"), - }); -} - -fn WriteOption(comptime T: type) type { - return struct { mask: ?T = null }; -} - -// TODO: also please rename -// TODO: Figure out a way to apply masks while calling writeToAddressOffset -// TODO: These aren't optimized well. Can we improve that? -pub inline fn writeToAddressOffset( - register: anytype, - address: u32, - value: anytype, - // mask: WriteOption(@typeInfo(@TypeOf(register)).Pointer.child), -) void { - const Ptr = @TypeOf(register); - const ChildT = @typeInfo(Ptr).Pointer.child; - const ValueT = @TypeOf(value); - - const left = register.*; - - register.* = switch (ChildT) { - u32 => switch (ValueT) { - u16 => blk: { - // TODO: This probably gets deleted - const offset: u1 = @truncate(address >> 1); - - break :blk switch (offset) { - 0b0 => (left & 0xFFFF_0000) | value, - 0b1 => (left & 0x0000_FFFF) | @as(u32, value) << 16, - }; - }, - u8 => blk: { - // TODO: Remove branching - const offset: u2 = @truncate(address); - - break :blk switch (offset) { - 0b00 => (left & 0xFFFF_FF00) | value, - 0b01 => (left & 0xFFFF_00FF) | @as(u32, value) << 8, - 0b10 => (left & 0xFF00_FFFF) | @as(u32, value) << 16, - 0b11 => (left & 0x00FF_FFFF) | @as(u32, value) << 24, - }; - }, - else => @compileError("for " ++ @typeName(Ptr) ++ ", T must be u16 or u8"), - }, - u16 => blk: { - if (ValueT != u8) @compileError("for " ++ @typeName(Ptr) ++ ", T must be u8"); - - const shamt = @as(u4, @truncate(address & 1)) << 3; - const mask: u16 = 0xFF00 >> shamt; - const value_shifted = @as(u16, value) << shamt; - - break :blk (left & mask) | value_shifted; - }, - else => @compileError("unsupported for " ++ @typeName(Ptr) ++ " values"), - }; -} - const IpcFifo = struct { const Sync = IpcSync; const Control = IpcFifoCnt; - /// IPC Synchronize + _nds7: Impl = .{}, + _nds9: Impl = .{}, + + const Source = enum { nds7, nds9 }; + + const Impl = struct { + /// IPC Synchronize + /// Read/Write + sync: Sync = .{ .raw = 0x0000_0000 }, + + /// IPC Fifo Control + /// Read/Write + cnt: Control = .{ .raw = 0x0000_0101 }, + + fifo: Fifo = Fifo{}, + + /// Latch containing thel last read value from a FIFO + last_read: ?u32 = null, + }; + + /// IPCSYNC /// Read/Write - sync: Sync = .{ .raw = 0x0000_0000 }, + pub fn setIpcSync(self: *@This(), comptime src: Source, value: anytype) void { + switch (src) { + .nds7 => { + 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); + }, + .nds9 => { + 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); + }, + } + } - /// IPC Fifo Control + /// IPCFIFOCNT /// Read/Write - cnt: Control = .{ .raw = 0x0000_0000 }, - - fifo: [2]Fifo = .{ Fifo{}, Fifo{} }, - - const Source = enum { arm7, arm9 }; + pub fn setIpcFifoCnt(self: *@This(), comptime src: Source, value: anytype) void { + switch (src) { + .nds7 => self._nds7.cnt.raw = masks.ipcFifoCnt(self._nds7.cnt.raw, value), + .nds9 => self._nds9.cnt.raw = masks.ipcFifoCnt(self._nds9.cnt.raw, value), + } + } /// IPC Send FIFO /// Write-Only pub fn send(self: *@This(), comptime src: Source, value: u32) !void { - const idx = switch (src) { - .arm7 => 0, - .arm9 => 1, - }; + switch (src) { + .nds7 => { + if (!self._nds7.cnt.enable_fifos.read()) return; + try self._nds7.fifo.push(value); - if (!self.cnt.enable_fifos.read()) return; + // update status bits + self._nds7.cnt.send_fifo_empty.write(self._nds7.fifo._len() == 0); + self._nds9.cnt.recv_fifo_empty.write(self._nds7.fifo._len() == 0); - try self.fifo[idx].push(value); + self._nds7.cnt.send_fifo_full.write(self._nds7.fifo._len() == 0x10); + self._nds9.cnt.recv_fifo_full.write(self._nds7.fifo._len() == 0x10); + }, + .nds9 => { + if (!self._nds9.cnt.enable_fifos.read()) return; + try self._nds9.fifo.push(value); + + // update status bits + self._nds9.cnt.send_fifo_empty.write(self._nds9.fifo._len() == 0); + self._nds7.cnt.recv_fifo_empty.write(self._nds9.fifo._len() == 0); + + self._nds9.cnt.send_fifo_full.write(self._nds9.fifo._len() == 0x10); + self._nds7.cnt.recv_fifo_full.write(self._nds9.fifo._len() == 0x10); + }, + } } /// 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, - }; + switch (src) { + .nds7 => { + const enabled = self._nds7.cnt.enable_fifos.read(); + const val_opt = if (enabled) self._nds9.fifo.pop() else self._nds9.fifo.peek(); - const enabled = self.cnt.enable_fifos.read(); - const val_opt = if (enabled) self.fifo[idx].pop() else self.fifo[idx].peek(); + const value = if (val_opt) |val| blk: { + self._nds9.last_read = val; + break :blk val; + } else blk: { + self._nds7.cnt.fifo_error.set(); + break :blk self._nds7.last_read orelse 0x0000_0000; + }; - return val_opt orelse blk: { - self.cnt.send_fifo_empty.set(); + // update status bits + self._nds7.cnt.recv_fifo_empty.write(self._nds9.fifo._len() == 0); + self._nds9.cnt.send_fifo_empty.write(self._nds9.fifo._len() == 0); - break :blk 0x0000_0000; - }; + self._nds7.cnt.recv_fifo_full.write(self._nds9.fifo._len() == 0x10); + self._nds9.cnt.send_fifo_full.write(self._nds9.fifo._len() == 0x10); + + return value; + }, + .nds9 => { + const enabled = self._nds9.cnt.enable_fifos.read(); + const val_opt = if (enabled) self._nds7.fifo.pop() else self._nds7.fifo.peek(); + + const value = if (val_opt) |val| blk: { + self._nds7.last_read = val; + break :blk val; + } else blk: { + self._nds9.cnt.fifo_error.set(); + break :blk self._nds7.last_read orelse 0x0000_0000; + }; + + // update status bits + self._nds9.cnt.recv_fifo_empty.write(self._nds7.fifo._len() == 0); + self._nds7.cnt.send_fifo_empty.write(self._nds7.fifo._len() == 0); + + self._nds9.cnt.recv_fifo_full.write(self._nds7.fifo._len() == 0x10); + self._nds7.cnt.send_fifo_full.write(self._nds7.fifo._len() == 0x10); + + return value; + }, + } } }; @@ -208,32 +222,26 @@ pub const masks = struct { const Bus9 = @import("nds9/Bus.zig"); const Bus7 = @import("nds7/Bus.zig"); - pub inline fn ipcFifoSync(bus: anytype, value: anytype) @TypeOf(value) { - comptime verifyBusType(@TypeOf(bus)); - const T = @TypeOf(value); - const _mask: T = 0xF; - - return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.sync.raw & _mask)); + inline fn ipcFifoSync(sync: u32, value: anytype) u32 { + const _mask: u32 = 0x6F00; + return (@as(u32, value) & _mask) | (sync & ~_mask); } - pub inline fn ipcFifoCnt(bus: anytype, value: anytype) @TypeOf(value) { - comptime verifyBusType(@TypeOf(bus)); - const T = @TypeOf(value); - const _mask: T = 0x0303; + inline fn ipcFifoCnt(cnt: u32, value: anytype) u32 { + const _mask: u32 = 0xC40C; + const err_mask: u32 = 0x4000; // bit 14 - return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.cnt.raw & _mask)); + const err_bit = (cnt & err_mask) & ~(value & err_mask); + if (value & 0b1000 != 0) log.err("TODO: handle IPCFIFOCNT.3", .{}); + + const without_err = (@as(u32, value) & _mask) | (cnt & ~_mask); + return (without_err & ~err_mask) | err_bit; } /// General Mask helper pub inline fn mask(original: anytype, value: @TypeOf(original), _mask: @TypeOf(original)) @TypeOf(original) { return (value & _mask) | (original & ~_mask); } - - fn verifyBusType(comptime BusT: type) void { - std.debug.assert(@typeInfo(BusT) == .Pointer); - std.debug.assert(@typeInfo(BusT).Pointer.size == .One); - std.debug.assert(@typeInfo(BusT).Pointer.child == Bus9 or @typeInfo(BusT).Pointer.child == Bus7); - } }; pub const nds7 = struct { diff --git a/src/core/nds7/io.zig b/src/core/nds7/io.zig index 7dae8c5..8a69f5c 100644 --- a/src/core/nds7/io.zig +++ b/src/core/nds7/io.zig @@ -24,12 +24,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { 0x0400_0210 => bus.io.shared.ie, 0x0400_0214 => bus.io.shared.irq, - 0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm7), + 0x0410_0000 => bus.io.shared.ipc_fifo.recv(.nds7), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u16 => switch (address) { - 0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw), - 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw), + 0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds7.sync.raw), + 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo._nds7.cnt.raw), else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u8 => switch (address) { @@ -46,12 +46,12 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 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}), + 0x0400_0188 => bus.io.shared.ipc_fifo.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.shared.ipc_fifo.sync.raw = masks.ipcFifoSync(bus, value), - 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value), + 0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds7, value), + 0x0400_0184 => bus.io.shared.ipc_fifo.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) { diff --git a/src/core/nds9/io.zig b/src/core/nds9/io.zig index bc2f2ac..504f373 100644 --- a/src/core/nds9/io.zig +++ b/src/core/nds9/io.zig @@ -43,15 +43,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.shared.ipc_fifo.recv(.arm9), + 0x0410_0000 => bus.io.shared.ipc_fifo.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.shared.ipc_fifo.sync.raw), - 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw), + 0x0400_0180 => @truncate(bus.io.shared.ipc_fifo._nds9.sync.raw), + 0x0400_0184 => @truncate(bus.io.shared.ipc_fifo._nds9.cnt.raw), 0x0400_0280 => @truncate(bus.io.div.cnt.raw), 0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw), @@ -69,9 +69,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.shared.ipc_fifo.sync.raw = masks.ipcFifoSync(bus, value), - 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value), - 0x0400_0188 => bus.io.shared.ipc_fifo.send(.arm9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}), + 0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value), + 0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value), + 0x0400_0188 => bus.io.shared.ipc_fifo.send(.nds9, value) catch |e| std.debug.panic("IPC FIFO Error: {}", .{e}), 0x0400_0240 => { bus.ppu.io.vramcnt_a.raw = @truncate(value >> 0); // 0x0400_0240 @@ -114,8 +114,8 @@ 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_fifo.sync.raw = masks.ipcFifoSync(bus, value), - 0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value), + 0x0400_0180 => bus.io.shared.ipc_fifo.setIpcSync(.nds9, value), + 0x0400_0184 => bus.io.shared.ipc_fifo.setIpcFifoCnt(.nds9, value), 0x0400_0208 => bus.io.shared.ime = value & 1 == 1, 0x0400_0280 => {