diff --git a/src/cpu.zig b/src/cpu.zig index 05b2c7f..2a23124 100644 --- a/src/cpu.zig +++ b/src/cpu.zig @@ -18,6 +18,7 @@ const branch = @import("cpu/arm/branch.zig").branch; const branchAndExchange = @import("cpu/arm/branch.zig").branchAndExchange; const softwareInterrupt = @import("cpu/arm/software_interrupt.zig").softwareInterrupt; const multiply = @import("cpu/arm/multiply.zig").multiply; +const multiplyLong = @import("cpu/arm/multiply_long.zig").multiplyLong; // THUMB Instruction Groups const format1 = @import("cpu/thumb/format1.zig").format1; @@ -533,6 +534,14 @@ fn armPopulate() [0x1000]ArmInstrFn { lut[i] = multiply(A, S); } + if (i >> 7 & 0x1F == 0b00001 and i & 0xF == 0b1001) { + const U = i >> 6 & 1 == 1; + const A = i >> 5 & 1 == 1; + const S = i >> 4 & 1 == 1; + + lut[i] = multiplyLong(U, A, S); + } + if (i >> 10 & 0x3 == 0b01) { const I = i >> 9 & 1 == 1; const P = i >> 8 & 1 == 1; diff --git a/src/cpu/arm/multiply_long.zig b/src/cpu/arm/multiply_long.zig new file mode 100644 index 0000000..89c0652 --- /dev/null +++ b/src/cpu/arm/multiply_long.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + +const Bus = @import("../../Bus.zig"); +const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; +const InstrFn = @import("../../cpu.zig").ArmInstrFn; + +pub fn multiplyLong(comptime U: bool, comptime A: bool, comptime S: bool) InstrFn { + return struct { + fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u32) void { + const rd_hi = opcode >> 16 & 0xF; + const rd_lo = opcode >> 12 & 0xF; + const rs = opcode >> 8 & 0xF; + const rm = opcode & 0xF; + + if (U) { + // Signed (WHY IS IT U THEN?) + var result: i64 = @as(i64, @bitCast(i32, cpu.r[rm])) * @as(i64, @bitCast(i32, cpu.r[rs])); + if (A) result += @bitCast(i64, @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo])); + + cpu.r[rd_hi] = @bitCast(u32, @truncate(i32, result >> 32)); + cpu.r[rd_lo] = @bitCast(u32, @truncate(i32, result)); + } else { + // Unsigned + var result: u64 = @as(u64, cpu.r[rm]) * @as(u64, cpu.r[rs]); + if (A) result += @as(u64, cpu.r[rd_hi]) << 32 | @as(u64, cpu.r[rd_lo]); + + cpu.r[rd_hi] = @truncate(u32, result >> 32); + cpu.r[rd_lo] = @truncate(u32, result); + } + + if (S) { + cpu.cpsr.z.write(cpu.r[rd_hi] == 0 and cpu.r[rd_lo] == 0); + cpu.cpsr.n.write(cpu.r[rd_hi] >> 31 & 1 == 1); + // C and V are set to meaningless values + } + } + }.inner; +}