ARM2ARM recompiler for melonds
I work on this with my UWP port. Please try to compile Capstone with ARM only and as C++ code. Incomplete instructions emitter, will work on soon, but Dynarmic example kinda works.
Small example that works:
#include <Arme/Arme.h>
struct callback_data
{
std::array<char, 2048> memory;
std::uint32_t ticks_left;
};
static void write_memory8(void *userdata, arme::address addr, std::uint8_t w)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
data->memory[addr] = w;
}
static void write_memory16(void *userdata, arme::address addr, std::uint16_t w)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
*reinterpret_cast<std::uint16_t*>(&(data->memory[addr])) = w;
}
static void write_memory32(void *userdata, arme::address addr, std::uint32_t w)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
*reinterpret_cast<std::uint32_t*>(&(data->memory[addr])) = w;
}
static std::uint8_t read_memory8(void *userdata, arme::address addr)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
return data->memory[addr];
}
static std::uint16_t read_memory16(void *userdata, arme::address addr)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
return *reinterpret_cast<std::uint16_t*>(&(data->memory[addr]));
}
static std::uint32_t read_memory32(void *userdata, arme::address addr)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
return *reinterpret_cast<std::uint32_t*>(&(data->memory[addr]));
}
static void add_ticks(void *userdata, std::uint32_t ticks)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
if (ticks > data->ticks_left) {
data->ticks_left = 0;
return;
}
data->ticks_left -= ticks;
}
static std::uint32_t get_remaining_ticks(void *userdata)
{
callback_data *data = reinterpret_cast<callback_data*>(userdata);
return data->ticks_left;
}
int main()
{
callback_data cbd;
arme::jit_callback callback;
callback.write_mem16 = write_memory16;
callback.write_mem32 = write_memory32;
callback.write_mem8 = write_memory8;
callback.read_mem16 = read_memory16;
callback.read_mem32 = read_memory32;
callback.read_mem8 = read_memory8;
callback.userdata = &cbd;
callback.add_cycles = add_ticks;
callback.get_remaining_cycles = get_remaining_ticks;
cbd.ticks_left = 1;
arme::jit dejit{ callback };
dejit.state.regs[15] = 0;
dejit.state.regs[0] = 1;
dejit.state.regs[1] = 2;
write_memory32(&cbd, 0, 0xE0811000); // ADD r0, r0, r1
write_memory32(&cbd, 4, 0xEA000000); // B +-0
dejit.execute();
assert((dejit.state.regs[0] == 3) && "Unexpected value");
}
The JIT supported the following architecture:
- Guest: ARMv4, ARMv3
- Host: ARMv5 and upper
Support:
- Low level exception handling: specify the exception base address in jit state.
- Varies of function callback: writing callback function in static C function with userdata available!
- Switching execution mode: between SVC, ABT etc
Limitation:
- Not cycle accurate
- Code generation not optimized
Speed should be x3 - x5 faster than interpreter in many cases (tested on Nokia Lumia 525 Win10 Mobile). Instructions WIP, CP opcode not finish yet.