2013-12-07 07:57:16 +01:00
|
|
|
/**
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
|
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
|
|
|
******************************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <alloy/backend/ivm/ivm_function.h>
|
|
|
|
|
|
|
|
|
|
#include <alloy/backend/tracing.h>
|
2013-12-07 22:09:22 +01:00
|
|
|
#include <alloy/runtime/runtime.h>
|
2013-12-07 07:57:16 +01:00
|
|
|
#include <alloy/runtime/thread_state.h>
|
|
|
|
|
|
|
|
|
|
using namespace alloy;
|
|
|
|
|
using namespace alloy::backend;
|
|
|
|
|
using namespace alloy::backend::ivm;
|
|
|
|
|
using namespace alloy::runtime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IVMFunction::IVMFunction(FunctionInfo* symbol_info) :
|
|
|
|
|
register_count_(0), intcode_count_(0), intcodes_(0),
|
2013-12-23 08:04:24 +01:00
|
|
|
source_map_count_(0), source_map_(0),
|
2013-12-07 07:57:16 +01:00
|
|
|
GuestFunction(symbol_info) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IVMFunction::~IVMFunction() {
|
|
|
|
|
xe_free(intcodes_);
|
2013-12-23 08:04:24 +01:00
|
|
|
xe_free(source_map_);
|
2013-12-07 07:57:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IVMFunction::Setup(TranslationContext& ctx) {
|
|
|
|
|
register_count_ = ctx.register_count;
|
|
|
|
|
intcode_count_ = ctx.intcode_count;
|
|
|
|
|
intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents();
|
2013-12-23 08:04:24 +01:00
|
|
|
source_map_count_ = ctx.source_map_count;
|
|
|
|
|
source_map_ = (SourceMapEntry*)ctx.source_map_arena->CloneContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IntCode* IVMFunction::GetIntCodeAtSourceOffset(uint64_t offset) {
|
|
|
|
|
for (size_t n = 0; n < source_map_count_; n++) {
|
|
|
|
|
auto entry = &source_map_[n];
|
|
|
|
|
if (entry->source_offset == offset) {
|
|
|
|
|
return &intcodes_[entry->intcode_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2013-12-07 07:57:16 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-23 07:03:06 +01:00
|
|
|
int IVMFunction::AddBreakpointImpl(Breakpoint* breakpoint) {
|
2013-12-23 08:04:24 +01:00
|
|
|
auto i = GetIntCodeAtSourceOffset(breakpoint->address());
|
|
|
|
|
if (!i) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TEMP breakpoints always overwrite normal ones.
|
|
|
|
|
if (!i->debug_flags ||
|
|
|
|
|
breakpoint->type() == Breakpoint::TEMP_TYPE) {
|
|
|
|
|
uint64_t breakpoint_ptr = (uint64_t)breakpoint;
|
|
|
|
|
i->src2_reg = (uint32_t)breakpoint_ptr;
|
|
|
|
|
i->src3_reg = (uint32_t)(breakpoint_ptr >> 32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Increment breakpoint counter.
|
|
|
|
|
++i->debug_flags;
|
|
|
|
|
|
2013-12-23 07:03:06 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int IVMFunction::RemoveBreakpointImpl(Breakpoint* breakpoint) {
|
2013-12-23 08:04:24 +01:00
|
|
|
auto i = GetIntCodeAtSourceOffset(breakpoint->address());
|
|
|
|
|
if (!i) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decrement breakpoint counter.
|
|
|
|
|
--i->debug_flags;
|
|
|
|
|
i->src2_reg = i->src3_reg = 0;
|
|
|
|
|
|
|
|
|
|
// If there were other breakpoints, see what they were.
|
|
|
|
|
if (i->debug_flags) {
|
|
|
|
|
auto old_breakpoint = FindBreakpoint(breakpoint->address());
|
|
|
|
|
if (old_breakpoint) {
|
2013-12-25 08:29:40 +01:00
|
|
|
uint64_t breakpoint_ptr = (uint64_t)old_breakpoint;
|
2013-12-23 08:04:24 +01:00
|
|
|
i->src2_reg = (uint32_t)breakpoint_ptr;
|
|
|
|
|
i->src3_reg = (uint32_t)(breakpoint_ptr >> 32);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-23 07:03:06 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-23 08:04:24 +01:00
|
|
|
void IVMFunction::OnBreakpointHit(ThreadState* thread_state, IntCode* i) {
|
|
|
|
|
uint64_t breakpoint_ptr = i->src2_reg | (uint64_t(i->src3_reg) << 32);
|
|
|
|
|
Breakpoint* breakpoint = (Breakpoint*)breakpoint_ptr;
|
|
|
|
|
|
|
|
|
|
// Notify debugger.
|
|
|
|
|
// The debugger may choose to wait (blocking us).
|
|
|
|
|
auto debugger = thread_state->runtime()->debugger();
|
|
|
|
|
debugger->OnBreakpointHit(thread_state, breakpoint);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-08 22:58:57 +01:00
|
|
|
int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) {
|
2013-12-07 07:57:16 +01:00
|
|
|
// Setup register file on stack.
|
2013-12-26 08:16:16 +01:00
|
|
|
const size_t max_stack_alloc = 16 * 1024;
|
2013-12-07 07:57:16 +01:00
|
|
|
size_t register_file_size = register_count_ * sizeof(Register);
|
2013-12-26 08:16:16 +01:00
|
|
|
Register* register_file = (Register*)(
|
|
|
|
|
register_file_size >= max_stack_alloc ?
|
|
|
|
|
xe_malloc(register_file_size) : alloca(register_file_size));
|
2013-12-07 07:57:16 +01:00
|
|
|
|
2013-12-08 23:15:28 +01:00
|
|
|
Memory* memory = thread_state->memory();
|
|
|
|
|
|
2013-12-07 07:57:16 +01:00
|
|
|
IntCodeState ics;
|
|
|
|
|
ics.rf = register_file;
|
|
|
|
|
ics.context = (uint8_t*)thread_state->raw_context();
|
2013-12-08 23:15:28 +01:00
|
|
|
ics.membase = memory->membase();
|
|
|
|
|
ics.reserve_address = memory->reserve_address();
|
2013-12-07 07:57:16 +01:00
|
|
|
ics.did_carry = 0;
|
2013-12-07 22:09:22 +01:00
|
|
|
ics.access_callbacks = thread_state->runtime()->access_callbacks();
|
2013-12-07 07:57:16 +01:00
|
|
|
ics.thread_state = thread_state;
|
2013-12-08 22:58:57 +01:00
|
|
|
ics.return_address = return_address;
|
|
|
|
|
ics.call_return_address = 0;
|
2013-12-07 07:57:16 +01:00
|
|
|
|
2013-12-24 04:46:35 +01:00
|
|
|
volatile int* suspend_flag_address = thread_state->suspend_flag_address();
|
|
|
|
|
|
2013-12-07 07:57:16 +01:00
|
|
|
// TODO(benvanik): DID_CARRY -- need HIR to set a OPCODE_FLAG_SET_CARRY
|
|
|
|
|
// or something so the fns can set an ics flag.
|
|
|
|
|
|
|
|
|
|
uint32_t ia = 0;
|
|
|
|
|
while (true) {
|
2013-12-24 04:46:35 +01:00
|
|
|
// Check suspend. We could do this only on certain instructions, if we
|
|
|
|
|
// wanted to speed things up.
|
|
|
|
|
if (*suspend_flag_address) {
|
|
|
|
|
thread_state->EnterSuspend();
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-23 08:04:24 +01:00
|
|
|
IntCode* i = &intcodes_[ia];
|
|
|
|
|
|
|
|
|
|
if (i->debug_flags) {
|
|
|
|
|
OnBreakpointHit(thread_state, i);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-07 07:57:16 +01:00
|
|
|
uint32_t new_ia = i->intcode_fn(ics, i);
|
|
|
|
|
if (new_ia == IA_NEXT) {
|
|
|
|
|
ia++;
|
|
|
|
|
} else if (new_ia == IA_RETURN) {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
ia = new_ia;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-26 08:16:16 +01:00
|
|
|
if (register_file_size >= max_stack_alloc) {
|
|
|
|
|
xe_free(register_file);
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-07 07:57:16 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|