473 lines
9.7 KiB
C++
473 lines
9.7 KiB
C++
/*=========================================================================
|
|
|
|
script.cpp
|
|
|
|
Author: PKG
|
|
Created:
|
|
Project: Spongebob
|
|
Purpose:
|
|
|
|
Copyright (c) 2000 Climax Development Ltd
|
|
|
|
===========================================================================*/
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Includes
|
|
-------- */
|
|
|
|
#include "script\script.h"
|
|
|
|
#ifndef __SCRIPT_FUNCTION_H__
|
|
#include "script\function.h"
|
|
#endif
|
|
|
|
#ifndef __SYSTEM_DBG_H__
|
|
#include "system\dbg.h"
|
|
#endif
|
|
|
|
#ifndef __MEMORY_HEADER__
|
|
#include "mem\memory.h"
|
|
#endif
|
|
|
|
|
|
/* Std Lib
|
|
------- */
|
|
|
|
/* Data
|
|
---- */
|
|
|
|
/*----------------------------------------------------------------------
|
|
Tyepdefs && Defines
|
|
------------------- */
|
|
|
|
//#define FULL_CODE_OUTPUT
|
|
//#define SHOW_RUN_COUNT
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Structure defintions
|
|
-------------------- */
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function Prototypes
|
|
------------------- */
|
|
|
|
/*----------------------------------------------------------------------
|
|
Vars
|
|
---- */
|
|
|
|
signed short CScript::s_globalVars[NUM_GLOBAL_VARS]=
|
|
{
|
|
0, // LIVES
|
|
};
|
|
|
|
|
|
// Buffer for passing arguments to functions
|
|
unsigned short CScript::s_argBuffer[MAX_FUNCTION_ARGS];
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::initialise(FileEquate _fe)
|
|
{
|
|
int i;
|
|
|
|
m_code=(unsigned short*)CFileIO::loadFile(_fe);
|
|
m_stack=(unsigned short*)MemAlloc(sizeof(unsigned short)*STACK_SIZE,"ScriptStack");
|
|
reset();
|
|
for(i=0;i<NUM_LOCAL_VARS;i++)
|
|
{
|
|
m_localVars[i]=0;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::dump()
|
|
{
|
|
MemFree(m_stack);
|
|
MemFree(m_code);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::run()
|
|
{
|
|
#ifdef SHOW_RUN_COUNT
|
|
int run=0;
|
|
#endif
|
|
if(m_state<=RUNNING)
|
|
{
|
|
m_state=RUNNING;
|
|
do
|
|
{
|
|
executeNextInstruction();
|
|
#ifdef SHOW_RUN_COUNT
|
|
run++;
|
|
#endif
|
|
}
|
|
while(m_state==RUNNING);
|
|
}
|
|
#ifdef SHOW_RUN_COUNT
|
|
if(run)
|
|
{
|
|
PAUL_DBGMSG("ran %d instructions",run);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::reset()
|
|
{
|
|
m_pc=0;
|
|
m_sp=0;
|
|
m_state=RESET;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
int CScript::isFinished()
|
|
{
|
|
return !(m_state<=RUNNING);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::executeNextInstruction()
|
|
{
|
|
unsigned short instruction;
|
|
signed short val1,val2,val3;
|
|
int i;
|
|
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("pc:0x%04d sp:%03d",m_pc*2,m_sp);
|
|
#endif
|
|
instruction=readNextInstruction();
|
|
switch(instruction)
|
|
{
|
|
case OP_NOP:
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("NOP");
|
|
#endif
|
|
break;
|
|
|
|
case OP_STOP: //
|
|
PAUL_DBGMSG("STOP");
|
|
if(m_sp==0)
|
|
{
|
|
m_state=STOPPED;
|
|
}
|
|
else
|
|
{
|
|
PAUL_DBGMSG("!STACK NOT EMPTY!");
|
|
m_state=STOPPED_STACK_NOT_EMPTY;
|
|
}
|
|
break;
|
|
|
|
case OP_PAUSE: //
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("PAUSE");
|
|
#endif
|
|
m_state=PAUSED;
|
|
break;
|
|
|
|
case OP_PUSHVALUE: // value
|
|
val1=readNextInstruction();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("PUSHVALUE %d",val1);
|
|
#endif
|
|
push(val1);
|
|
break;
|
|
|
|
case OP_PUSHVARVALUE: // varidx
|
|
val1=readNextInstruction();
|
|
val2=getVar(val1);
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("PUSHVARVALUE $%d ( %d )",val1,val2);
|
|
#endif
|
|
push(val2);
|
|
break;
|
|
|
|
case OP_POP: // value
|
|
val1=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("POP %d",val1);
|
|
#endif
|
|
break;
|
|
|
|
case OP_JMP: // jump
|
|
val1=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("JMP %d",val1);
|
|
#endif
|
|
jump(val1);
|
|
break;
|
|
|
|
case OP_JMPF: // jump, value
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("JMPF %d,%d",val1,val2);
|
|
#endif
|
|
if(val2==0)jump(val1);
|
|
break;
|
|
|
|
case OP_JMPT: // jump, value
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("JMPT %d,%d",val1,val2);
|
|
#endif
|
|
if(val2!=0)jump(val1);
|
|
break;
|
|
|
|
case OP_IS_EQUAL_VALUE: // value, value pushes result ( 0 or 1 ) to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("IS_EQUAL_VALUE %d,%d",val1,val2);
|
|
#endif
|
|
push(val1==val2);
|
|
break;
|
|
|
|
case OP_IS_NOTEQUAL_VALUE: // value, value pushes result ( 0 or 1 ) to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("IS_NOTEQUAL_VALUE %d,%d",val1,val2);
|
|
#endif
|
|
push(val1!=val2);
|
|
break;
|
|
|
|
case OP_IS_LESSTHAN_VALUE: // value, value pushes result ( 0 or 1 ) to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("OP_IS_LESSTHAN_VALUE %d,%d",val1,val2);
|
|
#endif
|
|
push(val1<val2);
|
|
break;
|
|
|
|
case OP_IS_GREATERTHAN_VALUE:// value, value pushes result ( 0 or 1 ) to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("OP_IS_GREATERTHAN_VALUE %d,%d",val1,val2);
|
|
#endif
|
|
push(val1>val2);
|
|
break;
|
|
|
|
case OP_ASSIGN: // varidx, value
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("$%d=%d",val1,val2);
|
|
#endif
|
|
setVar(val1,val2);
|
|
break;
|
|
|
|
case OP_ADD: // value, value pushes result to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("ADD %d,%d",val1,val2);
|
|
#endif
|
|
push(val1+val2);
|
|
break;
|
|
|
|
case OP_MULTIPLY: // value, value pushes result to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("MULTIPLY %d,%d",val1,val2);
|
|
#endif
|
|
push(val1*val2);
|
|
break;
|
|
|
|
case OP_DIVIDE: // value, value pushes result to stack
|
|
val1=pop();
|
|
val2=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("DIVIDE %d,%d",val1,val2);
|
|
#endif
|
|
if(val2==0)
|
|
{
|
|
SYSTEM_DBGMSG("[SCRIPT] DIVIDE BY ZERO @%d",m_pc);
|
|
m_state=CRASHED_DIVIDE_BY_ZERO;
|
|
}
|
|
else
|
|
{
|
|
push(val1/val2);
|
|
}
|
|
break;
|
|
|
|
case OP_NEG: // value pushes result to stack
|
|
val1=pop();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("NEG %d",val1);
|
|
#endif
|
|
push(-val1);
|
|
break;
|
|
|
|
case OP_PRINT: // value
|
|
val1=pop();
|
|
PAUL_DBGMSG("PRINT %d",val1);
|
|
break;
|
|
|
|
case OP_CALL_FUNCTION: // functionnumber, argcount args pushes return value to stack
|
|
val1=readNextInstruction();
|
|
val2=readNextInstruction();
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("CALL_FUNCTION %d ( %d args )",val1,val2);
|
|
#endif
|
|
ASSERT(val2<MAX_FUNCTION_ARGS); // Too many args.. just increase the #define to fix this
|
|
for(i=0;i<val2;i++)
|
|
{
|
|
s_argBuffer[val2-i-1]=pop();
|
|
}
|
|
val3=callFunction(val1,val2,s_argBuffer);
|
|
#ifdef FULL_CODE_OUTPUT
|
|
PAUL_DBGMSG("( return value is %d )",val3);
|
|
#endif
|
|
push(val3);
|
|
break;
|
|
|
|
default:
|
|
SYSTEM_DBGMSG("[SCRIPT] ILLEGAL OPCODE@%d ( %d )",m_pc,instruction);
|
|
m_state=CRASHED_ILLEGAL_OPCODE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
unsigned short CScript::readNextInstruction()
|
|
{
|
|
return m_code[m_pc++];
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::jump(signed short _distance)
|
|
{
|
|
m_pc+=_distance;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::push(unsigned short _data)
|
|
{
|
|
ASSERT(m_sp<=STACK_SIZE-1); // Stack overflow about to occur :(
|
|
m_stack[m_sp++]=_data;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
unsigned short CScript::pop()
|
|
{
|
|
ASSERT(m_sp>=1); // Stack underflow about to occur :(
|
|
return(m_stack[--m_sp]);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
void CScript::setVar(int _varIdx,signed short _value)
|
|
{
|
|
ASSERT(_varIdx>=0&&_varIdx<=NUM_GLOBAL_VARS+NUM_LOCAL_VARS-1);
|
|
|
|
if(_varIdx<NUM_GLOBAL_VARS)
|
|
{
|
|
ASSERT(0); // Need to update global vars.. (PKG)
|
|
s_globalVars[_varIdx]=_value;
|
|
}
|
|
else if(_varIdx<NUM_GLOBAL_VARS+NUM_LOCAL_VARS)
|
|
{
|
|
m_localVars[_varIdx-NUM_GLOBAL_VARS]=_value;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
Function:
|
|
Purpose:
|
|
Params:
|
|
Returns:
|
|
---------------------------------------------------------------------- */
|
|
signed short CScript::getVar(int _varIdx)
|
|
{
|
|
ASSERT(_varIdx>=0&&_varIdx<=NUM_GLOBAL_VARS+NUM_LOCAL_VARS-1);
|
|
|
|
int ret=0;
|
|
|
|
if(_varIdx<NUM_GLOBAL_VARS)
|
|
{
|
|
ASSERT(0); // Need to read global vars.. (PKG)
|
|
ret=s_globalVars[_varIdx];
|
|
}
|
|
else if(_varIdx<NUM_GLOBAL_VARS+NUM_LOCAL_VARS)
|
|
{
|
|
ret=m_localVars[_varIdx-NUM_GLOBAL_VARS];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*===========================================================================
|
|
end */
|