void ddbg_step_over() { uint16_t inst, op_a, op_b, offset = 1, bp; inst = INSTRUCTION_GET_OP(vm->ram[vm->pc]); op_a = INSTRUCTION_GET_A(vm->ram[vm->pc]); op_b = INSTRUCTION_GET_B(vm->ram[vm->pc]); vm->sleep_cycles = 0; if (op_a == NXT) offset += 1; if (op_a == NXT_LIT) offset += 1; if (op_b == NXT) offset += 1; if (op_b == NXT_LIT) offset += 1; if (inst == NBOP_RESERVED) { if (op_b == NBOP_JSR) { bp = op_a; } } else { bp = vm->pc + offset; } list_append(&breakpoints, breakpoint_create(bp, true, true)); vm->halted = false; vm_execute(vm, NULL); // Handle custom Lua commands. dbg_lua_handle_hook(&lstate, NULL, bautofree(bfromcstr("next")), 0); }
void ddbg_precycle_hook(vm_t* vm, uint16_t pos, void* ud) { unsigned int i = 0; struct breakpoint* bk; uint16_t op, a, b; // Handle any symbols that are at this cycle. list_t* symbols = ddbg_get_symbols(vm->pc); list_iterator_start(symbols); while (list_iterator_hasnext(symbols)) dbg_lua_handle_hook_symbol(&lstate, NULL, bautofree((bstring)list_iterator_next(symbols))); list_iterator_stop(symbols); list_empty(symbols); free(symbols); // Handle custom Lua commands. dbg_lua_handle_hook(&lstate, NULL, bautofree(bfromcstr("precycle")), pos); // Check to see if Lua halted the VM and return if it did. if (vm->halted) return; // Handle breakpoints. if (!ignore_next_breakpoint) { for (i = 0; i < list_size(&breakpoints); i++) { bk = (struct breakpoint*)list_get_at(&breakpoints, i); if (vm->pc == bk->addr) { vm->halted = true; ignore_next_breakpoint = true; vm_hook_break(vm); // Required for UI to update correctly. if (bk->temporary) list_delete_at(&breakpoints, i--); if (!bk->silent) ddbg_disassemble(max_int32((int32_t)vm->pc - 10, 0x0), min_int32((int32_t)vm->pc + 10, 0x10000) - vm->pc); printd(LEVEL_DEFAULT, "Breakpoint hit at 0x%04X.\n", bk->addr); return; } } } ignore_next_breakpoint = false; // Handle backtrace. op = INSTRUCTION_GET_OP(vm->ram[vm->pc]); a = INSTRUCTION_GET_A(vm->ram[vm->pc]); b = INSTRUCTION_GET_B(vm->ram[vm->pc]); if ((op == OP_SET && b == PC) || (op == OP_NONBASIC && b == NBOP_JSR)) { // FIXME: This doesn't handle every valid value correctly.. if (a == PUSH_POP) list_delete_at(&backtrace, list_size(&backtrace) - 1); else if (a == NXT_LIT) { printd(LEVEL_DEBUG, "jumping literally from 0x%04X to 0x%04X (0x%04X).\n", vm->pc, vm->ram[vm->pc + 1], vm->pc + 1); list_append(&backtrace, backtrace_entry_create(vm->pc, vm->ram[vm->pc + 1])); } else if (a == NXT) { //list_append(&backtrace, backtrace_entry_create(vm->pc, vm->ram[vm->ram[vm->pc+1]])); } else { // Unhandled. printd(LEVEL_DEBUG, "warning: unhandled backtrace jump occurred.\n"); } } }
/// /// Disassembles a single instruction located at the specified position. /// /// Disassembles a single instruction without affecting the state of the /// virtual machine by saving the state of the PC and SP registers before /// doing value resolution and then restoring them afterwards. /// /// @param vm The virtual machine. /// @param pos The position in RAM of the instruction to disassemble. /// @param pretty Whether the 'pretty' substructure should be filled. The caller is responsible for destroying the bstrings. /// struct inst vm_disassemble(vm_t* vm, uint16_t pos, bool pretty) { struct inst result; uint16_t pc_store = vm->pc; uint16_t sp_store = vm->sp; uint8_t debug_store = vm->debug; struct instruction_mapping* instmap; struct register_mapping* regmap; // Set pretty structure to NULL by default. result.pretty.op = NULL; result.pretty.a = NULL; result.pretty.b = NULL; // Set correct VM state for a resolving read. vm->pc = pos; vm->debug = false; // Read the basic instruction data. result.original.full = vm->ram[vm->pc++]; result.original.op = INSTRUCTION_GET_OP(result.original.full); result.original.a = INSTRUCTION_GET_A(result.original.full); result.original.b = INSTRUCTION_GET_B(result.original.full); // Resolve values. if (result.original.op == OP_NONBASIC) { result.op = result.original.b; result.a = vm_resolve_value(vm, result.original.a, POS_A); result.b = 0x0; } else { result.op = result.original.op; result.a = vm_resolve_value(vm, result.original.a, POS_A); result.b = vm_resolve_value(vm, result.original.b, POS_B); } result.size = vm->pc - pos; result.extra[0] = 0x0; result.extra[1] = 0x0; result.next[0] = 0x0; result.next[1] = 0x0; result.used[0] = false; result.used[1] = false; if (result.size - 1 >= 1) result.extra[0] = vm->ram[pos + 1]; if (result.size - 1 >= 2) result.extra[1] = vm->ram[pos + 2]; if (result.size - 1 >= 2) { result.used[0] = true; result.used[1] = true; result.next[0] = vm->ram[pos + 1]; result.next[1] = vm->ram[pos + 2]; } else if (result.size - 1 >= 1 && (result.original.a == NXT || result.original.a == NXT_LIT || result.original.a == PICK || (result.original.a >= NXT_VAL_A && result.original.a <= NXT_VAL_J))) { result.used[0] = true; result.next[0] = vm->ram[pos + 1]; } else if (result.size - 1 >= 1 && (result.original.b == NXT || result.original.b == NXT_LIT || result.original.b == PICK || (result.original.b >= NXT_VAL_A && result.original.b <= NXT_VAL_J))) { result.used[1] = true; result.next[1] = vm->ram[pos + 1]; } // Work out pretty values if required. instmap = get_instruction_by_value(result.original.op, result.original.b); if (pretty && instmap != NULL) { // Work out the instruction name. result.pretty.op = bfromcstr(instmap->name); // Work out the a parameter. regmap = get_register_by_value(result.original.a); if (result.original.a >= REG_A && result.original.a <= REG_J) result.pretty.a = bfromcstr(regmap->name); else if (result.original.a >= VAL_A && result.original.a <= VAL_J) result.pretty.a = bformat("[%s]", regmap->name); else if (result.original.a >= NXT_VAL_A && result.original.a <= NXT_VAL_J) result.pretty.a = bformat("[%s+0x%04X]", regmap->name, result.next[0]); else if (result.original.a == PUSH_POP) result.pretty.a = bfromcstr("POP"); else if (result.original.a == PEEK) result.pretty.a = bfromcstr("[SP]"); else if (result.original.a == PICK) result.pretty.a = bformat("[SP+0x%04X]", result.next[0]); else if (result.original.a >= SP && result.original.a <= EX) result.pretty.a = bfromcstr(regmap->name); else if (result.original.a == NXT) result.pretty.a = bformat("[0x%04X]", result.next[0]); else if (result.original.a == NXT_LIT) result.pretty.a = bformat("0x%04X", result.next[0]); else if (result.original.a >= 0x20 && result.original.a <= 0x3F) result.pretty.a = bformat("0x%04X", result.original.a - 0x21); else result.pretty.a = bfromcstr("???"); // Only fill the b parameter if the opcode was basic. if (result.pretty.op != OP_NONBASIC) { regmap = get_register_by_value(result.original.b); if (result.original.b >= REG_A && result.original.b <= REG_J) result.pretty.b = bfromcstr(regmap->name); else if (result.original.b >= VAL_A && result.original.b <= VAL_J) result.pretty.b = bformat("[%s]", regmap->name); else if (result.original.b >= NXT_VAL_A && result.original.b <= NXT_VAL_J) result.pretty.b = bformat("[%s+0x%04X]", regmap->name, result.next[1]); else if (result.original.b == PUSH_POP) result.pretty.b = bfromcstr("PUSH"); else if (result.original.b == PEEK) result.pretty.b = bfromcstr("[SP]"); else if (result.original.b == PICK) result.pretty.b = bformat("[SP+0x%04X]", result.next[1]); else if (result.original.b >= SP && result.original.b <= EX) result.pretty.b = bfromcstr(regmap->name); else if (result.original.b == NXT) result.pretty.b = bformat("[0x%04X]", result.next[1]); else if (result.original.b == NXT_LIT) result.pretty.b = bformat("0x%04X", result.next[1]); else if (result.original.b >= 0x20 && result.original.b <= 0x3F) result.pretty.b = bformat("0x%04X", result.original.b - 0x21); else result.pretty.b = bfromcstr("???"); } } // Restore state. vm->pc = pc_store; vm->sp = sp_store; vm->debug = debug_store; // Return instruction. return result; }