diff --git a/src/core/Bus.zig b/src/core/Bus.zig index db284e9..408b5ee 100644 --- a/src/core/Bus.zig +++ b/src/core/Bus.zig @@ -77,70 +77,29 @@ pub fn attach(self: *Self, cpu: *Arm7tdmi) void { self.cpu = cpu; } -pub fn debugRead(self: *const Self, comptime T: type, address: u32) T { - const cached = self.sched.tick; - defer self.sched.tick = cached; - - // FIXME: This is bad but it's a debug read so I don't care that much? - const this = @intToPtr(*Self, @ptrToInt(self)); - - return this.read(T, address); -} - -fn readOpenBus(self: *const Self, comptime T: type, address: u32) T { - const r15 = self.cpu.?.r[15]; - - const word = if (self.cpu.?.cpsr.t.read()) blk: { - const page = @truncate(u8, r15 >> 24); - - switch (page) { - // EWRAM, PALRAM, VRAM, and Game ROM (16-bit) - 0x02, 0x05, 0x06, 0x08...0x0D => { - const halfword = self.debugRead(u16, r15 + 2); - break :blk @as(u32, halfword) << 16 | halfword; - }, - // BIOS or OAM (32-bit) - 0x00, 0x07 => { - const offset: u32 = if (address & 3 == 0b00) 2 else 0; - break :blk @as(u32, self.debugRead(u16, (r15 + 2) + offset)) << 16 | self.debugRead(u16, r15 + offset); - }, - // IWRAM (16-bit but special) - 0x03 => { - const offset: u32 = if (address & 3 == 0b00) 2 else 0; - break :blk @as(u32, self.debugRead(u16, (r15 + 2) - offset)) << 16 | self.debugRead(u16, r15 + offset); - }, - else => unreachable, - } - } else self.debugRead(u32, r15 + 4); - - return @truncate(T, rotr(u32, word, 8 * (address & 3))); -} - -fn readBios(self: *Self, comptime T: type, address: u32) T { - if (address < Bios.size) return self.bios.checkedRead(T, self.cpu.?.r[15], alignAddress(T, address)); - - return self.readOpenBus(T, address); -} - -pub fn read(self: *Self, comptime T: type, address: u32) T { +pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { const page = @truncate(u8, address >> 24); - const align_addr = alignAddress(T, address); - defer self.sched.tick += timings[@boolToInt(T == u32)][@truncate(u4, page)]; + const aligned_addr = forceAlign(T, address); return switch (page) { // General Internal Memory - 0x00 => self.readBios(T, address), - 0x02 => self.ewram.read(T, align_addr), - 0x03 => self.iwram.read(T, align_addr), - 0x04 => io.read(self, T, align_addr), + 0x00 => blk: { + if (address < Bios.size) + break :blk self.bios.dbgRead(T, self.cpu.?.r[15], aligned_addr); + + break :blk self.readOpenBus(T, address); + }, + 0x02 => self.ewram.read(T, aligned_addr), + 0x03 => self.iwram.read(T, aligned_addr), + 0x04 => io.read(self, T, aligned_addr), // Internal Display Memory - 0x05 => self.ppu.palette.read(T, align_addr), - 0x06 => self.ppu.vram.read(T, align_addr), - 0x07 => self.ppu.oam.read(T, align_addr), + 0x05 => self.ppu.palette.read(T, aligned_addr), + 0x06 => self.ppu.vram.read(T, aligned_addr), + 0x07 => self.ppu.oam.read(T, aligned_addr), // External Memory (Game Pak) - 0x08...0x0D => self.pak.read(T, align_addr), + 0x08...0x0D => self.pak.dbgRead(T, aligned_addr), 0x0E...0x0F => blk: { const value = self.pak.backup.read(address); @@ -153,29 +112,100 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { break :blk @as(T, value) * multiplier; }, - else => readOpenBus(self, T, address), + else => self.readOpenBus(T, address), + }; +} + +fn readOpenBus(self: *const Self, comptime T: type, address: u32) T { + const r15 = self.cpu.?.r[15]; + + const word = if (self.cpu.?.cpsr.t.read()) blk: { + const page = @truncate(u8, r15 >> 24); + + switch (page) { + // EWRAM, PALRAM, VRAM, and Game ROM (16-bit) + 0x02, 0x05, 0x06, 0x08...0x0D => { + const halfword = self.dbgRead(u16, r15 + 2); + break :blk @as(u32, halfword) << 16 | halfword; + }, + // BIOS or OAM (32-bit) + 0x00, 0x07 => { + const offset: u32 = if (address & 3 == 0b00) 2 else 0; + break :blk @as(u32, self.dbgRead(u16, (r15 + 2) + offset)) << 16 | self.dbgRead(u16, r15 + offset); + }, + // IWRAM (16-bit but special) + 0x03 => { + const offset: u32 = if (address & 3 == 0b00) 2 else 0; + break :blk @as(u32, self.dbgRead(u16, (r15 + 2) - offset)) << 16 | self.dbgRead(u16, r15 + offset); + }, + else => unreachable, + } + } else self.dbgRead(u32, r15 + 4); + + return @truncate(T, rotr(u32, word, 8 * (address & 3))); +} + +pub fn read(self: *Self, comptime T: type, address: u32) T { + const page = @truncate(u8, address >> 24); + const aligned_addr = forceAlign(T, address); + + self.sched.tick += timings[@boolToInt(T == u32)][@truncate(u4, page)]; + + return switch (page) { + // General Internal Memory + 0x00 => blk: { + if (address < Bios.size) + break :blk self.bios.read(T, self.cpu.?.r[15], aligned_addr); + + break :blk self.readOpenBus(T, address); + }, + 0x02 => self.ewram.read(T, aligned_addr), + 0x03 => self.iwram.read(T, aligned_addr), + 0x04 => io.read(self, T, aligned_addr), + + // Internal Display Memory + 0x05 => self.ppu.palette.read(T, aligned_addr), + 0x06 => self.ppu.vram.read(T, aligned_addr), + 0x07 => self.ppu.oam.read(T, aligned_addr), + + // External Memory (Game Pak) + 0x08...0x0D => self.pak.read(T, aligned_addr), + 0x0E...0x0F => blk: { + const value = self.pak.backup.read(address); + + const multiplier = switch (T) { + u32 => 0x01010101, + u16 => 0x0101, + u8 => 1, + else => @compileError("Backup: Unsupported read width"), + }; + + break :blk @as(T, value) * multiplier; + }, + else => self.readOpenBus(T, address), }; } pub fn write(self: *Self, comptime T: type, address: u32, value: T) void { const page = @truncate(u8, address >> 24); - const align_addr = alignAddress(T, address); - defer self.sched.tick += timings[@boolToInt(T == u32)][@truncate(u4, page)]; + const aligned_addr = forceAlign(T, address); + + self.sched.tick += timings[@boolToInt(T == u32)][@truncate(u4, page)]; switch (page) { // General Internal Memory - 0x00 => self.bios.write(T, align_addr, value), - 0x02 => self.ewram.write(T, align_addr, value), - 0x03 => self.iwram.write(T, align_addr, value), - 0x04 => io.write(self, T, align_addr, value), + 0x00 => self.bios.write(T, aligned_addr, value), + 0x02 => self.ewram.write(T, aligned_addr, value), + 0x03 => self.iwram.write(T, aligned_addr, value), + 0x04 => io.write(self, T, aligned_addr, value), // Internal Display Memory - 0x05 => self.ppu.palette.write(T, align_addr, value), - 0x06 => self.ppu.vram.write(T, self.ppu.dispcnt, align_addr, value), - 0x07 => self.ppu.oam.write(T, align_addr, value), + 0x05 => self.ppu.palette.write(T, aligned_addr, value), + 0x06 => self.ppu.vram.write(T, self.ppu.dispcnt, aligned_addr, value), + 0x07 => self.ppu.oam.write(T, aligned_addr, value), // External Memory (Game Pak) - 0x08...0x0D => self.pak.write(T, self.dma[3].word_count, align_addr, value), + 0x08...0x0D => self.pak.write(T, self.dma[3].word_count, aligned_addr, value), 0x0E...0x0F => { const rotate_by = switch (T) { u32 => address & 3, @@ -190,7 +220,7 @@ pub fn write(self: *Self, comptime T: type, address: u32, value: T) void { } } -fn alignAddress(comptime T: type, address: u32) u32 { +fn forceAlign(comptime T: type, address: u32) u32 { return switch (T) { u32 => address & 0xFFFF_FFFC, u16 => address & 0xFFFF_FFFE, diff --git a/src/core/bus/Bios.zig b/src/core/bus/Bios.zig index 39f8e02..9c1cc7b 100644 --- a/src/core/bus/Bios.zig +++ b/src/core/bus/Bios.zig @@ -31,17 +31,22 @@ pub fn deinit(self: Self) void { if (self.buf) |buf| self.alloc.free(buf); } -pub fn checkedRead(self: *Self, comptime T: type, r15: u32, addr: u32) T { +pub fn read(self: *Self, comptime T: type, r15: u32, addr: u32) T { if (r15 < Self.size) { self.addr_latch = addr; - return self.read(T, addr); + return self.uncheckedRead(T, addr); } log.debug("Rejected read since r15=0x{X:0>8}", .{r15}); - return @truncate(T, self.read(T, self.addr_latch + 8)); + return @truncate(T, self.uncheckedRead(T, self.addr_latch + 8)); } -fn read(self: *const Self, comptime T: type, addr: u32) T { +pub fn dbgRead(self: *const Self, comptime T: type, r15: u32, addr: u32) T { + if (r15 < Self.size) return self.uncheckedRead(T, addr); + return @truncate(T, self.uncheckedRead(T, self.addr_latch + 8)); +} + +fn uncheckedRead(self: *const Self, comptime T: type, addr: u32) T { if (self.buf) |buf| { return switch (T) { u32, u16, u8 => std.mem.readIntSliceLittle(T, buf[addr..][0..@sizeOf(T)]), diff --git a/src/core/bus/GamePak.zig b/src/core/bus/GamePak.zig index fe7461d..98504d1 100644 --- a/src/core/bus/GamePak.zig +++ b/src/core/bus/GamePak.zig @@ -90,6 +90,33 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { }; } +pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { + const addr = address & 0x1FF_FFFF; + + if (self.backup.kind == .Eeprom) { + if (self.isLarge()) { + // Addresses 0x1FF_FF00 to 0x1FF_FFFF are reserved from EEPROM accesses if + // * Backup type is EEPROM + // * Large ROM (Size is greater than 16MB) + if (addr > 0x1FF_FEFF) + return self.backup.eeprom.dbgRead(); + } else { + // Addresses 0x0D00_0000 to 0x0DFF_FFFF are reserved for EEPROM accesses if + // * Backup type is EEPROM + // * Small ROM (less than 16MB) + if (@truncate(u8, address >> 24) == 0x0D) + return self.backup.eeprom.dbgRead(); + } + } + + return switch (T) { + u32 => (@as(T, self.get(addr + 3)) << 24) | (@as(T, self.get(addr + 2)) << 16) | (@as(T, self.get(addr + 1)) << 8) | (@as(T, self.get(addr))), + u16 => (@as(T, self.get(addr + 1)) << 8) | @as(T, self.get(addr)), + u8 => self.get(addr), + else => @compileError("GamePak: Unsupported read width"), + }; +} + pub fn write(self: *Self, comptime T: type, word_count: u16, address: u32, value: T) void { const addr = address & 0x1FF_FFFF; diff --git a/src/core/bus/backup.zig b/src/core/bus/backup.zig index 5e62088..5015d12 100644 --- a/src/core/bus/backup.zig +++ b/src/core/bus/backup.zig @@ -340,6 +340,10 @@ const Eeprom = struct { return self.reader.read(); } + pub fn dbgRead(self: *const Self) u1 { + return self.reader.dbgRead(); + } + pub fn write(self: *Self, word_count: u16, buf: *[]u8, bit: u1) void { if (self.guessKind(word_count)) |found| { log.info("EEPROM Kind: {}", .{found}); @@ -492,6 +496,19 @@ const Eeprom = struct { return bit; } + + fn dbgRead(self: *const This) u1 { + if (!self.enabled) return 1; + + const bit = if (self.i < 4) blk: { + break :blk 0; + } else blk: { + const idx = @intCast(u6, 63 - (self.i - 4)); + break :blk @truncate(u1, self.data >> idx); + }; + + return bit; + } }; const Writer = struct { diff --git a/src/core/cpu.zig b/src/core/cpu.zig index edab894..319841a 100644 --- a/src/core/cpu.zig +++ b/src/core/cpu.zig @@ -520,11 +520,11 @@ pub const Arm7tdmi = struct { prettyPrintPsr(&self.spsr); if (self.cpsr.t.read()) { - const opcode = self.bus.debugRead(u16, self.r[15] - 4); + const opcode = self.bus.dbgRead(u16, self.r[15] - 4); const id = thumbIdx(opcode); std.debug.print("opcode: ID: 0x{b:0>10} 0x{X:0>4}\n", .{ id, opcode }); } else { - const opcode = self.bus.debugRead(u32, self.r[15] - 4); + const opcode = self.bus.dbgRead(u32, self.r[15] - 4); const id = armIdx(opcode); std.debug.print("opcode: ID: 0x{X:0>3} 0x{X:0>8}\n", .{ id, opcode }); } @@ -590,7 +590,7 @@ pub const Arm7tdmi = struct { if (self.cpsr.t.read()) { if (opcode >> 11 == 0x1E) { // Instruction 1 of a BL Opcode, print in ARM mode - const other_half = self.bus.debugRead(u16, self.r[15]); + const other_half = self.bus.dbgRead(u16, self.r[15]); const bl_opcode = @as(u32, opcode) << 16 | other_half; log_str = try std.fmt.bufPrint(&buf, arm_fmt, .{ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, c_psr, bl_opcode }); diff --git a/src/core/util.zig b/src/core/util.zig index 38a9fd8..758bf88 100644 --- a/src/core/util.zig +++ b/src/core/util.zig @@ -137,7 +137,7 @@ pub const Logger = struct { if (arm7tdmi.cpsr.t.read()) { if (opcode >> 11 == 0x1E) { // Instruction 1 of a BL Opcode, print in ARM mode - const low = arm7tdmi.bus.debugRead(u16, arm7tdmi.r[15]); + const low = arm7tdmi.bus.dbgRead(u16, arm7tdmi.r[15]); const bl_opcode = @as(u32, opcode) << 16 | low; self.print(arm_fmt, Self.fmtArgs(arm7tdmi, bl_opcode)) catch @panic("failed to write to log file");