/*! Run without support of breakpoints. Check ASEBA_VM_EVENT_RUNNING_MASK to exit on interrupts or stepsLimit if > 0. */ void AsebaDebugBareRun(AsebaVMState *vm, uint16 stepsLimit) { AsebaMaskSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK); if (stepsLimit > 0) { // no breakpoint, still poll the mask and check stepsLimit while (AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK) && AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK) && stepsLimit ) { AsebaVMStep(vm); stepsLimit--; // TODO : send exception event on step limits overflow } } else { // no breakpoint, only poll the mask while (AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK) && AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK) ) AsebaVMStep(vm); } AsebaMaskClear(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK); }
/*! Run with support of breakpoints. Also check ASEBA_VM_EVENT_RUNNING_MASK to exit on interrupts. */ void AsebaDebugBreakpointRun(AsebaVMState *vm, uint16 stepsLimit) { AsebaMaskSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK); if (stepsLimit > 0) { // breakpoints, check before each step, poll the mask, and check stepsLimit while (AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK) && AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK) && stepsLimit ) { if (AsebaVMCheckBreakpoint(vm) != 0) { AsebaMaskSet(vm->flags, ASEBA_VM_STEP_BY_STEP_MASK); AsebaVMSendExecutionStateChanged(vm); return; } AsebaVMStep(vm); stepsLimit--; // TODO : send exception event on step limits overflow } } else { // breakpoints, check before each step and poll the mask while (AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK) && AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK) ) { if (AsebaVMCheckBreakpoint(vm) != 0) { AsebaMaskSet(vm->flags, ASEBA_VM_STEP_BY_STEP_MASK); AsebaVMSendExecutionStateChanged(vm); return; } AsebaVMStep(vm); } } AsebaMaskClear(vm->flags, ASEBA_VM_EVENT_RUNNING_MASK); }
void LocalVirtualMachine::timerEvent(QTimerEvent *event) { Q_UNUSED(event); // in some case we do not want to execute anything in background if (AsebaMaskIsClear(vmState.flags, ASEBA_VM_EVENT_ACTIVE_MASK)) return; if (!runEvent && AsebaMaskIsClear(vmState.flags, ASEBA_VM_RUN_BACKGROUND_MASK)) return; // execute const unsigned maxStepsPerTimeSlice = 100; unsigned i = 0; while (AsebaMaskIsSet(vmState.flags, ASEBA_VM_EVENT_ACTIVE_MASK) && (i < maxStepsPerTimeSlice)) { // execute if (AsebaVMStepBreakpoint(&vmState)) { runEvent = false; AsebaMaskClear(vmState.flags, ASEBA_VM_RUN_BACKGROUND_MASK); emit executionModeChanged(EXECUTION_STEP_BY_STEP); break; } i++; } // emit states changes if (i != 0) emit variablesMemoryChanged(vmVariables); // check if we are still running if (AsebaMaskIsSet(vmState.flags, ASEBA_VM_EVENT_ACTIVE_MASK)) { Q_ASSERT(vmState.pc < debugBytecode.size()); emit executionPosChanged(debugBytecode[vmState.pc].line); } else if (AsebaMaskIsClear(vmState.flags, ASEBA_VM_RUN_BACKGROUND_MASK)) stop(); }
void AsebaVMDebugMessage(AsebaVMState *vm, uint16 id, uint16 *data, uint16 dataLength) { // react to global presence if (id == ASEBA_MESSAGE_GET_DESCRIPTION) { AsebaSendDescription(vm); return; } // check if we are the destination, return otherwise if (bswap16(data[0]) != vm->nodeId) return; data++; dataLength--; switch (id) { case ASEBA_MESSAGE_SET_BYTECODE: { uint16 start = bswap16(data[0]); uint16 length = dataLength - 1; uint16 i; #ifdef ASEBA_ASSERT if (start + length > vm->bytecodeSize) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS); #endif for (i = 0; i < length; i++) vm->bytecode[start+i] = bswap16(data[i+1]); } // There is no break here because we want to do a reset after a set bytecode case ASEBA_MESSAGE_RESET: vm->flags = ASEBA_VM_STEP_BY_STEP_MASK; // try to setup event, if it fails, return the execution state anyway if (AsebaVMSetupEvent(vm, ASEBA_EVENT_INIT) == 0) AsebaVMSendExecutionStateChanged(vm); break; case ASEBA_MESSAGE_RUN: AsebaMaskClear(vm->flags, ASEBA_VM_STEP_BY_STEP_MASK); AsebaVMSendExecutionStateChanged(vm); if(AsebaVMRunCB) AsebaVMRunCB(vm); break; case ASEBA_MESSAGE_PAUSE: AsebaMaskSet(vm->flags, ASEBA_VM_STEP_BY_STEP_MASK); AsebaVMSendExecutionStateChanged(vm); break; case ASEBA_MESSAGE_STEP: if (AsebaMaskIsSet(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK)) { AsebaVMStep(vm); AsebaVMSendExecutionStateChanged(vm); } break; case ASEBA_MESSAGE_STOP: vm->flags = ASEBA_VM_STEP_BY_STEP_MASK; AsebaVMSendExecutionStateChanged(vm); break; case ASEBA_MESSAGE_GET_EXECUTION_STATE: AsebaVMSendExecutionStateChanged(vm); break; case ASEBA_MESSAGE_BREAKPOINT_SET: { uint16 buffer[2]; buffer[0] = bswap16(data[0]); buffer[1] = AsebaVMSetBreakpoint(vm, buffer[0]); AsebaSendMessageWords(vm, ASEBA_MESSAGE_BREAKPOINT_SET_RESULT, buffer, 2); } break; case ASEBA_MESSAGE_BREAKPOINT_CLEAR: AsebaVMClearBreakpoint(vm, bswap16(data[0])); break; case ASEBA_MESSAGE_BREAKPOINT_CLEAR_ALL: AsebaVMClearBreakpoints(vm); break; case ASEBA_MESSAGE_GET_VARIABLES: { uint16 start = bswap16(data[0]); uint16 length = bswap16(data[1]); #ifdef ASEBA_ASSERT if (start + length > vm->variablesSize) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS); #endif AsebaSendVariables(vm, start, length); } break; case ASEBA_MESSAGE_SET_VARIABLES: { uint16 start = bswap16(data[0]); uint16 length = dataLength - 1; uint16 i; #ifdef ASEBA_ASSERT if (start + length > vm->variablesSize) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS); #endif for (i = 0; i < length; i++) vm->variables[start+i] = bswap16(data[i+1]); } break; case ASEBA_MESSAGE_WRITE_BYTECODE: AsebaWriteBytecode(vm); break; case ASEBA_MESSAGE_REBOOT: AsebaResetIntoBootloader(vm); break; case ASEBA_MESSAGE_SUSPEND_TO_RAM: AsebaPutVmToSleep(vm); break; default: break; } }
/*! Execute one bytecode of the current VM thread. VM must be ready for run otherwise trashes may occur. */ void AsebaVMStep(AsebaVMState *vm) { uint16 bytecode = vm->bytecode[vm->pc]; #ifdef ASEBA_ASSERT if (AsebaMaskIsClear(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK)) AsebaAssert(vm, ASEBA_ASSERT_STEP_OUT_OF_RUN); #endif switch (bytecode >> 12) { // Bytecode: Stop case ASEBA_BYTECODE_STOP: { AsebaMaskClear(vm->flags, ASEBA_VM_EVENT_ACTIVE_MASK); } break; // Bytecode: Small Immediate case ASEBA_BYTECODE_SMALL_IMMEDIATE: { sint16 value = ((sint16)(bytecode << 4)) >> 4; // check sp #ifdef ASEBA_ASSERT if (vm->sp + 1 >= vm->stackSize) AsebaAssert(vm, ASEBA_ASSERT_STACK_OVERFLOW); #endif // push value in stack vm->stack[++vm->sp] = value; // increment PC vm->pc ++; } break; // Bytecode: Large Immediate case ASEBA_BYTECODE_LARGE_IMMEDIATE: { // check sp #ifdef ASEBA_ASSERT if (vm->sp + 1 >= vm->stackSize) AsebaAssert(vm, ASEBA_ASSERT_STACK_OVERFLOW); #endif // push value in stack vm->stack[++vm->sp] = vm->bytecode[vm->pc + 1]; // increment PC vm->pc += 2; } break; // Bytecode: Load case ASEBA_BYTECODE_LOAD: { uint16 variableIndex = bytecode & 0x0fff; // check sp and variable index #ifdef ASEBA_ASSERT if (vm->sp + 1 >= vm->stackSize) AsebaAssert(vm, ASEBA_ASSERT_STACK_OVERFLOW); if (variableIndex >= vm->variablesSize) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS); #endif // push value in stack vm->stack[++vm->sp] = vm->variables[variableIndex]; // increment PC vm->pc ++; } break; // Bytecode: Store case ASEBA_BYTECODE_STORE: { uint16 variableIndex = bytecode & 0x0fff; // check sp and variable index #ifdef ASEBA_ASSERT if (vm->sp < 0) AsebaAssert(vm, ASEBA_ASSERT_STACK_UNDERFLOW); if (variableIndex >= vm->variablesSize) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS); #endif // pop value from stack vm->variables[variableIndex] = vm->stack[vm->sp--]; // increment PC vm->pc ++; } break; // Bytecode: Load Indirect case ASEBA_BYTECODE_LOAD_INDIRECT: { uint16 arrayIndex; uint16 arraySize; uint16 variableIndex; // check sp #ifdef ASEBA_ASSERT if (vm->sp < 0) AsebaAssert(vm, ASEBA_ASSERT_STACK_UNDERFLOW); #endif // get indexes arrayIndex = bytecode & 0x0fff; arraySize = vm->bytecode[vm->pc + 1]; variableIndex = vm->stack[vm->sp]; // check variable index if (variableIndex >= arraySize) { uint16 buffer[3]; buffer[0] = vm->pc; buffer[1] = arraySize; buffer[2] = variableIndex; vm->flags = ASEBA_VM_STEP_BY_STEP_MASK; AsebaSendMessageWords(vm, ASEBA_MESSAGE_ARRAY_ACCESS_OUT_OF_BOUNDS, buffer, 3); if(AsebaVMErrorCB) AsebaVMErrorCB(vm,NULL); break; } // load variable vm->stack[vm->sp] = vm->variables[arrayIndex + variableIndex]; // increment PC vm->pc += 2; } break; // Bytecode: Store Indirect case ASEBA_BYTECODE_STORE_INDIRECT: { uint16 arrayIndex; uint16 arraySize; uint16 variableIndex; sint16 variableValue; // check sp #ifdef ASEBA_ASSERT if (vm->sp < 1) AsebaAssert(vm, ASEBA_ASSERT_STACK_UNDERFLOW); #endif // get value and indexes arrayIndex = bytecode & 0x0fff; arraySize = vm->bytecode[vm->pc + 1]; variableValue = vm->stack[vm->sp - 1]; variableIndex = (uint16)vm->stack[vm->sp]; // check variable index if (variableIndex >= arraySize) { uint16 buffer[3]; buffer[0] = vm->pc; buffer[1] = arraySize; buffer[2] = variableIndex; vm->flags = ASEBA_VM_STEP_BY_STEP_MASK; AsebaSendMessageWords(vm, ASEBA_MESSAGE_ARRAY_ACCESS_OUT_OF_BOUNDS, buffer, 3); if(AsebaVMErrorCB) AsebaVMErrorCB(vm,NULL); break; } // store variable and change sp vm->variables[arrayIndex + variableIndex] = variableValue; vm->sp -= 2; // increment PC vm->pc += 2; } break; // Bytecode: Unary Arithmetic case ASEBA_BYTECODE_UNARY_ARITHMETIC: { sint16 value, opResult; // check sp #ifdef ASEBA_ASSERT if (vm->sp < 0) AsebaAssert(vm, ASEBA_ASSERT_STACK_UNDERFLOW); #endif // get operand value = vm->stack[vm->sp]; // do operation opResult = AsebaVMDoUnaryOperation(vm, value, bytecode & ASEBA_UNARY_OPERATOR_MASK); // write result vm->stack[vm->sp] = opResult; // increment PC vm->pc ++; } break; // Bytecode: Binary Arithmetic case ASEBA_BYTECODE_BINARY_ARITHMETIC: { sint16 valueOne, valueTwo, opResult; // check sp #ifdef ASEBA_ASSERT if (vm->sp < 1) AsebaAssert(vm, ASEBA_ASSERT_STACK_UNDERFLOW); #endif // get operands valueOne = vm->stack[vm->sp - 1]; valueTwo = vm->stack[vm->sp]; // do operation opResult = AsebaVMDoBinaryOperation(vm, valueOne, valueTwo, bytecode & ASEBA_BINARY_OPERATOR_MASK); // write result vm->sp--; vm->stack[vm->sp] = opResult; // increment PC vm->pc ++; } break; // Bytecode: Jump case ASEBA_BYTECODE_JUMP: { sint16 disp = ((sint16)(bytecode << 4)) >> 4; // check pc #ifdef ASEBA_ASSERT if ((vm->pc + disp < 0) || (vm->pc + disp >= vm->bytecodeSize)) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS); #endif // do jump vm->pc += disp; } break; // Bytecode: Conditional Branch case ASEBA_BYTECODE_CONDITIONAL_BRANCH: { sint16 conditionResult; sint16 valueOne, valueTwo; sint16 disp; // check sp #ifdef ASEBA_ASSERT if (vm->sp < 1) AsebaAssert(vm, ASEBA_ASSERT_STACK_OVERFLOW); #endif // evaluate condition valueOne = vm->stack[vm->sp - 1]; valueTwo = vm->stack[vm->sp]; conditionResult = AsebaVMDoBinaryOperation(vm, valueOne, valueTwo, bytecode & ASEBA_BINARY_OPERATOR_MASK); vm->sp -= 2; // is the condition really true ? if (conditionResult && !(GET_BIT(bytecode, ASEBA_IF_IS_WHEN_BIT) && GET_BIT(bytecode, ASEBA_IF_WAS_TRUE_BIT))) { // if true disp disp = 2; } else { // if false disp disp = (sint16)vm->bytecode[vm->pc + 1]; } // write back condition result if (conditionResult) BIT_SET(vm->bytecode[vm->pc], ASEBA_IF_WAS_TRUE_BIT); else BIT_CLR(vm->bytecode[vm->pc], ASEBA_IF_WAS_TRUE_BIT); // check pc #ifdef ASEBA_ASSERT if ((vm->pc + disp < 0) || (vm->pc + disp >= vm->bytecodeSize)) AsebaAssert(vm, ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS); #endif // do branch vm->pc += disp; } break; // Bytecode: Emit case ASEBA_BYTECODE_EMIT: { // emit event uint16 start = vm->bytecode[vm->pc + 1]; uint16 length = vm->bytecode[vm->pc + 2]; #ifdef ASEBA_ASSERT if (length > ASEBA_MAX_EVENT_ARG_SIZE) AsebaAssert(vm, ASEBA_ASSERT_EMIT_BUFFER_TOO_LONG); #endif AsebaSendMessageWords(vm, bytecode & 0x0fff, vm->variables + start, length); // increment PC vm->pc += 3; } break; // Bytecode: Call case ASEBA_BYTECODE_NATIVE_CALL: { // call native function AsebaNativeFunction(vm, bytecode & 0x0fff); // increment PC vm->pc ++; } break; // Bytecode: Subroutine call case ASEBA_BYTECODE_SUB_CALL: { uint16 dest = bytecode & 0x0fff; // store return value on stack vm->stack[++vm->sp] = vm->pc + 1; // jump vm->pc = dest; } break; // Bytecode: Subroutine return case ASEBA_BYTECODE_SUB_RET: { // do return vm->pc = vm->stack[vm->sp--]; } break; default: #ifdef ASEBA_ASSERT AsebaAssert(vm, ASEBA_ASSERT_UNKNOWN_BYTECODE); #endif break; } // switch bytecode... }
void LocalVirtualMachine::stop() { runEvent = false; AsebaMaskClear(vmState.flags, ASEBA_VM_EVENT_ACTIVE_MASK | ASEBA_VM_RUN_BACKGROUND_MASK); emit executionModeChanged(EXECUTION_INACTIVE); }