zba/src/core/cpu/thumb/alu.zig

109 lines
4.3 KiB
Zig

const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn;
const adc = @import("../arm/data_processing.zig").adc;
const sbc = @import("../arm/data_processing.zig").sbc;
const lsl = @import("../barrel_shifter.zig").lsl;
const lsr = @import("../barrel_shifter.zig").lsr;
const asr = @import("../barrel_shifter.zig").asr;
const ror = @import("../barrel_shifter.zig").ror;
pub fn fmt4(comptime op: u4) InstrFn {
return struct {
fn inner(cpu: *Arm7tdmi, _: *Bus, opcode: u16) void {
const rs = opcode >> 3 & 0x7;
const rd = opcode & 0x7;
const carry = @boolToInt(cpu.cpsr.c.read());
const op1 = cpu.r[rd];
const op2 = cpu.r[rs];
var result: u32 = undefined;
var overflow: u1 = undefined;
switch (op) {
0x0 => result = op1 & op2, // AND
0x1 => result = op1 ^ op2, // EOR
0x2 => result = lsl(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSL
0x3 => result = lsr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // LSR
0x4 => result = asr(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ASR
0x5 => result = adc(&overflow, op1, op2, carry), // ADC
0x6 => result = sbc(op1, op2, carry), // SBC
0x7 => result = ror(true, &cpu.cpsr, op1, @truncate(u8, op2)), // ROR
0x8 => result = op1 & op2, // TST
0x9 => result = 0 -% op2, // NEG
0xA => result = op1 -% op2, // CMP
0xB => {
// CMN
const tmp = @addWithOverflow(op1, op2);
result = tmp[0];
overflow = tmp[1];
},
0xC => result = op1 | op2, // ORR
0xD => result = @truncate(u32, @as(u64, op2) * @as(u64, op1)),
0xE => result = op1 & ~op2,
0xF => result = ~op2,
}
// Write to Destination Register
switch (op) {
0x8, 0xA, 0xB => {},
else => cpu.r[rd] = result,
}
// Write Flags
switch (op) {
0x0, 0x1, 0x2, 0x3, 0x4, 0x7, 0xC, 0xE, 0xF => {
// Logic Operations
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
// C set by Barrel Shifter, V is unaffected
},
0x8, 0xA => {
// Test Flags
// CMN (0xB) is handled with ADC
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
if (op == 0xA) {
// CMP specific
cpu.cpsr.c.write(op2 <= op1);
cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1);
}
},
0x5, 0xB => {
// ADC, CMN
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
cpu.cpsr.c.write(overflow == 0b1);
cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1);
},
0x6 => {
// SBC
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
const subtrahend = @as(u64, op2) -% carry +% 1;
cpu.cpsr.c.write(subtrahend <= op1);
cpu.cpsr.v.write(((op1 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1);
},
0x9 => {
// NEG
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
cpu.cpsr.c.write(op2 <= 0);
cpu.cpsr.v.write(((0 ^ result) & (~op2 ^ result)) >> 31 & 1 == 1);
},
0xD => {
// Multiplication
cpu.cpsr.n.write(result >> 31 & 1 == 1);
cpu.cpsr.z.write(result == 0);
// V is unaffected, assuming similar behaviour to ARMv4 MUL C is undefined
},
}
}
}.inner;
}