mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
734 lines
20 KiB
C++
734 lines
20 KiB
C++
#include "pch.h"
|
|
#include "WS/Debugger/WsDisUtils.h"
|
|
#include "WS/Debugger/DummyWsCpu.h"
|
|
#include "WS/WsTypes.h"
|
|
#include "WS/WsMemoryManager.h"
|
|
#include "WS/WsConsole.h"
|
|
#include "Debugger/DisassemblyInfo.h"
|
|
#include "Debugger/LabelManager.h"
|
|
#include "Debugger/MemoryDumper.h"
|
|
#include "Shared/MemoryType.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Utilities/FastString.h"
|
|
#include "Utilities/HexUtilities.h"
|
|
|
|
static constexpr const char* _modRegLut8[8] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
|
|
static constexpr const char* _modRegLut16[8] = { "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI" };
|
|
static constexpr const char* _modSegRegLut[4] = { "ES", "CS", "SS", "DS" };
|
|
|
|
constexpr const char* _opTemplate[256] = {
|
|
//0-0F
|
|
"ADD m", "ADD m", "ADD m", "ADD m", "ADD AL, i", "ADD AX, j", "PUSH ES", "POP ES",
|
|
"OR m", "OR m", "OR m", "OR m", "OR AL, i", "OR AX, j", "PUSH CS", "UNDEFINED_0F",
|
|
|
|
//10-1F
|
|
"ADC m", "ADC m", "ADC m", "ADC m", "ADC AL, i", "ADC AX, j", "PUSH SS", "POP SS",
|
|
"SBB m", "SBB m", "SBB m", "SBB m", "SBB AL, i", "SBB AX, j", "PUSH DS", "POP DS",
|
|
|
|
//20-2F
|
|
"AND m", "AND m", "AND m", "AND m", "AND AL, i", "AND AX, j", "ES:", "DAA",
|
|
"SUB m", "SUB m", "SUB m", "SUB m", "SUB AL, i", "SUB AX, j", "CS:", "DAS",
|
|
|
|
//30-3F
|
|
"XOR m", "XOR m", "XOR m", "XOR m", "XOR AL, i", "XOR AX, j", "SS:", "AAA",
|
|
"CMP m", "CMP m", "CMP m", "CMP m", "CMP AL, i", "CMP AX, j", "DS:", "AAS",
|
|
|
|
//40-4F
|
|
"INC AX", "INC CX", "INC DX", "INC BX", "INC SP", "INC BP", "INC SI", "INC DI",
|
|
"DEC AX", "DEC CX", "DEC DX", "DEC BX", "DEC SP", "DEC BP", "DEC SI", "DEC DI",
|
|
|
|
//50-5F
|
|
"PUSH AX", "PUSH CX", "PUSH DX", "PUSH BX", "PUSH SP", "PUSH BP", "PUSH SI", "PUSH DI",
|
|
"POP AX", "POP CX", "POP DX", "POP BX", "POP SP", "POP BP", "POP SI", "POP DI",
|
|
|
|
//60-6F
|
|
"PUSHA", "POPA", "BOUND p", "UNDEFINED_63", "UNDEFINED_64", "UNDEFINED_65", "UNDEFINED_66", "UNDEFINED_67",
|
|
"PUSH j", "IMUL n, j", "PUSH i", "IMUL n, i", "INSB", "INSW", "OUTSB", "OUTSW",
|
|
|
|
//70-7F
|
|
"JO r", "JNO r", "JB r", "JNB r", "JZ r", "JNZ r", "JBE r", "JA r",
|
|
"JS r", "JNS r", "JPE r", "JPO r", "JL r", "JGE r", "JLE r", "JG r",
|
|
|
|
//80-8F
|
|
"v", "v", "v", "v", "TEST m", "TEST m", "XCHG m", "XCHG m",
|
|
"MOV m", "MOV m", "MOV m", "MOV m", "MOV q", "LEA o", "MOV q", "POP n",
|
|
|
|
//90-9F
|
|
"NOP", "XCHG CX, AX", "XCHG DX, AX", "XCHG BX, AX", "XCHG SP, AX", "XCHG BP, AX", "XCHG SI, AX", "XCHG DI, AX",
|
|
"CBW", "CWD", "CALL t", "WAIT", "PUSHF", "POPF", "SAHF", "LAHF",
|
|
|
|
//A0-AF
|
|
"MOV AL, a", "MOV AX, a", "MOV a, AL", "MOV a, AX", "MOVSB", "MOVSW", "CMPSB", "CMPSW",
|
|
"TEST AL, i", "TEST AX, j", "STOSB", "STOSW", "LODSB", "LODSW", "SCASB", "SCASW",
|
|
|
|
//B0-BF
|
|
"MOV AL, i", "MOV CL, i", "MOV DL, i", "MOV BL, i", "MOV AH, i", "MOV CH, i", "MOV DH, i", "MOV BH, i",
|
|
"MOV AX, j", "MOV CX, j", "MOV DX, j", "MOV BX, j", "MOV SP, j", "MOV BP, j", "MOV SI, j", "MOV DI, j",
|
|
|
|
//C0-CF
|
|
"w, i", "w, i", "RET j", "RET", "LES o", "LDS o", "MOV n, i", "MOV n, j",
|
|
"ENTER j, i", "LEAVE", "RETF j", "RETF", "INT 3", "INT i" , "INTO", "IRET",
|
|
|
|
//D0-DF
|
|
"w", "w", "w, CL", "w, CL", "AAM i", "AAD i", "SALC", "XLAT",
|
|
"UNDEFINED_D8", "UNDEFINED_D9", "UNDEFINED_DA", "UNDEFINED_DB", "UNDEFINED_DC", "UNDEFINED_DD", "UNDEFINED_DE", "UNDEFINED_DF",
|
|
|
|
//E0-EF
|
|
"LOOPNZ r", "LOOPZ r", "LOOP r", "JCXZ r", "IN AL, i", "IN AX, i", "OUT i, AL", "OUT i, AX",
|
|
"CALL s", "JMP s", "JMP t", "JMP r", "IN AL, DX", "IN AX, DX", "OUT DX, AL", "OUT DX, AX",
|
|
|
|
//F0-FF
|
|
"LOCK", "UNDEFINED_F1", "REPNZ", "REPZ", "HLT", "CMC", "x", "x",
|
|
"CLC", "STC", "CLI", "STI", "CLD", "STD", "y", "z"
|
|
};
|
|
|
|
void WsDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
|
|
{
|
|
FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly);
|
|
|
|
uint8_t* byteCode = info.GetByteCode();
|
|
uint8_t opCode = byteCode[0];
|
|
|
|
WsSegment segment = WsSegment::Default;
|
|
while(IsPrefix(opCode)) {
|
|
switch(opCode) {
|
|
case 0x26: segment = WsSegment::ES; break;
|
|
case 0x36: segment = WsSegment::SS; break;
|
|
case 0x2E: segment = WsSegment::CS; break;
|
|
case 0x3E: segment = WsSegment::DS; break;
|
|
|
|
case 0xF0:
|
|
case 0xF2:
|
|
case 0xF3:
|
|
//rep, lock
|
|
str.WriteAll(_opTemplate[opCode], ' ');
|
|
break;
|
|
}
|
|
|
|
byteCode++;
|
|
opCode = byteCode[0];
|
|
}
|
|
|
|
const char* op = _opTemplate[opCode];
|
|
|
|
int i = 0;
|
|
while(op[i]) {
|
|
switch(op[i]) {
|
|
//Address offset
|
|
case 'a':
|
|
GetSegment(str, segment, "DS");
|
|
str.WriteAll("[$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))), "]");
|
|
break;
|
|
|
|
//Immediate values
|
|
case 'i': str.WriteAll("$", HexUtilities::ToHex(byteCode[1])); byteCode++; break;
|
|
case 'j': str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8)))); byteCode += 2; break;
|
|
|
|
//ModRM (2 operands)
|
|
case 'm': {
|
|
bool word = opCode & 0x01;
|
|
bool direction = opCode & 0x02;
|
|
|
|
if(direction) {
|
|
GetModRegParam(str, byteCode, word);
|
|
str.Write(", ");
|
|
GetModRmParam(str, byteCode, segment, word);
|
|
} else {
|
|
GetModRmParam(str, byteCode, segment, word);
|
|
str.Write(", ");
|
|
GetModRegParam(str, byteCode, word);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//ModRM (+ immediate value)
|
|
case 'n': {
|
|
bool word = op[i] == 'l';
|
|
byteCode += GetModRmParam(str, byteCode, segment, word);
|
|
break;
|
|
}
|
|
|
|
//ModRM (2 operands, LEA/LDS/LES)
|
|
case 'o':
|
|
GetModRegParam(str, byteCode, true);
|
|
str.Write(", ");
|
|
GetModRmParam(str, byteCode, segment, true, true);
|
|
break;
|
|
|
|
//ModRM (2 operands, BOUND)
|
|
case 'p':
|
|
GetModRegParam(str, byteCode, true);
|
|
str.Write(", ");
|
|
GetModRmParam(str, byteCode, segment, true, false);
|
|
break;
|
|
|
|
//ModRM (mov segment)
|
|
case 'q': {
|
|
bool direction = opCode & 0x02;
|
|
if(direction) {
|
|
GetModSegRegParam(str, byteCode);
|
|
str.Write(", ");
|
|
GetModRmParam(str, byteCode, segment, true);
|
|
} else {
|
|
GetModRmParam(str, byteCode, segment, true);
|
|
str.Write(", ");
|
|
GetModSegRegParam(str, byteCode);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//Relative jumps
|
|
case 'r': GetJmpDestination(str, byteCode, 1); break;
|
|
case 's': GetJmpDestination(str, byteCode, 2); break;
|
|
case 't': GetJmpDestination(str, byteCode, 4); break;
|
|
|
|
case 'v': //GRP1
|
|
switch((byteCode[1] >> 3) & 0x07) {
|
|
case 0: str.Write("ADD "); break;
|
|
case 1: str.Write("OR "); break;
|
|
case 2: str.Write("ADC "); break;
|
|
case 3: str.Write("SBB "); break;
|
|
case 4: str.Write("AND "); break;
|
|
case 5: str.Write("SUB "); break;
|
|
case 6: str.Write("XOR "); break;
|
|
case 7: str.Write("CMP "); break;
|
|
}
|
|
|
|
byteCode += GetModRmParam(str, byteCode, segment, opCode & 0x01);
|
|
|
|
str.Write(", ");
|
|
if((opCode & 0x03) == 0x01) {
|
|
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
|
|
} else if((opCode & 0x03) == 0x03) {
|
|
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(int16_t)(int8_t)byteCode[1]));
|
|
} else {
|
|
str.WriteAll("$", HexUtilities::ToHex(byteCode[1]));
|
|
}
|
|
break;
|
|
|
|
case 'w': { //GRP2
|
|
switch((byteCode[1] >> 3) & 0x07) {
|
|
case 0: str.Write("ROL "); break;
|
|
case 1: str.Write("ROR "); break;
|
|
case 2: str.Write("RCL "); break;
|
|
case 3: str.Write("RCR "); break;
|
|
case 4: str.Write("SHL "); break;
|
|
case 5: str.Write("SHR "); break;
|
|
case 6: str.Write("UNDEFINED_GRP2_6 "); break;
|
|
case 7: str.Write("SAR "); break;
|
|
}
|
|
|
|
//Followed by an immediate value for C0/C1
|
|
byteCode += GetModRmParam(str, byteCode, segment, opCode & 0x01);
|
|
break;
|
|
}
|
|
|
|
case 'x': { //GRP3
|
|
uint8_t mode = (byteCode[1] >> 3) & 0x07;
|
|
bool word = opCode & 0x01;
|
|
switch(mode) {
|
|
case 0: str.Write("TEST "); break;
|
|
case 1: str.Write("UNDEFINED_GRP3_1 "); break;
|
|
case 2: str.Write("NOT "); break;
|
|
case 3: str.Write("NEG "); break;
|
|
case 4: str.WriteAll("MUL ", word ? "AX, " : ""); break;
|
|
case 5: str.WriteAll("IMUL ", word ? "AX, " : ""); break;
|
|
case 6: str.WriteAll("DIV ", word ? "AX, " : ""); break;
|
|
case 7: str.WriteAll("IDIV ", word ? "AX, " : ""); break;
|
|
}
|
|
byteCode += GetModRmParam(str, byteCode, segment, word);
|
|
if(mode == 0) {
|
|
if(word) {
|
|
str.WriteAll(", $", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
|
|
} else {
|
|
str.WriteAll(", $", HexUtilities::ToHex(byteCode[1]));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'y': //grp 4
|
|
case 'z': //grp 5
|
|
switch((byteCode[1] >> 3) & 0x07) {
|
|
case 0: str.Write("INC "); break;
|
|
case 1: str.Write("DEC "); break;
|
|
case 2: str.Write("CALL "); break;
|
|
case 3: str.Write("CALL FAR "); break;
|
|
case 4: str.Write("JMP "); break;
|
|
case 5: str.Write("JMP FAR "); break;
|
|
case 6: str.Write("PUSH "); break;
|
|
case 7: str.Write("UNDEFINED_GRP5_7 "); break;
|
|
}
|
|
GetModRmParam(str, byteCode, segment, opCode & 0x01);
|
|
break;
|
|
|
|
default: str.Write(op[i]); break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
out += str.ToString();
|
|
}
|
|
|
|
void WsDisUtils::GetJmpDestination(FastString& str, uint8_t* byteCode, uint8_t size)
|
|
{
|
|
if(size == 4) {
|
|
str.WriteAll("$", HexUtilities::ToHex((uint16_t)(byteCode[3] | (byteCode[4] << 8))));
|
|
str.WriteAll(":$", HexUtilities::ToHex((uint16_t)(byteCode[1] | (byteCode[2] << 8))));
|
|
} else if(size == 2) {
|
|
uint16_t jmpOffset = (uint16_t)(byteCode[1] | (byteCode[2] << 8));
|
|
str.WriteAll("+$", HexUtilities::ToHex(jmpOffset));
|
|
} else if(size == 1) {
|
|
uint8_t jmpOffset = (uint8_t)byteCode[1];
|
|
str.WriteAll("+$", HexUtilities::ToHex(jmpOffset));
|
|
}
|
|
}
|
|
|
|
void WsDisUtils::GetSegment(FastString& str, WsSegment segment, const char* defaultSegment)
|
|
{
|
|
switch(segment) {
|
|
default: case WsSegment::Default: str.WriteAll(defaultSegment, ':'); break;
|
|
case WsSegment::ES: str.Write("ES:"); break;
|
|
case WsSegment::SS: str.Write("SS:"); break;
|
|
case WsSegment::CS: str.Write("CS:"); break;
|
|
case WsSegment::DS: str.Write("DS:"); break;
|
|
}
|
|
}
|
|
|
|
void WsDisUtils::GetModRegParam(FastString& str, uint8_t* byteCode, bool word)
|
|
{
|
|
uint8_t modRm = byteCode[1];
|
|
uint8_t reg = (modRm >> 3) & 0x07;
|
|
str.Write(word ? _modRegLut16[reg] : _modRegLut8[reg]);
|
|
}
|
|
|
|
void WsDisUtils::GetModSegRegParam(FastString& str, uint8_t* byteCode)
|
|
{
|
|
uint8_t modRm = byteCode[1];
|
|
uint8_t reg = (modRm >> 3) & 0x03;
|
|
str.Write(_modSegRegLut[reg]);
|
|
}
|
|
|
|
int WsDisUtils::GetModRmParam(FastString& str, uint8_t* byteCode, WsSegment segment, bool word, bool forLeaLdsLes)
|
|
{
|
|
uint8_t modRm = byteCode[1];
|
|
uint8_t mode = (modRm >> 6) & 0x03;
|
|
uint8_t rm = modRm & 0x07;
|
|
int len = 1;
|
|
|
|
string param;
|
|
string param2;
|
|
|
|
if(mode == 3) {
|
|
if(forLeaLdsLes) {
|
|
switch(rm) {
|
|
case 0x00: GetSegment(str, segment, "DS"); str.Write("[BX+AX]"); break;
|
|
case 0x01: GetSegment(str, segment, "DS"); str.Write("[BX+CX]"); break;
|
|
case 0x02: GetSegment(str, segment, "SS"); str.Write("[BP+DX]"); break;
|
|
case 0x03: GetSegment(str, segment, "SS"); str.Write("[BP+BX]"); break;
|
|
case 0x04: GetSegment(str, segment, "DS"); str.Write("[SP+SI]"); break;
|
|
case 0x05: GetSegment(str, segment, "DS"); str.Write("[BP+DI]"); break;
|
|
case 0x06: GetSegment(str, segment, "SS"); str.Write("[BP+SI]"); break;
|
|
case 0x07: GetSegment(str, segment, "DS"); str.Write("[BX+DI]"); break;
|
|
}
|
|
} else {
|
|
str.Write(word ? _modRegLut16[rm] : _modRegLut8[rm]);
|
|
}
|
|
} else if(mode == 0 && rm == 0x06) {
|
|
GetSegment(str, segment, "DS");
|
|
str.WriteAll("[$", HexUtilities::ToHex((uint16_t)(byteCode[2] | (byteCode[3] << 8))), ']');
|
|
len += 2;
|
|
} else {
|
|
switch(rm) {
|
|
case 0x00: GetSegment(str, segment, "DS"); str.Write("[BX+SI"); break;
|
|
case 0x01: GetSegment(str, segment, "DS"); str.Write("[BX+DI"); break;
|
|
case 0x02: GetSegment(str, segment, "SS"); str.Write("[BP+SI"); break;
|
|
case 0x03: GetSegment(str, segment, "SS"); str.Write("[BP+DI"); break;
|
|
case 0x04: GetSegment(str, segment, "DS"); str.Write("[SI"); break;
|
|
case 0x05: GetSegment(str, segment, "DS"); str.Write("[DI"); break;
|
|
case 0x06: GetSegment(str, segment, "SS"); str.Write("[BP"); break;
|
|
case 0x07: GetSegment(str, segment, "DS"); str.Write("[BX"); break;
|
|
}
|
|
|
|
if(mode == 1) {
|
|
str.WriteAll("+$", HexUtilities::ToHex((uint8_t)byteCode[2]), ']');
|
|
len++;
|
|
} else if(mode == 2) {
|
|
str.WriteAll("+$", HexUtilities::ToHex((uint16_t)(byteCode[2] | (byteCode[3] << 8))), ']');
|
|
len += 2;
|
|
} else {
|
|
str.Write(']');
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
EffectiveAddressInfo WsDisUtils::GetEffectiveAddress(DisassemblyInfo& info, WsConsole* console, WsCpuState& state)
|
|
{
|
|
DummyWsCpu dummyCpu(nullptr, console->GetMemoryManager());
|
|
dummyCpu.SetDummyState(state);
|
|
dummyCpu.Exec();
|
|
|
|
uint16_t opCode = WsDisUtils::GetFullOpCode(info);
|
|
if(WsDisUtils::IsReturnInstruction(opCode) || WsDisUtils::IsPushPopInstruction(opCode)) {
|
|
return {};
|
|
}
|
|
|
|
if((uint8_t)opCode == 0xC8 || (uint8_t)opCode == 0xC9) {
|
|
//Show nothing for Enter/Leave
|
|
return {};
|
|
}
|
|
|
|
if(WsDisUtils::IsConditionalJump(opCode) || WsDisUtils::IsUnconditionalJump(opCode)) {
|
|
EffectiveAddressInfo result;
|
|
result.ShowAddress = true;
|
|
result.Address = dummyCpu.GetProgramCounter();
|
|
result.Type = MemoryType::WsMemory;
|
|
return result;
|
|
}
|
|
|
|
//For everything else, return the last read/write address
|
|
uint32_t count = dummyCpu.GetOperationCount();
|
|
for(int i = count - 1; i > 0; i--) {
|
|
MemoryOperationInfo opInfo = dummyCpu.GetOperationInfo(i);
|
|
|
|
if(opInfo.Type != MemoryOperationType::ExecOperand) {
|
|
MemoryOperationInfo prevOpInfo = dummyCpu.GetOperationInfo(i - 1);
|
|
EffectiveAddressInfo result;
|
|
result.ShowAddress = true;
|
|
|
|
if(dummyCpu.IsWordAccess(i)) {
|
|
result.ValueSize = 2;
|
|
if(dummyCpu.IsWordAccess(i - 1) && prevOpInfo.Type == opInfo.Type && prevOpInfo.Address == opInfo.Address - 2) {
|
|
result.Address = prevOpInfo.Address;
|
|
result.Type = prevOpInfo.MemType;
|
|
} else {
|
|
result.Address = opInfo.Address;
|
|
result.Type = opInfo.MemType;
|
|
}
|
|
} else {
|
|
if(prevOpInfo.Type == opInfo.Type && prevOpInfo.Address == opInfo.Address - 1) {
|
|
//For 16-bit read/writes that were split into 2 8-bit accesses, return the first address
|
|
result.Address = prevOpInfo.Address;
|
|
result.Type = prevOpInfo.MemType;
|
|
result.ValueSize = 2;
|
|
} else {
|
|
result.Address = opInfo.Address;
|
|
result.Type = opInfo.MemType;
|
|
result.ValueSize = 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
int WsDisUtils::GetModRmSize(uint8_t modRm)
|
|
{
|
|
uint8_t mode = (modRm >> 6) & 0x03;
|
|
uint8_t rm = modRm & 0x07;
|
|
if(mode == 2 || (mode == 0 && rm == 0x06)) {
|
|
return 3;
|
|
} else if(mode == 1) {
|
|
return 2;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
bool WsDisUtils::IsPrefix(uint8_t opCode)
|
|
{
|
|
return opCode == 0xF0 || opCode == 0xF2 || opCode == 0xF3 || opCode == 0x26 || opCode == 0x36 || opCode == 0x2E || opCode == 0x3F;
|
|
}
|
|
|
|
uint8_t WsDisUtils::GetOpSize(uint32_t cpuAddress, MemoryType memType, MemoryDumper* memoryDumper)
|
|
{
|
|
uint8_t size = 1;
|
|
uint8_t opCode = memoryDumper->GetMemoryValue(memType, cpuAddress);
|
|
while(IsPrefix(opCode)) {
|
|
cpuAddress++;
|
|
opCode = memoryDumper->GetMemoryValue(memType, cpuAddress);
|
|
size++;
|
|
}
|
|
|
|
const char* op = _opTemplate[opCode];
|
|
|
|
int i = 0;
|
|
|
|
while(op[i]) {
|
|
switch(op[i]) {
|
|
//Absolute address
|
|
case 'a': size+=2; break;
|
|
|
|
//Immediate values
|
|
case 'i': size++; break;
|
|
case 'j': size += 2; break;
|
|
|
|
//ModRM
|
|
case 'm':
|
|
case 'n':
|
|
case 'o':
|
|
case 'p':
|
|
case 'q':
|
|
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
|
|
break;
|
|
|
|
//Relative jumps
|
|
case 'r': size++; break;
|
|
case 's': size += 2; break;
|
|
case 't': size += 4; break;
|
|
|
|
case 'v': //grp1
|
|
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
|
|
size++;
|
|
if((opCode & 0x3) == 0x01) {
|
|
size++;
|
|
}
|
|
break;
|
|
|
|
case 'x': { //grp3
|
|
uint8_t modRm = memoryDumper->GetMemoryValue(memType, cpuAddress + 1);
|
|
size += GetModRmSize(modRm);
|
|
if(((modRm >> 3) & 0x07) == 0) {
|
|
size += (opCode & 0x01) ? 2 : 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'w': //grp2
|
|
case 'y': //grp4
|
|
case 'z': //grp5
|
|
size += GetModRmSize(memoryDumper->GetMemoryValue(memType, cpuAddress + 1));
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
uint16_t WsDisUtils::GetFullOpCode(uint16_t cs, uint16_t ip, WsMemoryManager* memoryManager)
|
|
{
|
|
int i = 0;
|
|
while(IsPrefix(memoryManager->DebugRead((cs << 4) + ip)) && i < 6) {
|
|
ip++;
|
|
i++;
|
|
}
|
|
|
|
return (
|
|
memoryManager->DebugRead((cs << 4) + ip) |
|
|
(memoryManager->DebugRead((cs << 4) + (uint16_t)(ip + 1)) << 8)
|
|
);
|
|
}
|
|
|
|
|
|
uint16_t WsDisUtils::GetFullOpCode(DisassemblyInfo& disInfo)
|
|
{
|
|
uint8_t* byteCode = disInfo.GetByteCode();
|
|
|
|
int i = 0;
|
|
while(IsPrefix(byteCode[i]) && i < 6) {
|
|
i++;
|
|
}
|
|
|
|
return byteCode[i] | (byteCode[i+1] << 8);
|
|
}
|
|
|
|
bool WsDisUtils::IsJumpToSub(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
case 0x9A:
|
|
case 0xE8:
|
|
return true;
|
|
|
|
case 0xFE:
|
|
case 0xFF: {
|
|
uint8_t mode = (opCode >> 11) & 0x07;
|
|
switch(mode) {
|
|
case 2: //CALL
|
|
case 3: //CALL FAR
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WsDisUtils::IsReturnInstruction(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
case 0xC2: //RET
|
|
case 0xC3: //RET
|
|
case 0xCA: //RETF
|
|
case 0xCB: //RETF
|
|
case 0xCF: //IRET
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WsDisUtils::IsUnconditionalJump(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
case 0x9A: //CALL
|
|
case 0xC2: //RET
|
|
case 0xC3: //RET
|
|
case 0xCA: //RETF
|
|
case 0xCB: //RETF
|
|
case 0xCF: //IRET
|
|
case 0xE8: //CALL relative
|
|
case 0xE9: //JMP
|
|
case 0xEA: //JMP
|
|
case 0xEB: //JMP
|
|
return true;
|
|
|
|
case 0xFE:
|
|
case 0xFF: {
|
|
uint8_t mode = (opCode >> 11) & 0x07;
|
|
switch(mode) {
|
|
case 2: //CALL
|
|
case 3: //CALL FAR
|
|
case 4: //JMP
|
|
case 5: //JMP FAR
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WsDisUtils::IsConditionalJump(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
case 0x70: //JO
|
|
case 0x71: //JNO
|
|
case 0x72: //JB
|
|
case 0x73: //JNB
|
|
case 0x74: //JZ
|
|
case 0x75: //JNZ
|
|
case 0x76: //JBE
|
|
case 0x77: //JA
|
|
case 0x78: //JS
|
|
case 0x79: //JNS
|
|
case 0x7A: //JPE
|
|
case 0x7B: //JPO
|
|
case 0x7C: //JL
|
|
case 0x7D: //JGE
|
|
case 0x7E: //JLE
|
|
case 0x7F: //JG
|
|
case 0xE0: //LOOPNZ
|
|
case 0xE1: //LOOPZ
|
|
case 0xE2: //LOOP
|
|
case 0xE3: //JCXZ
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WsDisUtils::IsUndefinedOpCode(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
case 0x0F:
|
|
case 0x63:
|
|
case 0x64:
|
|
case 0x65:
|
|
case 0x66:
|
|
case 0x67:
|
|
case 0xD8:
|
|
case 0xD9:
|
|
case 0xDA:
|
|
case 0xDB:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
case 0xDE:
|
|
case 0xDF:
|
|
case 0xF1:
|
|
return true;
|
|
|
|
case 0xC0:
|
|
case 0xC1:
|
|
case 0xD0:
|
|
case 0xD1:
|
|
case 0xD2:
|
|
case 0xD3:
|
|
return ((opCode >> 11) & 0x07) == 0x06;
|
|
|
|
case 0xF6:
|
|
case 0xF7:
|
|
return ((opCode >> 11) & 0x07) == 0x01;
|
|
|
|
case 0xFE:
|
|
case 0xFF:
|
|
return ((opCode >> 11) & 0x07) == 0x07;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool WsDisUtils::IsPushPopInstruction(uint16_t opCode)
|
|
{
|
|
switch((uint8_t)opCode) {
|
|
//Push/pop segments
|
|
case 0x06: case 0x07: case 0x0E: case 0x16:
|
|
case 0x17: case 0x1E: case 0x1F:
|
|
|
|
//Push
|
|
case 0x50: case 0x51: case 0x52: case 0x53:
|
|
case 0x54: case 0x55: case 0x56: case 0x57:
|
|
|
|
//Pop
|
|
case 0x58: case 0x59: case 0x5A: case 0x5B:
|
|
case 0x5C: case 0x5D: case 0x5E: case 0x5F:
|
|
|
|
//Push/pop all
|
|
case 0x60: case 0x61:
|
|
|
|
//Push immediate
|
|
case 0x68: case 0x6A:
|
|
|
|
//Push/pop flags
|
|
case 0x9C: case 0x9D:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CdlFlags::CdlFlags WsDisUtils::GetOpFlags(uint16_t opCode, uint32_t pc, uint32_t prevPc, uint8_t opSize)
|
|
{
|
|
if(WsDisUtils::IsJumpToSub(opCode)) {
|
|
return CdlFlags::SubEntryPoint;
|
|
} else if(WsDisUtils::IsUnconditionalJump(opCode) && !WsDisUtils::IsReturnInstruction(opCode)) {
|
|
return CdlFlags::JumpTarget;
|
|
} else if(WsDisUtils::IsConditionalJump(opCode) && (pc != prevPc + opSize)) {
|
|
return CdlFlags::JumpTarget;
|
|
}
|
|
|
|
return CdlFlags::None;
|
|
}
|
|
|
|
void WsDisUtils::UpdateAddressCsIp(uint32_t addr, WsCpuState& state)
|
|
{
|
|
uint32_t baseAddr = (state.CS << 4);
|
|
if(addr >= baseAddr && addr <= baseAddr + 0xFFFF) {
|
|
//address fits within the current code segment, only change the instruction pointer
|
|
state.IP = addr - baseAddr;
|
|
} else {
|
|
//address is outside the range of the current CS, change both values
|
|
state.CS = (addr & 0xF0000) >> 4;
|
|
state.IP = (addr & 0xFFFF);
|
|
}
|
|
}
|