feat(bus): implement Gameboy Advance MMIO
This commit is contained in:
parent
5cbfae677a
commit
d50aff30c9
131
src/bus.zig
131
src/bus.zig
|
@ -12,33 +12,128 @@ pub const Bus = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readWord(self: *const @This(), addr: u32) u32 {
|
pub fn read32(self: *const @This(), addr: u32) u32 {
|
||||||
return self.pak.readWord(addr);
|
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 {
|
pub fn write32(_: *@This(), addr: u32, word: u32) void {
|
||||||
// TODO: Actually implement the memory map
|
// TODO: write32 can write to GamePak Flash
|
||||||
if (addr > self.pak.buf.len) return;
|
|
||||||
self.pak.writeWord(addr, word);
|
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 {
|
pub fn read16(self: *const @This(), addr: u32) u16 {
|
||||||
return self.pak.readHalfWord(addr);
|
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 {
|
pub fn write16(_: *@This(), addr: u32, halfword: u16) void {
|
||||||
// TODO: Actually implement the memory map
|
// TODO: write16 can write to GamePak Flash
|
||||||
if (addr > self.pak.buf.len) return;
|
|
||||||
self.pak.writeHalfWord(addr, halfword);
|
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 {
|
pub fn read8(self: *const @This(), addr: u32) u8 {
|
||||||
return self.pak.readByte(addr);
|
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 {
|
pub fn write8(_: *@This(), addr: u32, byte: u8) void {
|
||||||
// TODO: Actually implement the memory map
|
switch (addr) {
|
||||||
if (addr > self.pak.buf.len) return;
|
// General Internal Memory
|
||||||
self.pak.writeByte(addr, byte);
|
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 }),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub const Arm7tdmi = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(self: *@This()) u32 {
|
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;
|
self.r[15] += 4;
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,17 +34,17 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti
|
||||||
},
|
},
|
||||||
0b01 => {
|
0b01 => {
|
||||||
// LDRH
|
// LDRH
|
||||||
const halfword = bus.readHalfWord(address);
|
const halfword = bus.read16(address);
|
||||||
cpu.r[rd] = @as(u32, halfword);
|
cpu.r[rd] = @as(u32, halfword);
|
||||||
},
|
},
|
||||||
0b10 => {
|
0b10 => {
|
||||||
// LDRSB
|
// LDRSB
|
||||||
const byte = bus.readByte(address);
|
const byte = bus.read8(address);
|
||||||
cpu.r[rd] = util.u32SignExtend(8, @as(u32, byte));
|
cpu.r[rd] = util.u32SignExtend(8, @as(u32, byte));
|
||||||
},
|
},
|
||||||
0b11 => {
|
0b11 => {
|
||||||
// LDRSH
|
// LDRSH
|
||||||
const halfword = bus.readHalfWord(address);
|
const halfword = bus.read16(address);
|
||||||
cpu.r[rd] = util.u32SignExtend(16, @as(u32, halfword));
|
cpu.r[rd] = util.u32SignExtend(16, @as(u32, halfword));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ pub fn comptimeHalfSignedDataTransfer(comptime P: bool, comptime U: bool, compti
|
||||||
// STRH
|
// STRH
|
||||||
const src = @truncate(u16, cpu.r[rd]);
|
const src = @truncate(u16, cpu.r[rd]);
|
||||||
|
|
||||||
bus.writeHalfWord(address + 2, src);
|
bus.write16(address + 2, src);
|
||||||
bus.writeHalfWord(address, src);
|
bus.write16(address, src);
|
||||||
} else {
|
} else {
|
||||||
std.debug.panic("TODO Figure out if this is also SWP", .{});
|
std.debug.panic("TODO Figure out if this is also SWP", .{});
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,29 +21,29 @@ pub fn comptimeSingleDataTransfer(comptime I: bool, comptime P: bool, comptime U
|
||||||
if (L) {
|
if (L) {
|
||||||
if (B) {
|
if (B) {
|
||||||
// LDRB
|
// LDRB
|
||||||
cpu.r[rd] = bus.readByte(address);
|
cpu.r[rd] = bus.read8(address);
|
||||||
} else {
|
} else {
|
||||||
// LDR
|
// LDR
|
||||||
|
|
||||||
// FIXME: Unsure about how I calculate the boundary offset
|
// 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 {
|
} else {
|
||||||
if (B) {
|
if (B) {
|
||||||
// STRB
|
// STRB
|
||||||
const src = @truncate(u8, cpu.r[rd]);
|
const src = @truncate(u8, cpu.r[rd]);
|
||||||
|
|
||||||
bus.writeByte(address + 3, src);
|
bus.write8(address + 3, src);
|
||||||
bus.writeByte(address + 2, src);
|
bus.write8(address + 2, src);
|
||||||
bus.writeByte(address + 1, src);
|
bus.write8(address + 1, src);
|
||||||
bus.writeByte(address, src);
|
bus.write8(address, src);
|
||||||
} else {
|
} else {
|
||||||
// STR
|
// STR
|
||||||
|
|
||||||
// FIXME: Is this right?
|
// FIXME: Is this right?
|
||||||
const src = cpu.r[rd];
|
const src = cpu.r[rd];
|
||||||
const aligned_addr = address - (address % 4);
|
const aligned_addr = address - (address % 4);
|
||||||
bus.writeWord(aligned_addr, src);
|
bus.write32(aligned_addr, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
src/pak.zig
28
src/pak.zig
|
@ -16,31 +16,15 @@ pub const GamePak = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readWord(self: *const @This(), addr: u32) u32 {
|
pub inline fn get32(self: *const @This(), idx: usize) 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]));
|
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 {
|
pub inline fn get16(self: *const @This(), idx: usize) u16 {
|
||||||
self.buf[addr + 3] = @truncate(u8, word >> 24);
|
return (@as(u16, self.buf[idx + 1]) << 8) | @as(u16, self.buf[idx]);
|
||||||
self.buf[addr + 2] = @truncate(u8, word >> 16);
|
|
||||||
self.buf[addr + 1] = @truncate(u8, word >> 8);
|
|
||||||
self.buf[addr] = @truncate(u8, word);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readHalfWord(self: *const @This(), addr: u32) u16 {
|
pub inline fn get8(self: *const @This(), idx: usize) u8 {
|
||||||
return (@as(u16, self.buf[addr + 1]) << 8) | @as(u16, self.buf[addr]);
|
return self.buf[idx];
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue