const std = @import("std"); const testing = std.testing; pub const arm = @import("arm.zig"); const Arm32 = @import("arm.zig").Arm32; pub const Arm7tdmi = Arm32(.v4t); pub const Arm946es = Arm32(.v5te); pub const Interpreter = union(enum) { const Self = @This(); v4t: *Arm7tdmi, v5te: *Arm946es, pub fn reset(self: Self) void { switch (self) { inline else => |s| s.reset(), } } pub fn step(self: Self) void { switch (self) { inline else => |s| s.step(), } } pub fn panic(self: Self, comptime format: []const u8, comptime args: anytype) noreturn { switch (self) { inline else => |s| s.panic(format, args), } } }; pub const Bus = struct { ptr: *anyopaque, vtable: *const Vtable, const Vtable = struct { read8: *const fn (ptr: *anyopaque, address: u32) u8, read16: *const fn (ptr: *anyopaque, address: u32) u16, read32: *const fn (ptr: *anyopaque, address: u32) u32, write8: *const fn (ptr: *anyopaque, address: u32, value: u8) void, write16: *const fn (ptr: *anyopaque, address: u32, value: u16) void, write32: *const fn (ptr: *anyopaque, address: u32, value: u32) void, reset: *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 alignment = info.Pointer.alignment; const impl = struct { fn read8(ptr: *anyopaque, address: u32) u8 { const self = @ptrCast(P, @alignCast(alignment, ptr)); return self.read(u8, address); } fn read16(ptr: *anyopaque, address: u32) u16 { const self = @ptrCast(P, @alignCast(alignment, ptr)); return self.read(u16, address); } fn read32(ptr: *anyopaque, address: u32) u32 { const self = @ptrCast(P, @alignCast(alignment, ptr)); return self.read(u32, address); } fn write8(ptr: *anyopaque, address: u32, value: u8) void { const self = @ptrCast(P, @alignCast(alignment, ptr)); self.write(u8, address, value); } fn write16(ptr: *anyopaque, address: u32, value: u16) void { const self = @ptrCast(P, @alignCast(alignment, ptr)); self.write(u16, address, value); } fn write32(ptr: *anyopaque, address: u32, value: u32) void { const self = @ptrCast(P, @alignCast(alignment, ptr)); self.write(u32, address, value); } fn reset(ptr: *anyopaque) void { const self = @ptrCast(P, @alignCast(alignment, ptr)); self.reset(); } }; return .{ .ptr = obj, .vtable = &.{ .read8 = impl.read8, .read16 = impl.read16, .read32 = impl.read32, .write8 = impl.write8, .write16 = impl.write16, .write32 = impl.write32, .reset = impl.reset, }, }; } pub fn read(self: @This(), comptime T: type, address: u32) T { return switch (T) { u32 => self.vtable.read32(self.ptr, address), u16 => self.vtable.read16(self.ptr, address), u8 => self.vtable.read8(self.ptr, address), else => @compileError("TODO: Fill this in"), }; } pub fn write(self: @This(), comptime T: type, address: u32, value: T) void { switch (T) { u32 => self.vtable.write32(self.ptr, address, value), u16 => self.vtable.write16(self.ptr, address, value), u8 => self.vtable.write8(self.ptr, address, value), else => @compileError("TODO: Fill this in"), } } pub fn dbgRead(self: @This(), comptime T: type, address: u32) T { _ = address; _ = self; @panic("TODO: Implement Debug Reads via Bus Interface"); } pub fn dbgWrite(self: @This(), comptime T: type, address: u32, value: T) void { _ = self; _ = value; _ = address; @panic("TODO: Implement Debug Writes via Bus Interface"); } pub fn reset(self: @This()) void { self.vtable.reset(self.ptr); } }; pub const Scheduler = struct { ptr: *anyopaque, // VTable nowFn: *const fn (ptr: *anyopaque) u64, 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 alignment = info.Pointer.alignment; const impl = struct { fn now(ptr: *anyopaque) u64 { const self = @ptrCast(P, @alignCast(alignment, ptr)); return self.now(); } fn reset(ptr: *anyopaque) void { const self = @ptrCast(P, @alignCast(alignment, ptr)); self.reset(); } }; return .{ .ptr = obj, .nowFn = impl.now, .resetFn = impl.reset }; } pub fn now(self: @This()) u64 { return self.nowFn(self.ptr); } pub fn reset(self: @This()) void { self.resetFn(self.ptr); } }; // --- // TESTING // --- const ExampleBus = struct { _: u32 = 0, // Note: need this field so that the ptr align of *lib.ExampleBus != 0 pub fn read(self: *@This(), comptime T: type, address: u32) T { _ = self; _ = address; return 0; } pub fn write(self: *@This(), comptime T: type, address: u32, value: T) void { _ = self; _ = value; _ = address; } pub fn reset(self: *@This()) void { self._ = 0; } }; const ExampleScheduler = struct { tick: u64 = 0, pub fn now(self: *const @This()) u64 { return self.tick; } pub fn reset(self: *@This()) void { self.tick = 0; } }; 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{ .sched = scheduler_interface, .bus = bus_interface, .cpsr = .{ .raw = 0x0000_001F }, .spsr = .{ .raw = 0x0000_0000 }, }; var icpu = arm7tdmi.interface(); icpu.reset(); icpu.step(); } 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{ .sched = scheduler_interface, .bus = bus_interface, .cpsr = .{ .raw = 0x0000_001F }, .spsr = .{ .raw = 0x0000_0000 }, }; _ = arm946es.interface(); }