feat(i/o): implement working sqrt unit, broken div unit
This commit is contained in:
		@@ -20,6 +20,15 @@ pub fn push(self: *@This(), kind: Event.Kind, offset: u64) void {
 | 
			
		||||
    self.queue.add(.{ .kind = kind, .tick = self.tick + offset }) catch unreachable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn remove(self: *@This(), needle: Event.Kind) void {
 | 
			
		||||
    for (self.queue.items, 0..) |event, i| {
 | 
			
		||||
        if (!std.meta.eql(event.kind, needle)) continue;
 | 
			
		||||
 | 
			
		||||
        _ = self.queue.removeIndex(i); // note: invalidates self.queue.items
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: @This()) void {
 | 
			
		||||
    self.queue.deinit();
 | 
			
		||||
}
 | 
			
		||||
@@ -58,6 +67,8 @@ pub fn handle(self: *@This(), bus_ptr: ?*anyopaque, event: Event, late: u64) voi
 | 
			
		||||
                },
 | 
			
		||||
                .hblank => bus.ppu.onHblankEnd(self, late),
 | 
			
		||||
                .vblank => bus.ppu.onHblankEnd(self, late),
 | 
			
		||||
                .sqrt => bus.io.sqrt.onSqrtCalc(),
 | 
			
		||||
                .div => bus.io.div.onDivCalc(),
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
@@ -68,7 +79,7 @@ pub const Event = struct {
 | 
			
		||||
    kind: Kind,
 | 
			
		||||
 | 
			
		||||
    const Kind7 = enum {};
 | 
			
		||||
    const Kind9 = enum { draw, hblank, vblank };
 | 
			
		||||
    const Kind9 = enum { draw, hblank, vblank, sqrt, div };
 | 
			
		||||
 | 
			
		||||
    pub const Kind = union(enum) {
 | 
			
		||||
        nds7: Kind7,
 | 
			
		||||
 
 | 
			
		||||
@@ -211,17 +211,22 @@ pub const masks = struct {
 | 
			
		||||
    pub inline fn ipcFifoSync(bus: anytype, value: anytype) @TypeOf(value) {
 | 
			
		||||
        comptime verifyBusType(@TypeOf(bus));
 | 
			
		||||
        const T = @TypeOf(value);
 | 
			
		||||
        const mask: T = 0xF;
 | 
			
		||||
        const _mask: T = 0xF;
 | 
			
		||||
 | 
			
		||||
        return value & ~mask | @as(T, @intCast(bus.io.shared.ipc_fifo.sync.raw & mask));
 | 
			
		||||
        return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.sync.raw & _mask));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub inline fn ipcFifoCnt(bus: anytype, value: anytype) @TypeOf(value) {
 | 
			
		||||
        comptime verifyBusType(@TypeOf(bus));
 | 
			
		||||
        const T = @TypeOf(value);
 | 
			
		||||
        const mask: T = 0x0303;
 | 
			
		||||
        const _mask: T = 0x0303;
 | 
			
		||||
 | 
			
		||||
        return value & ~mask | @as(T, @intCast(bus.io.shared.ipc_fifo.cnt.raw & mask));
 | 
			
		||||
        return value & ~_mask | @as(T, @intCast(bus.io.shared.ipc_fifo.cnt.raw & _mask));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// General Mask helper
 | 
			
		||||
    pub inline fn mask(original: anytype, value: @TypeOf(original), _mask: @TypeOf(original)) @TypeOf(original) {
 | 
			
		||||
        return (value & _mask) | (original & ~_mask);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn verifyBusType(comptime BusT: type) void {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ const Bus = @import("Bus.zig");
 | 
			
		||||
const SharedIo = @import("../io.zig").Io;
 | 
			
		||||
const masks = @import("../io.zig").masks;
 | 
			
		||||
 | 
			
		||||
const sext = @import("../../util.zig").sext;
 | 
			
		||||
 | 
			
		||||
const log = std.log.scoped(.nds9_io);
 | 
			
		||||
 | 
			
		||||
pub const Io = struct {
 | 
			
		||||
@@ -19,6 +21,10 @@ pub const Io = struct {
 | 
			
		||||
    // Read Only
 | 
			
		||||
    keyinput: AtomicKeyInput = .{},
 | 
			
		||||
 | 
			
		||||
    /// DS Maths
 | 
			
		||||
    div: Divisor = .{},
 | 
			
		||||
    sqrt: SquareRootUnit = .{},
 | 
			
		||||
 | 
			
		||||
    pub fn init(io: *SharedIo) @This() {
 | 
			
		||||
        return .{ .shared = io };
 | 
			
		||||
    }
 | 
			
		||||
@@ -31,6 +37,12 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
            0x0400_0210 => bus.io.shared.ie,
 | 
			
		||||
            0x0400_0214 => bus.io.shared.irq,
 | 
			
		||||
 | 
			
		||||
            0x0400_02A0 => @truncate(bus.io.div.result),
 | 
			
		||||
            0x0400_02A4 => @truncate(bus.io.div.result >> 32),
 | 
			
		||||
            0x0400_02A8 => @truncate(bus.io.div.remainder),
 | 
			
		||||
            0x0400_02AC => @truncate(bus.io.div.remainder >> 32),
 | 
			
		||||
            0x0400_02B4 => @truncate(bus.io.sqrt.result),
 | 
			
		||||
 | 
			
		||||
            0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm9),
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
@@ -40,6 +52,10 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
 | 
			
		||||
 | 
			
		||||
            0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw),
 | 
			
		||||
            0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw),
 | 
			
		||||
 | 
			
		||||
            0x0400_0280 => @truncate(bus.io.div.cnt.raw),
 | 
			
		||||
            0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw),
 | 
			
		||||
 | 
			
		||||
            else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
 | 
			
		||||
        },
 | 
			
		||||
        u8 => switch (address) {
 | 
			
		||||
@@ -68,6 +84,31 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
            0x0400_0210 => bus.io.shared.ie = value,
 | 
			
		||||
            0x0400_0214 => bus.io.shared.irq = value,
 | 
			
		||||
 | 
			
		||||
            0x0400_0290 => {
 | 
			
		||||
                bus.io.div.numerator = masks.mask(bus.io.div.numerator, value, 0xFFFF_FFFF);
 | 
			
		||||
                bus.io.div.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
            0x0400_0294 => {
 | 
			
		||||
                bus.io.div.numerator = masks.mask(bus.io.div.numerator, @as(u64, value) << 32, 0xFFFF_FFFF << 32);
 | 
			
		||||
                bus.io.div.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
            0x0400_0298 => {
 | 
			
		||||
                bus.io.div.denominator = masks.mask(bus.io.div.denominator, value, 0xFFFF_FFFF);
 | 
			
		||||
                bus.io.div.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
            0x0400_029C => {
 | 
			
		||||
                bus.io.div.denominator = masks.mask(bus.io.div.denominator, @as(u64, value) << 32, 0xFFFF_FFFF << 32);
 | 
			
		||||
                bus.io.div.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
            0x0400_02B8 => {
 | 
			
		||||
                bus.io.sqrt.param = masks.mask(bus.io.sqrt.param, value, 0xFFFF_FFFF);
 | 
			
		||||
                bus.io.sqrt.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
            0x0400_02BC => {
 | 
			
		||||
                bus.io.sqrt.param = masks.mask(bus.io.sqrt.param, @as(u64, value) << 32, 0xFFFF_FFFF << 32);
 | 
			
		||||
                bus.io.sqrt.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            0x0400_0304 => bus.io.powcnt.raw = value,
 | 
			
		||||
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
@@ -77,6 +118,16 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
 | 
			
		||||
            0x0400_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value),
 | 
			
		||||
            0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
 | 
			
		||||
 | 
			
		||||
            0x0400_0280 => {
 | 
			
		||||
                bus.io.div.cnt.raw = value;
 | 
			
		||||
                bus.io.div.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            0x0400_02B0 => {
 | 
			
		||||
                bus.io.sqrt.cnt.raw = value;
 | 
			
		||||
                bus.io.sqrt.schedule(bus.scheduler);
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
 | 
			
		||||
        },
 | 
			
		||||
        u8 => switch (address) {
 | 
			
		||||
@@ -107,6 +158,135 @@ const PowCnt = extern union {
 | 
			
		||||
    raw: u32,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Divisor
 | 
			
		||||
const Divisor = struct {
 | 
			
		||||
    const Scheduler = @import("../Scheduler.zig");
 | 
			
		||||
 | 
			
		||||
    /// DIVCNT - Division Control (R/W)
 | 
			
		||||
    cnt: Cnt = .{ .raw = 0x0000_0000 },
 | 
			
		||||
    /// DIV_NUMER - division numerator (R/W)
 | 
			
		||||
    numerator: u64 = 0x0000_0000_0000_0000,
 | 
			
		||||
    /// DIV_DENOM - division denominator (R/W)
 | 
			
		||||
    denominator: u64 = 0x0000_0000_0000_0000,
 | 
			
		||||
 | 
			
		||||
    /// DIV_RESULT - division quotient (R)
 | 
			
		||||
    result: u64 = 0x0000_0000_0000_0000,
 | 
			
		||||
    /// DIVREM_RESULT - remainder
 | 
			
		||||
    remainder: u64 = 0x0000_0000_0000_0000,
 | 
			
		||||
 | 
			
		||||
    const Cnt = extern union {
 | 
			
		||||
        mode: Bitfield(u32, 0, 2),
 | 
			
		||||
        div_by_zero: Bit(u32, 14),
 | 
			
		||||
        busy: Bit(u32, 15),
 | 
			
		||||
        raw: u32,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn schedule(self: *@This(), scheduler: *Scheduler) void {
 | 
			
		||||
        defer self.cnt.busy.set();
 | 
			
		||||
 | 
			
		||||
        const cycle_count: u64 = switch (self.cnt.mode.read()) {
 | 
			
		||||
            0b00 => 18,
 | 
			
		||||
            0b01, 0b10, 0b11 => 34,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        scheduler.remove(.{ .nds9 = .div });
 | 
			
		||||
        scheduler.push(.{ .nds9 = .div }, cycle_count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn onDivCalc(self: *@This()) void {
 | 
			
		||||
        defer self.cnt.busy.unset();
 | 
			
		||||
        self.cnt.div_by_zero.write(self.denominator == 0);
 | 
			
		||||
 | 
			
		||||
        switch (self.cnt.mode.read()) {
 | 
			
		||||
            0b00 => {
 | 
			
		||||
                // 32bit / 32bit = 32bit , 32bit
 | 
			
		||||
                const left = sext(i64, i32, self.numerator);
 | 
			
		||||
                const right = sext(i64, i32, self.denominator);
 | 
			
		||||
 | 
			
		||||
                if (right == 0) {
 | 
			
		||||
                    self.remainder = @bitCast(left);
 | 
			
		||||
                    self.result = if (left >> 63 & 1 == 1) @as(u64, 1) else @bitCast(@as(i64, -1));
 | 
			
		||||
 | 
			
		||||
                    // FIXME(chore): replace `>> 32 ) << 32` with mask
 | 
			
		||||
                    self.result = masks.mask(self.result, (~self.result >> 32) << 32, 0xFFFF_FFFF << 32);
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                self.result = @bitCast(@divTrunc(left, right));
 | 
			
		||||
                self.remainder = @bitCast(@rem(left, right));
 | 
			
		||||
            },
 | 
			
		||||
            0b01, 0b11 => {
 | 
			
		||||
                // 64bit / 32bit = 64bit , 32bit
 | 
			
		||||
                const left = sext(i128, i64, self.numerator);
 | 
			
		||||
                const right = sext(i128, i32, self.denominator);
 | 
			
		||||
 | 
			
		||||
                if (right == 0) {
 | 
			
		||||
                    self.remainder = @bitCast(@as(i64, @truncate(left)));
 | 
			
		||||
                    self.result = if (left >> 63 & 1 == 1) @as(u64, 1) else @bitCast(@as(i64, -1));
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                self.result = @bitCast(@as(i64, @truncate(@divTrunc(left, right))));
 | 
			
		||||
                self.remainder = @bitCast(@as(i64, @truncate(@rem(left, right))));
 | 
			
		||||
            },
 | 
			
		||||
            0b10 => {
 | 
			
		||||
                // 64bit / 64bit = 64bit , 64bit
 | 
			
		||||
                const left = sext(i128, i64, self.numerator);
 | 
			
		||||
                const right = sext(i128, i64, self.denominator);
 | 
			
		||||
 | 
			
		||||
                if (right == 0) {
 | 
			
		||||
                    self.remainder = @bitCast(@as(i64, @truncate(left)));
 | 
			
		||||
                    self.result = if (left >> 63 & 1 == 1) @as(u64, 1) else @bitCast(@as(i64, -1));
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                self.result = @bitCast(@as(i64, @truncate(@divTrunc(left, right))));
 | 
			
		||||
                self.remainder = @bitCast(@as(i64, @truncate(@rem(left, right))));
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Square Root Unit
 | 
			
		||||
const SquareRootUnit = struct {
 | 
			
		||||
    const Scheduler = @import("../Scheduler.zig");
 | 
			
		||||
 | 
			
		||||
    /// SQRTCNT - Division Control (R/W)
 | 
			
		||||
    cnt: Cnt = .{ .raw = 0x0000_0000 },
 | 
			
		||||
 | 
			
		||||
    /// SQRT_RESULT - square root result (R)
 | 
			
		||||
    result: u32 = 0x0000_0000,
 | 
			
		||||
    /// SQRT_PARAM - square root paramater input (R/W)
 | 
			
		||||
    param: u64 = 0x0000_0000_0000_0000,
 | 
			
		||||
 | 
			
		||||
    const Cnt = extern union {
 | 
			
		||||
        mode: Bit(u32, 0),
 | 
			
		||||
        busy: Bit(u32, 15),
 | 
			
		||||
        raw: u32,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn schedule(self: *@This(), scheduler: *Scheduler) void {
 | 
			
		||||
        defer self.cnt.busy.set();
 | 
			
		||||
 | 
			
		||||
        scheduler.remove(.{ .nds9 = .sqrt });
 | 
			
		||||
        scheduler.push(.{ .nds9 = .sqrt }, 13); // always takes 13 cycles
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn onSqrtCalc(self: *@This()) void {
 | 
			
		||||
        defer self.cnt.busy.unset();
 | 
			
		||||
 | 
			
		||||
        const mask: u64 = blk: {
 | 
			
		||||
            const value: u64 = @intFromBool(!self.cnt.mode.read());
 | 
			
		||||
            break :blk (value << 32) -% 1;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.result = @truncate(std.math.sqrt(self.param & mask));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub const DispcntA = extern union {
 | 
			
		||||
    bg_mode: Bitfield(u32, 0, 2),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user