arm32/src/lib.zig

358 lines
11 KiB
Zig

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,
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,
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 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);
}
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,
.dbg_read8 = impl.dbgRead8,
.dbg_read16 = impl.dbgRead16,
.dbg_read32 = impl.dbgRead32,
.dbg_write8 = impl.dbgWrite8,
.dbg_write16 = impl.dbgWrite16,
.dbg_write32 = impl.dbgWrite32,
.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 {
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"),
};
}
pub fn dbgWrite(self: @This(), comptime T: type, address: u32, value: T) void {
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"),
}
}
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 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;
}
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 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);
}
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();
// 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{
.sched = scheduler_interface,
.bus = bus_interface,
.cpsr = .{ .raw = 0x0000_001F },
.spsr = .{ .raw = 0x0000_0000 },
};
_ = arm946es.interface();
}