From 514e4d6014944398f488d6a5464add43221a8f3a Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 15 Sep 2023 11:02:40 -0500 Subject: [PATCH] feat: implement Coprocessor Interface --- src/arm.zig | 90 ++++++++++++++------ src/lib.zig | 241 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 249 insertions(+), 82 deletions(-) diff --git a/src/arm.zig b/src/arm.zig index 256351f..51686c2 100644 --- a/src/arm.zig +++ b/src/arm.zig @@ -4,6 +4,7 @@ const Architecture = enum { v4t, v5te }; const Interpreter = @import("lib.zig").Interpreter; const Bus = @import("lib.zig").Bus; const Scheduler = @import("lib.zig").Scheduler; +const Coprocessor = @import("lib.zig").Coprocessor; const Bitfield = @import("bitfield").Bitfield; const Bit = @import("bitfield").Bit; @@ -46,6 +47,7 @@ pub fn Arm32(comptime isa: Architecture) type { // The following will be `void` on.v4t but exist on v5te itcm: if (is_v5te) Itcm else void, dtcm: if (is_v5te) Dtcm else void, + cp15: if (is_v5te) Coprocessor else void, const arm = switch (isa) { .v4t => @import("arm/v4t.zig").arm, @@ -154,17 +156,64 @@ pub fn Arm32(comptime isa: Architecture) type { } }; - pub fn init(scheduler: Scheduler, bus: Bus) Self { - return .{ - .sched = scheduler, - .bus = bus, - .cpsr = .{ .raw = 0x0000_001F }, - .spsr = .{ .raw = 0x0000_0000 }, + // FIXME: Is this a hack or idiomatic? + // See https://github.com/ziglang/zig/blob/1a0e6bcdb140c844384d62b78a7f4247753f9ffd/lib/std/atomic/Atomic.zig#L156-L176 + pub usingnamespace if (is_v5te) struct { + // FIXME: this is pretty NDS9 specific lol + pub fn init(scheduler: Scheduler, bus: Bus, cp15: Coprocessor) Self { + return .{ + .sched = scheduler, + .bus = bus, + .cpsr = .{ .raw = 0x0000_001F }, + .spsr = .{ .raw = 0x0000_0000 }, - .dtcm = if (is_v5te) .{} else {}, - .itcm = if (is_v5te) .{} else {}, - }; - } + .cp15 = cp15, + .dtcm = .{}, + .itcm = .{}, + }; + } + + // FIXME: Resetting disables logging (if enabled) + pub fn reset(self: *Self) void { + self.* = .{ + .sched = self.sched, + .bus = self.bus, + .cpsr = .{ .raw = 0x0000_001F }, + .spsr = .{ .raw = 0x0000_0000 }, + + .dtcm = .{}, + .itcm = .{}, + .cp15 = self.cp15, + }; + } + } else struct { + pub fn init(scheduler: Scheduler, bus: Bus) Self { + return .{ + .sched = scheduler, + .bus = bus, + .cpsr = .{ .raw = 0x0000_001F }, + .spsr = .{ .raw = 0x0000_0000 }, + + .cp15 = {}, + .dtcm = {}, + .itcm = {}, + }; + } + + // FIXME: Resetting disables logging (if enabled) + pub fn reset(self: *Self) void { + self.* = .{ + .sched = self.sched, + .bus = self.bus, + .cpsr = .{ .raw = 0x0000_001F }, + .spsr = .{ .raw = 0x0000_0000 }, + + .dtcm = {}, + .itcm = {}, + .cp15 = {}, + }; + } + }; // CPU needs it's own read/write fns due to ICTM and DCTM present in v5te // I considered implementing Bus.cpu_read and Bus.cpu_write but ended up considering that a bit too leaky @@ -179,10 +228,10 @@ pub fn Arm32(comptime isa: Architecture) type { // FIXME: verify correctness + can this be faster? if (itcm_base < address and address < itcm_base + itcm_size) - return readInt(T, self.itcm.buf[address & 0x0000_7FFF ..][0..@sizeOf(T)]); + return readInt(T, self.itcm.buf[address..][0..@sizeOf(T)]); if (dtcm_base < address and address < dtcm_base + dtcm_size) - return readInt(T, self.itcm.buf[address & 0x0000_3FFF ..][0..@sizeOf(T)]); + return readInt(T, self.dtcm.buf[address..][0..@sizeOf(T)]); } return self.bus.read(T, address); @@ -199,28 +248,15 @@ pub fn Arm32(comptime isa: Architecture) type { // FIXME: verify correctness + can this be faster? if (itcm_base < address and address < itcm_base + itcm_size) - return writeInt(T, self.itcm.buf[address & 0x0000_7FFF ..][0..@sizeOf(T)], value); + return writeInt(T, self.itcm.buf[address..][0..@sizeOf(T)], value); if (dtcm_base < address and address < dtcm_base + dtcm_size) - return writeInt(T, self.itcm.buf[address & 0x0000_3FFF ..][0..@sizeOf(T)], value); + return writeInt(T, self.dtcm.buf[address..][0..@sizeOf(T)], value); } return self.bus.write(T, address, value); } - // FIXME: Resetting disables logging (if enabled) - pub fn reset(self: *Self) void { - self.* = .{ - .sched = self.sched, - .bus = self.bus, - .cpsr = .{ .raw = 0x0000_001F }, - .spsr = .{ .raw = 0x0000_0000 }, - - .dtcm = if (is_v5te) .{} else {}, - .itcm = if (is_v5te) .{} else {}, - }; - } - pub inline fn hasSPSR(self: *const Self) bool { const mode = Mode.getChecked(self, self.cpsr.mode.read()); return switch (mode) { diff --git a/src/lib.zig b/src/lib.zig index b5febbc..95d0599 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -32,6 +32,38 @@ pub const Interpreter = union(enum) { } }; +test "create ARMv4T interface" { + var bus_impl = ExampleBus{}; + var scheduler_impl = ExampleScheduler{}; + + const bus_interface = Bus.init(&bus_impl); + const scheduler_interface = Scheduler.init(&scheduler_impl); + + var arm7tdmi = Arm7tdmi.init(scheduler_interface, bus_interface); + var icpu = arm7tdmi.interface(); + + icpu.reset(); + icpu.step(); + // TODO: call icpu.panic() +} + +test "create ARMv5TE interface" { + var bus_impl = ExampleBus{}; + var scheduler_impl = ExampleScheduler{}; + var cop_impl = ExampleCoprocessor{}; + + const bus_interface = Bus.init(&bus_impl); + const scheduler_interface = Scheduler.init(&scheduler_impl); + const coprocessor_interface = Coprocessor.init(&cop_impl); + + var arm946es = Arm946es.init(scheduler_interface, bus_interface, coprocessor_interface); + var icpu = arm946es.interface(); + + icpu.reset(); + icpu.step(); + // TODO: call icpu.panic(); +} + pub const Bus = struct { ptr: *anyopaque, vtable: *const Vtable, @@ -196,6 +228,118 @@ pub const Bus = struct { } }; +test "create Bus" { + var bus_impl = ExampleBus{}; + const iface = Bus.init(&bus_impl); + _ = iface; +} + +test "call Bus reads" { + var bus_impl = ExampleBus{}; + const iface = Bus.init(&bus_impl); + + _ = iface.read(u32, 0x0000_0000); + _ = iface.read(u16, 0x0000_0000); + _ = iface.read(u8, 0x0000_0000); + + _ = iface.dbgRead(u32, 0x0000_0000); + _ = iface.dbgRead(u16, 0x0000_0000); + _ = iface.dbgRead(u8, 0x0000_0000); +} + +test "call Bus writes" { + var bus_impl = ExampleBus{}; + const iface = Bus.init(&bus_impl); + + _ = iface.write(u32, 0x0000_0000, 0x0000_0000); + _ = iface.write(u16, 0x0000_0000, 0x0000); + _ = iface.write(u8, 0x0000_0000, 0x00); + + _ = iface.dbgWrite(u32, 0x0000_0000, 0x0000_0000); + _ = iface.dbgWrite(u16, 0x0000_0000, 0x0000); + _ = iface.dbgWrite(u8, 0x0000_0000, 0x00); +} + +pub const Coprocessor = struct { + ptr: *anyopaque, + + // VTable + readFn: *const fn (ptr: *anyopaque, op1: u3, cn: u4, cm: u4, op2: u3) u32, + writeFn: *const fn (ptr: *anyopaque, op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void, + resetFn: *const fn (ptr: *anyopaque) void, + + pub fn init(obj: anytype) @This() { + const P = @TypeOf(obj); + const info = @typeInfo(P); + + std.debug.assert(info == .Pointer); // `anytype` is a Pointer + std.debug.assert(info.Pointer.size == .One); // Single-Item Pointer + std.debug.assert(@typeInfo(info.Pointer.child) == .Struct); // Pointer Child is a `struct` + + const impl = struct { + fn read(ptr: *anyopaque, op1: u3, cn: u4, cm: u4, op2: u3) u32 { + const self: P = @ptrCast(@alignCast(ptr)); + return self.read(op1, cn, cm, op2); + } + + fn write(ptr: *anyopaque, op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void { + const self: P = @ptrCast(@alignCast(ptr)); + return self.write(op1, cn, cm, op2, value); + } + + fn reset(ptr: *anyopaque) void { + const self: P = @ptrCast(@alignCast(ptr)); + return self.reset(); + } + }; + + return .{ + .ptr = obj, + .readFn = impl.read, + .writeFn = impl.write, + .resetFn = impl.reset, + }; + } + + pub fn read(self: @This(), op1: u3, cn: u4, cm: u4, op2: u3) u32 { + return self.readFn(self.ptr, op1, cn, cm, op2); + } + + pub fn write(self: @This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void { + return self.writeFn(self.ptr, op1, cn, cm, op2, value); + } + + pub fn reset(self: @This()) void { + return self.resetFn(self.ptr); + } +}; + +test "create Coprocessor" { + var cop_impl = ExampleCoprocessor{}; + _ = Coprocessor.init(&cop_impl); +} + +test "Coprocessor.read" { + var cop_impl = ExampleCoprocessor{}; + const iface = Coprocessor.init(&cop_impl); + + try testing.expectEqual(@as(u32, 0xDEADBEEF), iface.read(0, 0, 0, 0)); +} + +test "Coprocessor.write" { + var cop_impl = ExampleCoprocessor{}; + const iface = Coprocessor.init(&cop_impl); + + iface.write(0, 0, 0, 0, 0xDEADBEEF); +} + +test "Coprocessor.reset" { + var cop_impl = ExampleCoprocessor{}; + const iface = Coprocessor.init(&cop_impl); + + iface.reset(); +} + pub const Scheduler = struct { ptr: *anyopaque, @@ -235,6 +379,27 @@ pub const Scheduler = struct { } }; +test "create Scheduler" { + var scheduler_impl = ExampleScheduler{}; + const iface = Scheduler.init(&scheduler_impl); + _ = iface; +} + +test "Scheduler.now()" { + var scheduler_impl = ExampleScheduler{}; + const iface = Scheduler.init(&scheduler_impl); + + try testing.expectEqual(@as(u64, 0), iface.now()); +} + +test "Scheduler.reset()" { + var scheduler_impl = ExampleScheduler{ .tick = std.math.maxInt(u64) }; + const iface = Scheduler.init(&scheduler_impl); + iface.reset(); + + try testing.expectEqual(@as(u64, 0), scheduler_impl.tick); +} + // --- // TESTING // --- @@ -282,61 +447,27 @@ const ExampleScheduler = struct { } }; -test "create IBus" { - var bus_impl = ExampleBus{}; - const iface = Bus.init(&bus_impl); - _ = iface; -} +const ExampleCoprocessor = struct { + pub fn read(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3) u32 { + _ = op2; + _ = cm; + _ = cn; + _ = op1; + _ = self; -test "call IBus reads" { - var bus_impl = ExampleBus{}; - const iface = Bus.init(&bus_impl); + return 0xDEADBEEF; + } - _ = iface.read(u32, 0x0000_0000); - _ = iface.read(u16, 0x0000_0000); - _ = iface.read(u8, 0x0000_0000); + pub fn write(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void { + _ = value; + _ = op2; + _ = cm; + _ = cn; + _ = op1; + _ = self; + } - _ = iface.dbgRead(u32, 0x0000_0000); - _ = iface.dbgRead(u16, 0x0000_0000); - _ = iface.dbgRead(u8, 0x0000_0000); -} - -test "call IBus writes" { - var bus_impl = ExampleBus{}; - const iface = Bus.init(&bus_impl); - - _ = iface.write(u32, 0x0000_0000, 0x0000_0000); - _ = iface.write(u16, 0x0000_0000, 0x0000); - _ = iface.write(u8, 0x0000_0000, 0x00); - - _ = iface.dbgWrite(u32, 0x0000_0000, 0x0000_0000); - _ = iface.dbgWrite(u16, 0x0000_0000, 0x0000); - _ = iface.dbgWrite(u8, 0x0000_0000, 0x00); -} - -test "create ARMv4T interface" { - var bus_impl = ExampleBus{}; - var scheduler_impl = ExampleScheduler{}; - - const bus_interface = Bus.init(&bus_impl); - const scheduler_interface = Scheduler.init(&scheduler_impl); - - var arm7tdmi = Arm7tdmi.init(scheduler_interface, bus_interface); - var icpu = arm7tdmi.interface(); - - icpu.reset(); - icpu.step(); - // TODO: call icpu.panic() -} - -test "create ARMv5TE interface" { - var bus_impl = ExampleBus{}; - var scheduler_impl = ExampleScheduler{}; - - const bus_interface = Bus.init(&bus_impl); - const scheduler_interface = Scheduler.init(&scheduler_impl); - - var arm946es = Arm946es.init(scheduler_interface, bus_interface); - - _ = arm946es.interface(); -} + pub fn reset(self: *@This()) void { + self.* = .{}; + } +};