Compare commits
No commits in common. "30cf951d2a4ccba3ff7ce70ceeae44780af5f1a1" and "5d70e4bd1d7b53365e453c9d9311b5c64e92ea71" have entirely different histories.
30cf951d2a
...
5d70e4bd1d
93
src/arm.zig
93
src/arm.zig
|
@ -4,7 +4,6 @@ const Architecture = enum { v4t, v5te };
|
||||||
const Interpreter = @import("lib.zig").Interpreter;
|
const Interpreter = @import("lib.zig").Interpreter;
|
||||||
const Bus = @import("lib.zig").Bus;
|
const Bus = @import("lib.zig").Bus;
|
||||||
const Scheduler = @import("lib.zig").Scheduler;
|
const Scheduler = @import("lib.zig").Scheduler;
|
||||||
const Coprocessor = @import("lib.zig").Coprocessor;
|
|
||||||
|
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
|
@ -47,7 +46,6 @@ pub fn Arm32(comptime isa: Architecture) type {
|
||||||
// The following will be `void` on.v4t but exist on v5te
|
// The following will be `void` on.v4t but exist on v5te
|
||||||
itcm: if (is_v5te) Itcm else void,
|
itcm: if (is_v5te) Itcm else void,
|
||||||
dtcm: if (is_v5te) Dtcm else void,
|
dtcm: if (is_v5te) Dtcm else void,
|
||||||
cp15: if (is_v5te) Coprocessor else void,
|
|
||||||
|
|
||||||
const arm = switch (isa) {
|
const arm = switch (isa) {
|
||||||
.v4t => @import("arm/v4t.zig").arm,
|
.v4t => @import("arm/v4t.zig").arm,
|
||||||
|
@ -156,37 +154,6 @@ pub fn Arm32(comptime isa: Architecture) type {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 },
|
|
||||||
|
|
||||||
.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 {
|
pub fn init(scheduler: Scheduler, bus: Bus) Self {
|
||||||
return .{
|
return .{
|
||||||
.sched = scheduler,
|
.sched = scheduler,
|
||||||
|
@ -194,41 +161,28 @@ pub fn Arm32(comptime isa: Architecture) type {
|
||||||
.cpsr = .{ .raw = 0x0000_001F },
|
.cpsr = .{ .raw = 0x0000_001F },
|
||||||
.spsr = .{ .raw = 0x0000_0000 },
|
.spsr = .{ .raw = 0x0000_0000 },
|
||||||
|
|
||||||
.cp15 = {},
|
.dtcm = if (is_v5te) .{} else {},
|
||||||
.dtcm = {},
|
.itcm = if (is_v5te) .{} else {},
|
||||||
.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
|
// 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
|
// I considered implementing Bus.cpu_read and Bus.cpu_write but ended up considering that a bit too leaky
|
||||||
pub fn read(self: *Self, comptime T: type, address: u32) T {
|
pub fn read(self: *Self, comptime T: type, address: u32) T {
|
||||||
const readInt = std.mem.readIntSliceLittle;
|
const readInt = std.mem.readIntSliceLittle;
|
||||||
|
|
||||||
if (is_v5te) {
|
if (is_v5te) {
|
||||||
const dtcm_base = self.dtcm.base_address;
|
const itcm_base: u32 = self.itcm.base_address;
|
||||||
const dtcm_size = self.dtcm.virt.size;
|
const itcm_size = self.itcm.buf.len;
|
||||||
|
const dtcm_base: u32 = self.dtcm.base_address;
|
||||||
|
const dtcm_size = self.dtcm.buf.len;
|
||||||
|
|
||||||
if (address < 0x0000_0000 + self.itcm.virt.size)
|
// FIXME: verify correctness + can this be faster?
|
||||||
return readInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)]);
|
if (itcm_base < address and address < itcm_base + itcm_size)
|
||||||
|
return readInt(T, self.itcm.buf[address & 0x0000_7FFF ..][0..@sizeOf(T)]);
|
||||||
|
|
||||||
if (dtcm_base < address and address < dtcm_base + dtcm_size)
|
if (dtcm_base < address and address < dtcm_base + dtcm_size)
|
||||||
return readInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)]);
|
return readInt(T, self.itcm.buf[address & 0x0000_3FFF ..][0..@sizeOf(T)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.bus.read(T, address);
|
return self.bus.read(T, address);
|
||||||
|
@ -238,19 +192,35 @@ pub fn Arm32(comptime isa: Architecture) type {
|
||||||
const writeInt = std.mem.writeIntSliceLittle;
|
const writeInt = std.mem.writeIntSliceLittle;
|
||||||
|
|
||||||
if (is_v5te) {
|
if (is_v5te) {
|
||||||
const dtcm_base = self.dtcm.base_address;
|
const itcm_base: u32 = self.itcm.base_address;
|
||||||
const dtcm_size = self.dtcm.virt.size;
|
const itcm_size = self.itcm.buf.len;
|
||||||
|
const dtcm_base: u32 = self.dtcm.base_address;
|
||||||
|
const dtcm_size = self.dtcm.buf.len;
|
||||||
|
|
||||||
if (address < 0x0000_0000 + self.itcm.virt.size)
|
// FIXME: verify correctness + can this be faster?
|
||||||
return writeInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)], value);
|
if (itcm_base < address and address < itcm_base + itcm_size)
|
||||||
|
return writeInt(T, self.itcm.buf[address & 0x0000_7FFF ..][0..@sizeOf(T)], value);
|
||||||
|
|
||||||
if (dtcm_base < address and address < dtcm_base + dtcm_size)
|
if (dtcm_base < address and address < dtcm_base + dtcm_size)
|
||||||
return writeInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)], value);
|
return writeInt(T, self.itcm.buf[address & 0x0000_3FFF ..][0..@sizeOf(T)], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.bus.write(T, address, 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 {
|
pub inline fn hasSPSR(self: *const Self) bool {
|
||||||
const mode = Mode.getChecked(self, self.cpsr.mode.read());
|
const mode = Mode.getChecked(self, self.cpsr.mode.read());
|
||||||
return switch (mode) {
|
return switch (mode) {
|
||||||
|
@ -433,7 +403,6 @@ fn Tcm(comptime count: usize, comptime default_addr: u32) type {
|
||||||
return struct {
|
return struct {
|
||||||
buf: [count * KiB]u8 = [_]u8{0x00} ** (count * KiB),
|
buf: [count * KiB]u8 = [_]u8{0x00} ** (count * KiB),
|
||||||
base_address: u32 = default_addr,
|
base_address: u32 = default_addr,
|
||||||
virt: struct { size: u32, mask: u32 } = .{ .size = count * KiB, .mask = (count * KiB) - 1 },
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,133 +2,34 @@ const std = @import("std");
|
||||||
|
|
||||||
const log = std.log.scoped(.coprocessor_handler);
|
const log = std.log.scoped(.coprocessor_handler);
|
||||||
|
|
||||||
pub fn dataTransfer(
|
pub fn dataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: bool, comptime N: bool, comptime W: bool, comptime L: bool) InstrFn {
|
||||||
comptime InstrFn: type,
|
_ = L;
|
||||||
comptime P: bool,
|
_ = W;
|
||||||
comptime U: bool,
|
_ = N;
|
||||||
comptime N: bool,
|
_ = U;
|
||||||
comptime W: bool,
|
_ = P;
|
||||||
comptime L: bool,
|
|
||||||
) InstrFn {
|
|
||||||
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: *Arm32, opcode: u32) void {
|
fn inner(cpu: *Arm32, opcode: u32) void {
|
||||||
if (!P and !W and !U) return copExt(cpu, opcode); // Coprocessor Extension Space
|
|
||||||
|
|
||||||
const rn = opcode >> 16 & 0xF;
|
|
||||||
const crd = opcode >> 12 & 0xF;
|
|
||||||
const cp_num = opcode >> 8 & 0xF;
|
|
||||||
const offset = (opcode & 0xFF) << 2;
|
|
||||||
|
|
||||||
// TODO: Make sure this is comptime
|
|
||||||
const addr_mode: u2 = comptime @as(u2, @intFromBool(P)) << 1 | @intFromBool(W);
|
|
||||||
|
|
||||||
const start_address: u32 = switch (addr_mode) {
|
|
||||||
0b00 => blk: {
|
|
||||||
// Unindexed Addressing
|
|
||||||
std.debug.assert(U == true);
|
|
||||||
|
|
||||||
break :blk cpu.r[rn];
|
|
||||||
},
|
|
||||||
0b01 => blk: {
|
|
||||||
// Immediate Post-Indexed Addressing
|
|
||||||
const addr = cpu.r[rn];
|
|
||||||
cpu.r[rn] = if (U) cpu.r[rn] + offset else cpu.r[rn] - offset;
|
|
||||||
|
|
||||||
break :blk addr;
|
|
||||||
},
|
|
||||||
0b10 => if (U) cpu.r[rn] + offset else cpu.r[rn] - offset, // Immediate Offset Addressing
|
|
||||||
0b11 => blk: {
|
|
||||||
// Immediate Pre-Indexed Addressing
|
|
||||||
cpu.r[rn] = if (U) cpu.r[rn] + offset else cpu.r[rn] - offset;
|
|
||||||
|
|
||||||
break :blk cpu.r[rn];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Increment address + 4 (and perform op) until coprocessor says stop
|
|
||||||
|
|
||||||
if (L) {
|
|
||||||
log.debug("TODO: ldc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
|
|
||||||
} else {
|
|
||||||
log.debug("TODO: stc{s} p{}, c{}, 0x{X:0>8}", .{ [_]u8{if (N) 'l' else ' '}, cp_num, crd, start_address });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copExt(cpu: *Arm32, opcode: u32) void {
|
|
||||||
_ = cpu;
|
_ = cpu;
|
||||||
const cp_num = opcode >> 8 & 0xF;
|
|
||||||
const rd = opcode >> 12 & 0xF;
|
|
||||||
const rn = opcode >> 16 & 0xF;
|
|
||||||
const crm = opcode & 0xF;
|
|
||||||
|
|
||||||
const cp_opcode = opcode >> 4 & 0xF; // FIXME: We could get this value at comptime
|
log.err("TODO: handle 0x{X:0>8} which is a coprocessor data transfer instr", .{opcode});
|
||||||
|
|
||||||
std.debug.assert(rd != 15); // UNPREDICTABLE
|
|
||||||
std.debug.assert(rn != 15); // UNPREDICTABLE
|
|
||||||
|
|
||||||
if (L) {
|
|
||||||
// MRRC
|
|
||||||
log.debug("TODO: mrrc p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
|
|
||||||
} else {
|
|
||||||
// MCRR
|
|
||||||
log.debug("TODO: mcrr p{}, {}, r{}, r{}, c{}", .{ cp_num, cp_opcode, rd, rn, crm });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L: bool, comptime opcode2: u3) InstrFn {
|
pub fn registerTransfer(comptime InstrFn: type, comptime opcode1: u3, comptime L: bool, comptime opcode2: u3) InstrFn {
|
||||||
|
_ = opcode2;
|
||||||
|
_ = L;
|
||||||
|
_ = opcode1;
|
||||||
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
fn inner(cpu: *Arm32, opcode: u32) void {
|
fn inner(cpu: *Arm32, opcode: u32) void {
|
||||||
const crn: u4 = @intCast(opcode >> 16 & 0xF);
|
_ = cpu;
|
||||||
const rd = opcode >> 12 & 0xF;
|
|
||||||
const cp_num = opcode >> 8 & 0xF;
|
|
||||||
const crm: u4 = @intCast(opcode & 0xF);
|
|
||||||
|
|
||||||
std.debug.assert(cp_num == 0xF); // There's no other coprocessor on NDS9;
|
log.err("TODO: handle 0x{X:0>8} which is a coprocessor register transfer instr", .{opcode});
|
||||||
|
|
||||||
if (L) {
|
|
||||||
// MRC
|
|
||||||
const value = cpu.cp15.read(opcode1, crn, crm, opcode2);
|
|
||||||
|
|
||||||
if (rd != 0xF) {
|
|
||||||
cpu.r[rd] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: I can probably do this with a mask and the like
|
|
||||||
cpu.cpsr.n.write(value >> 31 & 1 == 1);
|
|
||||||
cpu.cpsr.z.write(value >> 30 & 1 == 1);
|
|
||||||
cpu.cpsr.c.write(value >> 29 & 1 == 1);
|
|
||||||
cpu.cpsr.v.write(value >> 28 & 1 == 1);
|
|
||||||
} else {
|
|
||||||
// MCR
|
|
||||||
std.debug.assert(rd != 0xF); // UNPREDICTABLE
|
|
||||||
|
|
||||||
cpu.cp15.write(opcode1, crn, crm, opcode2, cpu.r[rd]);
|
|
||||||
|
|
||||||
{
|
|
||||||
// OK so the idea is that I don't want to pass the coprocessor a reference to the CPU,
|
|
||||||
// so there's some side effects that we need to deal with. Right now I think I'll just process
|
|
||||||
// all side affects on every MCR write and hope this isn't too awful
|
|
||||||
// TODO: there has to be a better way.....
|
|
||||||
|
|
||||||
// ICTM / DTCM Stuff
|
|
||||||
const dtcm_size_base = cpu.cp15.read(0, 9, 1, 0); // mrc 0, c9, c1, 0
|
|
||||||
const itcm_size_base = cpu.cp15.read(0, 9, 1, 1); // mrc 0, c9, c1, 1
|
|
||||||
|
|
||||||
cpu.dtcm.base_address = dtcm_size_base & 0xFFFF_F000;
|
|
||||||
cpu.dtcm.virt.size = @as(u32, 0x200) << @truncate(std.math.clamp(dtcm_size_base >> 1 & 0x1F, 3, 23));
|
|
||||||
cpu.dtcm.virt.mask = std.math.clamp(cpu.dtcm.virt.size, 0, @as(u32, @intCast(cpu.dtcm.buf.len))) - 1;
|
|
||||||
|
|
||||||
cpu.itcm.virt.size = @as(u32, 0x200) << @truncate(std.math.clamp(itcm_size_base >> 1 & 0x1F, 3, 23));
|
|
||||||
cpu.itcm.virt.mask = std.math.clamp(cpu.itcm.virt.size, 0, @as(u32, @intCast(cpu.itcm.buf.len))) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
||||||
|
|
||||||
cpu.r[rd] = @bitCast(if (U) left -| right else left +| right);
|
cpu.r[rd] = @bitCast(if (U) left -| right else left +| right);
|
||||||
|
|
||||||
if (cpu.r[rd] == 0x8000_0000 or cpu.r[rd] == 0x7FFF_FFFF) cpu.cpsr.q.set();
|
if (cpu.r[rd] == if (U) 0x8000_0000 else 0x7FFF_FFFF) cpu.cpsr.q.set();
|
||||||
},
|
},
|
||||||
0b10_0101, 0b11_0101 => { // QDADD / QDSUB
|
0b10_0101, 0b11_0101 => { // QDADD / QDSUB
|
||||||
const U = op >> 4 & 1 == 1;
|
const U = op >> 4 & 1 == 1;
|
||||||
|
@ -76,7 +76,7 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
||||||
const left: i32 = @bitCast(cpu.r[rm]);
|
const left: i32 = @bitCast(cpu.r[rm]);
|
||||||
|
|
||||||
cpu.r[rd] = @bitCast(if (U) left -| product else left +| product);
|
cpu.r[rd] = @bitCast(if (U) left -| product else left +| product);
|
||||||
if (cpu.r[rd] == 0x8000_0000 or cpu.r[rd] == 0x7FFF_FFFF) cpu.cpsr.q.set();
|
if (cpu.r[rd] == if (U) 0x800_0000 else 0x7FFF_FFFF) cpu.cpsr.q.set();
|
||||||
},
|
},
|
||||||
0b01_0111 => cpu.panic("TODO: handle BKPT", .{}),
|
0b01_0111 => cpu.panic("TODO: handle BKPT", .{}),
|
||||||
0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA<x><y>
|
0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA<x><y>
|
||||||
|
@ -111,18 +111,16 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
||||||
const right: i32 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rs] >> 16 * Y))));
|
const right: i32 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rs] >> 16 * Y))));
|
||||||
|
|
||||||
// TODO: de-clutter this lmao
|
// TODO: de-clutter this lmao
|
||||||
const rdlo_val: i32 = @bitCast(cpu.r[rd_lo]);
|
cpu.r[rd_lo] = @bitCast(@as(i32, @bitCast(cpu.r[rd_lo])) + (left * right));
|
||||||
const product = left * right;
|
|
||||||
|
|
||||||
// WHY DOESN'T THIS WORK????????
|
|
||||||
|
|
||||||
cpu.r[rd_lo] = @bitCast(rdlo_val + product);
|
|
||||||
cpu.r[rd_hi] = blk: {
|
cpu.r[rd_hi] = blk: {
|
||||||
// FIXME: I think this has to do with correcting sign values?
|
const _left: i32 = @bitCast(cpu.r[rd_hi]);
|
||||||
const offset_thing: i32 = @bitCast(if (product < 0) 0xFFFF_FFFF else @as(u32, 0));
|
const _mid: i32 = if (left * right < 0) @bitCast(@as(u32, 0xFFFF_FFFF)) else 0;
|
||||||
|
|
||||||
const rdhi_val: i32 = @bitCast(cpu.r[rd_hi]);
|
// FIXME: chances are the read from rd_lo here is incorrect
|
||||||
break :blk @bitCast(rdhi_val + offset_thing + @addWithOverflow(rdlo_val, product)[1]);
|
const _right: i32 = @addWithOverflow(@as(i32, @bitCast(cpu.r[rd_lo])), left * right)[1];
|
||||||
|
|
||||||
|
break :blk @bitCast(_left + _mid + _right);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
0b01_1000, 0b01_1100 => { // SMLAW<y>
|
0b01_1000, 0b01_1100 => { // SMLAW<y>
|
||||||
|
@ -174,16 +172,6 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic on any integer?
|
|
||||||
inline fn carryFrom(left: i32, right: i32) u1 {
|
|
||||||
const _left: u32 = @bitCast(left);
|
|
||||||
const _right: u32 = @bitCast(right);
|
|
||||||
|
|
||||||
const sum = @as(u64, _left) + @as(u64, _right);
|
|
||||||
|
|
||||||
return @intCast(sum >> 32 & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn msr(comptime R: bool, comptime imm: bool, cpu: *Arm32, opcode: u32) void {
|
inline fn msr(comptime R: bool, comptime imm: bool, cpu: *Arm32, opcode: u32) void {
|
||||||
const field_mask: u4 = @truncate(opcode >> 16 & 0xF);
|
const field_mask: u4 = @truncate(opcode >> 16 & 0xF);
|
||||||
const rm_idx = opcode & 0xF;
|
const rm_idx = opcode & 0xF;
|
||||||
|
|
|
@ -38,8 +38,6 @@ pub const arm = struct {
|
||||||
return comptime comptime_blk: {
|
return comptime comptime_blk: {
|
||||||
@setEvalBranchQuota(0xE000);
|
@setEvalBranchQuota(0xE000);
|
||||||
var table = [_]InstrFn{und} ** 0x1000;
|
var table = [_]InstrFn{und} ** 0x1000;
|
||||||
// op : 27 26 25 24 23 22 21 20 07 06 05 04
|
|
||||||
// idx: 11 10 09 08 07 06 05 04 03 02 01 00
|
|
||||||
|
|
||||||
for (&table, 0..) |*handler, i| {
|
for (&table, 0..) |*handler, i| {
|
||||||
handler.* = switch (@as(u2, i >> 10)) {
|
handler.* = switch (@as(u2, i >> 10)) {
|
||||||
|
|
241
src/lib.zig
241
src/lib.zig
|
@ -32,38 +32,6 @@ 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 {
|
pub const Bus = struct {
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
vtable: *const Vtable,
|
vtable: *const Vtable,
|
||||||
|
@ -228,118 +196,6 @@ 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 {
|
pub const Scheduler = struct {
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
|
|
||||||
|
@ -379,27 +235,6 @@ 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
|
// TESTING
|
||||||
// ---
|
// ---
|
||||||
|
@ -447,27 +282,61 @@ const ExampleScheduler = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExampleCoprocessor = struct {
|
test "create IBus" {
|
||||||
pub fn read(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3) u32 {
|
var bus_impl = ExampleBus{};
|
||||||
_ = op2;
|
const iface = Bus.init(&bus_impl);
|
||||||
_ = cm;
|
_ = iface;
|
||||||
_ = cn;
|
}
|
||||||
_ = op1;
|
|
||||||
_ = self;
|
|
||||||
|
|
||||||
return 0xDEADBEEF;
|
test "call IBus reads" {
|
||||||
}
|
var bus_impl = ExampleBus{};
|
||||||
|
const iface = Bus.init(&bus_impl);
|
||||||
|
|
||||||
pub fn write(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void {
|
_ = iface.read(u32, 0x0000_0000);
|
||||||
_ = value;
|
_ = iface.read(u16, 0x0000_0000);
|
||||||
_ = op2;
|
_ = iface.read(u8, 0x0000_0000);
|
||||||
_ = cm;
|
|
||||||
_ = cn;
|
|
||||||
_ = op1;
|
|
||||||
_ = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(self: *@This()) void {
|
_ = iface.dbgRead(u32, 0x0000_0000);
|
||||||
self.* = .{};
|
_ = 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();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue