diff --git a/src/arm/cpu/arm/psr_transfer.zig b/src/arm/cpu/arm/psr_transfer.zig index 3dbf962..0f1ae47 100644 --- a/src/arm/cpu/arm/psr_transfer.zig +++ b/src/arm/cpu/arm/psr_transfer.zig @@ -49,34 +49,38 @@ pub fn control(comptime InstrFn: type, comptime I: bool, comptime op: u6) InstrF cpu.pipe.reload(cpu); }, - 0b00_0101, 0b01_0101 => { // QADD / QSUB + 0b00_0101, 0b01_0101, 0b10_0101, 0b11_0101 => { // QADD / QDADD / QSUB / QDSUB const U = op >> 4 & 1 == 1; + const D = op >> 5 & 1 == 1; const rm = opcode & 0xF; const rd = opcode >> 12 & 0xF; const rn = opcode >> 16 & 0xF; const left: i32 = @bitCast(cpu.r[rm]); - const right: i32 = @bitCast(cpu.r[rn]); + const right: i32 = blk: { + if (!D) break :blk @bitCast(cpu.r[rn]); - cpu.r[rd] = @bitCast(if (U) left -| right else left +| right); + const ret = @mulWithOverflow(@as(i32, @bitCast(cpu.r[rn])), 2); + var product: i32 = ret[0]; - if (cpu.r[rd] == 0x8000_0000 or cpu.r[rd] == 0x7FFF_FFFF) cpu.cpsr.q.set(); - }, - 0b10_0101, 0b11_0101 => { // QDADD / QDSUB - const U = op >> 4 & 1 == 1; + if (ret[1] == 0b1) { + product = if (product < 0) std.math.maxInt(i32) else std.math.minInt(i32); + cpu.cpsr.q.set(); + } - const rm = opcode & 0xF; - const rd = opcode >> 8 & 0xF; - const rn = opcode >> 16 & 0xF; + break :blk product; + }; - const product = @as(i32, @bitCast(cpu.r[rn])) *| 2; - if (product == 0x7FFF_FFFF) cpu.cpsr.q.set(); + const ret = if (U) @subWithOverflow(left, right) else @addWithOverflow(left, right); + var result: i32 = ret[0]; - const left: i32 = @bitCast(cpu.r[rm]); + if (ret[1] == 0b1) { + result = if (result < 0) std.math.maxInt(i32) else std.math.minInt(i32); + cpu.cpsr.q.set(); + } - cpu.r[rd] = @bitCast(if (U) left -| product else left +| product); - if (cpu.r[rd] == 0x8000_0000 or cpu.r[rd] == 0x7FFF_FFFF) cpu.cpsr.q.set(); + cpu.r[rd] = @bitCast(result); }, 0b01_0111 => cpu.panic("TODO: handle BKPT", .{}), 0b00_1000, 0b00_1010, 0b00_1100, 0b00_1110 => { // SMLA