mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2024-12-28 00:15:06 +00:00
b88f0d2dbd
Mostly to avoid string literal lookup by index with iteration over all preciding literals and calling strlen. This is very inefficient. In genral this makes code much more straightforward but also makes it portable since now int and float of different sizes are properly supported.
132 lines
3.1 KiB
C++
132 lines
3.1 KiB
C++
#include "interpreter.hpp"
|
|
|
|
#include <cassert>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include "opcodes.hpp"
|
|
#include "program.hpp"
|
|
|
|
namespace Interpreter
|
|
{
|
|
[[noreturn]] static void abortUnknownCode(int segment, int opcode)
|
|
{
|
|
const std::string error = "unknown opcode " + std::to_string(opcode) + " in segment " + std::to_string(segment);
|
|
throw std::runtime_error(error);
|
|
}
|
|
|
|
[[noreturn]] static void abortUnknownSegment(Type_Code code)
|
|
{
|
|
const std::string error = "opcode outside of the allocated segment range: " + std::to_string(code);
|
|
throw std::runtime_error(error);
|
|
}
|
|
|
|
template <typename T>
|
|
auto& getDispatcher(const T& segment, unsigned int seg, int opcode)
|
|
{
|
|
auto it = segment.find(opcode);
|
|
if (it == segment.end())
|
|
{
|
|
abortUnknownCode(seg, opcode);
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
void Interpreter::execute(Type_Code code)
|
|
{
|
|
unsigned int segSpec = code >> 30;
|
|
|
|
switch (segSpec)
|
|
{
|
|
case 0:
|
|
{
|
|
const int opcode = code >> 24;
|
|
const unsigned int arg0 = code & 0xffffff;
|
|
|
|
return getDispatcher(mSegment0, 0, opcode)->execute(mRuntime, arg0);
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
const int opcode = (code >> 20) & 0x3ff;
|
|
const unsigned int arg0 = code & 0xfffff;
|
|
|
|
return getDispatcher(mSegment2, 2, opcode)->execute(mRuntime, arg0);
|
|
}
|
|
}
|
|
|
|
segSpec = code >> 26;
|
|
|
|
switch (segSpec)
|
|
{
|
|
case 0x30:
|
|
{
|
|
const int opcode = (code >> 8) & 0x3ffff;
|
|
const unsigned int arg0 = code & 0xff;
|
|
|
|
return getDispatcher(mSegment3, 3, opcode)->execute(mRuntime, arg0);
|
|
}
|
|
|
|
case 0x32:
|
|
{
|
|
const int opcode = code & 0x3ffffff;
|
|
|
|
return getDispatcher(mSegment5, 5, opcode)->execute(mRuntime);
|
|
}
|
|
}
|
|
|
|
abortUnknownSegment(code);
|
|
}
|
|
|
|
void Interpreter::begin()
|
|
{
|
|
if (mRunning)
|
|
{
|
|
mCallstack.push(mRuntime);
|
|
mRuntime.clear();
|
|
}
|
|
else
|
|
{
|
|
mRunning = true;
|
|
}
|
|
}
|
|
|
|
void Interpreter::end()
|
|
{
|
|
if (mCallstack.empty())
|
|
{
|
|
mRuntime.clear();
|
|
mRunning = false;
|
|
}
|
|
else
|
|
{
|
|
mRuntime = mCallstack.top();
|
|
mCallstack.pop();
|
|
}
|
|
}
|
|
|
|
void Interpreter::run(const Program& program, Context& context)
|
|
{
|
|
begin();
|
|
|
|
try
|
|
{
|
|
mRuntime.configure(program, context);
|
|
|
|
while (mRuntime.getPC() >= 0 && static_cast<std::size_t>(mRuntime.getPC()) < program.mInstructions.size())
|
|
{
|
|
const Type_Code instruction = program.mInstructions[mRuntime.getPC()];
|
|
mRuntime.setPC(mRuntime.getPC() + 1);
|
|
execute(instruction);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
end();
|
|
throw;
|
|
}
|
|
|
|
end();
|
|
}
|
|
}
|