From 3c5d4acc5f679aca095673dd50a069d47a2d677d Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Tue, 19 Sep 2023 23:00:28 -0500 Subject: [PATCH] feat(v5te): implement THUMB BLX(1), BLX(2), and ARM BLX --- src/arm.zig | 2 +- src/arm/cpu/arm/branch.zig | 19 +++++++++-- src/arm/cpu/thumb/branch.zig | 47 +++++++++++++-------------- src/arm/cpu/thumb/data_processing.zig | 11 +++++++ src/arm/v4t.zig | 9 ++--- src/arm/v5te.zig | 11 +++---- 6 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/arm.zig b/src/arm.zig index 7ef3ac1..dfb8005 100644 --- a/src/arm.zig +++ b/src/arm.zig @@ -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); diff --git a/src/arm/cpu/arm/branch.zig b/src/arm/cpu/arm/branch.zig index cde48e5..818493b 100644 --- a/src/arm/cpu/arm/branch.zig +++ b/src/arm/cpu/arm/branch.zig @@ -5,9 +5,24 @@ pub fn branch(comptime InstrFn: type, comptime L: bool) InstrFn { return struct { fn inner(cpu: *Arm32, opcode: u32) void { - if (L) cpu.r[14] = cpu.r[15] - 4; + 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.r[15] +%= sext(u32, u24, opcode) << 2; cpu.pipe.reload(cpu); } }.inner; diff --git a/src/arm/cpu/thumb/branch.zig b/src/arm/cpu/thumb/branch.zig index a011746..055be94 100644 --- a/src/arm/cpu/thumb/branch.zig +++ b/src/arm/cpu/thumb/branch.zig @@ -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); + cpu.pipe.reload(cpu); + }, + 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; diff --git a/src/arm/cpu/thumb/data_processing.zig b/src/arm/cpu/thumb/data_processing.zig index 4b303f0..d0e4b63 100644 --- a/src/arm/cpu/thumb/data_processing.zig +++ b/src/arm/cpu/thumb/data_processing.zig @@ -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]; diff --git a/src/arm/v4t.zig b/src/arm/v4t.zig index b20d52b..3c6d1b2 100644 --- a/src/arm/v4t.zig +++ b/src/arm/v4t.zig @@ -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); }, }, }; diff --git a/src/arm/v5te.zig b/src/arm/v5te.zig index fc91c1a..69a5bec 100644 --- a/src/arm/v5te.zig +++ b/src/arm/v5te.zig @@ -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); }, }, };