feat(i/o): implement working sqrt unit, broken div unit
This commit is contained in:
parent
d90cd699b6
commit
7047743004
|
@ -29,6 +29,7 @@ pub fn build(b: *std.Build) void {
|
||||||
});
|
});
|
||||||
|
|
||||||
exe.addModule("arm32", arm32.module(b));
|
exe.addModule("arm32", arm32.module(b));
|
||||||
|
exe.addModule("zba-util", b.dependency("zba-util", .{}).module("zba-util"));
|
||||||
exe.addModule("zig-clap", b.dependency("zig-clap", .{}).module("clap"));
|
exe.addModule("zig-clap", b.dependency("zig-clap", .{}).module("clap"));
|
||||||
|
|
||||||
exe.addAnonymousModule("bitfield", .{ .source_file = .{ .path = "lib/bitfield.zig" } }); // https://github.com/FlorenceOS/
|
exe.addAnonymousModule("bitfield", .{ .source_file = .{ .path = "lib/bitfield.zig" } }); // https://github.com/FlorenceOS/
|
||||||
|
|
|
@ -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;
|
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 {
|
pub fn deinit(self: @This()) void {
|
||||||
self.queue.deinit();
|
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),
|
.hblank => bus.ppu.onHblankEnd(self, late),
|
||||||
.vblank => 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,
|
kind: Kind,
|
||||||
|
|
||||||
const Kind7 = enum {};
|
const Kind7 = enum {};
|
||||||
const Kind9 = enum { draw, hblank, vblank };
|
const Kind9 = enum { draw, hblank, vblank, sqrt, div };
|
||||||
|
|
||||||
pub const Kind = union(enum) {
|
pub const Kind = union(enum) {
|
||||||
nds7: Kind7,
|
nds7: Kind7,
|
||||||
|
|
|
@ -7,6 +7,8 @@ const Bus = @import("Bus.zig");
|
||||||
const SharedIo = @import("../io.zig").Io;
|
const SharedIo = @import("../io.zig").Io;
|
||||||
const masks = @import("../io.zig").masks;
|
const masks = @import("../io.zig").masks;
|
||||||
|
|
||||||
|
const sext = @import("zba-util").sext;
|
||||||
|
|
||||||
const log = std.log.scoped(.nds9_io);
|
const log = std.log.scoped(.nds9_io);
|
||||||
|
|
||||||
pub const Io = struct {
|
pub const Io = struct {
|
||||||
|
@ -19,6 +21,10 @@ pub const Io = struct {
|
||||||
// Read Only
|
// Read Only
|
||||||
keyinput: AtomicKeyInput = .{},
|
keyinput: AtomicKeyInput = .{},
|
||||||
|
|
||||||
|
/// DS Maths
|
||||||
|
div: Divisor = .{},
|
||||||
|
sqrt: SquareRootUnit = .{},
|
||||||
|
|
||||||
pub fn init(io: *SharedIo) @This() {
|
pub fn init(io: *SharedIo) @This() {
|
||||||
return .{ .shared = io };
|
return .{ .shared = io };
|
||||||
}
|
}
|
||||||
|
@ -31,6 +37,8 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
0x0400_0210 => bus.io.shared.ie,
|
0x0400_0210 => bus.io.shared.ie,
|
||||||
0x0400_0214 => bus.io.shared.irq,
|
0x0400_0214 => bus.io.shared.irq,
|
||||||
|
|
||||||
|
0x0400_02B4 => @truncate(bus.io.sqrt.result),
|
||||||
|
|
||||||
0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm9),
|
0x0410_0000 => bus.io.shared.ipc_fifo.recv(.arm9),
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
|
@ -40,6 +48,9 @@ pub fn read(bus: *const Bus, comptime T: type, address: u32) T {
|
||||||
|
|
||||||
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw),
|
0x0400_0180 => @truncate(bus.io.shared.ipc_fifo.sync.raw),
|
||||||
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw),
|
0x0400_0184 => @truncate(bus.io.shared.ipc_fifo.cnt.raw),
|
||||||
|
|
||||||
|
0x0400_02B0 => @truncate(bus.io.sqrt.cnt.raw),
|
||||||
|
|
||||||
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
else => warn("unexpected: read(T: {}, addr: 0x{X:0>8}) {} ", .{ T, address, T }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
|
@ -68,6 +79,15 @@ pub fn write(bus: *Bus, comptime T: type, address: u32, value: T) void {
|
||||||
0x0400_0210 => bus.io.shared.ie = value,
|
0x0400_0210 => bus.io.shared.ie = value,
|
||||||
0x0400_0214 => bus.io.shared.irq = value,
|
0x0400_0214 => bus.io.shared.irq = value,
|
||||||
|
|
||||||
|
0x0400_02B8 => {
|
||||||
|
bus.io.sqrt.param = (bus.io.sqrt.param & ~@as(u64, 0xFFFF_FFFF)) | value;
|
||||||
|
bus.io.sqrt.schedule(bus.scheduler);
|
||||||
|
},
|
||||||
|
0x0400_02BC => {
|
||||||
|
bus.io.sqrt.param = (@as(u64, value) << 32) | (bus.io.sqrt.param & @as(u64, 0xFFFF_FFFF));
|
||||||
|
bus.io.sqrt.schedule(bus.scheduler);
|
||||||
|
},
|
||||||
|
|
||||||
0x0400_0304 => bus.io.powcnt.raw = value,
|
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 }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
|
@ -77,6 +97,11 @@ 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_0184 => bus.io.shared.ipc_fifo.cnt.raw = masks.ipcFifoCnt(bus, value),
|
||||||
0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
|
0x0400_0208 => bus.io.shared.ime = value & 1 == 1,
|
||||||
|
|
||||||
|
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 }),
|
else => log.warn("unexpected: write(T: {}, addr: 0x{X:0>8}, value: 0x{X:0>8})", .{ T, address, value }),
|
||||||
},
|
},
|
||||||
u8 => switch (address) {
|
u8 => switch (address) {
|
||||||
|
@ -107,6 +132,121 @@ const PowCnt = extern union {
|
||||||
raw: u32,
|
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 {
|
||||||
|
self.cnt.busy.set();
|
||||||
|
|
||||||
|
const cycle_count = 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 {
|
||||||
|
self.cnt.busy.unset();
|
||||||
|
|
||||||
|
// const mask: u64 = blk: {
|
||||||
|
// const value: u64 = @intFromBool(self.cnt.mode.read());
|
||||||
|
// break :blk value -| 1;
|
||||||
|
// };
|
||||||
|
|
||||||
|
if (self.denominator == 0) {
|
||||||
|
log.err("TODO: deal with division error ({} / {})", .{ self.numerator, self.denominator });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (self.cnt.mode.read()) {
|
||||||
|
0b00 => {
|
||||||
|
// 32bit / 32bit = 32bit , 32bit
|
||||||
|
const left = self.numerator & 0xFFFF_FFFFF;
|
||||||
|
const right = self.denominator & 0xFFFF_FFFF;
|
||||||
|
|
||||||
|
self.result = sext(u64, u32, left / right);
|
||||||
|
self.remainder = sext(u64, u32, left % right);
|
||||||
|
},
|
||||||
|
0b01, 0b11 => {
|
||||||
|
// 64bit / 32bit = 64bit , 32bit
|
||||||
|
const left = self.numerator;
|
||||||
|
const right = self.denominator & 0xFFFF_FFFF;
|
||||||
|
|
||||||
|
self.result = left / right;
|
||||||
|
self.remainder = sext(u64, u32, left % right);
|
||||||
|
},
|
||||||
|
0b10 => {
|
||||||
|
// 64bit / 64bit = 64bit , 64bit
|
||||||
|
const left = self.numerator;
|
||||||
|
const right = self.denominator;
|
||||||
|
|
||||||
|
self.result = left / right;
|
||||||
|
self.remainder = 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));
|
||||||
|
std.log.debug("sqrt({}) == {}", .{ self.param & mask, self.result });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const DispcntA = extern union {
|
pub const DispcntA = extern union {
|
||||||
bg_mode: Bitfield(u32, 0, 2),
|
bg_mode: Bitfield(u32, 0, 2),
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue