BizHawk/libmeteor/source/disassembler/instruction.cpp

692 lines
18 KiB
C++

// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/instruction.hpp"
#include "ameteor/disassembler/argregister.hpp"
#include "ameteor/disassembler/argrelative.hpp"
#include "ameteor/disassembler/argimmediate.hpp"
#include "ameteor/disassembler/arguimmediate.hpp"
#include "ameteor/disassembler/argshift.hpp"
#include "ameteor/disassembler/argpsr.hpp"
#include "ameteor/disassembler/argmulregisters.hpp"
#include "../globals.hpp" // for ROR
namespace AMeteor
{
namespace Disassembler
{
void Instruction::Clear ()
{
m_operator.clear();
m_args.Clear();
}
#define Rn ((code >> 16) & 0xF)
#define Rd ((code >> 12) & 0xF)
#define Rs ((code >> 8) & 0xF)
#define Rm (code & 0xF)
void Instruction::ParseArm (uint32_t offset, uint32_t code)
{
Clear();
if ((code & 0x0FFFFFD0) == 0x12FFF10)
{
if (code & (0x1 << 5))
m_operator = "BLX";
else
m_operator = "BX";
m_args.AddArgument(ArgRegister(Rm));
ParseArmCondition(code);
}
else if (((code >> 25) & 0x7) == 0x5)
{
if (((code >> 28) & 0xF) == 0xF)
{
m_operator = "BLX";
m_args.AddArgument(ArgImmediate(offset + 8 +
(((code & (0x1 << 23)) ? 0xFF000000 | (code & 0x00FFFFFF)
: (code & 0x00FFFFFF)) << 2)
+ ((code & (0x1 << 24)) ? 2 : 0)));
}
else
{
if (code & (0x1 << 24))
m_operator = "BL";
else
m_operator = "B";
m_args.AddArgument(ArgImmediate(offset + 8 +
(((code & (0x1 << 23)) ? 0xFF000000 | (code & 0x00FFFFFF)
: (code & 0x00FFFFFF)) << 2)));
ParseArmCondition(code);
}
}
else if (((code >> 25) & 0x7) == 0x1)
{
ParseArmDataProc(code);
}
else if (((code >> 26) & 0x3) == 0x1)
{
if ((code & 0xF0100000) != 0xF0100000) // not PLD
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 25)) // register offset
{
switch ((code >> 5) & 0x3)
{
case 0: // Logical Shift Left
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSL, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 1: // Logical Shift Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_LSR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 2: // Arithmetic Shift Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ASR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_ASR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 3: // ROtate Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ROR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(1), SHIFT_RRX, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
}
}
else // immediate offset
{
m_args.AddArgument(ArgRelative(Rn, ArgImmediate(code & 0xFFF),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
if ((code & 0xF0100000) == 0xF0100000)
m_operator = "PLD";
else
{
if (code & (0x1 << 20))
m_operator = "LDR";
else
m_operator = "STR";
if (code & (0x1 << 22))
m_operator += "B";
ParseArmCondition(code);
}
}
else if (((code >> 25) & 0x7) == 0x4)
{
if (code & (0x1 << 20))
m_operator = "LDM";
else
m_operator = "STM";
if (code & (0x1 << 23))
m_operator += 'I';
else
m_operator += 'D';
if (code & (0x1 << 24))
m_operator += 'B';
else
m_operator += 'A';
m_args.AddArgument(ArgRegister(Rn, code & (0x1 << 21)));
ArgMulRegisters argRegs(code & (0x1 << 22));
for (register uint8_t n = 0; n < 16; ++n)
if (code & (0x1 << n))
argRegs.AddRegister(n);
m_args.AddArgument(argRegs);
ParseArmCondition(code);
}
else if (((code >> 25) & 0x7) == 0x0)
{
if ((code & 0x0FC000F0) == 0x00000090 ||
(code & 0x0F8000F0) == 0x00800090 ||
(code & 0x0F900090) == 0x01000080)
{
// NOTE : In this instruction Rn and Rd are inverted
static const char* Instructions[] = {"MUL", "MLA", "Reserved",
"Reserved", "UMULL", "UMLAL", "SMULL", "SMLAL", "SMLAxy",
"", // This is for SMLAWy and SMULWy
"SMLALxy", "SMULxy", "Reserved", "Reserved", "Reserved",
"Reserved"};
uint8_t opcode = (code >> 21) & 0xF;
if (opcode == 0x9)
m_operator = (code & (0x1 << 5)) ? "SMULWy" : "SMLAWy";
else
m_operator = Instructions[opcode];
if (!(opcode & (0x1 << 4)) && (code & (0x1 << 20)))
m_operator += 'S';
ParseArmCondition(code);
if ((opcode & 0xC) == 0x4 || opcode == 0xA)
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rn));
m_args.AddArgument(ArgRegister(Rm));
m_args.AddArgument(ArgRegister(Rs));
if ((opcode & 0xE) == 0x8 || opcode == 0x1)
m_args.AddArgument(ArgRegister(Rd));
}
else if ((code & (0x1 << 7)) && (code & (0x1 << 4)))
{
if (((code >> 23) & 0x3) == 0x2 && ((code >> 20) & 0x3) == 0x0
&& ((code >> 4) & 0xFF) == 0x09)
{
if (code & (0x1 << 22)) // SWPB
m_operator = "SWPB";
else // SWP
m_operator = "SWP";
ParseArmCondition(code);
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rm));
m_args.AddArgument(ArgRegister(Rn, false, false, true));
}
else
{
static const char* Instructions[] = {"Reserved", "STRH", "LDRD",
"STRD", "Reserved", "LDRH", "LDRSB", "LDRSH"};
m_operator = Instructions[((code >> 18) & 0x4)
| ((code >> 5) & 0x3)];
ParseArmCondition(code);
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 22)) // immediate
{
m_args.AddArgument(ArgRelative(ArgRegister(Rn),
ArgImmediate(((code >> 4) & 0xF0) | (code & 0xF)),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
else
{
m_args.AddArgument(ArgRelative(ArgRegister(Rn),
ArgRegister(code & 0xF),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
}
}
else if (((code >> 23) & 0x3) == 0x2)
{
if (!((code >> 20) & 0x1))
{
if (code & (0x1 << 21))
{
m_operator = "MSR";
m_args.AddArgument(ArgPsr(code & (0x1 << 22),
(code >> 16) & 0xF));
if (code & (0x1 << 25)) // immediate
{
m_args.AddArgument(ArgUImmediate(
ROR(code & 0xFF, (code >> 8) & 0xF)));
}
else
{
m_args.AddArgument(ArgRegister(Rm));
}
}
else
{
m_operator = "MRS";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgPsr(code & (0x1 << 22)));
}
ParseArmCondition(code);
}
else
{
ParseArmDataProc(code);
}
}
else
{
ParseArmDataProc(code);
}
}
else
{
m_operator = "Unknown";
}
}
void Instruction::ParseArmDataProc (uint32_t code)
{
static const char* ops[] = {"AND", "EOR", "SUB", "RSB", "ADD", "ADC",
"SBC", "RSC", "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"};
uint8_t opcode = (code >> 21) & 0xF;
if (opcode < 0x8 || opcode > 0xB)
m_args.AddArgument(ArgRegister(Rd));
if (opcode != 0xD && opcode != 0xF)
m_args.AddArgument(ArgRegister(Rn));
if (code & (0x1 << 25)) // Immediate operand 2
{
/*if (code & (0xF << 8))
m_args.AddArgument(ArgShift(ArgImmediate(code & 0xFF),
ArgImmediate(((code >> 8) & 0xF) << 1), SHIFT_ROR, false));
else
m_args.AddArgument(ArgImmediate(code & 0xFF));*/
m_args.AddArgument(ArgUImmediate(
ROR(code & 0xFF, (code >> 7) & 0x1E)));
}
else
{
switch ((code >> 5) & 0x3)
{
case 0: // Logical Shift Left
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_LSL, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSL, false));
else
m_args.AddArgument(ArgRegister(Rm));
}
break;
case 1: // Logical Shift Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_LSR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_LSR, false));
}
break;
case 2: // Arithmetic Shift Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_ASR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ASR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_ASR, false));
}
break;
case 3: // ROtate Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_ROR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ROR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(1), SHIFT_RRX, false));
}
break;
}
}
m_operator = ops[opcode];
if (code & (0x1 << 20) && (opcode < 0x8 || opcode > 0xB))
{
m_operator += "S";
}
ParseArmCondition(code);
}
void Instruction::ParseArmCondition (uint32_t code)
{
static const char* Conditions[] = {"EQ", "NE", "CS", "CC", "MI", "PL",
"VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "", "NV"};
m_operator += Conditions[code >> 28];
}
#undef Rn
#undef Rd
#undef Rs
#undef Rm
#define Rb ((code >> 8) & 0x7)
#define Ro ((code >> 6) & 0x7)
#define Rs ((code >> 3) & 0x7)
#define Rd ((code ) & 0x7)
#define Imm (code & 0xFF)
#define Off ((code >> 6) & 0x1F)
#define HiRs ((code >> 3) & 0xF)
#define HiRd (((code & (0x1 << 7)) >> 4) | Rd)
void Instruction::ParseThumb (uint32_t offset, uint16_t code)
{
Clear ();
if ((code >> 12) == 0xB && ((code >> 9) & 0x3) == 0x2) // 1011x10
{
if (code & (0x1 << 11))
m_operator = "POP";
else
m_operator = "PUSH";
ArgMulRegisters argRegs(false);
for (register uint8_t n = 0; n < 8; ++n)
if (Imm & (0x1 << n))
argRegs.AddRegister(n);
if (code & (0x1 << 8))
{
if (code & (0x1 << 11))
argRegs.AddLastRegister(SPREG_PC);
else
argRegs.AddLastRegister(SPREG_LR);
}
m_args.AddArgument(argRegs);
}
else if ((code >> 11) == 0x9) // 01001
{
m_operator = "LDR";
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgRelative(15, ArgImmediate(Imm << 2),
true, true, false));
}
else if ((code >> 12) == 0x8) // 1000
{
if (code & (0x1 << 11))
m_operator = "LDRH";
else
m_operator = "STRH";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off << 1),
true, true, false));
}
else if ((code >> 10) == 0x10) // 010000
{
static const char* Instructions[] = {"AND", "EOR", "LSL", "LSR", "ASR",
"ADC", "SBC", "ROR", "TST", "NEG", "CMP", "CMN", "ORR", "MUL", "BIC",
"MVN"};
m_operator = Instructions[(code >> 6) & 0xF];
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
}
else if ((code >> 10) == 0x11) // 010001
{
switch ((code >> 8) & 0x3)
{
case 0x0: // ADD
m_operator = "ADD";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
break;
case 0x1: // CMP
m_operator = "CMP";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
break;
case 0x2:
if (HiRd != 8 || HiRs != 8) // MOV
{
m_operator = "MOV";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
}
else
m_operator = "NOP";
break;
case 0x3:
if (code & (0x1 << 7)) // BLX
{
m_operator = "BLX";
m_args.AddArgument(ArgRegister(HiRs));
}
else // BX
{
m_operator = "BX";
m_args.AddArgument(ArgRegister(HiRs));
}
break;
}
}
else if ((code >> 13) == 0x1) // 001
{
static const char* Instructions[] = {"MOV", "CMP", "ADD", "SUB"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgImmediate(Imm));
}
else if ((code >> 13) == 0x3) // 011
{
static const char* Instructions[] = {"STR", "LDR", "STRB", "LDRB"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 12))
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off), true,
true, false));
else
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off << 2), true,
true, false));
}
else if ((code >> 12) == 0xC) // 1100
{
if (code & (0x1 << 11))
m_operator = "LDMIA";
else
m_operator = "STMIA";
m_args.AddArgument(ArgRegister(Rb, true));
ArgMulRegisters argRegs(false);
for (register uint8_t n = 0; n < 8; ++n)
if (Imm & (0x1 << n))
argRegs.AddRegister(n);
m_args.AddArgument(argRegs);
}
else if ((code >> 13) == 0x0) // 000
{
if ((code >> 11) == 0x3) // 00011
{
if ((code >> 9) & 0x1)
m_operator = "SUB";
else
m_operator = "ADD";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
if ((code >> 10) & 0x1) // imm
m_args.AddArgument(ArgImmediate(Ro));
else // reg
m_args.AddArgument(ArgRegister(Ro));
}
else // 000
{
static const char* Instructions[] = {"LSL", "LSR", "ASR", "Reserved"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
m_args.AddArgument(ArgImmediate(Off));
}
}
else if ((code >> 11) == 0x1E) // 11110
{
m_operator = "BL.W1";
m_args.AddArgument(ArgImmediate(offset + 4 + ((code & 0x7FF) << 12)));
}
else if ((code >> 13) == 0x7 && (code & (0x1 << 11))) // 111x1
{
m_operator = "BL.W2";
m_args.AddArgument(ArgImmediate((code & 0x7FF) << 1));
}
else if ((code >> 11) == 0x1C) // 11100
{
m_operator = "B";
if (code & (0x1 << 10))
m_args.AddArgument(ArgUImmediate(offset + 4 +
((int32_t)(((code & 0x3FF) << 1) | 0xFFFFF800))));
else
m_args.AddArgument(ArgUImmediate(offset + 4 + ((code & 0x3FF) << 1)));
}
else if ((code >> 12) == 0xD) // 1101
{
if (((code >> 8) & 0xF) == 0xF) // 11011111
{
m_operator = "SWI";
m_args.AddArgument(ArgImmediate(code & 0xFF));
}
else // 1101
{
static const char* Conditions[] = {"EQ", "NE", "CS", "CC", "MI", "PL",
"VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "__", "**"};
m_operator = "B";
m_operator += Conditions[(code >> 8) & 0xF];
m_args.AddArgument(ArgUImmediate(offset + 4 +
(((int32_t)(int8_t)Imm) << 1)));
}
}
else if ((code >> 8) == 0xB0) // 10110000
{
m_operator = "ADD";
m_args.AddArgument(ArgRegister(13, false, true));
if (code & (0x1 << 7)) // substract
m_args.AddArgument(ArgImmediate(-((code & 0x7F) << 2)));
else // add
m_args.AddArgument(ArgImmediate((code & 0x7F) << 2));
}
else if ((code >> 12) == 0x5) // 0101
{
if (code & (0x1 << 11))
m_operator = "LDR";
else
m_operator = "STR";
if (code & (0x1 << 10))
m_operator += 'B';
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRelative(Rs, ArgRegister(Ro), true, true, false));
}
else if ((code >> 12) == 0x9) // 1001
{
if (code & (0x1 << 11))
m_operator = "LDR";
else
m_operator = "STR";
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgRelative(ArgRegister(13, false, true),
ArgImmediate(Imm << 2), true, true, false));
}
else if ((code >> 12) == 0xA) // 1010
{
m_operator = "ADD";
m_args.AddArgument(ArgRegister(Rb));
if (code & (0x1 << 11)) // with SP
m_args.AddArgument(ArgRegister(13, false, true));
else // with PC
m_args.AddArgument(ArgRegister(15, false, true));
m_args.AddArgument(ArgImmediate(Imm << 2));
}
else
{
m_operator = "Unknown";
}
}
#undef Rb
#undef Ro
#undef Rs
#undef Rd
#undef Imm
#undef Off
#undef HiRs
#undef HiRd
}
}