feat(v5te): implement THUMB BLX(1), BLX(2), and ARM BLX
This commit is contained in:
parent
37b3fe3d10
commit
3c5d4acc5f
|
@ -371,7 +371,7 @@ pub fn Arm32(comptime isa: Architecture) type {
|
|||
|
||||
if (self.cpsr.check(Self.arch, cond)) {
|
||||
if (isa == .v5te and cond == 0b1111) {
|
||||
self.panic("TODO: Unconditional Instruction Extension Space\nopcode: 0x{X:0>8} | idx: 0x{X:} | ptr: {any}", .{ opcode, arm.idx(opcode), arm.lut[arm.idx(opcode)] });
|
||||
std.log.debug("TODO: Unconditional Instruction Extension Space\nopcode: 0x{X:0>8} | idx: 0x{X:} | ptr: {any}", .{ opcode, arm.idx(opcode), arm.lut[arm.idx(opcode)] });
|
||||
}
|
||||
|
||||
arm.lut[arm.idx(opcode)](self, opcode);
|
||||
|
|
|
@ -5,9 +5,24 @@ pub fn branch(comptime InstrFn: type, comptime L: bool) InstrFn {
|
|||
|
||||
return struct {
|
||||
fn inner(cpu: *Arm32, opcode: u32) void {
|
||||
const cond: u4 = @truncate(opcode >> 28);
|
||||
switch (cond) {
|
||||
0b1111 => { // BLX
|
||||
const H = L;
|
||||
const offset = sext(u32, u24, opcode) << 2 | @as(u32, @intFromBool(H)) << 1;
|
||||
|
||||
cpu.r[14] = cpu.r[15] - 4;
|
||||
cpu.cpsr.t.set();
|
||||
|
||||
cpu.r[15] +%= offset;
|
||||
},
|
||||
else => {
|
||||
if (L) cpu.r[14] = cpu.r[15] - 4;
|
||||
|
||||
cpu.r[15] +%= sext(u32, u24, opcode) << 2;
|
||||
},
|
||||
}
|
||||
|
||||
cpu.pipe.reload(cpu);
|
||||
}
|
||||
}.inner;
|
||||
|
|
|
@ -17,38 +17,37 @@ pub fn fmt16(comptime InstrFn: type, comptime cond: u4) InstrFn {
|
|||
}.inner;
|
||||
}
|
||||
|
||||
pub fn fmt18(comptime InstrFn: type) InstrFn {
|
||||
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
||||
|
||||
return struct {
|
||||
// B but conditional
|
||||
fn inner(cpu: *Arm32, opcode: u16) void {
|
||||
cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1;
|
||||
cpu.pipe.reload(cpu);
|
||||
}
|
||||
}.inner;
|
||||
}
|
||||
|
||||
pub fn fmt19(comptime InstrFn: type, comptime is_low: bool) InstrFn {
|
||||
pub fn linkExchange(comptime InstrFn: type, comptime H: u2) InstrFn {
|
||||
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
||||
|
||||
return struct {
|
||||
fn inner(cpu: *Arm32, opcode: u16) void {
|
||||
// BL
|
||||
const offset = opcode & 0x7FF;
|
||||
|
||||
if (is_low) {
|
||||
// Instruction 2
|
||||
const next_opcode = cpu.r[15] - 2;
|
||||
switch (H) {
|
||||
0b00 => { // Unconditional Branch
|
||||
cpu.r[15] +%= sext(u32, u11, opcode & 0x7FF) << 1;
|
||||
cpu.pipe.reload(cpu);
|
||||
},
|
||||
0b01 => { // BLX Pt. 2
|
||||
if (Arm32.arch == .v4t) cpu.panic("attempted to execute THUMB BLX(1), despite ARMv4T CPU", .{});
|
||||
const next_addr = cpu.r[15] - 2;
|
||||
|
||||
cpu.r[15] = cpu.r[14] +% (offset << 1);
|
||||
cpu.r[14] = next_opcode | 1;
|
||||
cpu.r[15] = (cpu.r[14] +% (offset << 1)) & ~@as(u32, 0x3);
|
||||
cpu.r[14] = next_addr | 1;
|
||||
cpu.cpsr.t.unset();
|
||||
|
||||
cpu.pipe.reload(cpu);
|
||||
} else {
|
||||
// Instruction 1
|
||||
const lr_offset = sext(u32, u11, offset) << 12;
|
||||
cpu.r[14] = (cpu.r[15] +% lr_offset) & ~@as(u32, 1);
|
||||
},
|
||||
0b10 => cpu.r[14] = cpu.r[15] +% (sext(u32, u11, offset) << 12), // BL / BLX Pt. 1
|
||||
0b11 => { // BL Pt. 2
|
||||
const next_addr = cpu.r[15] - 2;
|
||||
|
||||
cpu.r[15] = cpu.r[14] +% (offset << 1);
|
||||
cpu.r[14] = next_addr | 1;
|
||||
|
||||
cpu.pipe.reload(cpu);
|
||||
},
|
||||
}
|
||||
}
|
||||
}.inner;
|
||||
|
|
|
@ -60,6 +60,17 @@ pub fn fmt5(comptime InstrFn: type, comptime op: u2, comptime h1: u1, comptime h
|
|||
const rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7);
|
||||
const rd = @as(u4, h1) << 3 | (opcode & 0x7);
|
||||
|
||||
if (Arm32.arch == .v5te and op == 0b11 and h1 == 0b1) {
|
||||
// BLX
|
||||
const rm = rs;
|
||||
|
||||
cpu.r[14] = (cpu.r[15] - 2) | 1;
|
||||
cpu.cpsr.t.write(cpu.r[rm] & 1 == 1);
|
||||
|
||||
cpu.r[15] = cpu.r[rm] & ~@as(u32, 1);
|
||||
cpu.pipe.reload(cpu);
|
||||
}
|
||||
|
||||
const op1 = cpu.r[rd];
|
||||
const op2 = cpu.r[rs];
|
||||
|
||||
|
|
|
@ -205,12 +205,9 @@ pub const thumb = struct {
|
|||
const cond = i >> 2 & 0xF;
|
||||
break :blk branch.fmt16(InstrFn, cond);
|
||||
},
|
||||
0b110 => branch.fmt18(
|
||||
InstrFn,
|
||||
),
|
||||
0b111 => blk: {
|
||||
const is_low = i >> 5 & 1 == 1;
|
||||
break :blk branch.fmt19(InstrFn, is_low);
|
||||
0b110, 0b111 => blk: {
|
||||
const H = i >> 5 & 0x3;
|
||||
break :blk branch.linkExchange(InstrFn, H);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -149,6 +149,8 @@ pub const thumb = struct {
|
|||
return comptime comptime_blk: {
|
||||
@setEvalBranchQuota(5025); // This is exact
|
||||
var table = [_]InstrFn{und} ** 0x400;
|
||||
// 9 8 7 6 5 4 3 2 1 0
|
||||
// 15 14 13 12 11 10 9 8 7 6
|
||||
|
||||
for (&table, 0..) |*handler, i| {
|
||||
handler.* = switch (@as(u3, i >> 7 & 0x7)) {
|
||||
|
@ -230,12 +232,9 @@ pub const thumb = struct {
|
|||
const cond = i >> 2 & 0xF;
|
||||
break :blk branch.fmt16(InstrFn, cond);
|
||||
},
|
||||
0b110 => branch.fmt18(
|
||||
InstrFn,
|
||||
),
|
||||
0b111 => blk: {
|
||||
const is_low = i >> 5 & 1 == 1;
|
||||
break :blk branch.fmt19(InstrFn, is_low);
|
||||
0b110, 0b111 => blk: {
|
||||
const H = i >> 5 & 0x3;
|
||||
break :blk branch.linkExchange(InstrFn, H);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue