feat(v5te): implement THUMB BLX(1), BLX(2), and ARM BLX
This commit is contained in:
		| @@ -371,7 +371,7 @@ pub fn Arm32(comptime isa: Architecture) type { | |||||||
|  |  | ||||||
|                 if (self.cpsr.check(Self.arch, cond)) { |                 if (self.cpsr.check(Self.arch, cond)) { | ||||||
|                     if (isa == .v5te and cond == 0b1111) { |                     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); |                     arm.lut[arm.idx(opcode)](self, opcode); | ||||||
|   | |||||||
| @@ -5,9 +5,24 @@ pub fn branch(comptime InstrFn: type, comptime L: bool) InstrFn { | |||||||
|  |  | ||||||
|     return struct { |     return struct { | ||||||
|         fn inner(cpu: *Arm32, opcode: u32) void { |         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; |                     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); |             cpu.pipe.reload(cpu); | ||||||
|         } |         } | ||||||
|     }.inner; |     }.inner; | ||||||
|   | |||||||
| @@ -17,38 +17,37 @@ pub fn fmt16(comptime InstrFn: type, comptime cond: u4) InstrFn { | |||||||
|     }.inner; |     }.inner; | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn fmt18(comptime InstrFn: type) 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 { |  | ||||||
|         // 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 { |  | ||||||
|     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: u16) void { |         fn inner(cpu: *Arm32, opcode: u16) void { | ||||||
|             // BL |  | ||||||
|             const offset = opcode & 0x7FF; |             const offset = opcode & 0x7FF; | ||||||
|  |  | ||||||
|             if (is_low) { |             switch (H) { | ||||||
|                 // Instruction 2 |                 0b00 => { // Unconditional Branch | ||||||
|                 const next_opcode = cpu.r[15] - 2; |                     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[15] = (cpu.r[14] +% (offset << 1)) & ~@as(u32, 0x3); | ||||||
|                 cpu.r[14] = next_opcode | 1; |                     cpu.r[14] = next_addr | 1; | ||||||
|  |                     cpu.cpsr.t.unset(); | ||||||
|  |  | ||||||
|                     cpu.pipe.reload(cpu); |                     cpu.pipe.reload(cpu); | ||||||
|             } else { |                 }, | ||||||
|                 // Instruction 1 |                 0b10 => cpu.r[14] = cpu.r[15] +% (sext(u32, u11, offset) << 12), // BL / BLX Pt. 1 | ||||||
|                 const lr_offset = sext(u32, u11, offset) << 12; |                 0b11 => { // BL Pt. 2 | ||||||
|                 cpu.r[14] = (cpu.r[15] +% lr_offset) & ~@as(u32, 1); |                     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; |     }.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 rs = @as(u4, h2) << 3 | (opcode >> 3 & 0x7); | ||||||
|             const rd = @as(u4, h1) << 3 | (opcode & 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 op1 = cpu.r[rd]; | ||||||
|             const op2 = cpu.r[rs]; |             const op2 = cpu.r[rs]; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -205,12 +205,9 @@ pub const thumb = struct { | |||||||
|                             const cond = i >> 2 & 0xF; |                             const cond = i >> 2 & 0xF; | ||||||
|                             break :blk branch.fmt16(InstrFn, cond); |                             break :blk branch.fmt16(InstrFn, cond); | ||||||
|                         }, |                         }, | ||||||
|                         0b110 => branch.fmt18( |                         0b110, 0b111 => blk: { | ||||||
|                             InstrFn, |                             const H = i >> 5 & 0x3; | ||||||
|                         ), |                             break :blk branch.linkExchange(InstrFn, H); | ||||||
|                         0b111 => blk: { |  | ||||||
|                             const is_low = i >> 5 & 1 == 1; |  | ||||||
|                             break :blk branch.fmt19(InstrFn, is_low); |  | ||||||
|                         }, |                         }, | ||||||
|                     }, |                     }, | ||||||
|                 }; |                 }; | ||||||
|   | |||||||
| @@ -149,6 +149,8 @@ pub const thumb = struct { | |||||||
|         return comptime comptime_blk: { |         return comptime comptime_blk: { | ||||||
|             @setEvalBranchQuota(5025); // This is exact |             @setEvalBranchQuota(5025); // This is exact | ||||||
|             var table = [_]InstrFn{und} ** 0x400; |             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| { |             for (&table, 0..) |*handler, i| { | ||||||
|                 handler.* = switch (@as(u3, i >> 7 & 0x7)) { |                 handler.* = switch (@as(u3, i >> 7 & 0x7)) { | ||||||
| @@ -230,12 +232,9 @@ pub const thumb = struct { | |||||||
|                             const cond = i >> 2 & 0xF; |                             const cond = i >> 2 & 0xF; | ||||||
|                             break :blk branch.fmt16(InstrFn, cond); |                             break :blk branch.fmt16(InstrFn, cond); | ||||||
|                         }, |                         }, | ||||||
|                         0b110 => branch.fmt18( |                         0b110, 0b111 => blk: { | ||||||
|                             InstrFn, |                             const H = i >> 5 & 0x3; | ||||||
|                         ), |                             break :blk branch.linkExchange(InstrFn, H); | ||||||
|                         0b111 => blk: { |  | ||||||
|                             const is_low = i >> 5 & 1 == 1; |  | ||||||
|                             break :blk branch.fmt19(InstrFn, is_low); |  | ||||||
|                         }, |                         }, | ||||||
|                     }, |                     }, | ||||||
|                 }; |                 }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user