From 26e36b2ea2ed8c5d86c82dd617889c1f32e20958 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 29 Jan 2023 07:03:46 -0600 Subject: [PATCH] feat: allow gdb writes to certain mem regions --- lib/zba-gdbstub | 2 +- src/core/Bus.zig | 148 +++++++++++++++++++++++++++++++---------------- src/core/emu.zig | 6 +- 3 files changed, 101 insertions(+), 55 deletions(-) diff --git a/lib/zba-gdbstub b/lib/zba-gdbstub index 82bad92..81ff227 160000 --- a/lib/zba-gdbstub +++ b/lib/zba-gdbstub @@ -1 +1 @@ -Subproject commit 82bad92fcf92415f5ee1a1c3c0432c48073a7139 +Subproject commit 81ff227ea76d5b44a89e422e380e0fe15aec02aa diff --git a/src/core/Bus.zig b/src/core/Bus.zig index 864fa4a..00cd4e3 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -198,55 +198,6 @@ fn fillReadTableExternal(self: *Self, addr: u32) ?*anyopaque { return &self.pak.buf[masked_addr]; } -pub fn dbgRead(self: *const Self, comptime T: type, unaligned_address: u32) T { - const bits = @typeInfo(std.math.IntFittingRange(0, page_size - 1)).Int.bits; - const page = unaligned_address >> bits; - const offset = unaligned_address & (page_size - 1); - - // We're doing some serious out-of-bounds open-bus reads - if (page >= table_len) return self.openBus(T, unaligned_address); - - if (self.read_table[page]) |some_ptr| { - // We have a pointer to a page, cast the pointer to it's underlying type - const Ptr = [*]const T; - const ptr = @ptrCast(Ptr, @alignCast(@alignOf(std.meta.Child(Ptr)), some_ptr)); - - // Note: We don't check array length, since we force align the - // lower bits of the address as the GBA would - return ptr[forceAlign(T, offset) / @sizeOf(T)]; - } - - return self.dbgSlowRead(T, unaligned_address); -} - -fn dbgSlowRead(self: *const Self, comptime T: type, unaligned_address: u32) T { - const page = @truncate(u8, unaligned_address >> 24); - const address = forceAlign(T, unaligned_address); - - return switch (page) { - // General Internal Memory - 0x00 => blk: { - if (address < Bios.size) - break :blk self.bios.dbgRead(T, self.cpu.r[15], unaligned_address); - - break :blk self.openBus(T, address); - }, - 0x02 => unreachable, // handled by fastmem - 0x03 => unreachable, // handled by fastmem - 0x04 => self.readIo(T, address), - - // Internal Display Memory - 0x05 => unreachable, // handled by fastmem - 0x06 => unreachable, // handled by fastmem - 0x07 => unreachable, // handled by fastmem - - // External Memory (Game Pak) - 0x08...0x0D => self.pak.dbgRead(T, address), - 0x0E...0x0F => self.readBackup(T, unaligned_address), - else => self.openBus(T, address), - }; -} - fn readIo(self: *const Self, comptime T: type, address: u32) T { return io.read(self, T, address) orelse self.openBus(T, address); } @@ -336,6 +287,27 @@ pub fn read(self: *Self, comptime T: type, unaligned_address: u32) T { return self.slowRead(T, unaligned_address); } +pub fn dbgRead(self: *const Self, comptime T: type, unaligned_address: u32) T { + const bits = @typeInfo(std.math.IntFittingRange(0, page_size - 1)).Int.bits; + const page = unaligned_address >> bits; + const offset = unaligned_address & (page_size - 1); + + // We're doing some serious out-of-bounds open-bus reads + if (page >= table_len) return self.openBus(T, unaligned_address); + + if (self.read_table[page]) |some_ptr| { + // We have a pointer to a page, cast the pointer to it's underlying type + const Ptr = [*]const T; + const ptr = @ptrCast(Ptr, @alignCast(@alignOf(std.meta.Child(Ptr)), some_ptr)); + + // Note: We don't check array length, since we force align the + // lower bits of the address as the GBA would + return ptr[forceAlign(T, offset) / @sizeOf(T)]; + } + + return self.dbgSlowRead(T, unaligned_address); +} + fn slowRead(self: *Self, comptime T: type, unaligned_address: u32) T { @setCold(true); @@ -366,6 +338,34 @@ fn slowRead(self: *Self, comptime T: type, unaligned_address: u32) T { }; } +fn dbgSlowRead(self: *const Self, comptime T: type, unaligned_address: u32) T { + const page = @truncate(u8, unaligned_address >> 24); + const address = forceAlign(T, unaligned_address); + + return switch (page) { + // General Internal Memory + 0x00 => blk: { + if (address < Bios.size) + break :blk self.bios.dbgRead(T, self.cpu.r[15], unaligned_address); + + break :blk self.openBus(T, address); + }, + 0x02 => unreachable, // handled by fastmem + 0x03 => unreachable, // handled by fastmem + 0x04 => self.readIo(T, address), + + // Internal Display Memory + 0x05 => unreachable, // handled by fastmem + 0x06 => unreachable, // handled by fastmem + 0x07 => unreachable, // handled by fastmem + + // External Memory (Game Pak) + 0x08...0x0D => self.pak.dbgRead(T, address), + 0x0E...0x0F => self.readBackup(T, unaligned_address), + else => self.openBus(T, address), + }; +} + fn readBackup(self: *const Self, comptime T: type, unaligned_address: u32) T { const value = self.pak.backup.read(unaligned_address); @@ -406,6 +406,31 @@ pub fn write(self: *Self, comptime T: type, unaligned_address: u32, value: T) vo } } +/// Mostly Identical to `Bus.write`, slowmeme is handled by `Bus.dbgSlowWrite` +pub fn dbgWrite(self: *Self, comptime T: type, unaligned_address: u32, value: T) void { + const bits = @typeInfo(std.math.IntFittingRange(0, page_size - 1)).Int.bits; + const page = unaligned_address >> bits; + const offset = unaligned_address & (page_size - 1); + + // We're doing some serious out-of-bounds open-bus writes, they do nothing though + if (page >= table_len) return; + + if (self.write_tables[@boolToInt(T == u8)][page]) |some_ptr| { + // We have a pointer to a page, cast the pointer to it's underlying type + const Ptr = [*]T; + const ptr = @ptrCast(Ptr, @alignCast(@alignOf(std.meta.Child(Ptr)), some_ptr)); + + // Note: We don't check array length, since we force align the + // lower bits of the address as the GBA would + ptr[forceAlign(T, offset) / @sizeOf(T)] = value; + } else { + // we can return early if this is an 8-bit OAM write + if (T == u8 and @truncate(u8, unaligned_address >> 24) == 0x07) return; + + self.dbgSlowWrite(T, unaligned_address, value); + } +} + fn slowWrite(self: *Self, comptime T: type, unaligned_address: u32, value: T) void { @setCold(true); @@ -431,6 +456,31 @@ fn slowWrite(self: *Self, comptime T: type, unaligned_address: u32, value: T) vo } } +fn dbgSlowWrite(self: *Self, comptime T: type, unaligned_address: u32, value: T) void { + @setCold(true); + + const page = @truncate(u8, unaligned_address >> 24); + const address = forceAlign(T, unaligned_address); + + switch (page) { + // General Internal Memory + 0x00 => self.bios.write(T, address, value), + 0x02 => unreachable, // completely handled by fastmem + 0x03 => unreachable, // completely handled by fastmem + 0x04 => return, // FIXME: Let debug writes mess with I/O + + // Internal Display Memory + 0x05 => self.ppu.palette.write(T, address, value), + 0x06 => self.ppu.vram.write(T, self.ppu.dispcnt, address, value), + 0x07 => unreachable, // completely handled by fastmem + + // External Memory (Game Pak) + 0x08...0x0D => return, // FIXME: Debug Write to Backup/GPIO w/out messing with state + 0x0E...0x0F => return, // FIXME: Debug Write to Backup w/out messing with state + else => {}, + } +} + inline fn rotateBy(comptime T: type, address: u32) u32 { return switch (T) { u32 => address & 3, diff --git a/src/core/emu.zig b/src/core/emu.zig index 76918fb..422636e 100644 --- a/src/core/emu.zig +++ b/src/core/emu.zig @@ -184,11 +184,7 @@ pub const EmuThing = struct { } pub fn write(self: *Self, addr: u32, value: u8) void { - _ = value; - _ = self; - _ = addr; - - std.debug.panic("TODO: Implement Debug Writes?", .{}); + self.cpu.bus.dbgWrite(u8, addr, value); } pub fn registers(self: *const Self) *[16]u32 {