From f0b5bb12076aff8e8d041a93752806abcd46c507 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 28 Sep 2023 15:57:18 -0500 Subject: [PATCH] feat: implement WRAM --- lib/arm32 | 2 +- src/core/emu.zig | 133 +++++++++++++++++++++++++++++++++++++++--- src/core/io.zig | 27 +++++++++ src/core/nds7/Bus.zig | 11 ++++ src/core/nds7/io.zig | 1 + src/core/nds9/Bus.zig | 6 +- src/core/nds9/io.zig | 4 ++ 7 files changed, 174 insertions(+), 10 deletions(-) diff --git a/lib/arm32 b/lib/arm32 index 481271b..1bd9630 160000 --- a/lib/arm32 +++ b/lib/arm32 @@ -1 +1 @@ -Subproject commit 481271ba2abcaab82b0930a10d424134099143c8 +Subproject commit 1bd96304badf24d50698e04abaf8f45c31571d6d diff --git a/src/core/emu.zig b/src/core/emu.zig index 5bd1972..72421c1 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -92,33 +92,150 @@ pub fn runFrame(scheduler: *Scheduler, system: System) void { // FIXME: Perf win to allocating on the stack instead? pub const SharedContext = struct { const MiB = 0x100000; + const KiB = 0x400; io: *SharedIo, main: *[4 * MiB]u8, + wram: *Wram, pub fn init(allocator: Allocator) !@This() { + const wram = try allocator.create(Wram); + errdefer allocator.destroy(wram); + + try wram.init(allocator); + const ctx = .{ - .io = try allocator.create(SharedIo), + .io = blk: { + const io = try allocator.create(SharedIo); + io.* = .{}; + + break :blk io; + }, + .wram = wram, .main = try allocator.create([4 * MiB]u8), }; - ctx.io.* = .{}; return ctx; } pub fn deinit(self: @This(), allocator: Allocator) void { + self.wram.deinit(allocator); + allocator.destroy(self.wram); + allocator.destroy(self.io); allocator.destroy(self.main); } }; +// Before I implement Bus-wide Fastmem, Let's play with some more limited (read: less useful) +// fastmem implementations + +// TODO: move somewhere else ideally +pub const Wram = struct { + const page_size = 1 * KiB; // perhaps too big? + const addr_space_size = 0x8000; + const table_len = addr_space_size / page_size; + const IntFittingRange = std.math.IntFittingRange; + + const io = @import("io.zig"); + const KiB = 0x400; + + const log = std.log.scoped(.shared_wram); + + _buf: *[32 * KiB]u8, + + nds9_table: *const [table_len]?[*]u8, + nds7_table: *const [table_len]?[*]u8, + + pub fn init(self: *@This(), allocator: Allocator) !void { + const buf = try allocator.create([32 * KiB]u8); + errdefer allocator.destroy(buf); + + const tables = try allocator.alloc(?[*]u8, 2 * table_len); + @memset(tables, null); + + self.* = .{ + .nds9_table = tables[0..table_len], + .nds7_table = tables[table_len .. 2 * table_len], + ._buf = buf, + }; + } + + pub fn deinit(self: @This(), allocator: Allocator) void { + allocator.destroy(self._buf); + + const ptr: [*]?[*]const u8 = @ptrCast(@constCast(self.nds9_table)); + allocator.free(ptr[0 .. 2 * table_len]); + } + + pub fn update(self: *@This(), wramcnt: io.WramCnt) void { + const mode = wramcnt.mode.read(); + + const nds9_tbl = @constCast(self.nds9_table); + const nds7_tbl = @constCast(self.nds7_table); + + for (nds9_tbl, nds7_tbl, 0..) |*nds9_ptr, *nds7_ptr, i| { + const addr = i * page_size; + + switch (mode) { + 0b00 => { + nds9_ptr.* = self._buf[addr..].ptr; + nds7_ptr.* = null; + }, + 0b01 => { + nds9_ptr.* = self._buf[0x4000 + (addr & 0x3FFF) ..].ptr; + nds7_ptr.* = self._buf[(addr & 0x3FFF)..].ptr; + }, + 0b10 => { + nds9_ptr.* = self._buf[(addr & 0x3FFF)..].ptr; + nds7_ptr.* = self._buf[0x4000 + (addr & 0x3FFF) ..].ptr; + }, + 0b11 => { + nds9_ptr.* = null; + nds7_ptr.* = self._buf[addr..].ptr; + }, + } + } + } + + // TODO: Rename + const Device = enum { nds9, nds7 }; + + pub fn read(self: @This(), comptime T: type, comptime dev: Device, address: u32) T { + const bits = @typeInfo(IntFittingRange(0, page_size - 1)).Int.bits; + const page = address >> bits; + const offset = address & (page_size - 1); + const table = if (dev == .nds9) self.nds9_table else self.nds7_table; + + if (table[page]) |some_ptr| { + const ptr: [*]align(1) const T = @ptrCast(@alignCast(some_ptr)); + + return ptr[offset / @sizeOf(T)]; + } + + log.err("{s}: read(T: {}, addr: 0x{X:0>8}) was in un-mapped WRAM space", .{ @tagName(dev), T, 0x0300_0000 + address }); + return 0x00; + } + + pub fn write(self: *@This(), comptime T: type, comptime dev: Device, address: u32, value: T) void { + const bits = @typeInfo(IntFittingRange(0, page_size - 1)).Int.bits; + const page = address >> bits; + const offset = address & (page_size - 1); + const table = if (dev == .nds9) self.nds9_table else self.nds7_table; + + if (table[page]) |some_ptr| { + const ptr: [*]align(1) T = @ptrCast(@alignCast(some_ptr)); + ptr[offset / @sizeOf(T)] = value; + + return; + } + + log.err("{s}: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8}) was in un-mapped WRAM space", .{ @tagName(dev), T, 0x0300_0000 + address, value }); + } +}; + pub inline fn forceAlign(comptime T: type, address: u32) u32 { - return switch (T) { - u32 => address & ~@as(u32, 3), - u16 => address & ~@as(u32, 1), - u8 => address, - else => @compileError("Bus: Invalid read/write type"), - }; + return address & ~@as(u32, @sizeOf(T) - 1); } pub const System = struct { diff --git a/src/core/io.zig b/src/core/io.zig index 5f27fe1..acd0210 100644 --- a/src/core/io.zig +++ b/src/core/io.zig @@ -35,6 +35,8 @@ pub const Io = struct { /// Caller must cast the `u8` to either `nds7.PostFlg` or `nds9.PostFlg` post_flg: u8 = @intFromEnum(nds7.PostFlag.in_progress), + wramcnt: WramCnt = .{ .raw = 0x00 }, + // TODO: DS Cartridge I/O Ports }; @@ -74,10 +76,30 @@ const IpcFifo = struct { .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); + + if (value >> 3 & 1 == 1) { + self._nds7.fifo.reset(); + + self._nds7.cnt.send_fifo_empty.write(true); + self._nds9.cnt.recv_fifo_empty.write(true); + + self._nds7.cnt.send_fifo_full.write(false); + self._nds9.cnt.recv_fifo_full.write(false); + } }, .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); + + if (value >> 3 & 1 == 1) { + self._nds9.fifo.reset(); + + self._nds9.cnt.send_fifo_empty.write(true); + self._nds7.cnt.recv_fifo_empty.write(true); + + self._nds9.cnt.send_fifo_full.write(false); + self._nds7.cnt.recv_fifo_full.write(false); + } }, } } @@ -218,6 +240,11 @@ const IpcFifoCnt = extern union { raw: u32, }; +pub const WramCnt = extern union { + mode: Bitfield(u8, 0, 2), + raw: u8, +}; + pub const masks = struct { const Bus9 = @import("nds9/Bus.zig"); const Bus7 = @import("nds7/Bus.zig"); diff --git a/src/core/nds7/Bus.zig b/src/core/nds7/Bus.zig index 58fd941..6a7c4e6 100644 --- a/src/core/nds7/Bus.zig +++ b/src/core/nds7/Bus.zig @@ -4,6 +4,7 @@ const io = @import("io.zig"); const Scheduler = @import("../Scheduler.zig"); const SharedIo = @import("../io.zig").Io; const SharedContext = @import("../emu.zig").SharedContext; +const Wram = @import("../emu.zig").Wram; const forceAlign = @import("../emu.zig").forceAlign; const Allocator = std.mem.Allocator; @@ -16,6 +17,7 @@ const log = std.log.scoped(.nds7_bus); scheduler: *Scheduler, main: *[4 * MiB]u8, +wram_shr: *Wram, wram: *[64 * KiB]u8, io: io.Io, @@ -26,6 +28,7 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedConte return .{ .main = shared_ctx.main, + .wram_shr = shared_ctx.wram, .wram = wram, .scheduler = scheduler, .io = io.Io.init(shared_ctx.io), @@ -60,6 +63,10 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T return switch (aligned_addr) { 0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]), + 0x0300_0000...0x037F_FFFF => switch (self.io.shared.wramcnt.mode.read()) { + 0b00 => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]), + else => self.wram_shr.read(T, .nds7, address & 0x7FFF), + }, 0x0380_0000...0x0380_FFFF => readInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count]), 0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr), else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }), @@ -88,6 +95,10 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v switch (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.shared.wramcnt.mode.read()) { + 0b00 => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value), + else => self.wram_shr.write(T, .nds7, address & 0x7FFF, value), + }, 0x0380_0000...0x0380_FFFF => writeInt(T, self.wram[aligned_addr & 0x0000_FFFF ..][0..byte_count], value), 0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value), else => log.warn("unexpected write: 0x{X:}{} -> 0x{X:0>8}", .{ value, T, aligned_addr }), diff --git a/src/core/nds7/io.zig b/src/core/nds7/io.zig index 8a69f5c..f10f366 100644 --- a/src/core/nds7/io.zig +++ b/src/core/nds7/io.zig @@ -33,6 +33,7 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T { else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, u8 => switch (address) { + 0x0400_0241 => bus.io.shared.wramcnt.raw, else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }), }, else => @compileError(T ++ " is an unsupported bus read type"), diff --git a/src/core/nds9/Bus.zig b/src/core/nds9/Bus.zig index 19c3120..51622aa 100644 --- a/src/core/nds9/Bus.zig +++ b/src/core/nds9/Bus.zig @@ -4,6 +4,7 @@ const io = @import("io.zig"); const Ppu = @import("../ppu.zig").Ppu; const Scheduler = @import("../Scheduler.zig"); const SharedContext = @import("../emu.zig").SharedContext; +const Wram = @import("../emu.zig").Wram; const forceAlign = @import("../emu.zig").forceAlign; const Allocator = std.mem.Allocator; @@ -15,6 +16,7 @@ const KiB = 0x400; const log = std.log.scoped(.nds9_bus); main: *[4 * MiB]u8, +wram: *Wram, vram1: *[512 * KiB]u8, // TODO: Rename io: io.Io, ppu: Ppu, @@ -31,6 +33,7 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedConte return .{ .main = shared_ctx.main, + .wram = shared_ctx.wram, .vram1 = vram1_mem, .ppu = try Ppu.init(allocator), .scheduler = scheduler, @@ -40,7 +43,6 @@ pub fn init(allocator: Allocator, scheduler: *Scheduler, shared_ctx: SharedConte pub fn deinit(self: *@This(), allocator: Allocator) void { self.ppu.deinit(allocator); - allocator.destroy(self.vram1); } @@ -71,6 +73,7 @@ fn _read(self: *@This(), comptime T: type, comptime mode: Mode, address: u32) T return switch (aligned_addr) { 0x0200_0000...0x02FF_FFFF => readInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count]), + 0x0300_0000...0x03FF_FFFF => self.wram.read(T, .nds9, aligned_addr & 0x7FFF), 0x0400_0000...0x04FF_FFFF => io.read(self, T, aligned_addr), 0x0600_0000...0x06FF_FFFF => readInt(T, self.vram1[aligned_addr & 0x0007_FFFF ..][0..byte_count]), else => warn("unexpected read: 0x{x:0>8} -> {}", .{ aligned_addr, T }), @@ -99,6 +102,7 @@ fn _write(self: *@This(), comptime T: type, comptime mode: Mode, address: u32, v switch (aligned_addr) { 0x0200_0000...0x02FF_FFFF => writeInt(T, self.main[aligned_addr & 0x003F_FFFF ..][0..byte_count], value), + 0x0300_0000...0x03FF_FFFF => self.wram.write(T, .nds9, aligned_addr & 0x7FFF, value), 0x0400_0000...0x04FF_FFFF => io.write(self, T, aligned_addr, value), 0x0600_0000...0x06FF_FFFF => writeInt(T, self.vram1[aligned_addr & 0x0007_FFFF ..][0..byte_count], value), 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 504f373..9e5e8e2 100644 --- a/src/core/nds9/io.zig +++ b/src/core/nds9/io.zig @@ -135,6 +135,10 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void { 0x0400_0241 => bus.ppu.io.vramcnt_b.raw = value, 0x0400_0242 => bus.ppu.io.vramcnt_c.raw = value, 0x0400_0243 => bus.ppu.io.vramcnt_d.raw = value, + 0x0400_0247 => { + bus.io.shared.wramcnt.raw = value; + bus.wram.update(bus.io.shared.wramcnt); + }, else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }), },