187 lines
4.2 KiB
C++
187 lines
4.2 KiB
C++
//---------------------------------------------------------------------------//
|
|
// 1100101 |_ mov #0, r4 __ //
|
|
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
|
|
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
|
|
// |_ base# + offset |_| /_\_\___/__/ //
|
|
//---------------------------------------------------------------------------//
|
|
|
|
#include <fxos/disassembly.h>
|
|
#include <fxos/util/log.h>
|
|
#include <optional>
|
|
#include <array>
|
|
|
|
namespace FxOS {
|
|
|
|
/* Instruction map */
|
|
static std::array<std::optional<AsmInstruction>, 65536> insmap;
|
|
|
|
void register_instruction(AsmInstruction const &ins)
|
|
{
|
|
uint16_t opcode = ins.opcode;
|
|
|
|
if(insmap[opcode])
|
|
FxOS_log(ERR, "opcode collision between a %s and a %s at %04x",
|
|
insmap[opcode]->mnemonic, ins.mnemonic, opcode);
|
|
else
|
|
insmap[opcode] = ins;
|
|
}
|
|
|
|
//---
|
|
// Concrete (instantiated) arguments and instructions
|
|
//---
|
|
|
|
Argument::Argument()
|
|
{
|
|
location = RelConstDomain().bottom();
|
|
value = location;
|
|
syscall_id = -1;
|
|
}
|
|
|
|
Instruction::Instruction(AsmInstruction const *inst):
|
|
inst {inst}, args {}, opcode {inst->opcode},
|
|
leader {false}, delayslot {false}, terminal {false}, jump {false},
|
|
condjump {false}, jmptarget {0xffffffff}
|
|
{
|
|
}
|
|
|
|
Instruction::Instruction(uint16_t opcode):
|
|
inst {nullptr}, args {}, opcode {opcode},
|
|
leader {false}, delayslot {false}, terminal {false}, jump {false},
|
|
condjump {false}, jmptarget {0xffffffff}
|
|
{
|
|
}
|
|
|
|
//---
|
|
// Disassembler interface
|
|
//---
|
|
|
|
Disassembly::Disassembly(VirtualSpace &_space):
|
|
instructions {}, space {_space}
|
|
{
|
|
}
|
|
|
|
bool Disassembly::hasins(uint32_t pc)
|
|
{
|
|
return this->instructions.count(pc) > 0;
|
|
}
|
|
|
|
uint32_t Disassembly::minpc()
|
|
{
|
|
if(this->instructions.empty())
|
|
return 0xffffffff;
|
|
return this->instructions.cbegin()->first;
|
|
}
|
|
|
|
uint32_t Disassembly::maxpc()
|
|
{
|
|
if(this->instructions.empty())
|
|
return 0xffffffff;
|
|
return this->instructions.crbegin()->first;
|
|
}
|
|
|
|
Instruction &Disassembly::readins(uint32_t pc)
|
|
{
|
|
if(pc & 1) {
|
|
FxOS_log(ERR, "reading instruction for disassembly at %08x", pc);
|
|
pc &= -2;
|
|
}
|
|
|
|
try {
|
|
return this->instructions.at(pc);
|
|
}
|
|
catch(std::out_of_range &e) {
|
|
uint16_t opcode = this->space.read_u16(pc);
|
|
Instruction ci(opcode);
|
|
|
|
if(insmap[opcode])
|
|
ci = Instruction(&*insmap[opcode]);
|
|
|
|
this->instructions.emplace(pc, ci);
|
|
return this->instructions.at(pc);
|
|
}
|
|
}
|
|
|
|
//---
|
|
// Base pass
|
|
//---
|
|
|
|
DisassemblyPass::DisassemblyPass(Disassembly &disasm, std::string name):
|
|
m_disasm {disasm}, m_name {name}
|
|
{
|
|
}
|
|
|
|
void DisassemblyPass::enqueue(uint32_t pc)
|
|
{
|
|
if(m_next.count(pc))
|
|
return;
|
|
|
|
m_next.insert(pc);
|
|
m_queue.push(pc);
|
|
}
|
|
|
|
void DisassemblyPass::enqueue_next(uint32_t pc)
|
|
{
|
|
/* TODO: DisassemblyPass::enqueue_next is inefficient */
|
|
do pc += 2;
|
|
while(!m_disasm.hasins(pc));
|
|
|
|
enqueue(pc);
|
|
}
|
|
|
|
void DisassemblyPass::enqueue_unseen_successors(uint32_t pc, Instruction &i)
|
|
{
|
|
if(!i.terminal && !i.jump) {
|
|
if(!m_seen.count(pc + 2)) enqueue(pc + 2);
|
|
}
|
|
if(i.jump || i.condjump) {
|
|
if(!m_seen.count(i.jmptarget)) enqueue(i.jmptarget);
|
|
}
|
|
}
|
|
|
|
void DisassemblyPass::enqueue_all_successors(uint32_t pc, Instruction &i)
|
|
{
|
|
if(!i.terminal && !i.jump)
|
|
enqueue(pc + 2);
|
|
if(i.jump || i.condjump)
|
|
enqueue(i.jmptarget);
|
|
}
|
|
|
|
bool DisassemblyPass::run(uint32_t entry_pc)
|
|
{
|
|
enqueue(entry_pc);
|
|
|
|
while(m_queue.size()) {
|
|
uint32_t pc = m_queue.top();
|
|
|
|
m_queue.pop();
|
|
m_next.erase(m_next.find(pc));
|
|
|
|
Instruction &ci = m_disasm.readins(pc);
|
|
if(!analyze(pc, ci))
|
|
return false;
|
|
|
|
m_seen.insert(pc);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//---
|
|
// Base instruction-level pass
|
|
//---
|
|
|
|
InstructionDisassemblyPass::InstructionDisassemblyPass(Disassembly &disasm,
|
|
std::string name): DisassemblyPass(disasm, name)
|
|
{
|
|
}
|
|
|
|
bool InstructionDisassemblyPass::run()
|
|
{
|
|
for(auto &pair: m_disasm.instructions) {
|
|
if(!analyze(pair.first, pair.second))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} /* namespace FxOS */
|