xenia-canary/src/alloy/backend/ivm/ivm_function.cc

180 lines
5.1 KiB
C++

/**
******************************************************************************
* 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/ivm/ivm_stack.h>
#include <alloy/backend/tracing.h>
#include <alloy/runtime/runtime.h>
#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),
source_map_count_(0), source_map_(0),
Function(symbol_info) {
}
IVMFunction::~IVMFunction() {
xe_free(intcodes_);
xe_free(source_map_);
}
void IVMFunction::Setup(TranslationContext& ctx) {
register_count_ = ctx.register_count;
stack_size_ = ctx.stack_size;
intcode_count_ = ctx.intcode_count;
intcodes_ = (IntCode*)ctx.intcode_arena->CloneContents();
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;
}
int IVMFunction::AddBreakpointImpl(Breakpoint* breakpoint) {
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;
return 0;
}
int IVMFunction::RemoveBreakpointImpl(Breakpoint* breakpoint) {
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) {
uint64_t breakpoint_ptr = (uint64_t)old_breakpoint;
i->src2_reg = (uint32_t)breakpoint_ptr;
i->src3_reg = (uint32_t)(breakpoint_ptr >> 32);
}
}
return 0;
}
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);
}
#undef TRACE_SOURCE_OFFSET
int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) {
// Setup register file on stack.
auto stack = (IVMStack*)thread_state->backend_data();
auto register_file = (Register*)stack->Alloc(register_count_);
auto local_stack = (uint8_t*)alloca(stack_size_);
Memory* memory = thread_state->memory();
IntCodeState ics;
ics.rf = register_file;
ics.locals = local_stack;
ics.context = (uint8_t*)thread_state->raw_context();
ics.membase = memory->membase();
ics.page_table = ics.membase + memory->page_table();
ics.did_carry = 0;
ics.did_saturate = 0;
ics.thread_state = thread_state;
ics.return_address = return_address;
ics.call_return_address = 0;
volatile int* suspend_flag_address = thread_state->suspend_flag_address();
// TODO(benvanik): DID_CARRY -- need HIR to set a OPCODE_FLAG_SET_CARRY
// or something so the fns can set an ics flag.
#ifdef TRACE_SOURCE_OFFSET
size_t source_index = 0;
#endif
uint32_t ia = 0;
while (true) {
// Check suspend. We could do this only on certain instructions, if we
// wanted to speed things up.
if (*suspend_flag_address) {
thread_state->EnterSuspend();
}
#ifdef TRACE_SOURCE_OFFSET
uint64_t source_offset = -1;
if (source_index < this->source_map_count_ &&
this->source_map_[source_index].intcode_index <= ia) {
while (source_index + 1 < this->source_map_count_ &&
this->source_map_[source_index + 1].intcode_index <= ia) {
source_index++;
}
source_offset = this->source_map_[source_index].source_offset;
}
#endif
IntCode* i = &intcodes_[ia];
if (i->debug_flags) {
OnBreakpointHit(thread_state, i);
}
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;
#ifdef TRACE_SOURCE_OFFSET
source_index = 0;
#endif
}
}
stack->Free(register_count_);
return 0;
}