arm32/src/lib.zig

474 lines
14 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),
}
}
};
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,
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 impl = struct {
fn read8(ptr: *anyopaque, address: u32) u8 {
const self: P = @ptrCast(@alignCast(ptr));
return self.read(u8, address);
}
fn read16(ptr: *anyopaque, address: u32) u16 {
const self: P = @ptrCast(@alignCast(ptr));
return self.read(u16, address);
}
fn read32(ptr: *anyopaque, address: u32) u32 {
const self: P = @ptrCast(@alignCast(ptr));
return self.read(u32, address);
}
fn write8(ptr: *anyopaque, address: u32, value: u8) void {
const self: P = @ptrCast(@alignCast(ptr));
self.write(u8, address, value);
}
fn write16(ptr: *anyopaque, address: u32, value: u16) void {
const self: P = @ptrCast(@alignCast(ptr));
self.write(u16, address, value);
}
fn write32(ptr: *anyopaque, address: u32, value: u32) void {
const self: P = @ptrCast(@alignCast(ptr));
self.write(u32, address, value);
}
fn dbgRead8(ptr: *anyopaque, address: u32) u8 {
const self: P = @ptrCast(@alignCast(ptr));
return self.dbgRead(u8, address);
}
fn dbgRead16(ptr: *anyopaque, address: u32) u16 {
const self: P = @ptrCast(@alignCast(ptr));
return self.dbgRead(u16, address);
}
fn dbgRead32(ptr: *anyopaque, address: u32) u32 {
const self: P = @ptrCast(@alignCast(ptr));
return self.dbgRead(u32, address);
}
fn dbgWrite8(ptr: *anyopaque, address: u32, value: u8) void {
const self: P = @ptrCast(@alignCast(ptr));
self.dbgWrite(u8, address, value);
}
fn dbgWrite16(ptr: *anyopaque, address: u32, value: u16) void {
const self: P = @ptrCast(@alignCast(ptr));
self.dbgWrite(u16, address, value);
}
fn dbgWrite32(ptr: *anyopaque, address: u32, value: u32) void {
const self: P = @ptrCast(@alignCast(ptr));
self.dbgWrite(u32, address, value);
}
fn reset(ptr: *anyopaque) void {
const self: P = @ptrCast(@alignCast(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);
}
};
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,
// 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 impl = struct {
fn now(ptr: *anyopaque) u64 {
const self: P = @ptrCast(@alignCast(ptr));
return self.now();
}
fn reset(ptr: *anyopaque) void {
const self: P = @ptrCast(@alignCast(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);
}
};
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
// ---
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;
}
};
const ExampleCoprocessor = struct {
pub fn read(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3) u32 {
_ = op2;
_ = cm;
_ = cn;
_ = op1;
_ = self;
return 0xDEADBEEF;
}
pub fn write(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void {
_ = value;
_ = op2;
_ = cm;
_ = cn;
_ = op1;
_ = self;
}
pub fn reset(self: *@This()) void {
self.* = .{};
}
};