Compare commits

..

5 Commits

10 changed files with 110 additions and 76 deletions

View File

@ -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);

View File

@ -111,10 +111,13 @@ pub fn blockDataTransfer(comptime InstrFn: type, comptime P: bool, comptime U: b
cpu.r[i] = value; cpu.r[i] = value;
if (i == 0xF) { if (i == 0xF) {
cpu.r[i] &= ~@as(u32, 3); // Align r15 const mask: u32 = if (Arm32.arch == .v5te) 1 else 3;
cpu.pipe.reload(cpu); cpu.r[i] &= ~mask;
if (S) cpu.setCpsr(cpu.spsr.raw); 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.pipe.reload(cpu);
} }
} }
} else { } else {

View File

@ -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 {
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); cpu.pipe.reload(cpu);
} }
}.inner; }.inner;

View File

@ -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;

View File

@ -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;

View File

@ -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]);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}, },
}, },
}; };

View File

@ -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,13 +212,18 @@ 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))) {
const L = i >> 5 & 1 == 1; 0b10 => blk: {
const R = i >> 2 & 1 == 1; // PUSH / POP
break :blk block_transfer.fmt14(InstrFn, L, R); const L = i >> 5 & 1 == 1;
} else blk: { const R = i >> 2 & 1 == 1;
const S = i >> 1 & 1 == 1; break :blk block_transfer.fmt14(InstrFn, L, R);
break :blk processing.fmt13(InstrFn, S); },
0b11 => processing.bkpt(InstrFn),
else => blk: {
const S = i >> 1 & 1 == 1;
break :blk processing.fmt13(InstrFn, S);
},
}, },
0b100 => blk: { 0b100 => blk: {
const L = i >> 5 & 1 == 1; const L = i >> 5 & 1 == 1;
@ -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);
}, },
}, },
}; };