From 79773782ca7b2caf9fe5204cc9b06ddda260ce29 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 29 Sep 2023 02:35:55 -0500 Subject: [PATCH] feat: implement WRAM --- lib/arm32 | 2 +- src/core/emu.zig | 135 ++++++++++++++++++++++++++++++++++++++---- src/core/io.zig | 27 +++++++++ src/core/nds7/Bus.zig | 11 ++++ src/core/nds7/io.zig | 1 + src/core/nds9/Bus.zig | 74 ++--------------------- src/core/nds9/io.zig | 4 ++ 7 files changed, 172 insertions(+), 82 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 999f1e3..72421c1 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -96,33 +96,146 @@ pub const SharedContext = struct { io: *SharedIo, main: *[4 * MiB]u8, - wram: *[32 * KiB]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), - .wram = try allocator.create([32 * KiB]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); - allocator.destroy(self.wram); + } +}; + +// 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 bd49699..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,7 +16,7 @@ const KiB = 0x400; const log = std.log.scoped(.nds9_bus); main: *[4 * MiB]u8, -wram: *[32 * KiB]u8, +wram: *Wram, vram1: *[512 * KiB]u8, // TODO: Rename io: io.Io, ppu: Ppu, @@ -42,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); } @@ -73,7 +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]), - // TODO: Impl Shared WRAM + 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 }), @@ -102,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 }), @@ -112,70 +113,3 @@ fn warn(comptime format: []const u8, args: anytype) u0 { log.warn(format, args); return 0; } - -// Before I implement Bus-wide Fastmem, Let's play with some more limited (read: less useful) -// fastmem implementations -const Wram = struct { - const page_size = 1 * KiB; // perhaps too big? - const addr_space_size = 0x0100_0000; - const table_len = addr_space_size / page_size; - const IntFittingRange = std.math.IntFittingRange; - - _ptr: *[32 * KiB]u8, - - read_table: *const [table_len]?*const anyopaque, - write_table: *const [table_len]?*anyopaque, - - pub fn init(allocator: Allocator, ptr: *[32 * KiB]u8) @This() { - const tables = try allocator.create(?*anyopaque, 2 * table_len); - - return .{ - .read_table = tables[0..table_len], - .write_table = tables[table_len .. 2 * table_len], - ._ptr = ptr, - }; - } - - pub fn update(_: *@This()) void { - @panic("TODO: reload WRAM FASTMEM"); - } - - pub fn read(self: @This(), comptime T: type, address: u32) T { - const bits = @typeInfo(IntFittingRange(0, page_size - 1)).Int.bits; - const page = address >> bits; - const offset = address & (page_size - 1); - - std.debug.assert(page < table_len); - - if (self.read_table[page]) |some_ptr| { - const ptr: [*]const T = @ptrCast(@alignCast(some_ptr)); - - return ptr[forceAlign(T, offset) / @sizeOf(T)]; - } - - log.err("read(T: {}, addr: 0x{X:0>8}) was in un-mapped WRAM space", .{ T, address }); - return 0x00; - } - - pub fn write(self: *@This(), comptime T: type, 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); - - std.debug.assert(page < table_len); - - if (self.write_table[page]) |some_ptr| { - const ptr: [*]T = @ptrCast(@alignCast(some_ptr)); - ptr[forceAlign(T, offset) / @sizeOf(T)] = value; - - return; - } - - log.warn("write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8}) was in un-mapped WRAM space", .{ T, address, value }); - } - - pub fn deinit(self: @This(), allocator: Allocator) void { - const og_ptr: [*]?*anyopaque = @ptrCast(self.read_table); - allocator.free(og_ptr[0 .. 2 * table_len]); - } -}; 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 }), },