feat(bus): implement Gameboy Advance MMIO

This commit is contained in:
Rekai Nyangadzayi Musuka 2022-01-02 02:36:06 -06:00
parent b63eb2dabc
commit 65c3dd722c
5 changed files with 132 additions and 53 deletions

View File

@ -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 }),
}
}
};

View File

@ -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;
}

View File

@ -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", .{});
}

View File

@ -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);
}
}

View File

@ -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];
}
};