def arm_alu(cpu: CPU, instr: int):
    """Execute a Data Processing (ALU) instruction (AND, EOR, ADD, SUB, MOV, etc.)"""
    rn = get_bits(instr, 16, 19)
    rd = get_bits(instr, 12, 15)
    shift_carry = cpu.regs.cpsr.get_carry_flag()
    set_cond_codes = get_bit(instr, 20)
    immediate = get_bit(instr, 25)
    early_advance_pc = False
    if immediate:
        op1 = cpu.regs.get(rn)
        op2 = get_bits(instr, 0, 7)
        ror_amount = get_bits(instr, 8, 11) * 2
        if ror_amount > 0:
            shift_carry = get_bit(op2, ror_amount - 1)
            op2 = ror_32(op2, ror_amount)
    else:
        rm = get_bits(instr, 0, 3)
        shift = get_bits(instr, 4, 11)
        if get_bit(shift, 0):
            cpu.advance_pc_arm()
            early_advance_pc = True
            cpu.scheduler.idle(1)
        else:
            pass
        op1 = cpu.regs.get(rn)
        op2, shift_carry = cpu.decode_and_compute_shift(cpu.regs.get(rm), shift)
    opcode = get_bits(instr, 21, 24)
    if opcode == ALUOpcode.AND:
        arm_alu_and(cpu, op1, op2, rd, set_cond_codes, shift_carry)
    elif opcode == ALUOpcode.EOR:
        arm_alu_eor(cpu, op1, op2, rd, set_cond_codes, shift_carry)
    elif opcode == ALUOpcode.SUB:
        arm_alu_sub(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.RSB:
        arm_alu_rsb(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.ADD:
        arm_alu_add(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.ADC:
        arm_alu_adc(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.SBC:
        arm_alu_sbc(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.RSC:
        arm_alu_rsc(cpu, op1, op2, rd, set_cond_codes)
    elif opcode == ALUOpcode.TST:
        arm_alu_tst(cpu, op1, op2, shift_carry)
    elif opcode == ALUOpcode.TEQ:
        arm_alu_teq(cpu, op1, op2, shift_carry)
    elif opcode == ALUOpcode.CMP:
        arm_alu_cmp(cpu, op1, op2)
    elif opcode == ALUOpcode.CMN:
        arm_alu_cmn(cpu, op1, op2)
    elif opcode == ALUOpcode.ORR:
        arm_alu_orr(cpu, op1, op2, rd, set_cond_codes, shift_carry)
    elif opcode == ALUOpcode.MOV:
        arm_alu_mov(cpu, op2, rd, set_cond_codes, shift_carry)
    elif opcode == ALUOpcode.BIC:
        arm_alu_bic(cpu, op1, op2, rd, set_cond_codes, shift_carry)
    elif opcode == ALUOpcode.MVN:
        arm_alu_mvn(cpu, op2, rd, set_cond_codes, shift_carry)
    if rd == cpu.regs.PC:
        if set_cond_codes:
            new_cpsr_reg = cpu.regs.spsr.reg
            new_cpsr_mode = cpu.regs.spsr.get_mode()
            cpu.switch_mode(new_cpsr_mode)
            cpu.regs.cpsr.reg = new_cpsr_reg
        if opcode not in [ALUOpcode.TST, ALUOpcode.TEQ, ALUOpcode.CMP, ALUOpcode.CMN]:
            cpu.flush_pipeline()
    elif not early_advance_pc:
        cpu.advance_pc_arm()

def arm_alu_and(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    cpu.regs.set(rd, arm_alu_and_impl(cpu, op1, op2, rd, set_cond_codes, shift_carry))

def arm_alu_and_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint) -> int:
    result = op1 & op2
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)
    return result

def arm_alu_eor(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    cpu.regs.set(rd, arm_alu_eor_impl(cpu, op1, op2, rd, set_cond_codes, shift_carry))

def arm_alu_eor_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint) -> int:
    result = op1 ^ op2
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)
    return result

def arm_alu_sub(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    cpu.regs.set(rd, arm_alu_sub_impl(cpu, op1, op2, rd, set_cond_codes))

def arm_alu_sub_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint) -> int:
    mask = 4294967295
    result = op1 - op2 & mask
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(op1 >= op2)
        cpu.regs.cpsr.set_overflow_flag(sign_32((op1 ^ op2) & (op1 ^ result)))
    return result

def arm_alu_rsb(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    arm_alu_sub(cpu, op2, op1, rd, set_cond_codes)

def arm_alu_add(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    cpu.regs.set(rd, arm_alu_add_impl(cpu, op1, op2, rd, set_cond_codes))

def arm_alu_add_impl(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint) -> int:
    mask = 4294967295
    result = op1 + op2 & mask
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(result < op1)
        cpu.regs.cpsr.set_overflow_flag(sign_32(~(op1 ^ op2) & (op2 ^ result)))
    return result

def arm_alu_adc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    mask = 4294967295
    temp = op1 + op2 & mask
    carry_1 = temp < op1
    result = temp + cpu.regs.cpsr.get_carry_flag() & mask
    carry_2 = result < temp
    cpu.regs.set(rd, result)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(carry_1 or carry_2)
        cpu.regs.cpsr.set_overflow_flag(sign_32(~(op1 ^ op2) & (op2 ^ result)))

def arm_alu_sbc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    mask = 4294967295
    borrow_in = 1 - cpu.regs.cpsr.get_carry_flag()
    temp = op1 - op2 & mask
    borrow_1 = op1 < op2
    result = temp - borrow_in & mask
    borrow_2 = temp < borrow_in
    cpu.regs.set(rd, result)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(not (borrow_1 or borrow_2))
        cpu.regs.cpsr.set_overflow_flag(sign_32((op1 ^ op2) & (op1 ^ result)))

def arm_alu_rsc(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint):
    arm_alu_sbc(cpu, op2, op1, rd, set_cond_codes)

def arm_alu_tst(cpu: CPU, op1: int, op2: int, shift_carry: bint):
    arm_alu_and_impl(cpu, op1, op2, 0, True, shift_carry)

def arm_alu_teq(cpu: CPU, op1: int, op2: int, shift_carry: bint):
    arm_alu_eor_impl(cpu, op1, op2, 0, True, shift_carry)

def arm_alu_cmp(cpu: CPU, op1: int, op2: int):
    arm_alu_sub_impl(cpu, op1, op2, 0, True)

def arm_alu_cmn(cpu: CPU, op1: int, op2: int):
    arm_alu_add_impl(cpu, op1, op2, 0, True)

def arm_alu_orr(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    result = op1 | op2
    cpu.regs.set(rd, result)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)

def arm_alu_mov(cpu: CPU, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    cpu.regs.set(rd, op2)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(op2))
        cpu.regs.cpsr.set_zero_flag(op2 == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)

def arm_alu_bic(cpu: CPU, op1: int, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    result = op1 & ~op2
    cpu.regs.set(rd, result)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)

def arm_alu_mvn(cpu: CPU, op2: int, rd: int, set_cond_codes: bint, shift_carry: bint):
    mask = 4294967295
    result = ~op2 & mask
    cpu.regs.set(rd, result)
    if set_cond_codes and rd != cpu.regs.PC:
        cpu.regs.cpsr.set_sign_flag(sign_32(result))
        cpu.regs.cpsr.set_zero_flag(result == 0)
        cpu.regs.cpsr.set_carry_flag(shift_carry)