mikage-dev/source/arm/thumb.cpp
2024-09-29 16:09:48 +02:00

498 lines
22 KiB
C++

#include "thumb.hpp"
#include <framework/exceptions.hpp>
namespace ARM {
DecodedThumbInstr DecodeThumb(ARM::ThumbInstr instr) {
if (instr.opcode_upper5 == 0b00000) {
// LSL (1) - Logical Shift Left
ARM::ARMInstr arm_instr;
arm_instr.raw = ((0b1110'0001'1011'0000ul) << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 7)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00010) {
// ASR (1) - Arithmetic Shift Right
ARM::ARMInstr arm_instr;
// arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
// | (uint32_t { instr.idx_rd_low } << 12)
// | (uint32_t { instr.immed_mid_5 } << 7)
// | (0b100ul << 3)
// | instr.idx_rm;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 7)
| (0b100ul << 4)
| instr.idx_rm;
// arm_instr.raw = (0b1110'0001'1011'0000'0000'1111'1010'0000ul); // shift_imm = 31, shift = 1
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0000'00) {
// AND
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'0001ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0001'01) {
// ADC
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'1011ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0001110) {
// ADD (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.immed_mid_5_lower3;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00110) {
// ADD (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'1001ul << 20)
| (uint32_t { instr.idx_rd_high } << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| instr.immed_low_8;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0001100) {
// ADD (3)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper8 == 0b01000100) {
// ADD (4)
ARM::ARMInstr arm_instr;
auto idx_rd = uint32_t { instr.idx_rd_low } | (uint32_t { instr.idx_rd_upperbit } << 3);
auto idx_rm = uint32_t { instr.idx_rm } | (uint32_t { instr.idx_rm_upperbit } << 3);
arm_instr.raw = (0b1110'0000'1000ul << 20)
| (idx_rd << 16)
| (idx_rd << 12)
| idx_rm;
auto ret = DecodedThumbInstr { arm_instr }.SetMayReadPC();
if (idx_rd == 15) {
ret.SetMayModifyPC();
}
return ret;
// } else if (instr.opcode_upper5 == 0b10100) {
// // ADD (5)
// ARM::ARMInstr arm_instr;
// arm_instr.raw = (0b1110'0010'1000'1111ul << 16)
// | (uint32_t { instr.idx_rd_high } << 12)
// | (0b1111ul << 8)
// | instr.immed_low_8;
// return DecodedThumbInstr { arm_instr }.SetMayReadPC();
} else if (instr.opcode_upper5 == 0b10101) {
// ADD (6)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'1000'1101ul << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| (0b1111ul << 8)
| instr.immed_low_8;
return { arm_instr };
} else if (instr.opcode_upper9 == 0b1011'0000'0) {
// ADD (7)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110001010001101110111110ul << 7)
| instr.immed_low_7;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0011'10) {
// BIC
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b111000011101ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0010'00) {
// TST
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b111000010001ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0001'111) {
// SUB (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b111000100101ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.immed_mid_5_lower3;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00111) {
// SUB (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'0101ul << 20)
| (uint32_t { instr.idx_rd_high } << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| instr.immed_low_8;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0001'101) {
// SUB (3)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'0101ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper9 == 0b1011'0000'1) {
// SUB (4)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'0100'1101'1101'1111'0ul << 7)
| instr.immed_low_7;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0001'10) {
// SBC
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'1101ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0011'00) {
// ORR
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1001ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0000'01) {
// EOR
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'0011ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00100) {
// MOV (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0011'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| instr.immed_low_8;
return { arm_instr };
} else if (instr.opcode_upper8 == 0b0100'0110) {
// MOV (3)
ARM::ARMInstr arm_instr;
auto idx_rd = uint32_t { instr.idx_rd_low } | (uint32_t { instr.idx_rd_upperbit } << 3);
auto idx_rm = uint32_t { instr.idx_rm } | (uint32_t { instr.idx_rm_upperbit } << 3);
arm_instr.raw = (0b1110'0001'1010ul << 20)
| (idx_rd << 12)
| idx_rm;
auto ret = DecodedThumbInstr { arm_instr }.SetMayReadPC();
if (idx_rd == 15) {
ret.SetMayModifyPC();
}
return ret;
} else if (instr.opcode_upper10 == 0b0100'0011'11) {
// MVN
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1111'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00000) {
// LSL (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 7)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0000'10) {
// LSL (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.idx_rm } << 8)
| 0b1'0000
| instr.idx_rd_low;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00001) {
// LSR (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 7)
| 0b10'0000
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0000'11) {
// LSR (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.idx_rm } << 8)
| 0b11'0000
| instr.idx_rd_low;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0010'01) {
// NEG
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0010'0111ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12);
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0011'01) {
// MUL
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0000'0001ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rd_low } << 8)
| (0b1001ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b1011'1010'00) {
// REV
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0110'1011'1111ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b1111'0011ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0001'11) {
// ROR
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1011'0000ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.idx_rm } << 8)
| (0b0111ul << 4)
| instr.idx_rd_low;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b1011'0010'01) {
// SXTB
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0110'1010'1111ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b0111ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b1011'0010'00) {
// SXTH
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0110'1011'1111ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b0111ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b1011'0010'10) {
// UXTH
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0110'1111'1111ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b0111ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper10 == 0b1011'0010'11) {
// UXTB
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0110'1110'1111ul << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b0111ul << 4)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b11001) {
// LDMIA
ARM::ARMInstr arm_instr;
uint32_t has_rd = (instr.register_list >> instr.idx_rd_high) & 1;
arm_instr.raw = (0b1110'1000'1001ul << 20)
| (uint32_t { !has_rd } << 21)
| (uint32_t { instr.idx_rd_high } << 16)
| instr.register_list;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b01101) {
// LDR (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 2);
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101'100) {
// LDR (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0111'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b10011) {
// LDR (4)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1001'1101ul << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| (uint32_t { instr.immed_low_8 } << 2);
return { arm_instr };
} else if (instr.opcode_upper5 == 0b01111) {
// LDRB (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1101ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.immed_mid_5;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101110) {
// LDRB (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0111'1101ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101011) {
// LDRSB
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b1101ul << 4)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101111) {
// LDRSH
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1001ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b1111ul << 4)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b10001) {
// LDRH (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1101ul<< 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5_upper2 } << 8)
| (0b1011ul << 4)
| (uint32_t { instr.immed_mid_5_lower3 } << 1);
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101'101) {
// LDRH (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1001ul<< 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b1011ul << 4)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b00101) {
// CMP (1) - Compare
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0011'0101ul << 20)
| (uint32_t { instr.idx_rd_high } << 16)
| (instr.immed_low_8);
return { arm_instr };
} else if (instr.opcode_upper10 == 0b0100'0010'10) {
// CMP (2) - Compare
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b111000010101ul << 20)
| (uint32_t { instr.idx_rd_low } << 16)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper8 == 0b0100'0101) {
// CMP (3) - Compare
ARM::ARMInstr arm_instr;
if (!instr.idx_rd_upperbit && !instr.idx_rm_upperbit) {
throw Mikage::Exceptions::Invalid("Unpredictable configuration");
}
if (instr.idx_rm_upperbit && instr.idx_rm == 7) {
throw Mikage::Exceptions::Invalid("Unpredictable configuration");
}
if (instr.idx_rd_upperbit && instr.idx_rd_low == 7) {
throw Mikage::Exceptions::NotImplemented("Cannot use PC for this instruction, yet");
}
arm_instr.raw = (0b111000010101ul << 20)
| (uint32_t { instr.idx_rd_upperbit } << 19)
| (uint32_t { instr.idx_rd_low } << 16)
| (uint32_t { instr.idx_rm_upperbit } << 3)
| instr.idx_rm;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b11000) {
// STMIA
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'1000'1010ul << 20)
| (uint32_t { instr.idx_rd_high } << 16)
| instr.register_list;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b01100) {
// STR (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1000ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5 } << 2);
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101000) {
// STR (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0111'1000ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b10010) {
// STR (3)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1000'1101ul << 16)
| (uint32_t { instr.idx_rd_high } << 12)
| (uint32_t { instr.immed_low_8 } << 2);
return { arm_instr };
} else if (instr.opcode_upper5 == 0b01110) {
// STRB (1)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0101'1100ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.immed_mid_5;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101'010) {
// STRB (2)
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0111'1100ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper5 == 0b10000) {
// STRH (1) - Store Register Halfword
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1100ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (uint32_t { instr.immed_mid_5_upper2 } << 8)
| (0b1011 << 4)
| (uint32_t { instr.immed_mid_5_lower3 } << 1);
return { arm_instr };
} else if (instr.opcode_upper7 == 0b0101001) {
// STRH (2) - Store Register Halfword
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'0001'1000ul << 20)
| (uint32_t { instr.idx_rm } << 16)
| (uint32_t { instr.idx_rd_low } << 12)
| (0b1011 << 4)
| instr.idx_rn;
return { arm_instr };
} else if (instr.opcode_upper7 == 0b1011'010) {
// PUSH - Push Multiple Registers
ARM::ARMInstr arm_instr;
arm_instr.raw = (0b1110'1001'0010'1101ul << 16)
| ((uint32_t { instr.raw } & 0x100) << 6) // bit8 denotes whether to push LR
| instr.register_list;
return { arm_instr };
} else {
// Instruction has no ARM equivalent, let the caller handle it manually
return { };
}
}
} // namespace ARM