mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-25 12:30:15 +00:00
ARM/Decoder: Initial implementation
This commit is contained in:
parent
dccadce074
commit
a8dec47797
@ -1,6 +1,8 @@
|
|||||||
set(SRCS
|
set(SRCS
|
||||||
arm/disassembler/arm_disasm.cpp
|
arm/disassembler/arm_disasm.cpp
|
||||||
arm/disassembler/load_symbol_map.cpp
|
arm/disassembler/load_symbol_map.cpp
|
||||||
|
arm/decoder/arm.cpp
|
||||||
|
arm/decoder/thumb.cpp
|
||||||
arm/dyncom/arm_dyncom.cpp
|
arm/dyncom/arm_dyncom.cpp
|
||||||
arm/dyncom/arm_dyncom_dec.cpp
|
arm/dyncom/arm_dyncom_dec.cpp
|
||||||
arm/dyncom/arm_dyncom_interpreter.cpp
|
arm/dyncom/arm_dyncom_interpreter.cpp
|
||||||
@ -129,6 +131,7 @@ set(HEADERS
|
|||||||
arm/arm_interface.h
|
arm/arm_interface.h
|
||||||
arm/disassembler/arm_disasm.h
|
arm/disassembler/arm_disasm.h
|
||||||
arm/disassembler/load_symbol_map.h
|
arm/disassembler/load_symbol_map.h
|
||||||
|
arm/decoder/decoder.h
|
||||||
arm/dyncom/arm_dyncom.h
|
arm/dyncom/arm_dyncom.h
|
||||||
arm/dyncom/arm_dyncom_dec.h
|
arm/dyncom/arm_dyncom_dec.h
|
||||||
arm/dyncom/arm_dyncom_interpreter.h
|
arm/dyncom/arm_dyncom_interpreter.h
|
||||||
|
401
src/core/arm/decoder/arm.cpp
Normal file
401
src/core/arm/decoder/arm.cpp
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#include "core/arm/decoder/decoder.h"
|
||||||
|
|
||||||
|
namespace ArmDecoder {
|
||||||
|
|
||||||
|
namespace Impl {
|
||||||
|
template<typename T, size_t N, typename... Args>
|
||||||
|
struct Call {
|
||||||
|
static void call(Visitor* v, T fn, u32* list, Args... args) {
|
||||||
|
Call<T, N - 1, Args..., u32>::call(v, fn, ++list, args..., *list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
struct Call<T, 0, Args...> {
|
||||||
|
static void call(Visitor* v, T fn, u32* list, Args... args) {
|
||||||
|
(v->*fn)(args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t N, typename T>
|
||||||
|
struct MatcherImpl : Matcher {
|
||||||
|
std::array<u32, N> masks;
|
||||||
|
std::array<size_t, N> shifts;
|
||||||
|
T fn;
|
||||||
|
virtual void visit(Visitor *v, u32 inst) override {
|
||||||
|
std::array<u32, N> values;
|
||||||
|
for (int i = 0; i<N; i++) {
|
||||||
|
values[i] = (inst & masks[i]) >> shifts[i];
|
||||||
|
}
|
||||||
|
Call<T, N>::call(v, fn, values.data());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N, typename T>
|
||||||
|
static std::unique_ptr<Matcher> MakeMatcher(const char* const format, T fn) {
|
||||||
|
ASSERT(strlen(format) == 32);
|
||||||
|
|
||||||
|
auto ret = new Impl::MatcherImpl<N, T>();
|
||||||
|
ret->fn = fn;
|
||||||
|
ret->masks.fill(0);
|
||||||
|
ret->shifts.fill(0);
|
||||||
|
|
||||||
|
char ch = 0;
|
||||||
|
int j = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (format[i] == '0') {
|
||||||
|
ret->bit_mask |= 1 << (31 - i);
|
||||||
|
ch = 0;
|
||||||
|
continue;
|
||||||
|
} else if (format[i] == '1') {
|
||||||
|
ret->bit_mask |= 1 << (31 - i);
|
||||||
|
ret->expected |= 1 << (31 - i);
|
||||||
|
ch = 0;
|
||||||
|
continue;
|
||||||
|
} else if (format[i] == 'x') {
|
||||||
|
ch = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ban some characters
|
||||||
|
ASSERT(format[i] != 'I');
|
||||||
|
ASSERT(format[i] != 'l');
|
||||||
|
ASSERT(format[i] != 'O');
|
||||||
|
ASSERT(format[i] != 'i');
|
||||||
|
|
||||||
|
if (format[i] != ch){
|
||||||
|
j++;
|
||||||
|
ASSERT(j < N);
|
||||||
|
ch = format[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->masks[j] |= 1 << (31 - i);
|
||||||
|
ret->shifts[j] = 31 - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(j == N-1);
|
||||||
|
|
||||||
|
return std::unique_ptr<Matcher>(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::array<Instruction, 246> arm_instruction_table = {{
|
||||||
|
// Barrier instructions
|
||||||
|
{ "DSB", MakeMatcher<0>("1111010101111111111100000100xxxx", &Visitor::DSB) },
|
||||||
|
{ "DMB", MakeMatcher<0>("1111010101111111111100000101xxxx", &Visitor::DMB) },
|
||||||
|
{ "ISB", MakeMatcher<0>("1111010101111111111100000110xxxx", &Visitor::ISB) },
|
||||||
|
|
||||||
|
// Branch instructions
|
||||||
|
{ "BLX (immediate)", MakeMatcher<2>("1111101hvvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::BLX_imm) },
|
||||||
|
{ "BLX (register)", MakeMatcher<2>("cccc000100101111111111110011mmmm", &Visitor::BLX_reg) },
|
||||||
|
{ "B", MakeMatcher<2>("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::B) },
|
||||||
|
{ "BL", MakeMatcher<2>("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv", &Visitor::BL) },
|
||||||
|
{ "BX", MakeMatcher<2>("cccc000100101111111111110001mmmm", &Visitor::BX) },
|
||||||
|
{ "BXJ", MakeMatcher<2>("cccc000100101111111111110010mmmm", &Visitor::BXJ) },
|
||||||
|
|
||||||
|
// Coprocessor instructions
|
||||||
|
{ "CDP2", MakeMatcher<0>("11111110xxxxxxxxxxxxxxxxxxx1xxxx", &Visitor::CDP) },
|
||||||
|
{ "CDP", MakeMatcher<0>("xxxx1110xxxxxxxxxxxxxxxxxxx0xxxx", &Visitor::CDP) },
|
||||||
|
{ "LDC2", MakeMatcher<0>("1111110xxxx1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDC) },
|
||||||
|
{ "LDC", MakeMatcher<0>("xxxx110xxxx1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDC) },
|
||||||
|
{ "MCR2", MakeMatcher<0>("xxxx1110xxx0xxxxxxxxxxxxxxx1xxxx", &Visitor::MCR) },
|
||||||
|
{ "MCR", MakeMatcher<0>("xxxx1110xxx0xxxxxxxxxxxxxxx1xxxx", &Visitor::MCR) },
|
||||||
|
{ "MCRR2", MakeMatcher<0>("111111000100xxxxxxxxxxxxxxxxxxxx", &Visitor::MCRR) },
|
||||||
|
{ "MCRR", MakeMatcher<0>("xxxx11000100xxxxxxxxxxxxxxxxxxxx", &Visitor::MCRR) },
|
||||||
|
{ "MRC2", MakeMatcher<0>("11111110xxx1xxxxxxxxxxxxxxx1xxxx", &Visitor::MRC) },
|
||||||
|
{ "MRC", MakeMatcher<0>("xxxx1110xxx1xxxxxxxxxxxxxxx1xxxx", &Visitor::MRC) },
|
||||||
|
{ "MRRC2", MakeMatcher<0>("111111000101xxxxxxxxxxxxxxxxxxxx", &Visitor::MRRC) },
|
||||||
|
{ "MRRC", MakeMatcher<0>("xxxx11000101xxxxxxxxxxxxxxxxxxxx", &Visitor::MRRC) },
|
||||||
|
{ "STC2", MakeMatcher<0>("1111110xxxx0xxxxxxxxxxxxxxxxxxxx", &Visitor::STC) },
|
||||||
|
{ "STC", MakeMatcher<0>("xxxx110xxxx0xxxxxxxxxxxxxxxxxxxx", &Visitor::STC) },
|
||||||
|
|
||||||
|
// Data Processing instructions
|
||||||
|
{ "ADC (imm)", MakeMatcher<6>("cccc0010101Snnnnddddrrrrvvvvvvvv", &Visitor::ADC_imm) },
|
||||||
|
{ "ADC (reg)", MakeMatcher<7>("cccc0000101Snnnnddddvvvvvrr0mmmm", &Visitor::ADC_reg) },
|
||||||
|
{ "ADC (rsr)", MakeMatcher<7>("cccc0000101Snnnnddddssss0rr1mmmm", &Visitor::ADC_rsr) },
|
||||||
|
{ "ADD (imm)", MakeMatcher<6>("cccc0010100Snnnnddddrrrrvvvvvvvv", &Visitor::ADD_imm) },
|
||||||
|
{ "ADD (reg)", MakeMatcher<7>("cccc0000100Snnnnddddvvvvvrr0mmmm", &Visitor::ADD_reg) },
|
||||||
|
{ "ADD (rsr)", MakeMatcher<7>("cccc0000100Snnnnddddssss0rr1mmmm", &Visitor::ADD_rsr) },
|
||||||
|
{ "AND (imm)", MakeMatcher<6>("cccc0010000Snnnnddddrrrrvvvvvvvv", &Visitor::AND_imm) },
|
||||||
|
{ "AND (reg)", MakeMatcher<7>("cccc0000000Snnnnddddvvvvvrr0mmmm", &Visitor::AND_reg) },
|
||||||
|
{ "AND (rsr)", MakeMatcher<7>("cccc0000000Snnnnddddssss0rr1mmmm", &Visitor::AND_rsr) },
|
||||||
|
{ "BIC (imm)", MakeMatcher<6>("cccc0011110Snnnnddddrrrrvvvvvvvv", &Visitor::BIC_imm) },
|
||||||
|
{ "BIC (reg)", MakeMatcher<7>("cccc0001110Snnnnddddvvvvvrr0mmmm", &Visitor::BIC_reg) },
|
||||||
|
{ "BIC (rsr)", MakeMatcher<7>("cccc0001110Snnnnddddssss0rr1mmmm", &Visitor::BIC_rsr) },
|
||||||
|
{ "CMN (imm)", MakeMatcher<4>("cccc00110111nnnn0000rrrrvvvvvvvv", &Visitor::CMN_imm) },
|
||||||
|
{ "CMN (reg)", MakeMatcher<5>("cccc00010111nnnn0000vvvvvrr0mmmm", &Visitor::CMN_reg) },
|
||||||
|
{ "CMN (rsr)", MakeMatcher<5>("cccc00010111nnnn0000ssss0rr1mmmm", &Visitor::CMN_rsr) },
|
||||||
|
{ "CMP (imm)", MakeMatcher<4>("cccc00110101nnnn0000rrrrvvvvvvvv", &Visitor::CMP_imm) },
|
||||||
|
{ "CMP (reg)", MakeMatcher<5>("cccc00010101nnnn0000vvvvvrr0mmmm", &Visitor::CMP_reg) },
|
||||||
|
{ "CMP (rsr)", MakeMatcher<5>("cccc00010101nnnn0000ssss0rr1mmmm", &Visitor::CMP_rsr) },
|
||||||
|
{ "EOR (imm)", MakeMatcher<6>("cccc0010001Snnnnddddrrrrvvvvvvvv", &Visitor::EOR_imm) },
|
||||||
|
{ "EOR (reg)", MakeMatcher<7>("cccc0000001Snnnnddddvvvvvrr0mmmm", &Visitor::EOR_reg) },
|
||||||
|
{ "EOR (rsr)", MakeMatcher<7>("cccc0000001Snnnnddddssss0rr1mmmm", &Visitor::EOR_rsr) },
|
||||||
|
{ "MOV (imm)", MakeMatcher<5>("cccc0011101S0000ddddrrrrvvvvvvvv", &Visitor::MOV_imm) },
|
||||||
|
{ "MOV (reg)", MakeMatcher<6>("cccc0001101S0000ddddvvvvvrr0mmmm", &Visitor::MOV_reg) },
|
||||||
|
{ "MOV (rsr)", MakeMatcher<6>("cccc0001101S0000ddddssss0rr1mmmm", &Visitor::MOV_rsr) },
|
||||||
|
{ "MVN (imm)", MakeMatcher<5>("cccc0011111S0000ddddrrrrvvvvvvvv", &Visitor::MVN_imm) },
|
||||||
|
{ "MVN (reg)", MakeMatcher<6>("cccc0001111S0000ddddvvvvvrr0mmmm", &Visitor::MVN_reg) },
|
||||||
|
{ "MVN (rsr)", MakeMatcher<6>("cccc0001111S0000ddddssss0rr1mmmm", &Visitor::MVN_rsr) },
|
||||||
|
{ "ORR (imm)", MakeMatcher<6>("cccc0011100Snnnnddddrrrrvvvvvvvv", &Visitor::ORR_imm) },
|
||||||
|
{ "ORR (reg)", MakeMatcher<7>("cccc0001100Snnnnddddvvvvvrr0mmmm", &Visitor::ORR_reg) },
|
||||||
|
{ "ORR (rsr)", MakeMatcher<7>("cccc0001100Snnnnddddssss0rr1mmmm", &Visitor::ORR_rsr) },
|
||||||
|
{ "RSB (imm)", MakeMatcher<6>("cccc0010011Snnnnddddrrrrvvvvvvvv", &Visitor::RSB_imm) },
|
||||||
|
{ "RSB (reg)", MakeMatcher<7>("cccc0000011Snnnnddddvvvvvrr0mmmm", &Visitor::RSB_reg) },
|
||||||
|
{ "RSB (rsr)", MakeMatcher<7>("cccc0000011Snnnnddddssss0rr1mmmm", &Visitor::RSB_rsr) },
|
||||||
|
{ "RSC (imm)", MakeMatcher<6>("cccc0010111Snnnnddddrrrrvvvvvvvv", &Visitor::RSC_imm) },
|
||||||
|
{ "RSC (reg)", MakeMatcher<7>("cccc0000111Snnnnddddvvvvvrr0mmmm", &Visitor::RSC_reg) },
|
||||||
|
{ "RSC (rsr)", MakeMatcher<7>("cccc0000111Snnnnddddssss0rr1mmmm", &Visitor::RSC_rsr) },
|
||||||
|
{ "SBC (imm)", MakeMatcher<6>("cccc0010110Snnnnddddrrrrvvvvvvvv", &Visitor::SBC_imm) },
|
||||||
|
{ "SBC (reg)", MakeMatcher<7>("cccc0000110Snnnnddddvvvvvrr0mmmm", &Visitor::SBC_reg) },
|
||||||
|
{ "SBC (rsr)", MakeMatcher<7>("cccc0000110Snnnnddddssss0rr1mmmm", &Visitor::SBC_rsr) },
|
||||||
|
{ "SUB (imm)", MakeMatcher<6>("cccc0010010Snnnnddddrrrrvvvvvvvv", &Visitor::SUB_imm) },
|
||||||
|
{ "SUB (reg)", MakeMatcher<7>("cccc0000010Snnnnddddvvvvvrr0mmmm", &Visitor::SUB_reg) },
|
||||||
|
{ "SUB (rsr)", MakeMatcher<7>("cccc0000010Snnnnddddssss0rr1mmmm", &Visitor::SUB_rsr) },
|
||||||
|
{ "TEQ (imm)", MakeMatcher<4>("cccc00110011nnnn0000rrrrvvvvvvvv", &Visitor::TEQ_imm) },
|
||||||
|
{ "TEQ (reg)", MakeMatcher<5>("cccc00010011nnnn0000vvvvvrr0mmmm", &Visitor::TEQ_reg) },
|
||||||
|
{ "TEQ (rsr)", MakeMatcher<5>("cccc00010011nnnn0000ssss0rr1mmmm", &Visitor::TEQ_rsr) },
|
||||||
|
{ "TST (imm)", MakeMatcher<4>("cccc00110001nnnn0000rrrrvvvvvvvv", &Visitor::TST_imm) },
|
||||||
|
{ "TST (reg)", MakeMatcher<5>("cccc00010001nnnn0000vvvvvrr0mmmm", &Visitor::TST_reg) },
|
||||||
|
{ "TST (rsr)", MakeMatcher<5>("cccc00010001nnnn0000ssss0rr1mmmm", &Visitor::TST_rsr) },
|
||||||
|
|
||||||
|
// Exception Generating instructions
|
||||||
|
{ "BKPT", MakeMatcher<0>("xxxx00010010xxxxxxxxxxxx0111xxxx", &Visitor::BKPT) },
|
||||||
|
{ "HVC", MakeMatcher<0>("xxxx00010100xxxxxxxxxxxx0111xxxx", &Visitor::HVC) },
|
||||||
|
{ "SMC", MakeMatcher<0>("xxxx000101100000000000000111xxxx", &Visitor::SMC) },
|
||||||
|
{ "SVC", MakeMatcher<0>("xxxx1111xxxxxxxxxxxxxxxxxxxxxxxx", &Visitor::SVC) },
|
||||||
|
{ "UDF", MakeMatcher<0>("111001111111xxxxxxxxxxxx1111xxxx", &Visitor::UDF) },
|
||||||
|
|
||||||
|
// Extension instructions
|
||||||
|
{ "SXTB", MakeMatcher<0>("xxxx011010101111xxxxxx000111xxxx", &Visitor::SXTB) },
|
||||||
|
{ "SXTB16", MakeMatcher<0>("xxxx011010001111xxxxxx000111xxxx", &Visitor::SXTB16) },
|
||||||
|
{ "SXTH", MakeMatcher<0>("xxxx011010111111xxxxxx000111xxxx", &Visitor::SXTH) },
|
||||||
|
{ "SXTAB", MakeMatcher<0>("xxxx01101010xxxxxxxxxx000111xxxx", &Visitor::SXTAB) },
|
||||||
|
{ "SXTAB16", MakeMatcher<0>("xxxx01101000xxxxxxxxxx000111xxxx", &Visitor::SXTAB16) },
|
||||||
|
{ "SXTAH", MakeMatcher<0>("xxxx01101011xxxxxxxxxx000111xxxx", &Visitor::SXTAH) },
|
||||||
|
{ "UXTB", MakeMatcher<0>("xxxx011011101111xxxxxx000111xxxx", &Visitor::UXTB) },
|
||||||
|
{ "UXTB16", MakeMatcher<0>("xxxx011011001111xxxxxx000111xxxx", &Visitor::UXTB16) },
|
||||||
|
{ "UXTH", MakeMatcher<0>("xxxx011011111111xxxxxx000111xxxx", &Visitor::UXTH) },
|
||||||
|
{ "UXTAB", MakeMatcher<0>("xxxx01101110xxxxxxxxxx000111xxxx", &Visitor::UXTAB) },
|
||||||
|
{ "UXTAB16", MakeMatcher<0>("xxxx01101100xxxxxxxxxx000111xxxx", &Visitor::UXTAB16) },
|
||||||
|
{ "UXTAH", MakeMatcher<0>("xxxx01101111xxxxxxxxxx000111xxxx", &Visitor::UXTAH) },
|
||||||
|
|
||||||
|
// Hint instructions
|
||||||
|
{ "DBG", MakeMatcher<0>("xxxx001100100000111100001111xxxx", &Visitor::DBG) },
|
||||||
|
{ "PLD (imm)", MakeMatcher<0>("11110101x101xxxx1111xxxxxxxxxxxx", &Visitor::PLD) },
|
||||||
|
{ "PLD (lit)", MakeMatcher<0>("11110101010111111111xxxxxxxxxxxx", &Visitor::PLD) },
|
||||||
|
{ "PLD (reg)", MakeMatcher<0>("11110111x001xxxx1111xxxxxxx0xxxx", &Visitor::PLD) },
|
||||||
|
{ "PLDW (imm)", MakeMatcher<0>("11110101x001xxxx1111xxxxxxxxxxxx", &Visitor::PLD) },
|
||||||
|
{ "PLDW (reg)", MakeMatcher<0>("11110111x101xxxx1111xxxxxxx0xxxx", &Visitor::PLD) },
|
||||||
|
{ "PLI (imm lit)", MakeMatcher<0>("11110100x101xxxx1111xxxxxxxxxxxx", &Visitor::PLI) },
|
||||||
|
{ "PLI (reg)", MakeMatcher<0>("11110110x101xxxx1111xxxxxxx0xxxx", &Visitor::PLI) },
|
||||||
|
|
||||||
|
// Synchronization Primitive instructions
|
||||||
|
{ "CLREX", MakeMatcher<0>("11110101011111111111000000011111", &Visitor::CLREX) },
|
||||||
|
{ "LDREX", MakeMatcher<0>("xxxx00011001xxxxxxxx111110011111", &Visitor::LDREX) },
|
||||||
|
{ "LDREXB", MakeMatcher<0>("xxxx00011101xxxxxxxx111110011111", &Visitor::LDREXB) },
|
||||||
|
{ "LDREXD", MakeMatcher<0>("xxxx00011011xxxxxxxx111110011111", &Visitor::LDREXD) },
|
||||||
|
{ "LDREXH", MakeMatcher<0>("xxxx00011111xxxxxxxx111110011111", &Visitor::LDREXH) },
|
||||||
|
{ "STREX", MakeMatcher<0>("xxxx00011000xxxxxxxx11111001xxxx", &Visitor::STREX) },
|
||||||
|
{ "STREXB", MakeMatcher<0>("xxxx00011100xxxxxxxx11111001xxxx", &Visitor::STREXB) },
|
||||||
|
{ "STREXD", MakeMatcher<0>("xxxx00011010xxxxxxxx11111001xxxx", &Visitor::STREXD) },
|
||||||
|
{ "STREXH", MakeMatcher<0>("xxxx00011110xxxxxxxx11111001xxxx", &Visitor::STREXH) },
|
||||||
|
{ "SWP", MakeMatcher<0>("xxxx00010x00xxxxxxxx00001001xxxx", &Visitor::SWP) },
|
||||||
|
|
||||||
|
// Load/Store instructions
|
||||||
|
{ "LDR (imm)", MakeMatcher<0>("xxxx010xx0x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDR_imm) },
|
||||||
|
{ "LDR (reg)", MakeMatcher<0>("xxxx011xx0x1xxxxxxxxxxxxxxx0xxxx", &Visitor::LDR_reg) },
|
||||||
|
{ "LDRB (imm)", MakeMatcher<0>("xxxx010xx1x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDRB_imm) },
|
||||||
|
{ "LDRB (reg)", MakeMatcher<0>("xxxx011xx1x1xxxxxxxxxxxxxxx0xxxx", &Visitor::LDRB_reg) },
|
||||||
|
{ "LDRBT (A1)", MakeMatcher<0>("xxxx0100x111xxxxxxxxxxxxxxxxxxxx", &Visitor::LDRBT) },
|
||||||
|
{ "LDRBT (A2)", MakeMatcher<0>("xxxx0110x111xxxxxxxxxxxxxxx0xxxx", &Visitor::LDRBT) },
|
||||||
|
{ "LDRD (imm)", MakeMatcher<0>("xxxx000xx1x0xxxxxxxxxxxx1101xxxx", &Visitor::LDRD_imm) },
|
||||||
|
{ "LDRD (reg)", MakeMatcher<0>("xxxx000xx0x0xxxxxxxx00001101xxxx", &Visitor::LDRD_reg) },
|
||||||
|
{ "LDRH (imm)", MakeMatcher<0>("xxxx000xx1x1xxxxxxxxxxxx1011xxxx", &Visitor::LDRH_imm) },
|
||||||
|
{ "LDRH (reg)", MakeMatcher<0>("xxxx000xx0x1xxxxxxxx00001011xxxx", &Visitor::LDRH_reg) },
|
||||||
|
{ "LDRHT (A1)", MakeMatcher<0>("xxxx0000x111xxxxxxxxxxxx1011xxxx", &Visitor::LDRHT) },
|
||||||
|
{ "LDRHT (A2)", MakeMatcher<0>("xxxx0000x011xxxxxxxx00001011xxxx", &Visitor::LDRHT) },
|
||||||
|
{ "LDRSB (imm)", MakeMatcher<0>("xxxx000xx1x1xxxxxxxxxxxx1101xxxx", &Visitor::LDRSB_imm) },
|
||||||
|
{ "LDRSB (reg)", MakeMatcher<0>("xxxx000xx0x1xxxxxxxx00001101xxxx", &Visitor::LDRSB_reg) },
|
||||||
|
{ "LDRSBT (A1)", MakeMatcher<0>("xxxx0000x111xxxxxxxxxxxx1101xxxx", &Visitor::LDRSBT) },
|
||||||
|
{ "LDRSBT (A2)", MakeMatcher<0>("xxxx0000x011xxxxxxxx00001101xxxx", &Visitor::LDRSBT) },
|
||||||
|
{ "LDRSH (imm)", MakeMatcher<0>("xxxx000xx1x1xxxxxxxxxxxx1111xxxx", &Visitor::LDRSH_imm) },
|
||||||
|
{ "LDRSH (reg)", MakeMatcher<0>("xxxx000xx0x1xxxxxxxx00001111xxxx", &Visitor::LDRSH_reg) },
|
||||||
|
{ "LDRSHT (A1)", MakeMatcher<0>("xxxx0000x111xxxxxxxxxxxx1111xxxx", &Visitor::LDRSHT) },
|
||||||
|
{ "LDRSHT (A2)", MakeMatcher<0>("xxxx0000x011xxxxxxxx00001111xxxx", &Visitor::LDRSHT) },
|
||||||
|
{ "LDRT (A1)", MakeMatcher<0>("xxxx0100x011xxxxxxxxxxxxxxxxxxxx", &Visitor::LDRT) },
|
||||||
|
{ "LDRT (A2)", MakeMatcher<0>("xxxx0110x011xxxxxxxxxxxxxxx0xxxx", &Visitor::LDRT) },
|
||||||
|
{ "STR (imm)", MakeMatcher<0>("xxxx010xx0x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STR_imm) },
|
||||||
|
{ "STR (reg)", MakeMatcher<0>("xxxx011xx0x0xxxxxxxxxxxxxxx0xxxx", &Visitor::STR_reg) },
|
||||||
|
{ "STRB (imm)", MakeMatcher<0>("xxxx010xx1x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STRB_imm) },
|
||||||
|
{ "STRB (reg)", MakeMatcher<0>("xxxx011xx1x0xxxxxxxxxxxxxxx0xxxx", &Visitor::STRB_reg) },
|
||||||
|
{ "STRBT (A1)", MakeMatcher<0>("xxxx0100x110xxxxxxxxxxxxxxxxxxxx", &Visitor::STRBT) },
|
||||||
|
{ "STRBT (A2)", MakeMatcher<0>("xxxx0110x110xxxxxxxxxxxxxxx0xxxx", &Visitor::STRBT) },
|
||||||
|
{ "STRD (imm)", MakeMatcher<0>("xxxx000xx1x0xxxxxxxxxxxx1111xxxx", &Visitor::STRD_imm) },
|
||||||
|
{ "STRD (reg)", MakeMatcher<0>("xxxx000xx0x0xxxxxxxx00001111xxxx", &Visitor::STRD_reg) },
|
||||||
|
{ "STRH (imm)", MakeMatcher<0>("xxxx000xx1x0xxxxxxxxxxxx1011xxxx", &Visitor::STRH_imm) },
|
||||||
|
{ "STRH (reg)", MakeMatcher<0>("xxxx000xx0x0xxxxxxxx00001011xxxx", &Visitor::STRH_reg) },
|
||||||
|
{ "STRHT (A1)", MakeMatcher<0>("xxxx0000x110xxxxxxxxxxxx1011xxxx", &Visitor::STRHT) },
|
||||||
|
{ "STRHT (A2)", MakeMatcher<0>("xxxx0000x010xxxxxxxx00001011xxxx", &Visitor::STRHT) },
|
||||||
|
{ "STRT (A1)", MakeMatcher<0>("xxxx0100x010xxxxxxxxxxxxxxxxxxxx", &Visitor::STRT) },
|
||||||
|
{ "STRT (A2)", MakeMatcher<0>("xxxx0110x010xxxxxxxxxxxxxxx0xxxx", &Visitor::STRT) },
|
||||||
|
|
||||||
|
// Load/Store Multiple instructions
|
||||||
|
{ "LDMIA/LDMFD", MakeMatcher<0>("xxxx100010x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "LDMDA/LDMFA", MakeMatcher<0>("xxxx100000x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "LDMDB/LDMEA", MakeMatcher<0>("xxxx100100x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "LDMIB/LDMED", MakeMatcher<0>("xxxx100110x1xxxxxxxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "LDM (exc ret)", MakeMatcher<0>("xxxx100xx1x1xxxx1xxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "LDM (usr reg)", MakeMatcher<0>("xxxx100xx1x1xxxx0xxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "POP", MakeMatcher<0>("xxxx100010111101xxxxxxxxxxxxxxxx", &Visitor::LDM) },
|
||||||
|
{ "POP", MakeMatcher<0>("xxxx010010011101xxxx000000000100", &Visitor::LDM) },
|
||||||
|
{ "PUSH", MakeMatcher<0>("xxxx100100101101xxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
{ "PUSH", MakeMatcher<0>("xxxx010100101101xxxx000000000100", &Visitor::STM) },
|
||||||
|
{ "STMIA/STMEA", MakeMatcher<0>("xxxx100010x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
{ "STMDA/STMED", MakeMatcher<0>("xxxx100000x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
{ "STMDB/STMFD", MakeMatcher<0>("xxxx100100x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
{ "STMIB/STMFA", MakeMatcher<0>("xxxx100110x0xxxxxxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
{ "STMIB (usr reg)", MakeMatcher<0>("xxxx100xx100xxxxxxxxxxxxxxxxxxxx", &Visitor::STM) },
|
||||||
|
|
||||||
|
// Miscellaneous instructions
|
||||||
|
{ "CLZ", MakeMatcher<0>("xxxx000101101111xxxx11110001xxxx", &Visitor::CLZ) },
|
||||||
|
{ "NOP", MakeMatcher<0>("xxxx0011001000001111000000000000", &Visitor::NOP) },
|
||||||
|
{ "SEL", MakeMatcher<0>("xxxx01101000xxxxxxxx11111011xxxx", &Visitor::SEL) },
|
||||||
|
|
||||||
|
// Unsigned Sum of Absolute Differences instructions
|
||||||
|
{ "USAD8", MakeMatcher<0>("xxxx01111000xxxx1111xxxx0001xxxx", &Visitor::USAD8) },
|
||||||
|
{ "USADA8", MakeMatcher<0>("xxxx01111000xxxxxxxxxxxx0001xxxx", &Visitor::USADA8) },
|
||||||
|
|
||||||
|
// Packing instructions
|
||||||
|
{ "PKH", MakeMatcher<0>("xxxx01101000xxxxxxxxxxxxxx01xxxx", &Visitor::PKH) },
|
||||||
|
|
||||||
|
// Reversal instructions
|
||||||
|
{ "RBIT", MakeMatcher<0>("xxxx011011111111xxxx11110011xxxx", &Visitor::RBIT) },
|
||||||
|
{ "REV", MakeMatcher<0>("xxxx011010111111xxxx11110011xxxx", &Visitor::REV) },
|
||||||
|
{ "REV16", MakeMatcher<0>("xxxx011010111111xxxx11111011xxxx", &Visitor::REV16) },
|
||||||
|
{ "REVSH", MakeMatcher<0>("xxxx011011111111xxxx11111011xxxx", &Visitor::REVSH) },
|
||||||
|
|
||||||
|
// Saturation instructions
|
||||||
|
{ "SSAT", MakeMatcher<0>("xxxx0110101xxxxxxxxxxxxxxx01xxxx", &Visitor::SSAT) },
|
||||||
|
{ "SSAT16", MakeMatcher<0>("xxxx01101010xxxxxxxx11110011xxxx", &Visitor::SSAT16) },
|
||||||
|
{ "USAT", MakeMatcher<0>("xxxx0110111xxxxxxxxxxxxxxx01xxxx", &Visitor::USAT) },
|
||||||
|
{ "USAT16", MakeMatcher<0>("xxxx01101110xxxxxxxx11110011xxxx", &Visitor::USAT16) },
|
||||||
|
|
||||||
|
// Multiply (Normal) instructions
|
||||||
|
{ "MLA", MakeMatcher<0>("xxxx0000001xxxxxxxxxxxxx1001xxxx", &Visitor::MLA) },
|
||||||
|
{ "MLS", MakeMatcher<0>("xxxx00000110xxxxxxxxxxxx1001xxxx", &Visitor::MLS) },
|
||||||
|
{ "MUL", MakeMatcher<0>("xxxx0000000xxxxx0000xxxx1001xxxx", &Visitor::MUL) },
|
||||||
|
|
||||||
|
// Multiply (Long) instructions
|
||||||
|
{ "SMLAL", MakeMatcher<0>("xxxx0000111xxxxxxxxxxxxx1001xxxx", &Visitor::SMLAL) },
|
||||||
|
{ "SMULL", MakeMatcher<0>("xxxx0000110xxxxxxxxxxxxx1001xxxx", &Visitor::SMULL) },
|
||||||
|
{ "UMAAL", MakeMatcher<0>("xxxx00000100xxxxxxxxxxxx1001xxxx", &Visitor::UMAAL) },
|
||||||
|
{ "UMLAL", MakeMatcher<0>("xxxx0000101xxxxxxxxxxxxx1001xxxx", &Visitor::UMLAL) },
|
||||||
|
{ "UMULL", MakeMatcher<0>("xxxx0000100xxxxxxxxxxxxx1001xxxx", &Visitor::UMULL) },
|
||||||
|
|
||||||
|
// Multiply (Halfword) instructions
|
||||||
|
{ "SMLALXY", MakeMatcher<0>("xxxx00010100xxxxxxxxxxxx1xx0xxxx", &Visitor::SMLALxy) },
|
||||||
|
{ "SMLAXY", MakeMatcher<0>("xxxx00010000xxxxxxxxxxxx1xx0xxxx", &Visitor::SMLAxy) },
|
||||||
|
{ "SMULXY", MakeMatcher<0>("xxxx00010110xxxx0000xxxx1xx0xxxx", &Visitor::SMULxy) },
|
||||||
|
|
||||||
|
// Multiply (Word by Halfword) instructions
|
||||||
|
{ "SMLAWY", MakeMatcher<0>("xxxx00010010xxxxxxxxxxxx1x00xxxx", &Visitor::SMLAWy) },
|
||||||
|
{ "SMULWY", MakeMatcher<0>("xxxx00010010xxxx0000xxxx1x10xxxx", &Visitor::SMULWy) },
|
||||||
|
|
||||||
|
// Multiply (Most Significant Word) instructions
|
||||||
|
{ "SMMUL", MakeMatcher<0>("xxxx01110101xxxx1111xxxx00x1xxxx", &Visitor::SMMUL) },
|
||||||
|
{ "SMMLA", MakeMatcher<0>("xxxx01110101xxxxxxxxxxxx00x1xxxx", &Visitor::SMMLA) },
|
||||||
|
{ "SMMLS", MakeMatcher<0>("xxxx01110101xxxxxxxxxxxx11x1xxxx", &Visitor::SMMLS) },
|
||||||
|
|
||||||
|
// Multiply (Dual) instructions
|
||||||
|
{ "SMLAD", MakeMatcher<0>("xxxx01110000xxxxxxxxxxxx00x1xxxx", &Visitor::SMLAD) },
|
||||||
|
{ "SMLALD", MakeMatcher<0>("xxxx01110100xxxxxxxxxxxx00x1xxxx", &Visitor::SMLALD) },
|
||||||
|
{ "SMLSD", MakeMatcher<0>("xxxx01110000xxxxxxxxxxxx01x1xxxx", &Visitor::SMLSD) },
|
||||||
|
{ "SMLSLD", MakeMatcher<0>("xxxx01110100xxxxxxxxxxxx01x1xxxx", &Visitor::SMLSLD) },
|
||||||
|
{ "SMUAD", MakeMatcher<0>("xxxx01110000xxxx1111xxxx00x1xxxx", &Visitor::SMUAD) },
|
||||||
|
{ "SMUSD", MakeMatcher<0>("xxxx01110000xxxx1111xxxx01x1xxxx", &Visitor::SMUSD) },
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Modulo) instructions
|
||||||
|
{ "SADD8", MakeMatcher<0>("xxxx01100001xxxxxxxx11111001xxxx", &Visitor::SADD8) },
|
||||||
|
{ "SADD16", MakeMatcher<0>("xxxx01100001xxxxxxxx11110001xxxx", &Visitor::SADD16) },
|
||||||
|
{ "SASX", MakeMatcher<0>("xxxx01100001xxxxxxxx11110011xxxx", &Visitor::SASX) },
|
||||||
|
{ "SSAX", MakeMatcher<0>("xxxx01100001xxxxxxxx11110101xxxx", &Visitor::SSAX) },
|
||||||
|
{ "SSUB8", MakeMatcher<0>("xxxx01100001xxxxxxxx11111111xxxx", &Visitor::SSUB8) },
|
||||||
|
{ "SSUB16", MakeMatcher<0>("xxxx01100001xxxxxxxx11110111xxxx", &Visitor::SSUB16) },
|
||||||
|
{ "UADD8", MakeMatcher<0>("xxxx01100101xxxxxxxx11111001xxxx", &Visitor::UADD8) },
|
||||||
|
{ "UADD16", MakeMatcher<0>("xxxx01100101xxxxxxxx11110001xxxx", &Visitor::UADD16) },
|
||||||
|
{ "UASX", MakeMatcher<0>("xxxx01100101xxxxxxxx11110011xxxx", &Visitor::UASX) },
|
||||||
|
{ "USAX", MakeMatcher<0>("xxxx01100101xxxxxxxx11110101xxxx", &Visitor::USAX) },
|
||||||
|
{ "USUB8", MakeMatcher<0>("xxxx01100101xxxxxxxx11111111xxxx", &Visitor::USUB8) },
|
||||||
|
{ "USUB16", MakeMatcher<0>("xxxx01100101xxxxxxxx11110111xxxx", &Visitor::USUB16) },
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Saturating) instructions
|
||||||
|
{ "QADD8", MakeMatcher<0>("xxxx01100010xxxxxxxx11111001xxxx", &Visitor::QADD8) },
|
||||||
|
{ "QADD16", MakeMatcher<0>("xxxx01100010xxxxxxxx11110001xxxx", &Visitor::QADD16) },
|
||||||
|
{ "QASX", MakeMatcher<0>("xxxx01100010xxxxxxxx11110011xxxx", &Visitor::QASX) },
|
||||||
|
{ "QSAX", MakeMatcher<0>("xxxx01100010xxxxxxxx11110101xxxx", &Visitor::QSAX) },
|
||||||
|
{ "QSUB8", MakeMatcher<0>("xxxx01100010xxxxxxxx11111111xxxx", &Visitor::QSUB8) },
|
||||||
|
{ "QSUB16", MakeMatcher<0>("xxxx01100010xxxxxxxx11110111xxxx", &Visitor::QSUB16) },
|
||||||
|
{ "UQADD8", MakeMatcher<0>("xxxx01100110xxxxxxxx11111001xxxx", &Visitor::UQADD8) },
|
||||||
|
{ "UQADD16", MakeMatcher<0>("xxxx01100110xxxxxxxx11110001xxxx", &Visitor::UQADD16) },
|
||||||
|
{ "UQASX", MakeMatcher<0>("xxxx01100110xxxxxxxx11110011xxxx", &Visitor::UQASX) },
|
||||||
|
{ "UQSAX", MakeMatcher<0>("xxxx01100110xxxxxxxx11110101xxxx", &Visitor::UQSAX) },
|
||||||
|
{ "UQSUB8", MakeMatcher<0>("xxxx01100110xxxxxxxx11111111xxxx", &Visitor::UQSUB8) },
|
||||||
|
{ "UQSUB16", MakeMatcher<0>("xxxx01100110xxxxxxxx11110111xxxx", &Visitor::UQSUB16) },
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Halving) instructions
|
||||||
|
{ "SHADD8", MakeMatcher<0>("xxxx01100011xxxxxxxx11111001xxxx", &Visitor::SHADD8) },
|
||||||
|
{ "SHADD16", MakeMatcher<0>("xxxx01100011xxxxxxxx11110001xxxx", &Visitor::SHADD16) },
|
||||||
|
{ "SHASX", MakeMatcher<0>("xxxx01100011xxxxxxxx11110011xxxx", &Visitor::SHASX) },
|
||||||
|
{ "SHSAX", MakeMatcher<0>("xxxx01100011xxxxxxxx11110101xxxx", &Visitor::SHSAX) },
|
||||||
|
{ "SHSUB8", MakeMatcher<0>("xxxx01100011xxxxxxxx11111111xxxx", &Visitor::SHSUB8) },
|
||||||
|
{ "SHSUB16", MakeMatcher<0>("xxxx01100011xxxxxxxx11110111xxxx", &Visitor::SHSUB16) },
|
||||||
|
{ "UHADD8", MakeMatcher<0>("xxxx01100111xxxxxxxx11111001xxxx", &Visitor::UHADD8) },
|
||||||
|
{ "UHADD16", MakeMatcher<0>("xxxx01100111xxxxxxxx11110001xxxx", &Visitor::UHADD16) },
|
||||||
|
{ "UHASX", MakeMatcher<0>("xxxx01100111xxxxxxxx11110011xxxx", &Visitor::UHASX) },
|
||||||
|
{ "UHSAX", MakeMatcher<0>("xxxx01100111xxxxxxxx11110101xxxx", &Visitor::UHSAX) },
|
||||||
|
{ "UHSUB8", MakeMatcher<0>("xxxx01100111xxxxxxxx11111111xxxx", &Visitor::UHSUB8) },
|
||||||
|
{ "UHSUB16", MakeMatcher<0>("xxxx01100111xxxxxxxx11110111xxxx", &Visitor::UHSUB16) },
|
||||||
|
|
||||||
|
// Saturated Add/Subtract instructions
|
||||||
|
{ "QADD", MakeMatcher<0>("xxxx00010000xxxxxxxx00000101xxxx", &Visitor::QADD) },
|
||||||
|
{ "QSUB", MakeMatcher<0>("xxxx00010010xxxxxxxx00000101xxxx", &Visitor::QSUB) },
|
||||||
|
{ "QDADD", MakeMatcher<0>("xxxx00010100xxxxxxxx00000101xxxx", &Visitor::QDADD) },
|
||||||
|
{ "QDSUB", MakeMatcher<0>("xxxx00010110xxxxxxxx00000101xxxx", &Visitor::QDSUB) },
|
||||||
|
|
||||||
|
// Status Register Access instructions
|
||||||
|
{ "CPS", MakeMatcher<0>("xxxx00010000xxx00000000xxx0xxxxx", &Visitor::CPS) },
|
||||||
|
{ "ERET", MakeMatcher<0>("xxxx0001011000000000000001101110", &Visitor::ERET) },
|
||||||
|
{ "SETEND", MakeMatcher<0>("1111000100000001000000x000000000", &Visitor::SETEND) },
|
||||||
|
{ "MRS", MakeMatcher<0>("xxxx000100001111xxxx000000000000", &Visitor::MRS) },
|
||||||
|
{ "MRS (banked)", MakeMatcher<0>("xxxx00010x00xxxxxxxx001x00000000", &Visitor::MRS) },
|
||||||
|
{ "MRS (system)", MakeMatcher<0>("xxxx00010x001111xxxx000000000000", &Visitor::MRS) },
|
||||||
|
{ "MSR (imm)", MakeMatcher<0>("xxxx00110010xx001111xxxxxxxxxxxx", &Visitor::MSR) },
|
||||||
|
{ "MSR (reg)", MakeMatcher<0>("xxxx00010010xx00111100000000xxxx", &Visitor::MSR) },
|
||||||
|
{ "MSR (banked)", MakeMatcher<0>("xxxx00010x10xxxx1111001x0000xxxx", &Visitor::MSR) },
|
||||||
|
{ "MSR (imm special)", MakeMatcher<0>("xxxx00110x10xxxx1111xxxxxxxxxxxx", &Visitor::MSR) },
|
||||||
|
{ "MSR (reg special)", MakeMatcher<0>("xxxx00010x10xxxx111100000000xxxx", &Visitor::MSR) },
|
||||||
|
{ "RFE", MakeMatcher<0>("xxxx0001101x0000xxxxxxxxx110xxxx", &Visitor::RFE) },
|
||||||
|
{ "SRS", MakeMatcher<0>("0000011xx0x00000000000000001xxxx", &Visitor::SRS) },
|
||||||
|
}};
|
||||||
|
|
||||||
|
const Instruction& DecodeArm(u32 i) {
|
||||||
|
return *std::find_if(arm_instruction_table.cbegin(), arm_instruction_table.cend(), [i](const auto& instruction) {
|
||||||
|
return instruction.Match(i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
327
src/core/arm/decoder/decoder.h
Normal file
327
src/core/arm/decoder/decoder.h
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
|
||||||
|
namespace ArmDecoder {
|
||||||
|
|
||||||
|
// This is a generic ARMv6 decoder using double dispatch.
|
||||||
|
|
||||||
|
class Instruction;
|
||||||
|
class Visitor;
|
||||||
|
|
||||||
|
const Instruction& DecodeArm(u32 instruction);
|
||||||
|
const Instruction& DecodeThumb(u16 instruction);
|
||||||
|
|
||||||
|
struct Matcher {
|
||||||
|
u32 bit_mask;
|
||||||
|
u32 expected;
|
||||||
|
FORCE_INLINE bool Match(u32 x) const {
|
||||||
|
return (x & bit_mask) == expected;
|
||||||
|
}
|
||||||
|
virtual void visit(Visitor* v, u32 inst) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Instruction {
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Matcher> matcher;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Instruction(char* name, std::unique_ptr<Matcher> matcher) : name(name), matcher(std::move(matcher)) {}
|
||||||
|
|
||||||
|
const char* const name;
|
||||||
|
|
||||||
|
FORCE_INLINE bool Match(u32 instruction) const {
|
||||||
|
return (instruction & matcher->bit_mask) == matcher->expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
FORCE_INLINE void Visit(Visitor* v, u32 instruction) const {
|
||||||
|
matcher->visit(v, instruction);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Cond = u8;
|
||||||
|
using Imm5 = u32;
|
||||||
|
using Imm8 = u32;
|
||||||
|
using Imm11 = u32;
|
||||||
|
using Imm24 = u32;
|
||||||
|
using Register = int;
|
||||||
|
using ShiftType = int;
|
||||||
|
|
||||||
|
class Visitor {
|
||||||
|
public:
|
||||||
|
virtual ~Visitor() = default;
|
||||||
|
|
||||||
|
// Barrier instructions
|
||||||
|
virtual void DMB() = 0;
|
||||||
|
virtual void DSB() = 0;
|
||||||
|
virtual void ISB() = 0;
|
||||||
|
|
||||||
|
// Branch instructions
|
||||||
|
virtual void B(Cond cond, Imm24 imm24) = 0;
|
||||||
|
virtual void BL(Cond cond, Imm24 imm24) = 0;
|
||||||
|
virtual void BLX_imm(bool H, Imm24 imm24) = 0;
|
||||||
|
virtual void BLX_reg(Cond cond, Register Rm) = 0;
|
||||||
|
virtual void BX(Cond cond, Register Rm) = 0;
|
||||||
|
virtual void BXJ(Cond cond, Register Rm) = 0;
|
||||||
|
|
||||||
|
// Coprocessor instructions
|
||||||
|
virtual void CDP() = 0;
|
||||||
|
virtual void LDC() = 0;
|
||||||
|
virtual void MCR() = 0;
|
||||||
|
virtual void MCRR() = 0;
|
||||||
|
virtual void MRC() = 0;
|
||||||
|
virtual void MRRC() = 0;
|
||||||
|
virtual void STC() = 0;
|
||||||
|
|
||||||
|
// Data processing instructions
|
||||||
|
virtual void ADC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void ADC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void ADC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void ADD_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void ADD_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void ADD_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void AND_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void AND_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void AND_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void BIC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void BIC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void BIC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void CMN_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void CMN_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void CMN_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void CMP_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void CMP_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void CMP_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void EOR_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void EOR_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void EOR_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void MOV_imm(Cond cond, bool S, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void MOV_reg(Cond cond, bool S, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void MOV_rsr(Cond cond, bool S, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void MVN_imm(Cond cond, bool S, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void MVN_reg(Cond cond, bool S, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void MVN_rsr(Cond cond, bool S, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void ORR_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void ORR_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void ORR_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void RSB_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void RSB_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void RSB_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void RSC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void RSC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void RSC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void SBC_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void SBC_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void SBC_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void SUB_imm(Cond cond, bool S, Register Rn, Register Rd, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void SUB_reg(Cond cond, bool S, Register Rn, Register Rd, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void SUB_rsr(Cond cond, bool S, Register Rn, Register Rd, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void TEQ_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void TEQ_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void TEQ_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void TST_imm(Cond cond, Register Rn, int rotate, Imm8 imm8) = 0;
|
||||||
|
virtual void TST_reg(Cond cond, Register Rn, Imm5 imm5, ShiftType shift, Register Rm) = 0;
|
||||||
|
virtual void TST_rsr(Cond cond, Register Rn, Register Rs, ShiftType shift, Register Rm) = 0;
|
||||||
|
|
||||||
|
// Exception generation instructions
|
||||||
|
virtual void BKPT() = 0;
|
||||||
|
virtual void HVC() = 0;
|
||||||
|
virtual void SMC() = 0;
|
||||||
|
virtual void SVC() = 0;
|
||||||
|
virtual void UDF() = 0;
|
||||||
|
|
||||||
|
// Extension functions
|
||||||
|
virtual void SXTAB() = 0;
|
||||||
|
virtual void SXTAB16() = 0;
|
||||||
|
virtual void SXTAH() = 0;
|
||||||
|
virtual void SXTB() = 0;
|
||||||
|
virtual void SXTB16() = 0;
|
||||||
|
virtual void SXTH() = 0;
|
||||||
|
virtual void UXTAB() = 0;
|
||||||
|
virtual void UXTAB16() = 0;
|
||||||
|
virtual void UXTAH() = 0;
|
||||||
|
virtual void UXTB() = 0;
|
||||||
|
virtual void UXTB16() = 0;
|
||||||
|
virtual void UXTH() = 0;
|
||||||
|
|
||||||
|
// Hint instructions
|
||||||
|
virtual void DBG() = 0;
|
||||||
|
virtual void PLD() = 0;
|
||||||
|
virtual void PLI() = 0;
|
||||||
|
|
||||||
|
// Load/Store instructions
|
||||||
|
virtual void LDR_imm() = 0;
|
||||||
|
virtual void LDR_reg() = 0;
|
||||||
|
virtual void LDRB_imm() = 0;
|
||||||
|
virtual void LDRB_reg() = 0;
|
||||||
|
virtual void LDRBT() = 0;
|
||||||
|
virtual void LDRD_imm() = 0;
|
||||||
|
virtual void LDRD_reg() = 0;
|
||||||
|
virtual void LDRH_imm() = 0;
|
||||||
|
virtual void LDRH_reg() = 0;
|
||||||
|
virtual void LDRHT() = 0;
|
||||||
|
virtual void LDRSB_imm() = 0;
|
||||||
|
virtual void LDRSB_reg() = 0;
|
||||||
|
virtual void LDRSBT() = 0;
|
||||||
|
virtual void LDRSH_imm() = 0;
|
||||||
|
virtual void LDRSH_reg() = 0;
|
||||||
|
virtual void LDRSHT() = 0;
|
||||||
|
virtual void LDRT() = 0;
|
||||||
|
virtual void STR_imm() = 0;
|
||||||
|
virtual void STR_reg() = 0;
|
||||||
|
virtual void STRB_imm() = 0;
|
||||||
|
virtual void STRB_reg() = 0;
|
||||||
|
virtual void STRBT() = 0;
|
||||||
|
virtual void STRD_imm() = 0;
|
||||||
|
virtual void STRD_reg() = 0;
|
||||||
|
virtual void STRH_imm() = 0;
|
||||||
|
virtual void STRH_reg() = 0;
|
||||||
|
virtual void STRHT() = 0;
|
||||||
|
virtual void STRT() = 0;
|
||||||
|
|
||||||
|
// Load/Store multiple instructions
|
||||||
|
virtual void LDM() = 0;
|
||||||
|
virtual void STM() = 0;
|
||||||
|
|
||||||
|
// Miscellaneous instructions
|
||||||
|
virtual void CLZ() = 0;
|
||||||
|
virtual void ERET() = 0;
|
||||||
|
virtual void NOP() = 0;
|
||||||
|
virtual void SEL() = 0;
|
||||||
|
|
||||||
|
// Unsigned sum of absolute difference functions
|
||||||
|
virtual void USAD8() = 0;
|
||||||
|
virtual void USADA8() = 0;
|
||||||
|
|
||||||
|
// Packing instructions
|
||||||
|
virtual void PKH() = 0;
|
||||||
|
|
||||||
|
// Reversal instructions
|
||||||
|
virtual void RBIT() = 0;
|
||||||
|
virtual void REV() = 0;
|
||||||
|
virtual void REV16() = 0;
|
||||||
|
virtual void REVSH() = 0;
|
||||||
|
|
||||||
|
// Saturation instructions
|
||||||
|
virtual void SSAT() = 0;
|
||||||
|
virtual void SSAT16() = 0;
|
||||||
|
virtual void USAT() = 0;
|
||||||
|
virtual void USAT16() = 0;
|
||||||
|
|
||||||
|
// Multiply (Normal) instructions
|
||||||
|
virtual void MLA() = 0;
|
||||||
|
virtual void MLS() = 0;
|
||||||
|
virtual void MUL() = 0;
|
||||||
|
|
||||||
|
// Multiply (Long) instructions
|
||||||
|
virtual void SMLAL() = 0;
|
||||||
|
virtual void SMULL() = 0;
|
||||||
|
virtual void UMAAL() = 0;
|
||||||
|
virtual void UMLAL() = 0;
|
||||||
|
virtual void UMULL() = 0;
|
||||||
|
|
||||||
|
// Multiply (Halfword) instructions
|
||||||
|
virtual void SMLALxy() = 0;
|
||||||
|
virtual void SMLAxy() = 0;
|
||||||
|
virtual void SMULxy() = 0;
|
||||||
|
|
||||||
|
// Multiply (word by halfword) instructions
|
||||||
|
virtual void SMLAWy() = 0;
|
||||||
|
virtual void SMULWy() = 0;
|
||||||
|
|
||||||
|
// Multiply (Most significant word) instructions
|
||||||
|
virtual void SMMLA() = 0;
|
||||||
|
virtual void SMMLS() = 0;
|
||||||
|
virtual void SMMUL() = 0;
|
||||||
|
|
||||||
|
// Multiply (Dual) instructions
|
||||||
|
virtual void SMLAD() = 0;
|
||||||
|
virtual void SMLALD() = 0;
|
||||||
|
virtual void SMLSD() = 0;
|
||||||
|
virtual void SMLSLD() = 0;
|
||||||
|
virtual void SMUAD() = 0;
|
||||||
|
virtual void SMUSD() = 0;
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Modulo arithmetic) instructions
|
||||||
|
virtual void SADD8() = 0;
|
||||||
|
virtual void SADD16() = 0;
|
||||||
|
virtual void SASX() = 0;
|
||||||
|
virtual void SSAX() = 0;
|
||||||
|
virtual void SSUB8() = 0;
|
||||||
|
virtual void SSUB16() = 0;
|
||||||
|
virtual void UADD8() = 0;
|
||||||
|
virtual void UADD16() = 0;
|
||||||
|
virtual void UASX() = 0;
|
||||||
|
virtual void USAX() = 0;
|
||||||
|
virtual void USUB8() = 0;
|
||||||
|
virtual void USUB16() = 0;
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Saturating) instructions
|
||||||
|
virtual void QADD8() = 0;
|
||||||
|
virtual void QADD16() = 0;
|
||||||
|
virtual void QASX() = 0;
|
||||||
|
virtual void QSAX() = 0;
|
||||||
|
virtual void QSUB8() = 0;
|
||||||
|
virtual void QSUB16() = 0;
|
||||||
|
virtual void UQADD8() = 0;
|
||||||
|
virtual void UQADD16() = 0;
|
||||||
|
virtual void UQASX() = 0;
|
||||||
|
virtual void UQSAX() = 0;
|
||||||
|
virtual void UQSUB8() = 0;
|
||||||
|
virtual void UQSUB16() = 0;
|
||||||
|
|
||||||
|
// Parallel Add/Subtract (Halving) instructions
|
||||||
|
virtual void SHADD8() = 0;
|
||||||
|
virtual void SHADD16() = 0;
|
||||||
|
virtual void SHASX() = 0;
|
||||||
|
virtual void SHSAX() = 0;
|
||||||
|
virtual void SHSUB8() = 0;
|
||||||
|
virtual void SHSUB16() = 0;
|
||||||
|
virtual void UHADD8() = 0;
|
||||||
|
virtual void UHADD16() = 0;
|
||||||
|
virtual void UHASX() = 0;
|
||||||
|
virtual void UHSAX() = 0;
|
||||||
|
virtual void UHSUB8() = 0;
|
||||||
|
virtual void UHSUB16() = 0;
|
||||||
|
|
||||||
|
// Saturated Add/Subtract instructions
|
||||||
|
virtual void QADD() = 0;
|
||||||
|
virtual void QSUB() = 0;
|
||||||
|
virtual void QDADD() = 0;
|
||||||
|
virtual void QDSUB() = 0;
|
||||||
|
|
||||||
|
// Synchronization Primitive instructions
|
||||||
|
virtual void CLREX() = 0;
|
||||||
|
virtual void LDREX() = 0;
|
||||||
|
virtual void LDREXB() = 0;
|
||||||
|
virtual void LDREXD() = 0;
|
||||||
|
virtual void LDREXH() = 0;
|
||||||
|
virtual void STREX() = 0;
|
||||||
|
virtual void STREXB() = 0;
|
||||||
|
virtual void STREXD() = 0;
|
||||||
|
virtual void STREXH() = 0;
|
||||||
|
virtual void SWP() = 0;
|
||||||
|
|
||||||
|
// Status register access instructions
|
||||||
|
virtual void CPS() = 0;
|
||||||
|
virtual void MRS() = 0;
|
||||||
|
virtual void MSR() = 0;
|
||||||
|
virtual void RFE() = 0;
|
||||||
|
virtual void SETEND() = 0;
|
||||||
|
virtual void SRS() = 0;
|
||||||
|
|
||||||
|
// Thumb specific instructions
|
||||||
|
virtual void thumb_BLX_prefix(Imm11 imm11) = 0;
|
||||||
|
virtual void thumb_BLX_suffix(bool L, Imm11 imm11) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
453
src/core/arm/decoder/thumb.cpp
Normal file
453
src/core/arm/decoder/thumb.cpp
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/make_unique.h"
|
||||||
|
|
||||||
|
#include "core/arm/decoder/decoder.h"
|
||||||
|
|
||||||
|
namespace ArmDecoder {
|
||||||
|
|
||||||
|
namespace Impl {
|
||||||
|
struct MatcherImpl : Matcher {
|
||||||
|
MatcherImpl(u32 mask, u32 expect, std::function<void(Visitor* v, u32 instruction)> fn) : fn(fn) {
|
||||||
|
bit_mask = mask;
|
||||||
|
expected = expect;
|
||||||
|
}
|
||||||
|
std::function<void(Visitor* v, u32 instruction)> fn;
|
||||||
|
virtual void visit(Visitor *v, u32 inst) override {
|
||||||
|
fn(v, inst);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Matcher> MakeMatcher(const char* str, std::function<void(Visitor* v, u32 instruction)> fn) {
|
||||||
|
ASSERT(strlen(str) == 16);
|
||||||
|
|
||||||
|
u32 mask;
|
||||||
|
u32 expect;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
mask <<= 1;
|
||||||
|
expect <<= 1;
|
||||||
|
|
||||||
|
switch (str[i]) {
|
||||||
|
case '0':
|
||||||
|
mask |= 1;
|
||||||
|
expect |= 0;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
mask |= 1;
|
||||||
|
expect |= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mask |= 0;
|
||||||
|
expect |= 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Common::make_unique<Impl::MatcherImpl>(mask, expect, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t a, size_t b, typename T>
|
||||||
|
static constexpr T bits(T s){
|
||||||
|
return ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t num_bits>
|
||||||
|
s32 sign_extend(s32 x) {
|
||||||
|
return x << (32 - num_bits) >> (32 - num_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::array<Instruction, 27> thumb_instruction_table = { {
|
||||||
|
{ "LSL/LSR/ASR", MakeMatcher("000ooxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<11, 12>(instruction);
|
||||||
|
u32 imm5 = bits<6, 10>(instruction);
|
||||||
|
Register Rm = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // LSL <Rd>, <Rm>, #<imm5>
|
||||||
|
v->MOV_reg(0xE, /*S=*/true, Rd, imm5, 0b000, Rm);
|
||||||
|
break;
|
||||||
|
case 1: // LSR <Rd>, <Rm>, #<imm5>
|
||||||
|
v->MOV_reg(0xE, /*S=*/true, Rd, imm5, 0b010, Rm);
|
||||||
|
break;
|
||||||
|
case 2: // ASR <Rd>, <Rm>, #<imm5>
|
||||||
|
v->MOV_reg(0xE, /*S=*/true, Rd, imm5, 0b100, Rm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "ADD/SUB_reg", MakeMatcher("000110oxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<9, 9>(instruction);
|
||||||
|
Register Rm = bits<6, 8>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // ADD <Rd>, <Rn>, <Rm>
|
||||||
|
v->ADD_reg(0xE, /*S=*/true, Rn, Rd, 0, 0, Rm);
|
||||||
|
break;
|
||||||
|
case 1: // SUB <Rd>, <Rn>, <Rm>
|
||||||
|
v->SUB_reg(0xE, /*S=*/true, Rn, Rd, 0, 0, Rm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "ADD/SUB_imm", MakeMatcher("000111oxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<9, 9>(instruction);
|
||||||
|
u32 imm3 = bits<6, 8>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // ADD <Rd>, <Rn>, #<imm3>
|
||||||
|
v->ADD_imm(0xE, /*S=*/true, Rn, Rd, 0, imm3);
|
||||||
|
break;
|
||||||
|
case 1: // SUB <Rd>, <Rn>, #<imm3>
|
||||||
|
v->SUB_imm(0xE, /*S=*/true, Rn, Rd, 0, imm3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "add/sub/cmp/mov_imm", MakeMatcher("001ooxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<11, 12>(instruction);
|
||||||
|
Register Rd = bits<8, 10>(instruction);
|
||||||
|
u32 imm8 = bits<0, 7>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // MOV Rd, #imm8
|
||||||
|
v->MOV_imm(0xE, /*S=*/true, Rd, 0, imm8);
|
||||||
|
break;
|
||||||
|
case 1: // CMP Rn, #imm8
|
||||||
|
v->CMP_imm(0xE, Rd, 0, imm8);
|
||||||
|
break;
|
||||||
|
case 2: // ADD Rd, #imm8
|
||||||
|
v->ADD_imm(0xE, /*S=*/true, Rd, Rd, 0, imm8);
|
||||||
|
break;
|
||||||
|
case 3: // SUB Rd, #imm8
|
||||||
|
v->SUB_imm(0xE, /*S=*/true, Rd, Rd, 0, imm8);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "data processing reg", MakeMatcher("010000ooooxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<6, 9>(instruction);
|
||||||
|
Register Rm_Rs = bits<3, 5>(instruction);
|
||||||
|
Register Rd_Rn = bits<0, 2>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // AND Rd, Rm
|
||||||
|
v->AND_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 1: // EOR Rd, Rm
|
||||||
|
v->EOR_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 2: // LSL Rd, Rs
|
||||||
|
v->MOV_rsr(0xE, /*S=*/true, Rd_Rn, Rm_Rs, 0b0001, Rd_Rn);
|
||||||
|
break;
|
||||||
|
case 3: // LSR Rd, Rs
|
||||||
|
v->MOV_rsr(0xE, /*S=*/true, Rd_Rn, Rm_Rs, 0b0011, Rd_Rn);
|
||||||
|
break;
|
||||||
|
case 4: // ASR Rd, Rs
|
||||||
|
v->MOV_rsr(0xE, /*S=*/true, Rd_Rn, Rm_Rs, 0b0101, Rd_Rn);
|
||||||
|
break;
|
||||||
|
case 5: // ADC Rd, Rm
|
||||||
|
v->ADC_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 6: // SBC Rd, Rm
|
||||||
|
v->SBC_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 7: // ROR Rd, Rs
|
||||||
|
v->MOV_rsr(0xE, /*S=*/true, Rd_Rn, Rm_Rs, 0b0111, Rd_Rn);
|
||||||
|
break;
|
||||||
|
case 8: // TST Rm, Rn
|
||||||
|
v->TST_reg(0xE, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 9: // NEG Rd, Rm
|
||||||
|
v->RSB_imm(0xE, /*S=*/true, Rm_Rs, Rd_Rn, 0, 0);
|
||||||
|
break;
|
||||||
|
case 10: // CMP Rm, Rn
|
||||||
|
v->CMP_reg(0xE, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 11: // CMN Rm, Rn
|
||||||
|
v->CMN_reg(0xE, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 12: // ORR Rd, Rm
|
||||||
|
v->ORR_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 13: // MUL Rd, Rm
|
||||||
|
v->MUL();
|
||||||
|
break;
|
||||||
|
case 14: // BIC Rm, Rd
|
||||||
|
v->BIC_reg(0xE, /*S=*/true, Rd_Rn, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
case 15: // MVN Rd, Rm
|
||||||
|
v->MVN_reg(0xE, /*S=*/true, Rd_Rn, 0, 0, Rm_Rs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "special data processing", MakeMatcher("010001ooxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<8, 9>(instruction);
|
||||||
|
Register Rm = bits<3, 6>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction) | (bits<7, 7>(instruction) << 3);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // ADD Rd, Rm
|
||||||
|
v->ADD_reg(0xE, /*S=*/false, Rd, Rd, 0, 0, Rm);
|
||||||
|
break;
|
||||||
|
case 1: // CMP Rm, Rn
|
||||||
|
v->CMP_reg(0xE, Rd, 0, 0, Rm);
|
||||||
|
break;
|
||||||
|
case 2: // MOV Rd, Rm
|
||||||
|
v->MOV_reg(0xE, /*S=*/false, Rd, 0, 0, Rm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "BLX/BX", MakeMatcher("01000111xxxxx000", [](Visitor* v, u32 instruction) {
|
||||||
|
bool L = bits<7, 7>(instruction);
|
||||||
|
Register Rm = bits<3, 6>(instruction);
|
||||||
|
if (!L) { // BX Rm
|
||||||
|
v->BX(0xE, Rm);
|
||||||
|
} else { // BLX Rm
|
||||||
|
v->BLX_reg(0xE, Rm);
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "load from literal pool", MakeMatcher("01001xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// LDR Rd, [PC, #]
|
||||||
|
Register Rd = bits<8, 10>(instruction);
|
||||||
|
u32 imm8 = bits<0, 7>(instruction);
|
||||||
|
v->LDR_imm();
|
||||||
|
})},
|
||||||
|
{ "load/store reg offset", MakeMatcher("0101oooxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opcode = bits<9, 11>(instruction);
|
||||||
|
Register Rm = bits<6, 8>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opcode) {
|
||||||
|
case 0: // STR Rd, [Rn, Rm]
|
||||||
|
v->STR_reg();
|
||||||
|
break;
|
||||||
|
case 1: // STRH Rd, [Rn, Rm]
|
||||||
|
v->STRH_reg();
|
||||||
|
break;
|
||||||
|
case 2: // STRB Rd, [Rn, Rm]
|
||||||
|
v->STRB_reg();
|
||||||
|
break;
|
||||||
|
case 3: // LDRSB Rd, [Rn, Rm]
|
||||||
|
v->LDRSB_reg();
|
||||||
|
break;
|
||||||
|
case 4: // LDR Rd, [Rn, Rm]
|
||||||
|
v->LDR_reg();
|
||||||
|
break;
|
||||||
|
case 5: // LDRH Rd, [Rn, Rm]
|
||||||
|
v->LDRH_reg();
|
||||||
|
break;
|
||||||
|
case 6: // LDRB Rd, [Rn, Rm]
|
||||||
|
v->LDRB_reg();
|
||||||
|
break;
|
||||||
|
case 7: // LDRSH Rd, [Rn, Rm]
|
||||||
|
v->LDRSH_reg();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "STR(B)/LDR(B)_imm", MakeMatcher("011xxxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool opc = bits<11, 12>(instruction);
|
||||||
|
Register offset = bits<6, 10>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opc) {
|
||||||
|
case 0: // STR Rd, [Rn, #offset]
|
||||||
|
v->STR_imm();
|
||||||
|
break;
|
||||||
|
case 1: // LDR Rd, [Rn, #offset]
|
||||||
|
v->LDR_imm();
|
||||||
|
break;
|
||||||
|
case 2: // STRB Rd, [Rn, #offset]
|
||||||
|
v->STRB_imm();
|
||||||
|
break;
|
||||||
|
case 3: // LDRB Rd, [Rn, #offset]
|
||||||
|
v->LDRB_imm();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "STRH/LDRH_imm", MakeMatcher("1000xxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool L = bits<11, 11>(instruction);
|
||||||
|
Register offset = bits<6, 10>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
if (!L) { // STRH Rd, [Rn, #offset]
|
||||||
|
v->STRH_imm();
|
||||||
|
} else { // LDRH Rd, [Rn, #offset]
|
||||||
|
v->LDRH_imm();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "load/store stack", MakeMatcher("1001xxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool L = bits<11, 11>(instruction);
|
||||||
|
Register Rd = bits<8, 10>(instruction);
|
||||||
|
u32 offset = bits<0, 7>(instruction);
|
||||||
|
if (!L) { // STR Rd, [SP, #offset]
|
||||||
|
v->STR_imm();
|
||||||
|
} else { // LDR Rd, [SP, #offset]
|
||||||
|
v->LDR_imm();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "add to sp/pc", MakeMatcher("1010oxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// ADD Rd, PC/SP, #imm8
|
||||||
|
Register Rn = bits<11, 11>(instruction) ? 13 : 15;
|
||||||
|
Register Rd = bits<8, 10>(instruction);
|
||||||
|
u32 imm8 = bits<0, 7>(instruction);
|
||||||
|
v->ADD_imm(0xE, /*S=*/false, Rn, Rd, 0xF, imm8);
|
||||||
|
})},
|
||||||
|
{ "adjust stack ptr", MakeMatcher("10110000oxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// SUB SP, SP, #<imm7*4>
|
||||||
|
u32 opc = bits<7, 7>(instruction);
|
||||||
|
u32 imm7 = bits<0, 6>(instruction);
|
||||||
|
switch (opc) {
|
||||||
|
case 0:
|
||||||
|
v->ADD_imm(0xE, /*S=*/false, 13, 13, 0xF, imm7);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
v->SUB_imm(0xE, /*S=*/false, 13, 13, 0xF, imm7);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "sign/zero extend", MakeMatcher("10110010ooxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
u32 opc = bits<6, 7>(instruction);
|
||||||
|
Register Rm = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opc) {
|
||||||
|
case 0: // SXTH Rd, Rm
|
||||||
|
v->SXTH();
|
||||||
|
break;
|
||||||
|
case 1: // SXTB Rd, Rm
|
||||||
|
v->SXTB();
|
||||||
|
break;
|
||||||
|
case 2: // UXTH Rd, Rm
|
||||||
|
v->UXTH();
|
||||||
|
break;
|
||||||
|
case 3: // UXTB Rd, Rm
|
||||||
|
v->UXTB();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "PUSH/POP_reglist", MakeMatcher("1011x10xxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool L = bits<11, 11>(instruction);
|
||||||
|
bool R = bits<8, 8>(instruction);
|
||||||
|
u32 reglist = bits<0, 7>(instruction);
|
||||||
|
if (!L) { // PUSH {reglist, <R>=LR}
|
||||||
|
reglist |= R << 14;
|
||||||
|
v->STM();
|
||||||
|
} else { // POP {reglist, <R>=PC}
|
||||||
|
reglist |= R << 15;
|
||||||
|
v->LDM();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "SETEND", MakeMatcher("101101100101x000", [](Visitor* v, u32 instruction) {
|
||||||
|
bool E = bits<3, 3>(instruction);
|
||||||
|
v->SETEND();
|
||||||
|
})},
|
||||||
|
{ "change processor state", MakeMatcher("10110110011x0xxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool imod = bits<4, 4>(instruction);
|
||||||
|
bool A = bits<2, 2>(instruction);
|
||||||
|
bool I = bits<1, 1>(instruction);
|
||||||
|
bool F = bits<0, 0>(instruction);
|
||||||
|
v->CPS();
|
||||||
|
})},
|
||||||
|
{ "reverse bytes", MakeMatcher("10111010ooxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool opc = bits<6, 7>(instruction);
|
||||||
|
Register Rn = bits<3, 5>(instruction);
|
||||||
|
Register Rd = bits<0, 2>(instruction);
|
||||||
|
switch (opc) {
|
||||||
|
case 0: // REV Rd, Rn
|
||||||
|
v->REV();
|
||||||
|
break;
|
||||||
|
case 1: // REV16 Rd, Rn
|
||||||
|
v->REV16();
|
||||||
|
break;
|
||||||
|
case 2: // undefined
|
||||||
|
v->UDF();
|
||||||
|
break;
|
||||||
|
case 3: // REVSH Rd, Rn
|
||||||
|
v->REVSH();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "BKPT", MakeMatcher("10111110xxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// BKPT #imm8
|
||||||
|
Imm8 imm8 = bits<0, 7>(instruction);
|
||||||
|
v->BKPT();
|
||||||
|
})},
|
||||||
|
{ "STMIA/LDMIA", MakeMatcher("1100xxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
bool L = bits<11, 11>(instruction);
|
||||||
|
Register Rn = bits<8, 10>(instruction);
|
||||||
|
u32 reglist = bits<0, 7>(instruction);
|
||||||
|
if (!L) { // STMIA Rn!, { reglist }
|
||||||
|
v->STM();
|
||||||
|
} else { // LDMIA Rn!, { reglist }
|
||||||
|
v->LDM();
|
||||||
|
}
|
||||||
|
})},
|
||||||
|
{ "B<cond>", MakeMatcher("1101xxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// B<cond> <PC + #offset*2>
|
||||||
|
Cond cond = bits<8, 11>(instruction);
|
||||||
|
s32 offset = bits<0, 7>(instruction);
|
||||||
|
v->B(cond, sign_extend<8>(offset));
|
||||||
|
})},
|
||||||
|
{ "SWI", MakeMatcher("11011111xxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// SWI #imm8
|
||||||
|
Imm8 imm8 = bits<0, 7>(instruction);
|
||||||
|
v->SVC(/*imm8*/);
|
||||||
|
})},
|
||||||
|
{ "B", MakeMatcher("11100xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
// B <PC + #offset*2>
|
||||||
|
Imm11 imm11 = bits<0, 10>(instruction);
|
||||||
|
v->B(0xE, sign_extend<11>(imm11));
|
||||||
|
})},
|
||||||
|
{ "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) {
|
||||||
|
Imm11 imm11 = bits<0, 10>(instruction);
|
||||||
|
v->thumb_BLX_suffix(/*L=*/true, imm11);
|
||||||
|
})},
|
||||||
|
{ "BL/BLX (prefix)", MakeMatcher("11110xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
Imm11 imm11 = bits<0, 10>(instruction);
|
||||||
|
v->thumb_BLX_prefix(imm11);
|
||||||
|
})},
|
||||||
|
{ "BL (suffix)", MakeMatcher("11111xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
|
||||||
|
Imm11 imm11 = bits<0, 10>(instruction);
|
||||||
|
v->thumb_BLX_suffix(/*L=*/false, imm11);
|
||||||
|
})}
|
||||||
|
}};
|
||||||
|
|
||||||
|
const Instruction& DecodeThumb(u16 i) {
|
||||||
|
// NOTE: The reverse search direction is important. Searching forwards would result in incorrect behavior.
|
||||||
|
// This is because the entries in thumb_instruction_table have more specific matches coming after less specific ones.
|
||||||
|
// Example:
|
||||||
|
// 000ooxxxxxxxxxxx comes before 000110oxxxxxxxxx
|
||||||
|
// with a forward search direction notice how the first one will always be matched and the latter never will be.
|
||||||
|
return *std::find_if(thumb_instruction_table.crbegin(), thumb_instruction_table.crend(), [i](const auto& instruction) {
|
||||||
|
return instruction.Match(i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user