feat: move thumb instr decoding to module

This commit is contained in:
Rekai Nyangadzayi Musuka 2022-07-27 13:10:58 -03:00
parent 53191b0eeb
commit 9d037fdc3e
8 changed files with 169 additions and 186 deletions

View File

@ -16,7 +16,7 @@ const psrTransfer = @import("cpu/arm/psr_transfer.zig").psrTransfer;
const singleDataTransfer = @import("cpu/arm/single_data_transfer.zig").singleDataTransfer;
const halfAndSignedDataTransfer = @import("cpu/arm/half_signed_data_transfer.zig").halfAndSignedDataTransfer;
const blockDataTransfer = @import("cpu/arm/block_data_transfer.zig").blockDataTransfer;
const branch = @import("cpu/arm/branch.zig").branch;
const armBranch = @import("cpu/arm/branch.zig").branch;
const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange;
const armSoftwareInterrupt = @import("cpu/arm/software_interrupt.zig").armSoftwareInterrupt;
const singleDataSwap = @import("cpu/arm/single_data_swap.zig").singleDataSwap;
@ -25,33 +25,126 @@ const multiply = @import("cpu/arm/multiply.zig").multiply;
const multiplyLong = @import("cpu/arm/multiply.zig").multiplyLong;
// THUMB Instruction Groups
const format1 = @import("cpu/thumb/data_processing.zig").format1;
const format2 = @import("cpu/thumb/data_processing.zig").format2;
const format3 = @import("cpu/thumb/data_processing.zig").format3;
const format12 = @import("cpu/thumb/data_processing.zig").format12;
const format13 = @import("cpu/thumb/data_processing.zig").format13;
const format4 = @import("cpu/thumb/alu.zig").format4;
const format5 = @import("cpu/thumb/processing_branch.zig").format5;
pub const thumb = struct {
pub const InstrFn = fn (*Arm7tdmi, *Bus, u16) void;
const lut: [0x400]InstrFn = populate();
const format6 = @import("cpu/thumb/data_transfer.zig").format6;
const format78 = @import("cpu/thumb/data_transfer.zig").format78;
const format9 = @import("cpu/thumb/data_transfer.zig").format9;
const format10 = @import("cpu/thumb/data_transfer.zig").format10;
const format11 = @import("cpu/thumb/data_transfer.zig").format11;
const format14 = @import("cpu/thumb/block_data_transfer.zig").format14;
const format15 = @import("cpu/thumb/block_data_transfer.zig").format15;
const processing = @import("cpu/thumb/data_processing.zig");
const alu = @import("cpu/thumb/alu.zig").fmt4;
const transfer = @import("cpu/thumb/data_transfer.zig");
const block_transfer = @import("cpu/thumb/block_data_transfer.zig");
const swi = @import("cpu/thumb/software_interrupt.zig").fmt17;
const branch = @import("cpu/thumb/branch.zig");
const format16 = @import("cpu/thumb/branch.zig").format16;
const format18 = @import("cpu/thumb/branch.zig").format18;
const format19 = @import("cpu/thumb/branch.zig").format19;
/// Undefined THUMB Instruction Handler
fn und(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const id = thumbIdx(opcode);
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
}
const thumbSoftwareInterrupt = @import("cpu/thumb/software_interrupt.zig").thumbSoftwareInterrupt;
fn populate() [0x400]InstrFn {
return comptime {
@setEvalBranchQuota(5025); // This is exact
var ret = [_]InstrFn{und} ** 0x400;
var i: usize = 0;
while (i < ret.len) : (i += 1) {
ret[i] = switch (@as(u3, i >> 7 & 0x7)) {
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
const I = i >> 4 & 1 == 1;
const is_sub = i >> 3 & 1 == 1;
const rn = i & 0x7;
break :blk processing.fmt2(I, is_sub, rn);
} else blk: {
const op = i >> 5 & 0x3;
const offset = i & 0x1F;
break :blk processing.fmt1(op, offset);
},
0b001 => blk: {
const op = i >> 5 & 0x3;
const rd = i >> 2 & 0x7;
break :blk processing.fmt3(op, rd);
},
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
0b00 => if (i >> 4 & 1 == 1) blk: {
const op = i >> 2 & 0x3;
const h1 = i >> 1 & 1;
const h2 = i & 1;
break :blk processing.fmt5(op, h1, h2);
} else blk: {
const op = i & 0xF;
break :blk alu(op);
},
0b01 => blk: {
const rd = i >> 2 & 0x7;
break :blk transfer.fmt6(rd);
},
else => blk: {
const op = i >> 4 & 0x3;
const T = i >> 3 & 1 == 1;
break :blk transfer.fmt78(op, T);
},
},
0b011 => blk: {
const B = i >> 6 & 1 == 1;
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
break :blk transfer.fmt9(B, L, offset);
},
else => switch (@as(u3, i >> 6 & 0x7)) {
// MSB is guaranteed to be 1
0b000 => blk: {
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
break :blk transfer.fmt10(L, offset);
},
0b001 => blk: {
const L = i >> 5 & 1 == 1;
const rd = i >> 2 & 0x7;
break :blk transfer.fmt11(L, rd);
},
0b010 => blk: {
const isSP = i >> 5 & 1 == 1;
const rd = i >> 2 & 0x7;
break :blk processing.fmt12(isSP, rd);
},
0b011 => if (i >> 4 & 1 == 1) blk: {
const L = i >> 5 & 1 == 1;
const R = i >> 2 & 1 == 1;
break :blk block_transfer.fmt14(L, R);
} else blk: {
const S = i >> 1 & 1 == 1;
break :blk processing.fmt13(S);
},
0b100 => blk: {
const L = i >> 5 & 1 == 1;
const rb = i >> 2 & 0x7;
break :blk block_transfer.fmt15(L, rb);
},
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
break :blk thumb.swi();
} else blk: {
const cond = i >> 2 & 0xF;
break :blk branch.fmt16(cond);
},
0b110 => branch.fmt18(),
0b111 => blk: {
const is_low = i >> 5 & 1 == 1;
break :blk branch.fmt19(is_low);
},
},
};
}
return ret;
};
}
};
pub const ArmInstrFn = fn (*Arm7tdmi, *Bus, u32) void;
pub const ThumbInstrFn = fn (*Arm7tdmi, *Bus, u16) void;
const arm_lut: [0x1000]ArmInstrFn = armPopulate();
const thumb_lut: [0x400]ThumbInstrFn = thumbPopulate();
const enable_logging = false;
const log = std.log.scoped(.Arm7Tdmi);
@ -260,7 +353,7 @@ pub const Arm7tdmi = struct {
const opcode = self.fetch(u16);
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
thumb_lut[thumbIdx(opcode)](self, &self.bus, opcode);
thumb.lut[thumbIdx(opcode)](self, &self.bus, opcode);
} else {
const opcode = self.fetch(u32);
if (enable_logging) if (self.log_file) |file| self.debug_log(file, opcode);
@ -497,105 +590,6 @@ pub fn checkCond(cpsr: PSR, cond: u4) bool {
};
}
fn thumbPopulate() [0x400]ThumbInstrFn {
return comptime {
@setEvalBranchQuota(5025); // This is exact
var lut = [_]ThumbInstrFn{thumbUndefined} ** 0x400;
var i: usize = 0;
while (i < lut.len) : (i += 1) {
lut[i] = switch (@as(u3, i >> 7 & 0x7)) {
0b000 => if (i >> 5 & 0x3 == 0b11) blk: {
const I = i >> 4 & 1 == 1;
const is_sub = i >> 3 & 1 == 1;
const rn = i & 0x7;
break :blk format2(I, is_sub, rn);
} else blk: {
const op = i >> 5 & 0x3;
const offset = i & 0x1F;
break :blk format1(op, offset);
},
0b001 => blk: {
const op = i >> 5 & 0x3;
const rd = i >> 2 & 0x7;
break :blk format3(op, rd);
},
0b010 => switch (@as(u2, i >> 5 & 0x3)) {
0b00 => if (i >> 4 & 1 == 1) blk: {
const op = i >> 2 & 0x3;
const h1 = i >> 1 & 1;
const h2 = i & 1;
break :blk format5(op, h1, h2);
} else blk: {
const op = i & 0xF;
break :blk format4(op);
},
0b01 => blk: {
const rd = i >> 2 & 0x7;
break :blk format6(rd);
},
else => blk: {
const op = i >> 4 & 0x3;
const T = i >> 3 & 1 == 1;
break :blk format78(op, T);
},
},
0b011 => blk: {
const B = i >> 6 & 1 == 1;
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
break :blk format9(B, L, offset);
},
else => switch (@as(u3, i >> 6 & 0x7)) {
// MSB is guaranteed to be 1
0b000 => blk: {
const L = i >> 5 & 1 == 1;
const offset = i & 0x1F;
break :blk format10(L, offset);
},
0b001 => blk: {
const L = i >> 5 & 1 == 1;
const rd = i >> 2 & 0x7;
break :blk format11(L, rd);
},
0b010 => blk: {
const isSP = i >> 5 & 1 == 1;
const rd = i >> 2 & 0x7;
break :blk format12(isSP, rd);
},
0b011 => if (i >> 4 & 1 == 1) blk: {
const L = i >> 5 & 1 == 1;
const R = i >> 2 & 1 == 1;
break :blk format14(L, R);
} else blk: {
const S = i >> 1 & 1 == 1;
break :blk format13(S);
},
0b100 => blk: {
const L = i >> 5 & 1 == 1;
const rb = i >> 2 & 0x7;
break :blk format15(L, rb);
},
0b101 => if (i >> 2 & 0xF == 0b1111) blk: {
break :blk thumbSoftwareInterrupt();
} else blk: {
const cond = i >> 2 & 0xF;
break :blk format16(cond);
},
0b110 => format18(),
0b111 => blk: {
const is_low = i >> 5 & 1 == 1;
break :blk format19(is_low);
},
},
};
}
return lut;
};
}
fn armPopulate() [0x1000]ArmInstrFn {
return comptime {
@setEvalBranchQuota(0xE000);
@ -657,7 +651,7 @@ fn armPopulate() [0x1000]ArmInstrFn {
},
0b01 => blk: {
const L = i >> 8 & 1 == 1;
break :blk branch(L);
break :blk armBranch(L);
},
0b10 => armUndefined, // COP Data Transfer
0b11 => if (i >> 8 & 1 == 1) armSoftwareInterrupt() else armUndefined, // COP Data Operation + Register Transfer
@ -708,8 +702,3 @@ fn armUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void {
const id = armIdx(opcode);
cpu.panic("[CPU/Decode] ID: 0x{X:0>3} 0x{X:0>8} is an illegal opcode", .{ id, opcode });
}
fn thumbUndefined(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const id = thumbIdx(opcode);
cpu.panic("[CPU/Decode] ID: 0b{b:0>10} 0x{X:0>2} is an illegal opcode", .{ id, opcode });
}

View File

@ -1,6 +1,6 @@
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
const adc = @import("../arm/data_processing.zig").adc;
const sbc = @import("../arm/data_processing.zig").sbc;
@ -15,7 +15,7 @@ const logicalRight = @import("../barrel_shifter.zig").logicalRight;
const arithmeticRight = @import("../barrel_shifter.zig").arithmeticRight;
const rotateRight = @import("../barrel_shifter.zig").rotateRight;
pub fn format4(comptime op: u4) InstrFn {
pub fn fmt4(comptime op: u4) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const rs = opcode >> 3 & 0x7;

View File

@ -1,8 +1,8 @@
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
pub fn format14(comptime L: bool, comptime R: bool) InstrFn {
pub fn fmt14(comptime L: bool, comptime R: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const count = @boolToInt(R) + countRlist(opcode);
@ -45,7 +45,7 @@ pub fn format14(comptime L: bool, comptime R: bool) InstrFn {
}.inner;
}
pub fn format15(comptime L: bool, comptime rb: u3) InstrFn {
pub fn fmt15(comptime L: bool, comptime rb: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
var address = cpu.r[rb];

View File

@ -1,11 +1,11 @@
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
const checkCond = @import("../../cpu.zig").checkCond;
const sext = @import("../../util.zig").sext;
pub fn format16(comptime cond: u4) InstrFn {
pub fn fmt16(comptime cond: u4) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// B
@ -23,7 +23,7 @@ pub fn format16(comptime cond: u4) InstrFn {
}.inner;
}
pub fn format18() InstrFn {
pub fn fmt18() InstrFn {
return struct {
// B but conditional
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
@ -33,7 +33,7 @@ pub fn format18() InstrFn {
}.inner;
}
pub fn format19(comptime is_low: bool) InstrFn {
pub fn fmt19(comptime is_low: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// BL

View File

@ -2,7 +2,7 @@ const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
const shifter = @import("../barrel_shifter.zig");
const add = @import("../arm/data_processing.zig").add;
@ -12,7 +12,7 @@ const setLogicOpFlags = @import("../arm/data_processing.zig").setLogicOpFlags;
const log = std.log.scoped(.Thumb1);
pub fn format1(comptime op: u2, comptime offset: u5) InstrFn {
pub fn fmt1(comptime op: u2, comptime offset: u5) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const rs = opcode >> 3 & 0x7;
@ -55,7 +55,37 @@ pub fn format1(comptime op: u2, comptime offset: u5) InstrFn {
}.inner;
}
pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
pub fn fmt5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const src_idx = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
const dst_idx = @as(u4, h1) << 3 | (opcode & 0x7);
const src = if (src_idx == 0xF) (cpu.r[src_idx] + 2) & 0xFFFF_FFFE else cpu.r[src_idx];
const dst = if (dst_idx == 0xF) (cpu.r[dst_idx] + 2) & 0xFFFF_FFFE else cpu.r[dst_idx];
switch (op) {
0b00 => {
// ADD
const sum = add(false, cpu, dst, src);
cpu.r[dst_idx] = if (dst_idx == 0xF) sum & 0xFFFF_FFFE else sum;
},
0b01 => cmp(cpu, dst, src), // CMP
0b10 => {
// MOV
cpu.r[dst_idx] = if (dst_idx == 0xF) src & 0xFFFF_FFFE else src;
},
0b11 => {
// BX
cpu.cpsr.t.write(src & 1 == 1);
cpu.r[15] = src & 0xFFFF_FFFE;
},
}
}
}.inner;
}
pub fn fmt2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const rs = opcode >> 3 & 0x7;
@ -80,7 +110,7 @@ pub fn format2(comptime I: bool, is_sub: bool, rn: u3) InstrFn {
}.inner;
}
pub fn format3(comptime op: u2, comptime rd: u3) InstrFn {
pub fn fmt3(comptime op: u2, comptime rd: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const offset = @truncate(u8, opcode);
@ -99,7 +129,7 @@ pub fn format3(comptime op: u2, comptime rd: u3) InstrFn {
}.inner;
}
pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn {
pub fn fmt12(comptime isSP: bool, comptime rd: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// ADD
@ -111,7 +141,7 @@ pub fn format12(comptime isSP: bool, comptime rd: u3) InstrFn {
}.inner;
}
pub fn format13(comptime S: bool) InstrFn {
pub fn fmt13(comptime S: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
// ADD

View File

@ -2,11 +2,11 @@ const std = @import("std");
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
const rotr = @import("../../util.zig").rotr;
pub fn format6(comptime rd: u3) InstrFn {
pub fn fmt6(comptime rd: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
// LDR
@ -18,7 +18,7 @@ pub fn format6(comptime rd: u3) InstrFn {
const sext = @import("../../util.zig").sext;
pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
pub fn fmt78(comptime op: u2, comptime T: bool) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const ro = opcode >> 6 & 0x7;
@ -78,7 +78,7 @@ pub fn format78(comptime op: u2, comptime T: bool) InstrFn {
}.inner;
}
pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn {
pub fn fmt9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const rb = opcode >> 3 & 0x7;
@ -110,7 +110,7 @@ pub fn format9(comptime B: bool, comptime L: bool, comptime offset: u5) InstrFn
}.inner;
}
pub fn format10(comptime L: bool, comptime offset: u5) InstrFn {
pub fn fmt10(comptime L: bool, comptime offset: u5) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const rb = opcode >> 3 & 0x7;
@ -130,7 +130,7 @@ pub fn format10(comptime L: bool, comptime offset: u5) InstrFn {
}.inner;
}
pub fn format11(comptime L: bool, comptime rd: u3) InstrFn {
pub fn fmt11(comptime L: bool, comptime rd: u3) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, bus: *Bus, opcode: u16) void {
const offset = (opcode & 0xFF) << 2;

View File

@ -1,36 +0,0 @@
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const cmp = @import("../arm/data_processing.zig").cmp;
const add = @import("../arm/data_processing.zig").add;
pub fn format5(comptime op: u2, comptime h1: u1, comptime h2: u1) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const src_idx = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
const dst_idx = @as(u4, h1) << 3 | (opcode & 0x7);
const src = if (src_idx == 0xF) (cpu.r[src_idx] + 2) & 0xFFFF_FFFE else cpu.r[src_idx];
const dst = if (dst_idx == 0xF) (cpu.r[dst_idx] + 2) & 0xFFFF_FFFE else cpu.r[dst_idx];
switch (op) {
0b00 => {
// ADD
const sum = add(false, cpu, dst, src);
cpu.r[dst_idx] = if (dst_idx == 0xF) sum & 0xFFFF_FFFE else sum;
},
0b01 => cmp(cpu, dst, src), // CMP
0b10 => {
// MOV
cpu.r[dst_idx] = if (dst_idx == 0xF) src & 0xFFFF_FFFE else src;
},
0b11 => {
// BX
cpu.cpsr.t.write(src & 1 == 1);
cpu.r[15] = src & 0xFFFF_FFFE;
},
}
}
}.inner;
}

View File

@ -1,8 +1,8 @@
const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").ThumbInstrFn;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
pub fn thumbSoftwareInterrupt() InstrFn {
pub fn fmt17() InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, _: u16) void {
// Copy Values from Current Mode