Relocation of ExecutionIn order to accommodate for the things we would like to achieve regarding program flow and data accessing opcodes, we are going to want to move execution-specific implementation over to the appropriately named Execution class. At the moment, the execution-specific code lies directly in the virtual machine. Instead, we would like the execution to take place as a member function of the Execution class, and have the virtual machine call to Execute redirect the execution to the proper Execution's execution. Say that ten times fast. If that sentence was as hard to follow as it was to type, the code will explain. This is what we would like to have happen: void VirtualMachine::Execute(size_t scriptId) { _execution.Execute(SelectScript(scriptId)); // select our script by ID } The first step is to move the script and instruction pointers over to the Execution class. Simple enough. Next we will need to make a change to the SelectScript() utility: ScriptRef SelectScript(size_t index) const // set current script by id { assert(index < _scriptList.size()); // make sure the id is valid return &_scriptList[index]; } Instead of implicitly updating the pointers to the script and instructions (which it no longer possesses), it now simply returns the proper script pointer so that it may be passed to an Execution. It will also guard against improper script ids from being accepted through assertion. Now we will need to create the Execution class's Execute() similar to that of VirtualMachine's before being changed: void VirtualMachine::Execution::Execute(ScriptRef scriptPtr) { _scriptPtr = scriptPtr; SetDataSize(_scriptPtr->VarCount()); // initialize variable data _instrPtr = _scriptPtr->InstrPtr(); // initialize root pointer _instrEnd = _scriptPtr->End(); // initialize end marker _instr = _instrPtr; // set our iterator to the beginning while (_instr < _instrEnd) // ensure pointer stays in-bounds { switch(_instr->Code()) { // message functionality case op_talk: std::cout << "I am talking." << std::endl; ++_instr; // iterate break; case op_print: std::cout << _instr->Data() << std::endl; // print data ++_instr; // iterate break; // other functionality . . . // end case op_end: _instr = _instrEnd; // discontinue the loop break; } } } Some changes have been made to how the loop is terminated after some re-evaluation. It will be safer this way once we bring in some new opcodes. To allow for this change, we add an additional accessor to the Script class to obtain a pointer to the end of its instruction list, and an end pointer to the Execution class. This is all there is to it since the cast should be implicit: // in the Script class const Instruction* End() const { return _instrList.end(); } With all the said changes in place the loading example should still execute properly.
|