/** lc3_finish * * Finishes the current subroutine */ void lc3_finish(lc3_state& state) { // Subroutine depth We assume the user is already in a subroutine and just wants to get out of it int depth = 1; do { // Get Next Instruction. lc3_instr instr = lc3_decode(state, state.mem[state.pc]); // So if we get a JSR/JSRR or if we get a TRAP and true traps are enabled if (instr.data.opcode == JSR_INSTR || (instr.data.opcode == TRAP_INSTR && state.true_traps)) depth++; // If we get a RET instruction JMP R7 if (instr.data.opcode == JMP_INSTR && instr.jmp.base_r == 7) depth--; // If we got an RTI instruction if (instr.data.opcode == RTI_INSTR) depth--; // Execute lc3_step(state); // If we got interrupted if (state.interrupt_enabled && state.undo_stack.back().changes == LC3_INTERRUPT_BEGIN) depth++; } while (depth != 0 && !state.halted); }
/** lc3_prev_line * * Undos the current instruction ignoring subroutines */ void lc3_prev_line(lc3_state& state) { // Subroutine depth int depth = 0; do { if (!state.undo_stack.empty()) { lc3_state_change& last = state.undo_stack.back(); // Can't backstep through interrupt if (last.changes == LC3_INTERRUPT_BEGIN) return; // Get rid of all processed interrupts. while (last.changes == LC3_INTERRUPT && !state.undo_stack.empty()) { lc3_back(state); last = state.undo_stack.back(); } } // Execute (Have to do this first you can't assume mem[pc - 1] was the last // instruction due to jumps. lc3_back(state); // Get the instruction that got you where you are. lc3_instr instr = lc3_decode(state, state.mem[state.pc]); // If we get a RET instruction JMP R7 if (instr.data.opcode == JMP_INSTR && instr.jmp.base_r == 7) depth++; // So if we get a JSR/JSRR or if we get a TRAP and true traps are enabled if (instr.data.opcode == JSR_INSTR || (instr.data.opcode == TRAP_INSTR && state.true_traps)) depth--; // Don't have to handle interrupts here... } while (depth != 0 && !state.halted && !state.undo_stack.empty()); }
/** Entry * * */ void* LC3RunThread::Entry() { ///TODO consider writing this without rewriting next_line/prev_line. int depth = 0; lc3_instr instr; bool interrupt_begin = false; state.halted = 0; switch(run_mode) { case RUNMODE_RUN: while(!state.halted) { lc3_step(state); if (TestDestroy()) break; Yield(); } break; case RUNMODE_RUNFOR: if (runtime > 0) lc3_run(state, runtime); else lc3_rewind(state, -runtime); break; case RUNMODE_STEP: lc3_step(state); break; case RUNMODE_BACK: lc3_back(state); break; case RUNMODE_FINISH: depth = 1; case RUNMODE_NEXTLINE: // Subroutine depth do { // Get Next Instruction. instr = lc3_decode(state, state.mem[state.pc]); // So if we get a JSR/JSRR or if we get a TRAP and true traps are enabled if (instr.data.opcode == JSR_INSTR || (instr.data.opcode == TRAP_INSTR && state.true_traps)) depth++; // If we get a RET instruction JMP R7 if (instr.data.opcode == JMP_INSTR && instr.jmp.base_r == 7) depth--; // If we get an RTI instruction if (instr.data.opcode == RTI_INSTR) depth--; // Execute lc3_step(state); // If we got interrupted if (state.interrupt_enabled && state.undo_stack.back().changes == LC3_INTERRUPT_BEGIN) depth++; if (TestDestroy()) break; Yield(); } while (depth != 0 && !state.halted); break; case RUNMODE_PREVLINE: // Subroutine depth depth = 0; do { if (!state.undo_stack.empty()) { lc3_state_change& last = state.undo_stack.back(); // Can't backstep through interrupt if (last.changes == LC3_INTERRUPT_BEGIN) break; // Get rid of all processed interrupts. while (last.changes == LC3_INTERRUPT && !state.undo_stack.empty()) { lc3_back(state); last = state.undo_stack.back(); if (TestDestroy()) break; Yield(); } } // Execute (Have to do this first you can't assume mem[pc - 1] was the last // instruction due to jumps. lc3_back(state); // Get the instruction that got you where you are. lc3_instr instr = lc3_decode(state, state.mem[state.pc]); // If we get a RET instruction JMP R7 if (instr.data.opcode == JMP_INSTR && instr.jmp.base_r == 7) depth++; // So if we get a JSR/JSRR or if we get a TRAP and true traps are enabled if (instr.data.opcode == JSR_INSTR || (instr.data.opcode == TRAP_INSTR && state.true_traps)) depth--; if (TestDestroy()) break; Yield(); // Don't have to handle interrupts here... } while (depth != 0 && !state.halted && !state.undo_stack.empty()); break; case RUNMODE_REWIND: // Do this until no more changes. while (!state.undo_stack.empty() && !interrupt_begin) { lc3_state_change& last = state.undo_stack.back(); interrupt_begin = (last.changes == LC3_INTERRUPT_BEGIN); // Backstep lc3_back(state); if (TestDestroy()) break; Yield(); } break; } wxQueueEvent(frame, new wxThreadEvent(wxEVT_COMMAND_RUNTHREAD_COMPLETED)); return NULL; }
/** lc3_step * * Executes one instruction */ void lc3_step(lc3_state& state) { // If we are halted then don't step. if (state.halted) return; // Tick all plugins lc3_tick_plugins(state); // Fetch Instruction unsigned short data = state.mem[state.pc]; // Test for blackbox (If the line was a JSR statement and it had a blackbox). bool blackbox_finish = lc3_blackbox_test(state); // Increment PC state.pc++; // Form Instruction lc3_instr instr = lc3_decode(state, data); // Execute Instruction const lc3_state_change change = lc3_execute(state, instr); // Test for blackbox (If the first line of subroutine had a blackbox). if (instr.data.opcode == JSR_INSTR || (instr.data.opcode == TRAP_INSTR && state.true_traps)) blackbox_finish = blackbox_finish || lc3_blackbox_test(state); // Increment executions state.executions++; if (state.max_stack_size != 0) { // If the change is INTERRUPT END if (change.changes == LC3_INTERRUPT_END) { // After you've scrubbed all of the floors in hyrule (remove all instructions that have been added except the LC3_INTERRUPT change. lc3_state_change* hmm = &state.undo_stack.back(); while (hmm->changes != LC3_INTERRUPT_BEGIN) { state.undo_stack.pop_back(); hmm = &state.undo_stack.back(); } // Please sire have mercy (Change LC3_INTERRUPT_BEGIN to LC3_INTERRUPT to signal a completed interrupt) lc3_state_change& lib = state.undo_stack.back(); lib.changes = LC3_INTERRUPT; } else { // Push Old into state state.undo_stack.push_back(change); } // Never pop if you are in privileged mode. if (state.privilege && state.max_stack_size < state.undo_stack.size()) state.undo_stack.pop_front(); } // Tock all plugins lc3_tock_plugins(state); if (blackbox_finish) lc3_finish(state); // If we hit an error or a halt instruction return. no need to do any breakpoint tests. if (state.halted) return; // Breakpoint test lc3_break_test(state, &change); if (!state.interrupt_enabled) return; // Chime in on all observers. std::list<interrupt_test_func>::iterator i; for (i = state.interrupt_test.begin(); i != state.interrupt_test.end(); ++i) { interrupt_test_func func = *i; func(); } // Interrupt? if (!lc3_interrupt(state)) return; lc3_state_change interrupt; interrupt.changes = LC3_INTERRUPT_BEGIN; interrupt.warnings = state.warnings; interrupt.executions = state.executions; if (state.max_stack_size != 0) state.undo_stack.push_back(interrupt); // Another breakpoint test lc3_break_test(state, &interrupt); }
BOOST_CHECK_EQUAL(instr.arith.inv.unused, 0x3E); instr = lc3_decode(state, 0xF100); BOOST_REQUIRE_EQUAL(instr.trap.opcode, TRAP_INSTR); BOOST_CHECK_EQUAL(instr.trap.unused, 1); BOOST_CHECK_EQUAL(instr.trap.vector, 0); } BOOST_FIXTURE_TEST_CASE(MalformedInstructionTest, LC3BasicTest) { const std::vector<unsigned short> malformed_instructions = {0, 1, 0x1008, 0x4001, 0x4200, 0x5008, 0x8001, 0x903E, 0xC001, 0xC200, 0xF100}; for (const auto& data : malformed_instructions) { BOOST_TEST_MESSAGE("data = x" << std::hex << data); lc3_instr instr = lc3_decode(state, data); lc3_state_change change = lc3_execute(state, instr); BOOST_CHECK(state.halted); BOOST_CHECK_EQUAL(state.warnings, 1); BOOST_CHECK_EQUAL(state.pc, 0x2FFF); BOOST_CHECK_EQUAL(change.pc, 0x3000); BOOST_CHECK(!change.halted); lc3_init(state, false, false); } } BOOST_FIXTURE_TEST_CASE(MalformedInstructionDisassemble, LC3BasicTest) { const std::vector<unsigned short> malformed_instructions = {0, 1, 0x1008, 0x4001, 0x4200, 0x5008, 0x8001, 0x903E, 0xC001, 0xC200, 0xF100}; const std::vector<std::string> answers = {"NOP *", "NOP *", "ADD R0, R0, R0 *", "JSRR R0 *", "JSRR R0 *", "AND R0, R0, R0 *", "RTI *", "NOT R0, R0 *", "JMP R0 *", "JMP R0 *", "TRAP x00 *"};