153 lines
4.4 KiB
Zig
153 lines
4.4 KiB
Zig
|
const std = @import("std");
|
||
|
const Bus = @import("bus.zig").Bus;
|
||
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||
|
|
||
|
const comptimeDataProcessing = @import("cpu/data_processing.zig").comptimeDataProcessing;
|
||
|
const comptimeSingleDataTransfer = @import("cpu/single_data_transfer.zig").comptimeSingleDataTransfer;
|
||
|
const comptimeHalfSignedDataTransfer = @import("cpu/half_signed_data_transfer.zig").comptimeHalfSignedDataTransfer;
|
||
|
|
||
|
pub const InstrFn = fn (*ARM7TDMI, *Bus, u32) void;
|
||
|
const ARM_LUT: [0x1000]InstrFn = populate();
|
||
|
|
||
|
pub const ARM7TDMI = struct {
|
||
|
r: [16]u32,
|
||
|
sch: *Scheduler,
|
||
|
bus: *Bus,
|
||
|
cpsr: CPSR,
|
||
|
|
||
|
pub fn new(scheduler: *Scheduler, bus: *Bus) @This() {
|
||
|
const cpsr: u32 = 0x0000_00DF;
|
||
|
return .{
|
||
|
.r = [_]u32{0x00} ** 16,
|
||
|
.sch = scheduler,
|
||
|
.bus = bus,
|
||
|
.cpsr = @bitCast(CPSR, cpsr),
|
||
|
};
|
||
|
}
|
||
|
|
||
|
pub inline fn step(self: *@This()) u64 {
|
||
|
const opcode = self.fetch();
|
||
|
// Debug
|
||
|
std.debug.print("R15: 0x{X:}\n", .{ opcode });
|
||
|
|
||
|
ARM_LUT[armIdx(opcode)](self, self.bus, opcode);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
fn fetch(self: *@This()) u32 {
|
||
|
const word = self.bus.readWord(self.r[15]);
|
||
|
self.r[15] += 4;
|
||
|
return word;
|
||
|
}
|
||
|
|
||
|
fn fakePC(self: *const @This()) u32 {
|
||
|
return self.r[15] + 4;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
fn armIdx(opcode: u32) u12 {
|
||
|
return @truncate(u12, opcode >> 20 & 0xFF) << 4 | @truncate(u12, opcode >> 8 & 0xF);
|
||
|
}
|
||
|
|
||
|
fn populate() [0x1000]InstrFn {
|
||
|
return comptime {
|
||
|
@setEvalBranchQuota(0x5000);
|
||
|
var lut = [_]InstrFn{undefined_instr} ** 0x1000;
|
||
|
|
||
|
var i: usize = 0;
|
||
|
while (i < lut.len) : (i += 1) {
|
||
|
if (i >> 10 & 0x3 == 0b00) {
|
||
|
const I = i >> 9 & 0x01 == 0x01;
|
||
|
const S = i >> 4 & 0x01 == 0x01;
|
||
|
const instrKind = i >> 5 & 0x0F;
|
||
|
|
||
|
lut[i] = comptimeDataProcessing(I, S, instrKind);
|
||
|
}
|
||
|
|
||
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x00 and i & 0xF == 0x0) {
|
||
|
// Halfword and Signed Data Transfer with register offset
|
||
|
const P = i >> 8 & 0x01 == 0x01;
|
||
|
const U = i >> 7 & 0x01 == 0x01;
|
||
|
const I = true;
|
||
|
const W = i >> 5 & 0x01 == 0x01;
|
||
|
const L = i >> 4 & 0x01 == 0x01;
|
||
|
|
||
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||
|
}
|
||
|
|
||
|
if (i >> 9 & 0x7 == 0b000 and i >> 6 & 0x01 == 0x01) {
|
||
|
// Halfword and Signed Data Tranfer with immediate offset
|
||
|
const P = i >> 8 & 0x01 == 0x01;
|
||
|
const U = i >> 7 & 0x01 == 0x01;
|
||
|
const I = false;
|
||
|
const W = i >> 5 & 0x01 == 0x01;
|
||
|
const L = i >> 4 & 0x01 == 0x01;
|
||
|
|
||
|
lut[i] = comptimeHalfSignedDataTransfer(P, U, I, W, L);
|
||
|
}
|
||
|
|
||
|
if (i >> 10 & 0x3 == 0b01 and i & 0x01 == 0x00) {
|
||
|
const I = i >> 9 & 0x01 == 0x01;
|
||
|
const P = i >> 8 & 0x01 == 0x01;
|
||
|
const U = i >> 7 & 0x01 == 0x01;
|
||
|
const B = i >> 6 & 0x01 == 0x01;
|
||
|
const W = i >> 5 & 0x01 == 0x01;
|
||
|
const L = i >> 4 & 0x01 == 0x01;
|
||
|
|
||
|
lut[i] = comptimeSingleDataTransfer(I, P, U, B, W, L);
|
||
|
}
|
||
|
|
||
|
if (i >> 9 & 0x7 == 0b101) {
|
||
|
const L = i >> 8 & 0x01 == 0x01;
|
||
|
lut[i] = comptimeBranch(L);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return lut;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const CPSR = packed struct {
|
||
|
n: bool, // Negative / Less Than
|
||
|
z: bool, // Zero
|
||
|
c: bool, // Carry / Borrow / Extend
|
||
|
v: bool, // Overflow
|
||
|
_: u20,
|
||
|
i: bool, // IRQ Disable
|
||
|
f: bool, // FIQ Diable
|
||
|
t: bool, // State
|
||
|
m: Mode, // Mode
|
||
|
};
|
||
|
|
||
|
const Mode = enum(u5) {
|
||
|
User = 0b10000,
|
||
|
Fiq = 0b10001,
|
||
|
Irq = 0b10010,
|
||
|
Supervisor = 0b10011,
|
||
|
Abort = 0b10111,
|
||
|
Undefined = 0b11011,
|
||
|
System = 0b11111,
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
fn undefined_instr(_: *ARM7TDMI, _: *Bus, opcode: u32) void {
|
||
|
const id = armIdx(opcode);
|
||
|
std.debug.panic("[0x{X:}] 0x{X:} is an illegal opcode", .{ id, opcode });
|
||
|
}
|
||
|
|
||
|
fn comptimeBranch(comptime L: bool) InstrFn {
|
||
|
return struct {
|
||
|
fn branch(cpu: *ARM7TDMI, _: *Bus, opcode: u32) void {
|
||
|
if (L) {
|
||
|
cpu.r[14] = cpu.r[15] - 4;
|
||
|
}
|
||
|
|
||
|
const offset = @bitCast(i32, (opcode << 2) << 8) >> 8;
|
||
|
cpu.r[15] = cpu.fakePC() + @bitCast(u32, offset);
|
||
|
}
|
||
|
}.branch;
|
||
|
}
|