From 9baadadba274f7dc830743c47f70e6c4b0e2e90a Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 21 Oct 2022 05:13:03 -0300 Subject: [PATCH] style(bus): refactor several hardware abstractions --- src/core/apu.zig | 4 +- src/core/bus/Bios.zig | 61 +++++++------- src/core/bus/Ewram.zig | 30 +++---- src/core/bus/GamePak.zig | 167 +++++++++++++++++++++------------------ src/core/bus/Iwram.zig | 30 +++---- src/core/bus/dma.zig | 142 ++++++++++++++++----------------- src/core/bus/timer.zig | 76 +++++++++--------- src/core/ppu.zig | 6 +- src/core/scheduler.zig | 8 +- 9 files changed, 266 insertions(+), 258 deletions(-) diff --git a/src/core/apu.zig b/src/core/apu.zig index 45aa03d..a4dcdf5 100644 --- a/src/core/apu.zig +++ b/src/core/apu.zig @@ -403,12 +403,12 @@ pub const Apu = struct { if (@boolToInt(self.dma_cnt.chA_timer.read()) == tim_id) { self.chA.updateSample(); - if (self.chA.len() <= 15) cpu.bus.dma[1].requestSoundDma(0x0400_00A0); + if (self.chA.len() <= 15) cpu.bus.dma[1].requestAudio(0x0400_00A0); } if (@boolToInt(self.dma_cnt.chB_timer.read()) == tim_id) { self.chB.updateSample(); - if (self.chB.len() <= 15) cpu.bus.dma[2].requestSoundDma(0x0400_00A4); + if (self.chB.len() <= 15) cpu.bus.dma[2].requestAudio(0x0400_00A4); } } }; diff --git a/src/core/bus/Bios.zig b/src/core/bus/Bios.zig index 8515cdb..8e81ce1 100644 --- a/src/core/bus/Bios.zig +++ b/src/core/bus/Bios.zig @@ -12,6 +12,36 @@ allocator: Allocator, addr_latch: u32, +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); + } + + log.debug("Rejected read since r15=0x{X:0>8}", .{r15}); + return @truncate(T, self._read(T, self.addr_latch + 8)); +} + +pub fn dbgRead(self: *const Self, comptime T: type, r15: u32, addr: u32) T { + if (r15 < Self.size) return self._read(T, addr); + return @truncate(T, self._read(T, self.addr_latch + 8)); +} + +/// Read without the GBA safety checks +fn _read(self: *const Self, comptime T: type, addr: u32) T { + const buf = self.buf orelse std.debug.panic("[BIOS] ZBA tried to read {} from 0x{X:0>8} but not BIOS was present", .{ T, addr }); + + return switch (T) { + u32, u16, u8 => std.mem.readIntSliceLittle(T, buf[addr..][0..@sizeOf(T)]), + else => @compileError("BIOS: Unsupported read width"), + }; +} + +pub fn write(_: *Self, comptime T: type, addr: u32, value: T) void { + @setCold(true); + log.debug("Tried to write {} 0x{X:} to 0x{X:0>8} ", .{ T, value, addr }); +} + pub fn init(allocator: Allocator, maybe_path: ?[]const u8) !Self { const buf: ?[]u8 = if (maybe_path) |path| blk: { const file = try std.fs.cwd().openFile(path, .{}); @@ -31,34 +61,3 @@ pub fn deinit(self: *Self) void { if (self.buf) |buf| self.allocator.free(buf); self.* = undefined; } - -pub fn read(self: *Self, comptime T: type, r15: u32, addr: u32) T { - if (r15 < Self.size) { - self.addr_latch = addr; - return self.uncheckedRead(T, addr); - } - - log.debug("Rejected read since r15=0x{X:0>8}", .{r15}); - return @truncate(T, self.uncheckedRead(T, self.addr_latch + 8)); -} - -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)]), - else => @compileError("BIOS: Unsupported read width"), - }; - } - - std.debug.panic("[BIOS] ZBA tried to read {} from 0x{X:0>8} but not BIOS was present", .{ T, addr }); -} - -pub fn write(_: *Self, comptime T: type, addr: u32, value: T) void { - @setCold(true); - log.debug("Tried to write {} 0x{X:} to 0x{X:0>8} ", .{ T, value, addr }); -} diff --git a/src/core/bus/Ewram.zig b/src/core/bus/Ewram.zig index f3d952c..c36d998 100644 --- a/src/core/bus/Ewram.zig +++ b/src/core/bus/Ewram.zig @@ -7,21 +7,6 @@ const Self = @This(); buf: []u8, allocator: Allocator, -pub fn init(allocator: Allocator) !Self { - const buf = try allocator.alloc(u8, ewram_size); - std.mem.set(u8, buf, 0); - - return Self{ - .buf = buf, - .allocator = allocator, - }; -} - -pub fn deinit(self: *Self) void { - self.allocator.free(self.buf); - self.* = undefined; -} - pub fn read(self: *const Self, comptime T: type, address: usize) T { const addr = address & 0x3FFFF; @@ -39,3 +24,18 @@ pub fn write(self: *const Self, comptime T: type, address: usize, value: T) void else => @compileError("EWRAM: Unsupported write width"), }; } + +pub fn init(allocator: Allocator) !Self { + const buf = try allocator.alloc(u8, ewram_size); + std.mem.set(u8, buf, 0); + + return Self{ + .buf = buf, + .allocator = allocator, + }; +} + +pub fn deinit(self: *Self) void { + self.allocator.free(self.buf); + self.* = undefined; +} diff --git a/src/core/bus/GamePak.zig b/src/core/bus/GamePak.zig index 275223f..c96ee3a 100644 --- a/src/core/bus/GamePak.zig +++ b/src/core/bus/GamePak.zig @@ -19,78 +19,11 @@ allocator: Allocator, backup: Backup, gpio: *Gpio, -pub fn init(allocator: Allocator, cpu: *Arm7tdmi, rom_path: []const u8, save_path: ?[]const u8) !Self { - const file = try std.fs.cwd().openFile(rom_path, .{}); - defer file.close(); - - const file_buf = try file.readToEndAlloc(allocator, try file.getEndPos()); - const title = file_buf[0xA0..0xAC].*; - const kind = Backup.guessKind(file_buf); - const device = if (force_rtc) .Rtc else guessDevice(file_buf); - - logHeader(file_buf, &title); - - return .{ - .buf = file_buf, - .allocator = allocator, - .title = title, - .backup = try Backup.init(allocator, kind, title, save_path), - .gpio = try Gpio.init(allocator, cpu, device), - }; -} - -/// Searches the ROM to see if it can determine whether the ROM it's searching uses -/// any GPIO device, like a RTC for example. -fn guessDevice(buf: []const u8) Gpio.Device.Kind { - // Try to Guess if ROM uses RTC - const needle = "RTC_V"; // I was told SIIRTC_V, though Pokemen Firered (USA) is a false negative - - var i: usize = 0; - while ((i + needle.len) < buf.len) : (i += 1) { - if (std.mem.eql(u8, needle, buf[i..(i + needle.len)])) return .Rtc; - } - - // TODO: Detect other GPIO devices - - return .None; -} - -fn logHeader(buf: []const u8, title: *const [12]u8) void { - const code = buf[0xAC..0xB0]; - const maker = buf[0xB0..0xB2]; - const version = buf[0xBC]; - - log.info("Title: {s}", .{title}); - if (version != 0) log.info("Version: {}", .{version}); - log.info("Game Code: {s}", .{code}); - if (lookupMaker(maker)) |c| log.info("Maker: {s}", .{c}) else log.info("Maker Code: {s}", .{maker}); -} - -fn lookupMaker(slice: *const [2]u8) ?[]const u8 { - const id = @as(u16, slice[1]) << 8 | @as(u16, slice[0]); - return switch (id) { - 0x3130 => "Nintendo", - else => null, - }; -} - -inline fn isLarge(self: *const Self) bool { - return self.buf.len > 0x100_0000; -} - -pub fn deinit(self: *Self) void { - self.backup.deinit(); - self.gpio.deinit(self.allocator); - self.allocator.destroy(self.gpio); - self.allocator.free(self.buf); - self.* = undefined; -} - pub fn read(self: *Self, comptime T: type, address: u32) T { const addr = address & 0x1FF_FFFF; if (self.backup.kind == .Eeprom) { - if (self.isLarge()) { + if (self.buf.len > 0x100_0000) { // Large // Addresses 0x1FF_FF00 to 0x1FF_FFFF are reserved from EEPROM accesses if // * Backup type is EEPROM // * Large ROM (Size is greater than 16MB) @@ -142,11 +75,19 @@ pub fn read(self: *Self, comptime T: type, address: u32) T { }; } +inline fn get(self: *const Self, i: u32) u8 { + @setRuntimeSafety(false); + if (i < self.buf.len) return self.buf[i]; + + const lhs = i >> 1 & 0xFFFF; + return @truncate(u8, lhs >> 8 * @truncate(u5, i & 1)); +} + 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()) { + if (self.buf.len > 0x100_0000) { // Large // Addresses 0x1FF_FF00 to 0x1FF_FFFF are reserved from EEPROM accesses if // * Backup type is EEPROM // * Large ROM (Size is greater than 16MB) @@ -161,6 +102,35 @@ pub fn dbgRead(self: *const Self, comptime T: type, address: u32) T { } } + if (self.gpio.cnt == 1) { + // GPIO Can be read from + // We assume that this will only be true when a ROM actually does want something from GPIO + + switch (T) { + u32 => switch (address) { + // TODO: Do I even need to implement these? + 0x0800_00C4 => std.debug.panic("Handle 32-bit GPIO Data/Direction Reads", .{}), + 0x0800_00C6 => std.debug.panic("Handle 32-bit GPIO Direction/Control Reads", .{}), + 0x0800_00C8 => std.debug.panic("Handle 32-bit GPIO Control Reads", .{}), + else => {}, + }, + u16 => switch (address) { + // FIXME: What do 16-bit GPIO Reads look like? + 0x0800_00C4 => return self.gpio.read(.Data), + 0x0800_00C6 => return self.gpio.read(.Direction), + 0x0800_00C8 => return self.gpio.read(.Control), + else => {}, + }, + u8 => switch (address) { + 0x0800_00C4 => return self.gpio.read(.Data), + 0x0800_00C6 => return self.gpio.read(.Direction), + 0x0800_00C8 => return self.gpio.read(.Control), + else => {}, + }, + else => @compileError("GamePak[GPIO]: Unsupported read width"), + } + } + 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)), @@ -175,7 +145,7 @@ pub fn write(self: *Self, comptime T: type, word_count: u16, address: u32, value if (self.backup.kind == .Eeprom) { const bit = @truncate(u1, value); - if (self.isLarge()) { + if (self.buf.len > 0x100_0000) { // Large // Addresses 0x1FF_FF00 to 0x1FF_FFFF are reserved from EEPROM accesses if // * Backup type is EEPROM // * Large ROM (Size is greater than 16MB) @@ -213,12 +183,59 @@ pub fn write(self: *Self, comptime T: type, word_count: u16, address: u32, value } } -fn get(self: *const Self, i: u32) u8 { - @setRuntimeSafety(false); - if (i < self.buf.len) return self.buf[i]; +pub fn init(allocator: Allocator, cpu: *Arm7tdmi, rom_path: []const u8, save_path: ?[]const u8) !Self { + const file = try std.fs.cwd().openFile(rom_path, .{}); + defer file.close(); - const lhs = i >> 1 & 0xFFFF; - return @truncate(u8, lhs >> 8 * @truncate(u5, i & 1)); + const file_buf = try file.readToEndAlloc(allocator, try file.getEndPos()); + const title = file_buf[0xA0..0xAC].*; + const kind = Backup.guessKind(file_buf); + const device = if (force_rtc) .Rtc else guessDevice(file_buf); + + logHeader(file_buf, &title); + + return .{ + .buf = file_buf, + .allocator = allocator, + .title = title, + .backup = try Backup.init(allocator, kind, title, save_path), + .gpio = try Gpio.init(allocator, cpu, device), + }; +} + +pub fn deinit(self: *Self) void { + self.backup.deinit(); + self.gpio.deinit(self.allocator); + self.allocator.destroy(self.gpio); + self.allocator.free(self.buf); + self.* = undefined; +} + +/// Searches the ROM to see if it can determine whether the ROM it's searching uses +/// any GPIO device, like a RTC for example. +fn guessDevice(buf: []const u8) Gpio.Device.Kind { + // Try to Guess if ROM uses RTC + const needle = "RTC_V"; // I was told SIIRTC_V, though Pokemen Firered (USA) is a false negative + + var i: usize = 0; + while ((i + needle.len) < buf.len) : (i += 1) { + if (std.mem.eql(u8, needle, buf[i..(i + needle.len)])) return .Rtc; + } + + // TODO: Detect other GPIO devices + + return .None; +} + +fn logHeader(buf: []const u8, title: *const [12]u8) void { + const code = buf[0xAC..0xB0]; + const maker = buf[0xB0..0xB2]; + const version = buf[0xBC]; + + log.info("Title: {s}", .{title}); + if (version != 0) log.info("Version: {}", .{version}); + log.info("Game Code: {s}", .{code}); + log.info("Maker Code: {s}", .{maker}); } test "OOB Access" { diff --git a/src/core/bus/Iwram.zig b/src/core/bus/Iwram.zig index d58ccb7..383075e 100644 --- a/src/core/bus/Iwram.zig +++ b/src/core/bus/Iwram.zig @@ -7,21 +7,6 @@ const Self = @This(); buf: []u8, allocator: Allocator, -pub fn init(allocator: Allocator) !Self { - const buf = try allocator.alloc(u8, iwram_size); - std.mem.set(u8, buf, 0); - - return Self{ - .buf = buf, - .allocator = allocator, - }; -} - -pub fn deinit(self: *Self) void { - self.allocator.free(self.buf); - self.* = undefined; -} - pub fn read(self: *const Self, comptime T: type, address: usize) T { const addr = address & 0x7FFF; @@ -39,3 +24,18 @@ pub fn write(self: *const Self, comptime T: type, address: usize, value: T) void else => @compileError("IWRAM: Unsupported write width"), }; } + +pub fn init(allocator: Allocator) !Self { + const buf = try allocator.alloc(u8, iwram_size); + std.mem.set(u8, buf, 0); + + return Self{ + .buf = buf, + .allocator = allocator, + }; +} + +pub fn deinit(self: *Self) void { + self.allocator.free(self.buf); + self.* = undefined; +} diff --git a/src/core/bus/dma.zig b/src/core/bus/dma.zig index 719287b..4678642 100644 --- a/src/core/bus/dma.zig +++ b/src/core/bus/dma.zig @@ -40,48 +40,48 @@ pub fn write(comptime T: type, dma: *DmaTuple, addr: u32, value: T) void { switch (T) { u32 => switch (byte) { - 0xB0 => dma.*[0].setSad(value), - 0xB4 => dma.*[0].setDad(value), - 0xB8 => dma.*[0].setCnt(value), - 0xBC => dma.*[1].setSad(value), - 0xC0 => dma.*[1].setDad(value), - 0xC4 => dma.*[1].setCnt(value), - 0xC8 => dma.*[2].setSad(value), - 0xCC => dma.*[2].setDad(value), - 0xD0 => dma.*[2].setCnt(value), - 0xD4 => dma.*[3].setSad(value), - 0xD8 => dma.*[3].setDad(value), - 0xDC => dma.*[3].setCnt(value), + 0xB0 => dma.*[0].setDmasad(value), + 0xB4 => dma.*[0].setDmadad(value), + 0xB8 => dma.*[0].setDmacnt(value), + 0xBC => dma.*[1].setDmasad(value), + 0xC0 => dma.*[1].setDmadad(value), + 0xC4 => dma.*[1].setDmacnt(value), + 0xC8 => dma.*[2].setDmasad(value), + 0xCC => dma.*[2].setDmadad(value), + 0xD0 => dma.*[2].setDmacnt(value), + 0xD4 => dma.*[3].setDmasad(value), + 0xD8 => dma.*[3].setDmadad(value), + 0xDC => dma.*[3].setDmacnt(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, u16 => switch (byte) { - 0xB0 => dma.*[0].setSad(setU32L(dma.*[0].sad, value)), - 0xB2 => dma.*[0].setSad(setU32H(dma.*[0].sad, value)), - 0xB4 => dma.*[0].setDad(setU32L(dma.*[0].dad, value)), - 0xB6 => dma.*[0].setDad(setU32H(dma.*[0].dad, value)), - 0xB8 => dma.*[0].setCntL(value), - 0xBA => dma.*[0].setCntH(value), + 0xB0 => dma.*[0].setDmasad(setU32L(dma.*[0].sad, value)), + 0xB2 => dma.*[0].setDmasad(setU32H(dma.*[0].sad, value)), + 0xB4 => dma.*[0].setDmadad(setU32L(dma.*[0].dad, value)), + 0xB6 => dma.*[0].setDmadad(setU32H(dma.*[0].dad, value)), + 0xB8 => dma.*[0].setDmacntL(value), + 0xBA => dma.*[0].setDmacntH(value), - 0xBC => dma.*[1].setSad(setU32L(dma.*[1].sad, value)), - 0xBE => dma.*[1].setSad(setU32H(dma.*[1].sad, value)), - 0xC0 => dma.*[1].setDad(setU32L(dma.*[1].dad, value)), - 0xC2 => dma.*[1].setDad(setU32H(dma.*[1].dad, value)), - 0xC4 => dma.*[1].setCntL(value), - 0xC6 => dma.*[1].setCntH(value), + 0xBC => dma.*[1].setDmasad(setU32L(dma.*[1].sad, value)), + 0xBE => dma.*[1].setDmasad(setU32H(dma.*[1].sad, value)), + 0xC0 => dma.*[1].setDmadad(setU32L(dma.*[1].dad, value)), + 0xC2 => dma.*[1].setDmadad(setU32H(dma.*[1].dad, value)), + 0xC4 => dma.*[1].setDmacntL(value), + 0xC6 => dma.*[1].setDmacntH(value), - 0xC8 => dma.*[2].setSad(setU32L(dma.*[2].sad, value)), - 0xCA => dma.*[2].setSad(setU32H(dma.*[2].sad, value)), - 0xCC => dma.*[2].setDad(setU32L(dma.*[2].dad, value)), - 0xCE => dma.*[2].setDad(setU32H(dma.*[2].dad, value)), - 0xD0 => dma.*[2].setCntL(value), - 0xD2 => dma.*[2].setCntH(value), + 0xC8 => dma.*[2].setDmasad(setU32L(dma.*[2].sad, value)), + 0xCA => dma.*[2].setDmasad(setU32H(dma.*[2].sad, value)), + 0xCC => dma.*[2].setDmadad(setU32L(dma.*[2].dad, value)), + 0xCE => dma.*[2].setDmadad(setU32H(dma.*[2].dad, value)), + 0xD0 => dma.*[2].setDmacntL(value), + 0xD2 => dma.*[2].setDmacntH(value), - 0xD4 => dma.*[3].setSad(setU32L(dma.*[3].sad, value)), - 0xD6 => dma.*[3].setSad(setU32H(dma.*[3].sad, value)), - 0xD8 => dma.*[3].setDad(setU32L(dma.*[3].dad, value)), - 0xDA => dma.*[3].setDad(setU32H(dma.*[3].dad, value)), - 0xDC => dma.*[3].setCntL(value), - 0xDE => dma.*[3].setCntH(value), + 0xD4 => dma.*[3].setDmasad(setU32L(dma.*[3].sad, value)), + 0xD6 => dma.*[3].setDmasad(setU32H(dma.*[3].sad, value)), + 0xD8 => dma.*[3].setDmadad(setU32L(dma.*[3].dad, value)), + 0xDA => dma.*[3].setDmadad(setU32H(dma.*[3].dad, value)), + 0xDC => dma.*[3].setDmacntL(value), + 0xDE => dma.*[3].setDmacntH(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), @@ -110,15 +110,12 @@ fn DmaController(comptime id: u2) type { cnt: DmaControl, /// Internal. Currrent Source Address - _sad: u32, + sad_latch: u32, /// Internal. Current Destination Address - _dad: u32, + dad_latch: u32, /// Internal. Word Count _word_count: if (id == 3) u16 else u14, - // Internal. FIFO Word Count - _fifo_word_count: u8, - /// Some DMA Transfers are enabled during Hblank / VBlank and / or /// have delays. Thefore bit 15 of DMACNT isn't actually something /// we can use to control when we do or do not execute a step in a DMA Transfer @@ -132,33 +129,32 @@ fn DmaController(comptime id: u2) type { .cnt = .{ .raw = 0x000 }, // Internals - ._sad = 0, - ._dad = 0, + .sad_latch = 0, + .dad_latch = 0, ._word_count = 0, - ._fifo_word_count = 4, .in_progress = false, }; } - pub fn setSad(self: *Self, addr: u32) void { + pub fn setDmasad(self: *Self, addr: u32) void { self.sad = addr & sad_mask; } - pub fn setDad(self: *Self, addr: u32) void { + pub fn setDmadad(self: *Self, addr: u32) void { self.dad = addr & dad_mask; } - pub fn setCntL(self: *Self, halfword: u16) void { + pub fn setDmacntL(self: *Self, halfword: u16) void { self.word_count = @truncate(@TypeOf(self.word_count), halfword); } - pub fn setCntH(self: *Self, halfword: u16) void { + pub fn setDmacntH(self: *Self, halfword: u16) void { const new = DmaControl{ .raw = halfword }; if (!self.cnt.enabled.read() and new.enabled.read()) { // Reload Internals on Rising Edge. - self._sad = self.sad; - self._dad = self.dad; + self.sad_latch = self.sad; + self.dad_latch = self.dad; self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count; // Only a Start Timing of 00 has a DMA Transfer immediately begin @@ -168,15 +164,15 @@ fn DmaController(comptime id: u2) type { self.cnt.raw = halfword; } - pub fn setCnt(self: *Self, word: u32) void { - self.setCntL(@truncate(u16, word)); - self.setCntH(@truncate(u16, word >> 16)); + pub fn setDmacnt(self: *Self, word: u32) void { + self.setDmacntL(@truncate(u16, word)); + self.setDmacntH(@truncate(u16, word >> 16)); } pub fn step(self: *Self, cpu: *Arm7tdmi) void { const is_fifo = (id == 1 or id == 2) and self.cnt.start_timing.read() == 0b11; - const sad_adj = Self.adjustment(self.cnt.sad_adj.read()); - const dad_adj = if (is_fifo) .Fixed else Self.adjustment(self.cnt.dad_adj.read()); + const sad_adj = @intToEnum(Adjustment, self.cnt.sad_adj.read()); + const dad_adj = if (is_fifo) .Fixed else @intToEnum(Adjustment, self.cnt.dad_adj.read()); const transfer_type = is_fifo or self.cnt.transfer_type.read(); const offset: u32 = if (transfer_type) @sizeOf(u32) else @sizeOf(u16); @@ -184,22 +180,22 @@ fn DmaController(comptime id: u2) type { const mask = if (transfer_type) ~@as(u32, 3) else ~@as(u32, 1); if (transfer_type) { - cpu.bus.write(u32, self._dad & mask, cpu.bus.read(u32, self._sad & mask)); + cpu.bus.write(u32, self.dad_latch & mask, cpu.bus.read(u32, self.sad_latch & mask)); } else { - cpu.bus.write(u16, self._dad & mask, cpu.bus.read(u16, self._sad & mask)); + cpu.bus.write(u16, self.dad_latch & mask, cpu.bus.read(u16, self.sad_latch & mask)); } switch (sad_adj) { - .Increment => self._sad +%= offset, - .Decrement => self._sad -%= offset, - // TODO: Is just ignoring this ok? + .Increment => self.sad_latch +%= offset, + .Decrement => self.sad_latch -%= offset, + // FIXME: Is just ignoring this ok? .IncrementReload => log.err("{} is a prohibited adjustment on SAD", .{sad_adj}), .Fixed => {}, } switch (dad_adj) { - .Increment, .IncrementReload => self._dad +%= offset, - .Decrement => self._dad -%= offset, + .Increment, .IncrementReload => self.dad_latch +%= offset, + .Decrement => self.dad_latch -%= offset, .Fixed => {}, } @@ -227,7 +223,7 @@ fn DmaController(comptime id: u2) type { } } - pub fn pollBlankingDma(self: *Self, comptime kind: DmaKind) void { + fn poll(self: *Self, comptime kind: DmaKind) void { if (self.in_progress) return; // If there's an ongoing DMA Transfer, exit early // No ongoing DMA Transfer, We want to check if we should repeat an existing one @@ -243,11 +239,11 @@ fn DmaController(comptime id: u2) type { // Reload internal DAD latch if we are in IncrementRelaod if (self.in_progress) { self._word_count = if (self.word_count == 0) std.math.maxInt(@TypeOf(self._word_count)) else self.word_count; - if (Self.adjustment(self.cnt.dad_adj.read()) == .IncrementReload) self._dad = self.dad; + if (@intToEnum(Adjustment, self.cnt.dad_adj.read()) == .IncrementReload) self.dad_latch = self.dad; } } - pub fn requestSoundDma(self: *Self, _: u32) void { + pub fn requestAudio(self: *Self, _: u32) void { comptime std.debug.assert(id == 1 or id == 2); if (self.in_progress) return; // APU must wait their turn @@ -259,23 +255,19 @@ fn DmaController(comptime id: u2) type { // We Assume DMACNT_L is set to 4 // FIXME: Safe to just assume whatever DAD is set to is the FIFO Address? - // self._dad = fifo_addr; + // self.dad_latch = fifo_addr; self.cnt.repeat.set(); self._word_count = 4; self.in_progress = true; } - - fn adjustment(idx: u2) Adjustment { - return std.meta.intToEnum(Adjustment, idx) catch unreachable; - } }; } -pub fn pollBlankingDma(bus: *Bus, comptime kind: DmaKind) void { - bus.dma[0].pollBlankingDma(kind); - bus.dma[1].pollBlankingDma(kind); - bus.dma[2].pollBlankingDma(kind); - bus.dma[3].pollBlankingDma(kind); +pub fn pollDmaOnBlank(bus: *Bus, comptime kind: DmaKind) void { + bus.dma[0].poll(kind); + bus.dma[1].poll(kind); + bus.dma[2].poll(kind); + bus.dma[3].poll(kind); } const Adjustment = enum(u2) { diff --git a/src/core/bus/timer.zig b/src/core/bus/timer.zig index f93ee36..7273f1a 100644 --- a/src/core/bus/timer.zig +++ b/src/core/bus/timer.zig @@ -19,20 +19,20 @@ pub fn read(comptime T: type, tim: *const TimerTuple, addr: u32) ?T { return switch (T) { u32 => switch (nybble) { - 0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].getCntL(), - 0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].getCntL(), - 0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].getCntL(), - 0xC => @as(T, tim.*[3].cnt.raw) << 16 | tim.*[3].getCntL(), + 0x0 => @as(T, tim.*[0].cnt.raw) << 16 | tim.*[0].timcntL(), + 0x4 => @as(T, tim.*[1].cnt.raw) << 16 | tim.*[1].timcntL(), + 0x8 => @as(T, tim.*[2].cnt.raw) << 16 | tim.*[2].timcntL(), + 0xC => @as(T, tim.*[3].cnt.raw) << 16 | tim.*[3].timcntL(), else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, u16 => switch (nybble) { - 0x0 => tim.*[0].getCntL(), + 0x0 => tim.*[0].timcntL(), 0x2 => tim.*[0].cnt.raw, - 0x4 => tim.*[1].getCntL(), + 0x4 => tim.*[1].timcntL(), 0x6 => tim.*[1].cnt.raw, - 0x8 => tim.*[2].getCntL(), + 0x8 => tim.*[2].timcntL(), 0xA => tim.*[2].cnt.raw, - 0xC => tim.*[3].getCntL(), + 0xC => tim.*[3].timcntL(), 0xE => tim.*[3].cnt.raw, else => util.io.read.undef(T, log, "Tried to perform a {} read to 0x{X:0>8}", .{ T, addr }), }, @@ -46,21 +46,21 @@ pub fn write(comptime T: type, tim: *TimerTuple, addr: u32, value: T) void { return switch (T) { u32 => switch (nybble) { - 0x0 => tim.*[0].setCnt(value), - 0x4 => tim.*[1].setCnt(value), - 0x8 => tim.*[2].setCnt(value), - 0xC => tim.*[3].setCnt(value), + 0x0 => tim.*[0].setTimcnt(value), + 0x4 => tim.*[1].setTimcnt(value), + 0x8 => tim.*[2].setTimcnt(value), + 0xC => tim.*[3].setTimcnt(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>8}{} to 0x{X:0>8}", .{ value, T, addr }), }, u16 => switch (nybble) { - 0x0 => tim.*[0].setCntL(value), - 0x2 => tim.*[0].setCntH(value), - 0x4 => tim.*[1].setCntL(value), - 0x6 => tim.*[1].setCntH(value), - 0x8 => tim.*[2].setCntL(value), - 0xA => tim.*[2].setCntH(value), - 0xC => tim.*[3].setCntL(value), - 0xE => tim.*[3].setCntH(value), + 0x0 => tim.*[0].setTimcntL(value), + 0x2 => tim.*[0].setTimcntH(value), + 0x4 => tim.*[1].setTimcntL(value), + 0x6 => tim.*[1].setTimcntH(value), + 0x8 => tim.*[2].setTimcntL(value), + 0xA => tim.*[2].setTimcntH(value), + 0xC => tim.*[3].setTimcntL(value), + 0xE => tim.*[3].setTimcntH(value), else => util.io.write.undef(log, "Tried to write 0x{X:0>4}{} to 0x{X:0>8}", .{ value, T, addr }), }, u8 => util.io.write.undef(log, "Tried to write 0x{X:0>2}{} to 0x{X:0>8}", .{ value, T, addr }), @@ -72,13 +72,13 @@ fn Timer(comptime id: u2) type { return struct { const Self = @This(); - /// Read Only, Internal. Please use self.getCntL() + /// Read Only, Internal. Please use self.timcntL() _counter: u16, - /// Write Only, Internal. Please use self.setCntL() + /// Write Only, Internal. Please use self.setTimcntL() _reload: u16, - /// Write Only, Internal. Please use self.setCntH() + /// Write Only, Internal. Please use self.setTimcntH() cnt: TimerControl, /// Internal. @@ -97,26 +97,26 @@ fn Timer(comptime id: u2) type { }; } - /// TIMCNT_L - pub fn getCntL(self: *const Self) u16 { + /// TIMCNT_L Getter + pub fn timcntL(self: *const Self) u16 { if (self.cnt.cascade.read() or !self.cnt.enabled.read()) return self._counter; return self._counter +% @truncate(u16, (self.sched.now() - self._start_timestamp) / self.frequency()); } - /// TIMCNT_L - pub fn setCntL(self: *Self, halfword: u16) void { + /// TIMCNT_L Setter + pub fn setTimcntL(self: *Self, halfword: u16) void { self._reload = halfword; } /// TIMCNT_L & TIMCNT_H - pub fn setCnt(self: *Self, word: u32) void { - self.setCntL(@truncate(u16, word)); - self.setCntH(@truncate(u16, word >> 16)); + pub fn setTimcnt(self: *Self, word: u32) void { + self.setTimcntL(@truncate(u16, word)); + self.setTimcntH(@truncate(u16, word >> 16)); } /// TIMCNT_H - pub fn setCntH(self: *Self, halfword: u16) void { + pub fn setTimcntH(self: *Self, halfword: u16) void { const new = TimerControl{ .raw = halfword }; // If Timer happens to be enabled, It will either be resheduled or disabled @@ -132,12 +132,12 @@ fn Timer(comptime id: u2) type { if (!self.cnt.enabled.read() and new.enabled.read()) self._counter = self._reload; // If Timer is enabled and we're not cascading, we need to schedule an overflow event - if (new.enabled.read() and !new.cascade.read()) self.scheduleOverflow(0); + if (new.enabled.read() and !new.cascade.read()) self.rescheduleTimerExpire(0); self.cnt.raw = halfword; } - pub fn handleOverflow(self: *Self, cpu: *Arm7tdmi, late: u64) void { + pub fn onTimerExpire(self: *Self, cpu: *Arm7tdmi, late: u64) void { // Fire IRQ if enabled const io = &cpu.bus.io; @@ -161,15 +161,15 @@ fn Timer(comptime id: u2) type { switch (id) { 0 => if (cpu.bus.tim[1].cnt.cascade.read()) { cpu.bus.tim[1]._counter +%= 1; - if (cpu.bus.tim[1]._counter == 0) cpu.bus.tim[1].handleOverflow(cpu, late); + if (cpu.bus.tim[1]._counter == 0) cpu.bus.tim[1].onTimerExpire(cpu, late); }, 1 => if (cpu.bus.tim[2].cnt.cascade.read()) { cpu.bus.tim[2]._counter +%= 1; - if (cpu.bus.tim[2]._counter == 0) cpu.bus.tim[2].handleOverflow(cpu, late); + if (cpu.bus.tim[2]._counter == 0) cpu.bus.tim[2].onTimerExpire(cpu, late); }, 2 => if (cpu.bus.tim[3].cnt.cascade.read()) { cpu.bus.tim[3]._counter +%= 1; - if (cpu.bus.tim[3]._counter == 0) cpu.bus.tim[3].handleOverflow(cpu, late); + if (cpu.bus.tim[3]._counter == 0) cpu.bus.tim[3].onTimerExpire(cpu, late); }, 3 => {}, // There is no Timer for TIM3 to "cascade" to, } @@ -177,11 +177,11 @@ fn Timer(comptime id: u2) type { // Reschedule Timer if we're not cascading if (!self.cnt.cascade.read()) { self._counter = self._reload; - self.scheduleOverflow(late); + self.rescheduleTimerExpire(late); } } - fn scheduleOverflow(self: *Self, late: u64) void { + fn rescheduleTimerExpire(self: *Self, late: u64) void { const when = (@as(u64, 0x10000) - self._counter) * self.frequency(); self._start_timestamp = self.sched.now(); diff --git a/src/core/ppu.zig b/src/core/ppu.zig index c3aace5..8c10999 100644 --- a/src/core/ppu.zig +++ b/src/core/ppu.zig @@ -10,7 +10,7 @@ const Bitfield = @import("bitfield").Bitfield; const Allocator = std.mem.Allocator; const log = std.log.scoped(.PPU); -const pollBlankingDma = @import("bus/dma.zig").pollBlankingDma; +const pollDmaOnBlank = @import("bus/dma.zig").pollDmaOnBlank; /// This is used to generate byuu / Talurabi's Color Correction algorithm const COLOUR_LUT = genColourLut(); @@ -572,7 +572,7 @@ pub const Ppu = struct { // See if HBlank DMA is present and not enabled if (!self.dispstat.vblank.read()) - pollBlankingDma(cpu.bus, .HBlank); + pollDmaOnBlank(cpu.bus, .HBlank); self.dispstat.hblank.set(); self.sched.push(.HBlank, 68 * 4 -| late); @@ -614,7 +614,7 @@ pub const Ppu = struct { self.aff_bg[1].latchRefPoints(); // See if Vblank DMA is present and not enabled - pollBlankingDma(cpu.bus, .VBlank); + pollDmaOnBlank(cpu.bus, .VBlank); } if (scanline == 227) self.dispstat.vblank.unset(); diff --git a/src/core/scheduler.zig b/src/core/scheduler.zig index 65b815f..9369a2d 100644 --- a/src/core/scheduler.zig +++ b/src/core/scheduler.zig @@ -47,10 +47,10 @@ pub const Scheduler = struct { }, .TimerOverflow => |id| { switch (id) { - 0 => cpu.bus.tim[0].handleOverflow(cpu, late), - 1 => cpu.bus.tim[1].handleOverflow(cpu, late), - 2 => cpu.bus.tim[2].handleOverflow(cpu, late), - 3 => cpu.bus.tim[3].handleOverflow(cpu, late), + 0 => cpu.bus.tim[0].onTimerExpire(cpu, late), + 1 => cpu.bus.tim[1].onTimerExpire(cpu, late), + 2 => cpu.bus.tim[2].onTimerExpire(cpu, late), + 3 => cpu.bus.tim[3].onTimerExpire(cpu, late), } }, .ApuChannel => |id| {