style(bus): refactor several hardware abstractions
This commit is contained in:
		@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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 });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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" {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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| {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user