2023-06-25 06:03:14 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const testing = std.testing;
|
|
|
|
|
2023-06-25 23:27:10 +00:00
|
|
|
pub const arm = @import("arm.zig");
|
2023-06-25 21:37:43 +00:00
|
|
|
|
2023-06-25 23:27:10 +00:00
|
|
|
const Arm32 = @import("arm.zig").Arm32;
|
2023-06-25 21:37:43 +00:00
|
|
|
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,
|
|
|
|
|
2023-06-30 04:12:08 +00:00
|
|
|
dbg_read8: *const fn (ptr: *anyopaque, address: u32) u8,
|
|
|
|
dbg_read16: *const fn (ptr: *anyopaque, address: u32) u16,
|
|
|
|
dbg_read32: *const fn (ptr: *anyopaque, address: u32) u32,
|
|
|
|
|
|
|
|
dbg_write8: *const fn (ptr: *anyopaque, address: u32, value: u8) void,
|
|
|
|
dbg_write16: *const fn (ptr: *anyopaque, address: u32, value: u16) void,
|
|
|
|
dbg_write32: *const fn (ptr: *anyopaque, address: u32, value: u32) void,
|
|
|
|
|
2023-06-25 21:37:43 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-06-30 04:12:08 +00:00
|
|
|
fn dbgRead8(ptr: *anyopaque, address: u32) u8 {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
return self.dbgRead(u8, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dbgRead16(ptr: *anyopaque, address: u32) u16 {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
return self.dbgRead(u16, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dbgRead32(ptr: *anyopaque, address: u32) u32 {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
return self.dbgRead(u32, address);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dbgWrite8(ptr: *anyopaque, address: u32, value: u8) void {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
self.dbgWrite(u8, address, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dbgWrite16(ptr: *anyopaque, address: u32, value: u16) void {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
self.dbgWrite(u16, address, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dbgWrite32(ptr: *anyopaque, address: u32, value: u32) void {
|
|
|
|
const self = @ptrCast(P, @alignCast(alignment, ptr));
|
|
|
|
self.dbgWrite(u32, address, value);
|
|
|
|
}
|
|
|
|
|
2023-06-25 21:37:43 +00:00
|
|
|
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,
|
|
|
|
|
2023-06-30 04:12:08 +00:00
|
|
|
.dbg_read8 = impl.dbgRead8,
|
|
|
|
.dbg_read16 = impl.dbgRead16,
|
|
|
|
.dbg_read32 = impl.dbgRead32,
|
|
|
|
|
|
|
|
.dbg_write8 = impl.dbgWrite8,
|
|
|
|
.dbg_write16 = impl.dbgWrite16,
|
|
|
|
.dbg_write32 = impl.dbgWrite32,
|
|
|
|
|
2023-06-25 21:37:43 +00:00
|
|
|
.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 {
|
2023-06-30 04:12:08 +00:00
|
|
|
return switch (T) {
|
|
|
|
u32 => self.vtable.dbg_read32(self.ptr, address),
|
|
|
|
u16 => self.vtable.dbg_read16(self.ptr, address),
|
|
|
|
u8 => self.vtable.dbg_read8(self.ptr, address),
|
|
|
|
else => @compileError("TODO: Fill this in"),
|
|
|
|
};
|
2023-06-25 21:37:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dbgWrite(self: @This(), comptime T: type, address: u32, value: T) void {
|
2023-06-30 04:12:08 +00:00
|
|
|
switch (T) {
|
|
|
|
u32 => self.vtable.dbg_write32(self.ptr, address, value),
|
|
|
|
u16 => self.vtable.dbg_write16(self.ptr, address, value),
|
|
|
|
u8 => self.vtable.dbg_write8(self.ptr, address, value),
|
|
|
|
else => @compileError("TODO: Fill this in"),
|
|
|
|
}
|
2023-06-25 21:37:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-06-30 04:12:08 +00:00
|
|
|
pub fn dbgRead(self: *@This(), comptime T: type, address: u32) T {
|
|
|
|
_ = self;
|
|
|
|
_ = address;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dbgWrite(self: *@This(), comptime T: type, address: u32, value: T) void {
|
|
|
|
_ = self;
|
|
|
|
_ = value;
|
|
|
|
_ = address;
|
|
|
|
}
|
2023-06-25 21:37:43 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-06-30 04:12:08 +00:00
|
|
|
test "create IBus" {
|
|
|
|
var bus_impl = ExampleBus{};
|
|
|
|
const iface = Bus.init(&bus_impl);
|
|
|
|
_ = iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
test "call IBus 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 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);
|
|
|
|
}
|
|
|
|
|
2023-06-25 21:37:43 +00:00
|
|
|
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();
|
2023-06-30 04:12:08 +00:00
|
|
|
// TODO: call icpu.panic()
|
2023-06-25 06:03:14 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 21:37:43 +00:00
|
|
|
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();
|
2023-06-25 06:03:14 +00:00
|
|
|
}
|