From 65c3dd722ce070bf10839c1bbb82f57114a334b9 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Sun, 2 Jan 2022 02:36:06 -0600 Subject: [PATCH] feat(bus): implement Gameboy Advance MMIO --- src/bus.zig | 131 ++++++++++++++++++++++---- src/cpu.zig | 2 +- src/cpu/half_signed_data_transfer.zig | 10 +- src/cpu/single_data_transfer.zig | 14 +-- src/pak.zig | 28 ++---- 5 files changed, 132 insertions(+), 53 deletions(-) diff --git a/src/bus.zig b/src/bus.zig index c8a7400..e780e37 100644 --- a/src/bus.zig +++ b/src/bus.zig @@ -12,33 +12,128 @@ pub const Bus = struct { }; } - pub fn readWord(self: *const @This(), addr: u32) u32 { - return self.pak.readWord(addr); + pub fn read32(self: *const @This(), addr: u32) u32 { + return switch (addr) { + // General Internal Memory + 0x0000_0000...0x0000_3FFF => std.debug.panic("read32 from 0x{X:} in BIOS", .{addr}), + 0x0200_0000...0x0203FFFF => std.debug.panic("read32 from 0x{X:} in IWRAM", .{addr}), + 0x0300_0000...0x0300_7FFF => std.debug.panic("read32 from 0x{X:} in EWRAM", .{addr}), + 0x0400_0000...0x0400_03FE => std.debug.panic("read32 from 0x{X:} in I/O", .{addr}), + + // Internal Display Memory + 0x0500_0000...0x0500_03FF => std.debug.panic("read32 from 0x{X:} in BG/OBJ Palette RAM", .{addr}), + 0x0600_0000...0x0601_7FFF => std.debug.panic("read32 from 0x{X:} in VRAM", .{addr}), + 0x0700_0000...0x0700_03FF => std.debug.panic("read32 from 0x{X:} in OAM", .{addr}), + + // External Memory (Game Pak) + 0x0800_0000...0x09FF_FFFF => self.pak.get32(@as(usize, addr - 0x0800_0000)), + 0x0A00_0000...0x0BFF_FFFF => self.pak.get32(@as(usize, addr - 0x0A00_0000)), + 0x0C00_0000...0x0DFF_FFFF => self.pak.get32(@as(usize, addr - 0x0C00_0000)), + + else => { + std.log.warn("ZBA tried to read32 from 0x{X:}", .{addr}); + return 0x0000_0000; + }, + }; } - pub fn writeWord(self: *@This(), addr: u32, word: u32) void { - // TODO: Actually implement the memory map - if (addr > self.pak.buf.len) return; - self.pak.writeWord(addr, word); + pub fn write32(_: *@This(), addr: u32, word: u32) void { + // TODO: write32 can write to GamePak Flash + + switch (addr) { + // General Internal Memory + 0x0200_0000...0x0203FFFF => std.debug.panic("write32 0x{X:} to 0x{X:} in IWRAM", .{ word, addr }), + 0x0300_0000...0x0300_7FFF => std.debug.panic("write32 0x{X:} to 0x{X:} in EWRAM", .{ word, addr }), + 0x0400_0000...0x0400_03FE => std.debug.panic("write32 0x{X:} to 0x{X:} in I/O", .{ word, addr }), + + // Internal Display Memory + 0x0500_0000...0x0500_03FF => std.debug.panic("write32 0x{X:} to 0x{X:} in BG/OBJ Palette RAM", .{ word, addr }), + 0x0600_0000...0x0601_7FFF => std.debug.panic("write32 0x{X:} to 0x{X:} in VRAM", .{ word, addr }), + 0x0700_0000...0x0700_03FF => std.debug.panic("write32 0x{X:} to 0x{X:} in OAM", .{ word, addr }), + + else => std.log.warn("ZBA tried to write32 0x{X:} to 0x{X:}", .{ word, addr }), + } } - pub fn readHalfWord(self: *const @This(), addr: u32) u16 { - return self.pak.readHalfWord(addr); + pub fn read16(self: *const @This(), addr: u32) u16 { + return switch (addr) { + // General Internal Memory + 0x0000_0000...0x0000_3FFF => std.debug.panic("read16 from 0x{X:} in BIOS", .{addr}), + 0x0200_0000...0x0203FFFF => std.debug.panic("read16 from 0x{X:} in IWRAM", .{addr}), + 0x0300_0000...0x0300_7FFF => std.debug.panic("read16 from 0x{X:} in EWRAM", .{addr}), + 0x0400_0000...0x0400_03FE => std.debug.panic("read16 from 0x{X:} in I/O", .{addr}), + + // Internal Display Memory + 0x0500_0000...0x0500_03FF => std.debug.panic("read16 from 0x{X:} in BG/OBJ Palette RAM", .{addr}), + 0x0600_0000...0x0601_7FFF => std.debug.panic("read16 from 0x{X:} in VRAM", .{addr}), + 0x0700_0000...0x0700_03FF => std.debug.panic("read16 from 0x{X:} in OAM", .{addr}), + + // External Memory (Game Pak) + 0x0800_0000...0x09FF_FFFF => self.pak.get16(@as(usize, addr - 0x0800_0000)), + 0x0A00_0000...0x0BFF_FFFF => self.pak.get16(@as(usize, addr - 0x0A00_0000)), + 0x0C00_0000...0x0DFF_FFFF => self.pak.get16(@as(usize, addr - 0x0C00_0000)), + + else => { + std.log.warn("ZBA tried to read16 from 0x{X:}", .{addr}); + return 0x0000; + }, + }; } - pub fn writeHalfWord(self: *@This(), addr: u32, halfword: u16) void { - // TODO: Actually implement the memory map - if (addr > self.pak.buf.len) return; - self.pak.writeHalfWord(addr, halfword); + pub fn write16(_: *@This(), addr: u32, halfword: u16) void { + // TODO: write16 can write to GamePak Flash + + switch (addr) { + // General Internal Memory + 0x0200_0000...0x0203FFFF => std.debug.panic("write16 0x{X:} to 0x{X:} in IWRAM", .{ halfword, addr }), + 0x0300_0000...0x0300_7FFF => std.debug.panic("write16 0x{X:} to 0x{X:} in EWRAM", .{ halfword, addr }), + 0x0400_0000...0x0400_03FE => std.debug.panic("write16 0x{X:} to 0x{X:} in I/O", .{ halfword, addr }), + + // Internal Display Memory + 0x0500_0000...0x0500_03FF => std.debug.panic("write16 0x{X:} to 0x{X:} in BG/OBJ Palette RAM", .{ halfword, addr }), + 0x0600_0000...0x0601_7FFF => std.debug.panic("write16 0x{X:} to 0x{X:} in VRAM", .{ halfword, addr }), + 0x0700_0000...0x0700_03FF => std.debug.panic("write16 0x{X:} to 0x{X:} in OAM", .{ halfword, addr }), + + else => std.log.warn("ZBA tried to write16 0x{X:} to 0x{X:}", .{ halfword, addr }), + } } - pub fn readByte(self: *const @This(), addr: u32) u8 { - return self.pak.readByte(addr); + pub fn read8(self: *const @This(), addr: u32) u8 { + return switch (addr) { + // General Internal Memory + 0x0000_0000...0x0000_3FFF => std.debug.panic("read8 from 0x{X:} in BIOS", .{addr}), + 0x0200_0000...0x0203FFFF => std.debug.panic("read8 from 0x{X:} in IWRAM", .{addr}), + 0x0300_0000...0x0300_7FFF => std.debug.panic("read8 from 0x{X:} in EWRAM", .{addr}), + 0x0400_0000...0x0400_03FE => std.debug.panic("read8 from 0x{X:} in I/O", .{addr}), + + // Internal Display Memory + 0x0500_0000...0x0500_03FF => std.debug.panic("read8 from 0x{X:} in BG/OBJ Palette RAM", .{addr}), + 0x0600_0000...0x0601_7FFF => std.debug.panic("read8 from 0x{X:} in VRAM", .{addr}), + 0x0700_0000...0x0700_03FF => std.debug.panic("read8 from 0x{X:} in OAM", .{addr}), + + // External Memory (Game Pak) + 0x0800_0000...0x09FF_FFFF => self.pak.get8(@as(usize, addr - 0x0800_0000)), + 0x0A00_0000...0x0BFF_FFFF => self.pak.get8(@as(usize, addr - 0x0A00_0000)), + 0x0C00_0000...0x0DFF_FFFF => self.pak.get8(@as(usize, addr - 0x0C00_0000)), + 0x0E00_0000...0x0E00_FFFF => std.debug.panic("read8 from 0x{X:} in Game Pak SRAM", .{addr}), + + else => { + std.log.warn("ZBA tried to read8 from 0x{X:}", .{addr}); + return 0x00; + }, + }; } - pub fn writeByte(self: *@This(), addr: u32, byte: u8) void { - // TODO: Actually implement the memory map - if (addr > self.pak.buf.len) return; - self.pak.writeByte(addr, byte); + pub fn write8(_: *@This(), addr: u32, byte: u8) void { + switch (addr) { + // General Internal Memory + 0x0200_0000...0x0203FFFF => std.debug.panic("write8 0x{X:} to 0x{X:} in IWRAM", .{ byte, addr }), + 0x0300_0000...0x0300_7FFF => std.debug.panic("write8 0x{X:} to 0x{X:} in EWRAM", .{ byte, addr }), + 0x0400_0000...0x0400_03FE => std.debug.panic("write8 0x{X:} to 0x{X:} in I/O", .{ byte, addr }), + + // External Memory (Game Pak) + 0x0E00_0000...0x0E00_FFFF => std.debug.panic("write8 0x{X:} to 0x{X:} in Game Pak SRAM", .{ byte, addr }), + else => std.log.warn("ZBA tried to write8 0x{X:} to 0x{X:}", .{ byte, addr }), + } } }; diff --git a/src/cpu.zig b/src/cpu.zig index bb1a2d5..5a627c8 100644 --- a/src/cpu.zig +++ b/src/cpu.zig @@ -34,7 +34,7 @@ pub const Arm7tdmi = struct { } fn fetch(self: *@This()) u32 { - const word = self.bus.readWord(self.r[15]); + const word = self.bus.read32(self.r[15]); self.r[15] += 4; return word; } diff --git a/src/cpu/half_signed_data_transfer.zig b/src/cpu/half_signed_data_transfer.zig index cd94ac2..ef49fe6 100644 --- a/src/cpu/half_signed_data_transfer.zig +++ b/src/cpu/half_signed_data_transfer.zig @@ -34,17 +34,17 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti }, 0b01 => { // LDRH - const halfword = bus.readHalfWord(address); + const halfword = bus.read16(address); cpu.r[rd] = @as(u32, halfword); }, 0b10 => { // LDRSB - const byte = bus.readByte(address); + const byte = bus.read8(address); cpu.r[rd] = util.u32SignExtend(8, @as(u32, byte)); }, 0b11 => { // LDRSH - const halfword = bus.readHalfWord(address); + const halfword = bus.read16(address); cpu.r[rd] = util.u32SignExtend(16, @as(u32, halfword)); }, } @@ -53,8 +53,8 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti // STRH const src = @truncate(u16, cpu.r[rd]); - bus.writeHalfWord(address + 2, src); - bus.writeHalfWord(address, src); + bus.write16(address + 2, src); + bus.write16(address, src); } else { std.debug.panic("TODO Figure out if this is also SWP", .{}); } diff --git a/src/cpu/single_data_transfer.zig b/src/cpu/single_data_transfer.zig index 1cf9251..dbb1028 100644 --- a/src/cpu/single_data_transfer.zig +++ b/src/cpu/single_data_transfer.zig @@ -21,29 +21,29 @@ pub fn comptimeSingleDataTransfer(comptime I: bool, comptime P: bool, comptime U if (L) { if (B) { // LDRB - cpu.r[rd] = bus.readByte(address); + cpu.r[rd] = bus.read8(address); } else { // LDR // FIXME: Unsure about how I calculate the boundary offset - cpu.r[rd] = std.math.rotl(u32, bus.readWord(address), address % 4); + cpu.r[rd] = std.math.rotl(u32, bus.read32(address), address % 4); } } else { if (B) { // STRB const src = @truncate(u8, cpu.r[rd]); - bus.writeByte(address + 3, src); - bus.writeByte(address + 2, src); - bus.writeByte(address + 1, src); - bus.writeByte(address, src); + bus.write8(address + 3, src); + bus.write8(address + 2, src); + bus.write8(address + 1, src); + bus.write8(address, src); } else { // STR // FIXME: Is this right? const src = cpu.r[rd]; const aligned_addr = address - (address % 4); - bus.writeWord(aligned_addr, src); + bus.write32(aligned_addr, src); } } diff --git a/src/pak.zig b/src/pak.zig index dafba49..b9ab9aa 100644 --- a/src/pak.zig +++ b/src/pak.zig @@ -16,31 +16,15 @@ pub const GamePak = struct { }; } - pub fn readWord(self: *const @This(), addr: u32) u32 { - return (@as(u32, self.buf[addr + 3]) << 24) | (@as(u32, self.buf[addr + 2]) << 16) | (@as(u32, self.buf[addr + 1]) << 8) | (@as(u32, self.buf[addr])); + pub inline fn get32(self: *const @This(), idx: usize) u32 { + return (@as(u32, self.buf[idx + 3]) << 24) | (@as(u32, self.buf[idx + 2]) << 16) | (@as(u32, self.buf[idx + 1]) << 8) | (@as(u32, self.buf[idx])); } - pub fn writeWord(self: *const @This(), addr: u32, word: u32) void { - self.buf[addr + 3] = @truncate(u8, word >> 24); - self.buf[addr + 2] = @truncate(u8, word >> 16); - self.buf[addr + 1] = @truncate(u8, word >> 8); - self.buf[addr] = @truncate(u8, word); + pub inline fn get16(self: *const @This(), idx: usize) u16 { + return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]); } - pub fn readHalfWord(self: *const @This(), addr: u32) u16 { - return (@as(u16, self.buf[addr + 1]) << 8) | @as(u16, self.buf[addr]); - } - - pub fn writeHalfWord(self: *@This(), addr: u32, halfword: u16) void { - self.buf[addr + 1] = @truncate(u8, halfword >> 8); - self.buf[addr] = @truncate(u8, halfword); - } - - pub fn readByte(self: *const @This(), addr: u32) u8 { - return self.buf[addr]; - } - - pub fn writeByte(self: *@This(), addr: u32, byte: u8) void { - self.buf[addr] = byte; + pub inline fn get8(self: *const @This(), idx: usize) u8 { + return self.buf[idx]; } };