Compare commits
6 Commits
106820b444
...
481271ba2a
Author | SHA1 | Date | |
---|---|---|---|
481271ba2a | |||
2c5d474c56 | |||
4d3814db36 | |||
502647806c | |||
3c5d4acc5f | |||
37b3fe3d10 |
@@ -229,7 +229,7 @@ pub fn Arm32(comptime isa: Architecture) type {
|
|||||||
if (address < 0x0000_0000 + self.itcm.virt.size)
|
if (address < 0x0000_0000 + self.itcm.virt.size)
|
||||||
return readInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)]);
|
return readInt(T, self.itcm.buf[address & self.itcm.virt.mask ..][0..@sizeOf(T)]);
|
||||||
|
|
||||||
if (dtcm_base < address and address < dtcm_base + dtcm_size)
|
if (dtcm_base <= address and address < dtcm_base + dtcm_size)
|
||||||
return readInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)]);
|
return readInt(T, self.dtcm.buf[address & self.dtcm.virt.mask ..][0..@sizeOf(T)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
@@ -12,18 +12,18 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
|||||||
// U determines whether the LDM/STM transfer is made upwards (U == 1)
|
// U determines whether the LDM/STM transfer is made upwards (U == 1)
|
||||||
// or downwards (U == 0).
|
// or downwards (U == 0).
|
||||||
|
|
||||||
|
const base_addr = cpu.r[rn];
|
||||||
|
|
||||||
const start_addr: u32 = if (U) blk: {
|
const start_addr: u32 = if (U) blk: {
|
||||||
break :blk cpu.r[rn] + if (P) 4 else 0;
|
break :blk base_addr + if (P) 4 else 0;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
break :blk cpu.r[rn] - (4 * reg_count) + if (!P) 4 else 0;
|
break :blk base_addr - (4 * reg_count) + if (!P) 4 else 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME : why 4 * reg_count?
|
|
||||||
|
|
||||||
const new_base_addr: u32 = if (U) blk: {
|
const new_base_addr: u32 = if (U) blk: {
|
||||||
break :blk cpu.r[rn] + 4 * reg_count;
|
break :blk base_addr + 4 * reg_count;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
break :blk cpu.r[rn] - 4 * reg_count;
|
break :blk base_addr - 4 * reg_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
var address = start_addr;
|
var address = start_addr;
|
||||||
@@ -36,9 +36,9 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
|||||||
if (rlist == 0) {
|
if (rlist == 0) {
|
||||||
if (Arm32.arch == .v4t) {
|
if (Arm32.arch == .v4t) {
|
||||||
const undefined_addr: u32 = if (U) blk: {
|
const undefined_addr: u32 = if (U) blk: {
|
||||||
break :blk cpu.r[rn] + if (P) 4 else 0;
|
break :blk base_addr + if (P) 4 else 0;
|
||||||
} else blk: {
|
} else blk: {
|
||||||
break :blk cpu.r[rn] - (0x40 - if (!P) 4 else 0);
|
break :blk base_addr - (0x40 - if (!P) 4 else 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (L) {
|
if (L) {
|
||||||
@@ -49,41 +49,34 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.r[rn] = if (U) cpu.r[rn] + 0x40 else cpu.r[rn] - 0x40;
|
cpu.r[rn] = if (U) base_addr + 0x40 else base_addr - 0x40;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// What happens when W is set and Rn is in the rlist? (STM)
|
|
||||||
//
|
|
||||||
// Armv4: Store OLD Base if Rb is FIRST entry in Rlist, otherwise store NEW base
|
|
||||||
// Armv5: Always store OLD Base
|
|
||||||
|
|
||||||
// FIXME: This absolutely needs revisiting :skull:
|
|
||||||
|
|
||||||
const r15_present = rlist >> 15 & 1 == 1;
|
|
||||||
var write_to_base = true;
|
|
||||||
|
|
||||||
for (first_in_list..16) |idx| {
|
for (first_in_list..16) |idx| {
|
||||||
const i: u4 = @intCast(idx);
|
const i: u4 = @intCast(idx);
|
||||||
|
|
||||||
if (rlist >> i & 1 == 1) {
|
if (rlist >> i & 1 == 1) {
|
||||||
transfer(cpu, r15_present, i, address);
|
if (L) {
|
||||||
|
load(cpu, i, rlist, address);
|
||||||
|
} else {
|
||||||
|
store(cpu, rn, i, rlist, address, .{ .old_addr = base_addr, .new_addr = new_base_addr });
|
||||||
|
}
|
||||||
|
|
||||||
address += 4;
|
address += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (W and !L and write_to_base) {
|
if (W and !L)
|
||||||
cpu.r[rn] = new_base_addr;
|
cpu.r[rn] = new_base_addr;
|
||||||
write_to_base = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (W and L) {
|
||||||
// What happens when W is set and Rn is in the rlist? (LDM)
|
// What happens when W is set and Rn is in the rlist? (LDM)
|
||||||
//
|
//
|
||||||
// ARMv4: No writeback
|
// ARMv4: No writeback
|
||||||
// ARMv5: writeback if Rn is "the ONLY register" or NOT the LAST register
|
// ARMv5: writeback if Rn is "the ONLY register" or NOT the LAST register
|
||||||
|
|
||||||
if (W and L) {
|
if (rlist >> rn & 1 == 0) { // rn is not in rlist
|
||||||
if (rlist >> rn & 1 == 0) {
|
|
||||||
cpu.r[rn] = new_base_addr;
|
cpu.r[rn] = new_base_addr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,32 +94,60 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transfer(cpu: *Arm32, r15_present: bool, i: u5, address: u32) void {
|
fn load(cpu: *Arm32, ri: u4, rlist: u16, address: u32) void {
|
||||||
if (L) {
|
const has_r15 = rlist >> 15 & 1 == 1;
|
||||||
if (S and !r15_present) {
|
|
||||||
|
if (S and !has_r15) {
|
||||||
// Always Transfer User mode Registers
|
// Always Transfer User mode Registers
|
||||||
cpu.setUserModeRegister(i, cpu.read(u32, address));
|
cpu.setUserModeRegister(ri, cpu.read(u32, address));
|
||||||
} else {
|
} else {
|
||||||
const value = cpu.read(u32, address);
|
const value = cpu.read(u32, address);
|
||||||
|
cpu.r[ri] = value;
|
||||||
|
|
||||||
|
if (ri == 0xF) {
|
||||||
|
const mask: u32 = if (Arm32.arch == .v5te) 1 else 3;
|
||||||
|
cpu.r[ri] &= ~mask;
|
||||||
|
|
||||||
|
if (Arm32.arch == .v5te) cpu.cpsr.t.write(value & 1 == 1);
|
||||||
|
if (S) cpu.setCpsr(cpu.spsr.raw); // FIXME: before or after the reload?
|
||||||
|
|
||||||
cpu.r[i] = value;
|
|
||||||
if (i == 0xF) {
|
|
||||||
cpu.r[i] &= ~@as(u32, 3); // Align r15
|
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (S) cpu.setCpsr(cpu.spsr.raw);
|
const BaseAddrs = struct { old_addr: u32, new_addr: u32 };
|
||||||
}
|
|
||||||
}
|
fn store(cpu: *Arm32, rn: u4, ri: u4, rlist: u16, address: u32, base: BaseAddrs) void {
|
||||||
} else {
|
const value = if (S) blk: {
|
||||||
if (S) {
|
// if S == true:
|
||||||
// Always Transfer User mode Registers
|
// Always Transfer User mode Registers
|
||||||
// This happens regardless if r15 is in the list
|
// This happens regardless if r15 is in the list
|
||||||
const value = cpu.getUserModeRegister(i);
|
|
||||||
cpu.write(u32, address, value + if (i == 0xF) 4 else @as(u32, 0)); // PC is already 8 ahead to make 12
|
break :blk cpu.getUserModeRegister(ri);
|
||||||
} else {
|
} else blk: {
|
||||||
cpu.write(u32, address, cpu.r[i] + if (i == 0xF) 4 else @as(u32, 0));
|
if (ri == rn) {
|
||||||
}
|
// What happens when W is set and Rn is in the rlist? (STM)
|
||||||
|
//
|
||||||
|
// Armv4: Store OLD Base if Rb is FIRST entry in Rlist, otherwise store NEW base
|
||||||
|
// Armv5: Always store OLD Base
|
||||||
|
|
||||||
|
if (rlist >> rn & 1 == 0)
|
||||||
|
break :blk base.new_addr;
|
||||||
|
|
||||||
|
const mask = @as(u16, 1) << rn;
|
||||||
|
const is_first = @popCount(rlist & (mask - 1)) == 0;
|
||||||
|
|
||||||
|
break :blk switch (Arm32.arch) {
|
||||||
|
.v4t => if (is_first) base.old_addr else base.new_addr,
|
||||||
|
.v5te => base.old_addr,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break :blk cpu.r[ri];
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu.write(u32, address, value + if (ri == 0xF) 4 else @as(u32, 0));
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
@@ -108,26 +108,21 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
|||||||
|
|
||||||
const rm = opcode & 0xF;
|
const rm = opcode & 0xF;
|
||||||
const rs = opcode >> 8 & 0xF;
|
const rs = opcode >> 8 & 0xF;
|
||||||
const rd_lo = opcode >> 12 & 0xF;
|
const rdlo = opcode >> 12 & 0xF;
|
||||||
const rd_hi = opcode >> 16 & 0xF;
|
const rdhi = opcode >> 16 & 0xF;
|
||||||
|
|
||||||
const left: i32 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rm] >> 16 * X))));
|
const left: i64 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rm] >> 16 * X))));
|
||||||
const right: i32 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rs] >> 16 * Y))));
|
const right: i64 = @as(i16, @bitCast(@as(u16, @truncate(cpu.r[rs] >> 16 * Y))));
|
||||||
|
|
||||||
// TODO: de-clutter this lmao
|
|
||||||
const rdlo_val: i32 = @bitCast(cpu.r[rd_lo]);
|
|
||||||
const product = left * right;
|
const product = left * right;
|
||||||
|
|
||||||
// WHY DOESN'T THIS WORK????????
|
const rdhi_val: i32 = @bitCast(cpu.r[rdhi]);
|
||||||
|
const rdlo_val: i32 = @bitCast(cpu.r[rdlo]);
|
||||||
|
|
||||||
cpu.r[rd_lo] = @bitCast(rdlo_val + product);
|
const accumulate = @as(i64, rdhi_val) << 32 | rdlo_val;
|
||||||
cpu.r[rd_hi] = blk: {
|
const sum = product +% accumulate;
|
||||||
// FIXME: I think this has to do with correcting sign values?
|
|
||||||
const offset_thing: i32 = @bitCast(if (product < 0) 0xFFFF_FFFF else @as(u32, 0));
|
|
||||||
|
|
||||||
const rdhi_val: i32 = @bitCast(cpu.r[rd_hi]);
|
cpu.r[rdhi] = @bitCast(@as(i32, @truncate(sum >> 32)));
|
||||||
break :blk @bitCast(rdhi_val + offset_thing + @addWithOverflow(rdlo_val, product)[1]);
|
cpu.r[rdlo] = @bitCast(@as(i32, @truncate(sum)));
|
||||||
};
|
|
||||||
},
|
},
|
||||||
0b01_1000, 0b01_1100 => { // SMLAW<y>
|
0b01_1000, 0b01_1100 => { // SMLAW<y>
|
||||||
const Y = op >> 2 & 1;
|
const Y = op >> 2 & 1;
|
||||||
@@ -178,16 +173,6 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make generic on any integer?
|
|
||||||
inline fn carryFrom(left: i32, right: i32) u1 {
|
|
||||||
const _left: u32 = @bitCast(left);
|
|
||||||
const _right: u32 = @bitCast(right);
|
|
||||||
|
|
||||||
const sum = @as(u64, _left) + @as(u64, _right);
|
|
||||||
|
|
||||||
return @intCast(sum >> 32 & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn msr(comptime R: bool, comptime imm: bool, cpu: *Arm32, opcode: u32) void {
|
inline fn msr(comptime R: bool, comptime imm: bool, cpu: *Arm32, opcode: u32) void {
|
||||||
const field_mask: u4 = @truncate(opcode >> 16 & 0xF);
|
const field_mask: u4 = @truncate(opcode >> 16 & 0xF);
|
||||||
const rm_idx = opcode & 0xF;
|
const rm_idx = opcode & 0xF;
|
||||||
|
@@ -49,7 +49,15 @@ pub fn singleDataTransfer(comptime InstrFn: type, comptime I: bool, comptime P:
|
|||||||
if (L) {
|
if (L) {
|
||||||
// This emulates the LDR rd == rn behaviour
|
// This emulates the LDR rd == rn behaviour
|
||||||
cpu.r[rd] = result;
|
cpu.r[rd] = result;
|
||||||
if (rd == 0xF) cpu.pipe.reload(cpu);
|
|
||||||
|
if (rd == 0xF) {
|
||||||
|
if (Arm32.arch == .v5te) {
|
||||||
|
cpu.r[rd] &= ~@as(u32, 1);
|
||||||
|
cpu.cpsr.t.write(result & 1 == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.pipe.reload(cpu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
|
@@ -32,6 +32,8 @@ pub fn fmt14(comptime InstrFn: type, comptime L: bool, comptime R: bool) InstrFn
|
|||||||
if (L) {
|
if (L) {
|
||||||
const value = cpu.read(u32, address);
|
const value = cpu.read(u32, address);
|
||||||
cpu.r[15] = value & ~@as(u32, 1);
|
cpu.r[15] = value & ~@as(u32, 1);
|
||||||
|
|
||||||
|
if (Arm32.arch == .v5te) cpu.cpsr.t.write(value & 1 == 1);
|
||||||
cpu.pipe.reload(cpu);
|
cpu.pipe.reload(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu.write(u32, address, cpu.r[14]);
|
cpu.write(u32, address, cpu.r[14]);
|
||||||
|
@@ -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,3 +216,13 @@ pub fn fmt13(comptime InstrFn: type, comptime S: bool) InstrFn {
|
|||||||
}
|
}
|
||||||
}.inner;
|
}.inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bkpt(comptime InstrFn: type) InstrFn {
|
||||||
|
const Arm32 = @typeInfo(@typeInfo(@typeInfo(InstrFn).Pointer.child).Fn.params[0].type.?).Pointer.child;
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
fn inner(cpu: *Arm32, _: u16) void {
|
||||||
|
cpu.panic("TODO: handle THUMB BKPT", .{});
|
||||||
|
}
|
||||||
|
}.inner;
|
||||||
|
}
|
||||||
|
@@ -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)) {
|
||||||
@@ -210,14 +212,19 @@ pub const thumb = struct {
|
|||||||
const rd = i >> 2 & 0x7;
|
const rd = i >> 2 & 0x7;
|
||||||
break :blk processing.fmt12(InstrFn, isSP, rd);
|
break :blk processing.fmt12(InstrFn, isSP, rd);
|
||||||
},
|
},
|
||||||
0b011 => if (i >> 4 & 1 == 1) blk: {
|
0b011 => switch (@as(u2, @truncate(i >> 3 & 0x3))) {
|
||||||
|
0b10 => blk: {
|
||||||
|
// PUSH / POP
|
||||||
const L = i >> 5 & 1 == 1;
|
const L = i >> 5 & 1 == 1;
|
||||||
const R = i >> 2 & 1 == 1;
|
const R = i >> 2 & 1 == 1;
|
||||||
break :blk block_transfer.fmt14(InstrFn, L, R);
|
break :blk block_transfer.fmt14(InstrFn, L, R);
|
||||||
} else blk: {
|
},
|
||||||
|
0b11 => processing.bkpt(InstrFn),
|
||||||
|
else => blk: {
|
||||||
const S = i >> 1 & 1 == 1;
|
const S = i >> 1 & 1 == 1;
|
||||||
break :blk processing.fmt13(InstrFn, S);
|
break :blk processing.fmt13(InstrFn, S);
|
||||||
},
|
},
|
||||||
|
},
|
||||||
0b100 => blk: {
|
0b100 => blk: {
|
||||||
const L = i >> 5 & 1 == 1;
|
const L = i >> 5 & 1 == 1;
|
||||||
const rb = i >> 2 & 0x7;
|
const rb = i >> 2 & 0x7;
|
||||||
@@ -230,12 +237,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