Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
84 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Implementing
 a Stack

 Relocation
 of Execution

 Incorporating
 the Stack

 Program Flow
 Demonstration

 Source code
 Printable version
 Discuss this article

The Series
 An Introduction
 Data Manipulation
 Dynamic Loading
 The Stack and
 Program Flow


Relocation of Execution

In 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.



Next : Incorporating the Stack