Files
scummvm-cursorfix/engines/m4/wscript/ws_cruncher.cpp
2026-02-02 04:50:13 +01:00

1588 lines
49 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "m4/wscript/ws_cruncher.h"
#include "m4/wscript/wscript.h"
#include "m4/wscript/wst_regs.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/dbg/debug.h"
#include "m4/mem/mem.h"
#include "m4/vars.h"
namespace M4 {
#define VERIFY_INTIALIZED(s) if (!_GWS(cruncherInitialized)) error_show(FL, 'WSCI', "%s failed.", s);
static int32 dataFormats[] = { 0, 5, 8, 12, 16 };
static const frac16 sinCosTable[320] = {
(frac16)0, (frac16)1608, (frac16)3215, (frac16)4821,
(frac16)6423, (frac16)8022, (frac16)9616, (frac16)11204,
(frac16)12785, (frac16)14359, (frac16)15923, (frac16)17479,
(frac16)19024, (frac16)20557, (frac16)22078, (frac16)23586,
(frac16)25079, (frac16)26557, (frac16)28020, (frac16)29465,
(frac16)30893, (frac16)32302, (frac16)33692, (frac16)35061,
(frac16)36409, (frac16)37736, (frac16)39039, (frac16)40319,
(frac16)41575, (frac16)42806, (frac16)44011, (frac16)45189,
(frac16)46340, (frac16)47464, (frac16)48558, (frac16)49624,
(frac16)50660, (frac16)51665, (frac16)52639, (frac16)53581,
(frac16)54491, (frac16)55368, (frac16)56212, (frac16)57022,
(frac16)57797, (frac16)58538, (frac16)59243, (frac16)59913,
(frac16)60547, (frac16)61144, (frac16)61705, (frac16)62228,
(frac16)62714, (frac16)63162, (frac16)63571, (frac16)63943,
(frac16)64276, (frac16)64571, (frac16)64826, (frac16)65043,
(frac16)65220, (frac16)65358, (frac16)65457, (frac16)65516,
(frac16)65536, (frac16)65516, (frac16)65457, (frac16)65358,
(frac16)65220, (frac16)65043, (frac16)64826, (frac16)64571,
(frac16)64276, (frac16)63943, (frac16)63571, (frac16)63162,
(frac16)62714, (frac16)62228, (frac16)61705, (frac16)61144,
(frac16)60547, (frac16)59913, (frac16)59243, (frac16)58538,
(frac16)57797, (frac16)57022, (frac16)56212, (frac16)55368,
(frac16)54491, (frac16)53581, (frac16)52639, (frac16)51665,
(frac16)50660, (frac16)49624, (frac16)48558, (frac16)47464,
(frac16)46340, (frac16)45189, (frac16)44011, (frac16)42806,
(frac16)41575, (frac16)40319, (frac16)39039, (frac16)37736,
(frac16)36409, (frac16)35061, (frac16)33692, (frac16)32302,
(frac16)30893, (frac16)29465, (frac16)28020, (frac16)26557,
(frac16)25079, (frac16)23586, (frac16)22078, (frac16)20557,
(frac16)19024, (frac16)17479, (frac16)15923, (frac16)14359,
(frac16)12785, (frac16)11204, (frac16)9616, (frac16)8022,
(frac16)6423, (frac16)4821, (frac16)3215, (frac16)1608,
(frac16)0, (frac16)-1608, (frac16)-3215, (frac16)-4821,
(frac16)-6423, (frac16)-8022, (frac16)-9616, (frac16)-11204,
(frac16)-12785, (frac16)-14359, (frac16)-15923, (frac16)-17479,
(frac16)-19024, (frac16)-20557, (frac16)-22078, (frac16)-23586,
(frac16)-25079, (frac16)-26557, (frac16)-28020, (frac16)-29465,
(frac16)-30893, (frac16)-32302, (frac16)-33692, (frac16)-35061,
(frac16)-36409, (frac16)-37736, (frac16)-39039, (frac16)-40319,
(frac16)-41575, (frac16)-42806, (frac16)-44011, (frac16)-45189,
(frac16)-46340, (frac16)-47464, (frac16)-48558, (frac16)-49624,
(frac16)-50660, (frac16)-51665, (frac16)-52639, (frac16)-53581,
(frac16)-54491, (frac16)-55368, (frac16)-56212, (frac16)-57022,
(frac16)-57797, (frac16)-58538, (frac16)-59243, (frac16)-59913,
(frac16)-60547, (frac16)-61144, (frac16)-61705, (frac16)-62228,
(frac16)-62714, (frac16)-63162, (frac16)-63571, (frac16)-63943,
(frac16)-64276, (frac16)-64571, (frac16)-64826, (frac16)-65043,
(frac16)-65220, (frac16)-65358, (frac16)-65457, (frac16)-65516,
(frac16)-65536, (frac16)-65516, (frac16)-65457, (frac16)-65358,
(frac16)-65220, (frac16)-65043, (frac16)-64826, (frac16)-64571,
(frac16)-64276, (frac16)-63943, (frac16)-63571, (frac16)-63162,
(frac16)-62714, (frac16)-62228, (frac16)-61705, (frac16)-61144,
(frac16)-60547, (frac16)-59913, (frac16)-59243, (frac16)-58538,
(frac16)-57797, (frac16)-57022, (frac16)-56212, (frac16)-55368,
(frac16)-54491, (frac16)-53581, (frac16)-52639, (frac16)-51665,
(frac16)-50660, (frac16)-49624, (frac16)-48558, (frac16)-47464,
(frac16)-46340, (frac16)-45189, (frac16)-44011, (frac16)-42806,
(frac16)-41575, (frac16)-40319, (frac16)-39039, (frac16)-37736,
(frac16)-36409, (frac16)-35061, (frac16)-33692, (frac16)-32302,
(frac16)-30893, (frac16)-29465, (frac16)-28020, (frac16)-26557,
(frac16)-25079, (frac16)-23586, (frac16)-22078, (frac16)-20557,
(frac16)-19024, (frac16)-17479, (frac16)-15923, (frac16)-14359,
(frac16)-12785, (frac16)-11204, (frac16)-9616, (frac16)-8022,
(frac16)-6423, (frac16)-4821, (frac16)-3215, (frac16)-1608,
(frac16)0, (frac16)1608, (frac16)3215, (frac16)4821,
(frac16)6423, (frac16)8022, (frac16)9616, (frac16)11204,
(frac16)12785, (frac16)14359, (frac16)15923, (frac16)17479,
(frac16)19024, (frac16)20557, (frac16)22078, (frac16)23586,
(frac16)25079, (frac16)26557, (frac16)28020, (frac16)29465,
(frac16)30893, (frac16)32302, (frac16)33692, (frac16)35061,
(frac16)36409, (frac16)37736, (frac16)39039, (frac16)40319,
(frac16)41575, (frac16)42806, (frac16)44011, (frac16)45189,
(frac16)46340, (frac16)47464, (frac16)48558, (frac16)49624,
(frac16)50660, (frac16)51665, (frac16)52639, (frac16)53581,
(frac16)54491, (frac16)55368, (frac16)56212, (frac16)57022,
(frac16)57797, (frac16)58538, (frac16)59243, (frac16)59913,
(frac16)60547, (frac16)61144, (frac16)61705, (frac16)62228,
(frac16)62714, (frac16)63162, (frac16)63571, (frac16)63943,
(frac16)64276, (frac16)64571, (frac16)64826, (frac16)65043,
(frac16)65220, (frac16)65358, (frac16)65457, (frac16)65516
};
static const frac16 *sinTable = &(sinCosTable[0]);
static const frac16 *cosTable = &(sinCosTable[64]);
int32 *ws_GetDataFormats() {
return &dataFormats[0];
}
bool ws_InitCruncher(void) {
// Make sure the cruncher has not been initialized
if (_GWS(cruncherInitialized))
error_show(FL, 'WSCR');
// Register the end of sequence struct with the stash manager
mem_register_stash_type(&_GWS(memtypeEOS), sizeof(EOSreq), 32, "+EOS");
if (_GWS(memtypeEOS) < 0)
error_show(FL, 'WSCE');
if ((_GWS(myCruncher) = (cruncher *)mem_alloc(sizeof(cruncher), "cruncher")) == nullptr)
error_show(FL, 'OOM!', "%d bytes.", sizeof(cruncher));
_GWS(myCruncher)->backLayerAnim8 = nullptr;
_GWS(myCruncher)->frontLayerAnim8 = nullptr;
_GWS(myCruncher)->firstAnim8ToCrunch = nullptr;
_GWS(myCruncher)->lastAnim8ToCrunch = nullptr;
// Set up stack
_GWS(stackSize) = 2048;
if ((_GWS(stackBase) = (uint32 *)mem_alloc(_GWS(stackSize), "crunchstack")) == nullptr) {
error_show(FL, 'OOM!', "%d bytes.", _GWS(stackSize));
}
_GWS(stackTop) = _GWS(stackBase);
_GWS(stackLimit) = (uint32 *)((byte *)_GWS(stackBase) + (uint32)_GWS(stackSize));
_GWS(cruncherInitialized) = true;
return true;
}
void ws_KillCruncher() {
if (_GWS(cruncherInitialized)) {
Anim8 *myAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch;
while (myAnim8) {
_GWS(myCruncher)->firstAnim8ToCrunch = myAnim8->next;
if (myAnim8->myCCB) {
KillCCB(myAnim8->myCCB, false);
}
mem_free(myAnim8->myRegs);
myAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch;
}
mem_free(_GWS(myCruncher));
if (_GWS(stackBase)) {
mem_free(_GWS(stackBase));
}
_GWS(cruncherInitialized) = false;
}
}
Anim8 *ws_AddAnim8ToCruncher(machine *m, int32 sequHash) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_AddAnim8ToCruncher()");
// Allocate an anim8 structure
Anim8 *myAnim8 = (Anim8 *)mem_alloc(sizeof(Anim8), "Anim8");
if (myAnim8 == nullptr) {
ws_LogErrorMsg(FL, "Out of memory - mem requested: %d.", sizeof(Anim8));
return nullptr;
}
// Find the sequence
int32 numLocalVars;
myAnim8->sequHandle = ws_GetSEQU((uint32)sequHash, &numLocalVars, &myAnim8->pcOffset);
if (myAnim8->sequHandle == nullptr) {
mem_free(myAnim8);
return nullptr;
}
// Allocate an array of registers
frac16 *my_regs = (frac16 *)mem_alloc(sizeof(frac16) * (IDX_COUNT + numLocalVars), "Anim8 regs");
if (my_regs == nullptr) {
ws_LogErrorMsg(FL, "Out of memory - mem requested: %d.", sizeof(frac16) * (IDX_COUNT + numLocalVars));
return nullptr;
}
// Initialize the Anim8 structure
myAnim8->active = true;
myAnim8->sequHash = sequHash;
myAnim8->myMachine = m; // Pointer back to myMachine
myAnim8->eosReqOffset = -1;
myAnim8->myParent = m->parentAnim8; // The parent anim8
myAnim8->myCCB = nullptr;
myAnim8->dataHash = m->dataHash; // The array of data
myAnim8->dataHandle = m->dataHandle;
myAnim8->dataOffset = m->dataOffset;
myAnim8->startTime = 0;
myAnim8->switchTime = 0;
myAnim8->flags = 0;
myAnim8->numLocalVars = numLocalVars;
myAnim8->myRegs = my_regs;
myAnim8->returnStackIndex = 0;
// Link it into the execution list
myAnim8->next = nullptr;
myAnim8->prev = _GWS(myCruncher)->lastAnim8ToCrunch;
if (_GWS(myCruncher)->lastAnim8ToCrunch) {
_GWS(myCruncher)->lastAnim8ToCrunch->next = myAnim8;
} else {
_GWS(myCruncher)->firstAnim8ToCrunch = myAnim8;
}
_GWS(myCruncher)->lastAnim8ToCrunch = myAnim8;
// Now link it into the layering list
myAnim8->myLayer = 0;
myAnim8->infront = nullptr;
myAnim8->behind = _GWS(myCruncher)->frontLayerAnim8;
if (_GWS(myCruncher)->frontLayerAnim8) {
_GWS(myCruncher)->frontLayerAnim8->infront = myAnim8;
} else {
_GWS(myCruncher)->backLayerAnim8 = myAnim8;
}
_GWS(myCruncher)->frontLayerAnim8 = myAnim8;
// Now clear the registers, but set the scale = 100%
for (int32 i = 0; i < IDX_COUNT + numLocalVars; i++) {
myAnim8->myRegs[i] = 0;
}
myAnim8->myRegs[IDX_S] = 0x10000;
myAnim8->myRegs[IDX_MACH_ID] = m->machID;
return myAnim8;
}
bool ws_ChangeAnim8Program(machine *m, int32 newSequHash) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_ChangeAnim8Program()");
// Parameter verification
if ((!m) || (!m->myAnim8)) {
error_show(FL, 'WSMI');
}
Anim8 *myAnim8 = m->myAnim8;
// Find the sequence
int32 numLocalVars;
myAnim8->sequHandle = ws_GetSEQU((uint32)newSequHash, &numLocalVars, &myAnim8->pcOffset);
if (myAnim8->sequHandle == nullptr) {
return false;
}
// Now see if we've started a sequence requiring more vars than the prev
if (myAnim8->numLocalVars < numLocalVars) {
ws_LogErrorMsg(FL, "Can't launch a sequence with more local vars than the previous sequence.");
return false;
}
// Intialize the Anim8
myAnim8->switchTime = 0;
myAnim8->active = true;
myAnim8->eosReqOffset = -1;
myAnim8->sequHash = newSequHash;
myAnim8->returnStackIndex = 0;
return true;
}
void ws_RemoveAnim8FromCruncher(Anim8 *myAnim8) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_RemoveAnim8FromCruncher()");
if (!myAnim8)
error_show(FL, 'WSAI');
// In case we are crunching the current list of EOS requests, remove any for this machine
EOSreq *tempEOSreq = _GWS(EOSreqList);
while (tempEOSreq && (tempEOSreq->myAnim8 != myAnim8)) {
tempEOSreq = tempEOSreq->next;
}
if (tempEOSreq) {
if (tempEOSreq->next) {
tempEOSreq->next->prev = tempEOSreq->prev;
}
if (tempEOSreq->prev) {
tempEOSreq->prev->next = tempEOSreq->next;
} else {
_GWS(EOSreqList) = tempEOSreq->next;
}
mem_free_to_stash((void *)tempEOSreq, _GWS(memtypeEOS));
}
// Incase we are in the middle of crunching
if (myAnim8 == _GWS(crunchNext)) {
_GWS(crunchNext) = myAnim8->next;
}
// Remove myAnim8 from the crunch list
if (myAnim8->prev) {
myAnim8->prev->next = myAnim8->next;
} else {
_GWS(myCruncher)->firstAnim8ToCrunch = myAnim8->next;
}
if (myAnim8->next) {
myAnim8->next->prev = myAnim8->prev;
} else {
_GWS(myCruncher)->lastAnim8ToCrunch = myAnim8->prev;
}
// Now remove it from the anim8 layer
if (myAnim8->infront) {
myAnim8->infront->behind = myAnim8->behind;
} else {
_GWS(myCruncher)->frontLayerAnim8 = myAnim8->behind;
}
if (myAnim8->behind) {
myAnim8->behind->infront = myAnim8->infront;
} else {
_GWS(myCruncher)->backLayerAnim8 = myAnim8->infront;
}
// Clean up and free the CCB for this anim8
if (myAnim8->myCCB) {
KillCCB(myAnim8->myCCB, true);
}
// Deallocate the register list
mem_free(myAnim8->myRegs);
// Finally, turf myAnim8
mem_free(myAnim8);
}
// This procedure flags the anim8 slot as inactive, but still owned by the machine
bool ws_PauseAnim8(Anim8 *myAnim8) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_PauseAnim8()");
if (!myAnim8)
error_show(FL, 'WSAI');
myAnim8->active = false;
HideCCB(myAnim8->myCCB);
return true;
}
// This procedure reactivates the anim8 slot owned by the machine
bool ws_ResumeAnim8(Anim8 *myAnim8) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_PauseAnim8()");
if (!myAnim8)
error_show(FL, 'WSAI');
myAnim8->active = true;
if (myAnim8->myCCB) {
ShowCCB(myAnim8->myCCB);
myAnim8->myCCB->flags |= CCB_SKIP;
}
return true;
}
static bool ExtractArg(Anim8 *myAnim8, int32 myFormat, int32 myData, frac16 **argPtr, frac16 *argValue) {
int32 myIndex;
Anim8 *parentAnim8;
uint32 *dataArray;
Common::String prefix;
// If the format indicates the argument is a local source (parent, register, or data)
if (myFormat == FMT_LOCAL_SRC) {
if (!myAnim8) {
ws_LogErrorMsg(FL, "INTERNAL ERROR - ExtractArg() failed - An invalid Anim8* was passed.");
return false;
}
// Find out if the index has been previously stored in a special index register
if (myData & REG_SET_IDX_REG) {
myIndex = _GWS(indexReg);
prefix = "S";
} else {
// Else the index is part of the data segment for this arg
myIndex = myData & REG_SET_IDX;
}
// Find the right register set
switch (myData & LOCAL_FMT) {
case LOCAL_FMT_PARENT:
parentAnim8 = myAnim8->myParent;
// Range check to make sure we don't index off into hyperspace
if ((!parentAnim8) || (myIndex >= IDX_COUNT + parentAnim8->numLocalVars)) {
if (!parentAnim8) {
ws_LogErrorMsg(FL, "Trying to access a parent register - no parent exists");
} else {
ws_LogErrorMsg(FL, "Parent Reg Index out of range - max: %d, requested %d.",
IDX_COUNT + parentAnim8->numLocalVars, myIndex);
}
return false;
}
*argPtr = &parentAnim8->myRegs[myIndex];
prefix += "P";
dbg_AddRegParamToCurrMachInstr(myIndex, prefix.c_str());
break;
case LOCAL_FMT_REG:
// Range check to make sure we don't index off into hyperspace
if (myIndex >= IDX_COUNT + myAnim8->numLocalVars) {
ws_LogErrorMsg(FL, "Register Index out of range - max: %d, requested %d.",
IDX_COUNT + myAnim8->numLocalVars, myIndex);
return false;
}
*argPtr = &myAnim8->myRegs[myIndex];
dbg_AddRegParamToCurrMachInstr(myIndex, prefix.c_str());
break;
case LOCAL_FMT_DATA:
// Ensure we have a dataHandle
if (!myAnim8->dataHandle || !*(myAnim8->dataHandle)) {
ws_LogErrorMsg(FL, "Trying to access a DATA field when no DATA has been set");
return false;
}
// Dereferrence the dataHandle, add the offset to find the array of data for this anim8
dataArray = (uint32 *)((intptr)*(myAnim8->dataHandle) + myAnim8->dataOffset);
// Copy the data field into dataArg1, and set myArg1 to point to this location
*argValue = (int32)FROM_LE_32(dataArray[myIndex]);
*argPtr = argValue;
prefix += Common::String::format("DATA %d", myIndex);
dbg_AddParamToCurrMachInstr(prefix.c_str());
break;
default:
break;
}
} else if (myFormat == FMT_GLOBAL_SRC) {
// Else if the format indicates the argument is from the ws_globals register set
// Find out if the index has been previously stored in a special index register
if (myData & REG_SET_IDX_REG) {
myIndex = _GWS(indexReg);
prefix = "S";
} else {
// Else the index is part of the data segment for this arg
myIndex = myData & REG_SET_IDX;
}
// Finally, set myArg1 to point to the location in the ws_globals array, whichever index
*argPtr = &(_GWS(ws_globals)[myIndex]);
dbg_AddGlobalParamToCurrMachInstr(myIndex, prefix.c_str());
} else {
// Else the argument is not a variable, but an actual value
// The top bit of the data segment is a negative flag, the format determines how far the other
// 15 bits of the data segment are shifted left, so the value requested is in frac16 format.
// The value is stored in the frac16 (dataArg1), and...
if (myData & OP_DATA_SIGN) {
*argValue = -(myData & OP_DATA_VALUE) << dataFormats[myFormat - 3];
} else {
*argValue = (myData & OP_DATA_VALUE) << dataFormats[myFormat - 3];
}
// myArg1 will point to this location
*argPtr = argValue;
prefix += Common::String::format("%" PRIdPTR, *argValue);
dbg_AddParamToCurrMachInstr(prefix.c_str());
}
return true;
}
int32 ws_PreProcessPcode(uint32 **PC, Anim8 *myAnim8) {
uint32 word2;
if (!PC) {
ws_LogErrorMsg(FL, "INTERNAL ERROR - ws_PreProcessPcode() failed - An invalid PC was passed.");
return -1;
}
uint32 *myPC = *PC;
// Get the opCode
const uint32 opCode = FROM_LE_32(*myPC++);
// Get the instruction number
const int32 myInstruction = (opCode & OP_INSTR) >> 25;
dbg_AddOpcodeToMachineInstr(myInstruction);
// Get the format for the first arg
int32 myFormat = (opCode & OP_FORMAT1) >> 22;
// Get the data for the first arg
int32 myData = opCode & OP_LOW_DATA;
// Verify we have an argument
if (myFormat) {
if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg1), &_GWS(dataArg1))) {
return -1;
}
} else {
// Otherwise this argument is called with no args
_GWS(myArg1) = nullptr;
_GWS(myArg2) = nullptr;
_GWS(myArg3) = nullptr;
*PC = myPC;
return myInstruction;
}
// Check for arg2
myFormat = (opCode & OP_FORMAT2) >> 19;
if (myFormat) {
word2 = FROM_LE_32(*myPC++);
myData = word2 >> 16;
if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg2), &_GWS(dataArg2))) {
return -1;
}
} else {
_GWS(myArg2) = nullptr;
_GWS(myArg3) = nullptr;
*PC = myPC;
return myInstruction;
}
// Finally check for arg3
myFormat = (opCode & OP_FORMAT3) >> 16;
if (myFormat) {
myData = word2 & OP_LOW_DATA;
if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg3), &_GWS(dataArg3))) {
return -1;
}
} else {
_GWS(myArg3) = nullptr;
*PC = myPC;
return myInstruction;
}
*PC = myPC;
return myInstruction;
}
static void op_END(Anim8 *myAnim8) {
_GWS(terminated) = true;
_GWS(keepProcessing) = false;
}
static void op_CLEAR(Anim8 *myAnim8) {
// Now clear the registers, but set the scale = 100%
for (int32 i = 0; i <= IDX_COUNT + myAnim8->numLocalVars; i++) {
myAnim8->myRegs[i] = 0;
}
myAnim8->myRegs[IDX_S] = 0x10000;
myAnim8->myRegs[IDX_MACH_ID] = myAnim8->myMachine->machID;
}
static void op_SET(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = arg2 or arg1 = rand(arg2, arg3)");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
*_GWS(myArg1) = *_GWS(myArg2);
}
}
static void op_COMPARE(Anim8 *myAnim8) {
frac16 myArg;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: cmp arg1, arg2 or cmp arg1, rand(arg2, arg3) **sets CCR");
}
if (_GWS(myArg3)) {
myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
myArg = *_GWS(myArg2);
}
if (*_GWS(myArg1) < myArg) {
_GWS(compareCCR) = -1;
} else if (*_GWS(myArg1) > myArg) {
_GWS(compareCCR) = 1;
} else {
_GWS(compareCCR) = 0;
}
}
static void op_ADD(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 += arg2 or arg1 += rand(arg2, arg3)");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) += imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
*_GWS(myArg1) += *_GWS(myArg2);
}
}
static void op_SUB(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 -= arg2 or arg1 -= rand(arg2, arg3)");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) -= imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
*_GWS(myArg1) -= *_GWS(myArg2);
}
}
static void op_MULT(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 *= arg2 or arg1 *= rand(arg2, arg3)");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) = MulSF16(*_GWS(myArg1), imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)));
} else {
*_GWS(myArg1) = MulSF16(*_GWS(myArg1), *_GWS(myArg2));
}
}
static void op_DIV(Anim8 *myAnim8) {
frac16 divisor;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 /= arg2 or arg1 /= rand(arg2, arg3)");
}
if (_GWS(myArg3)) {
divisor = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
divisor = *_GWS(myArg2);
}
if (divisor == 0) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0253, nullptr);
} else {
*_GWS(myArg1) = DivSF16(*_GWS(myArg1), divisor);
}
}
static void op_SIN(Anim8 *myAnim8) {
int32 tempAngle;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = sin(arg2) or arg1 = sin(rand(arg2, arg3))");
}
if (_GWS(myArg3)) {
tempAngle = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16;
} else {
tempAngle = *_GWS(myArg2) >> 16;
}
if (tempAngle < 0) {
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
} else {
tempAngle &= 0xff;
}
*_GWS(myArg1) = -(int)cosTable[tempAngle];
}
static void op_COS(Anim8 *myAnim8) {
int32 tempAngle;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = cos(arg2) or arg1 = cos(rand(arg2, arg3))");
}
if (_GWS(myArg3)) {
tempAngle = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16;
} else {
tempAngle = *_GWS(myArg2) >> 16;
}
if (tempAngle < 0) {
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
} else {
tempAngle &= 0xff;
}
*_GWS(myArg1) = sinTable[tempAngle];
}
static void op_AND(Anim8 *myAnim8) {
frac16 myArg;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 &= arg2 or arg1 &= rand(arg2, arg3) **also sets CCR");
}
if (_GWS(myArg3)) {
myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
myArg = *_GWS(myArg2);
}
if ((*_GWS(myArg1)) & myArg) {
_GWS(compareCCR) = 0;
} else {
_GWS(compareCCR) = 1;
}
*_GWS(myArg1) &= myArg;
}
static void op_OR(Anim8 *myAnim8) {
frac16 myArg;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 |= arg2 or arg1 |= rand(arg2, arg3) **also sets CCR");
}
if (_GWS(myArg3)) {
myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
myArg = *_GWS(myArg2);
}
if ((*_GWS(myArg1)) | myArg) {
_GWS(compareCCR) = 0;
} else {
_GWS(compareCCR) = 1;
}
*_GWS(myArg1) |= myArg;
}
static void op_NOT(Anim8 *myAnim8) {
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: arg1 = (arg1 ? 0 : 1) **also sets CCR");
}
if (*_GWS(myArg1) == 0) {
*_GWS(myArg1) = 0x10000;
_GWS(compareCCR) = 1;
} else {
*_GWS(myArg1) = 0;
_GWS(compareCCR) = 0;
}
}
static void op_ABS(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = abs(arg2)");
}
if (*_GWS(myArg2) < 0) {
*_GWS(myArg1) = -(int)(*_GWS(myArg2));
} else {
*_GWS(myArg1) = *_GWS(myArg2);
}
}
static void op_MIN(Anim8 *myAnim8) {
if (!_GWS(myArg3)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = min(arg2, arg3)");
}
if (*_GWS(myArg2) < *_GWS(myArg3)) {
*_GWS(myArg1) = *_GWS(myArg2);
} else {
*_GWS(myArg1) = *_GWS(myArg3);
}
}
static void op_MAX(Anim8 *myAnim8) {
if (!_GWS(myArg3)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = max(arg2, arg3)");
}
if (*_GWS(myArg2) < *_GWS(myArg3)) {
*_GWS(myArg1) = *_GWS(myArg3);
} else {
*_GWS(myArg1) = *_GWS(myArg2);
}
}
static void op_MOD(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 %= arg2 or arg1 = arg2%arg3");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) = (*_GWS(myArg1)) % (imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)));
} else {
*_GWS(myArg1) = (*_GWS(myArg1)) % (*_GWS(myArg2));
}
}
static void op_FLOOR(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = floor(arg2) or arg1 = floor(rand(arg2,arg3))");
}
if (_GWS(myArg3)) {
*_GWS(myArg1) = (imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16) << 16;
} else {
*_GWS(myArg1) = (*_GWS(myArg2) >> 16) << 16;
}
}
static void op_ROUND(Anim8 *myAnim8) {
frac16 myArg;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = round(arg2) or arg1 = round(rand(arg2,arg3))");
}
if (_GWS(myArg3)) {
myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
myArg = *_GWS(myArg2);
}
if ((myArg & 0xffff) >= 0x8000) {
*_GWS(myArg1) = ((myArg + 0x10000) >> 16) << 16;
} else {
*_GWS(myArg1) = (myArg >> 16) << 16;
}
}
static void op_CEIL(Anim8 *myAnim8) {
frac16 myArg;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = ceil(arg2) or arg1 = ceil(rand(arg2,arg3))");
}
if (_GWS(myArg3)) {
myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3));
} else {
myArg = *_GWS(myArg2);
}
if ((myArg & 0xffff) > 0) {
*_GWS(myArg1) = ((myArg >> 16) << 16) + 0x10000;
} else {
*_GWS(myArg1) = (myArg >> 16) << 16;
}
}
static void op_POINT(Anim8 *myAnim8) {
if (!_GWS(myArg3)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = angle of line segment (x, y) , (arg2, arg3)");
}
*_GWS(myArg1) = Atan2F16(-(int)(*_GWS(myArg3)) + myAnim8->myRegs[IDX_Y], *_GWS(myArg2) - myAnim8->myRegs[IDX_X]);
}
static void op_DIST2D(Anim8 *myAnim8) {
if (!_GWS(myArg3)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = distance from (x, y) to (arg2, arg3)");
}
int32 temp1 = (int32)(imath_abs(*_GWS(myArg2) - myAnim8->myRegs[IDX_X]));
int32 temp2 = (int32)(imath_abs(*_GWS(myArg3) - myAnim8->myRegs[IDX_Y]));
if ((temp1 >= 0x800000) || (temp2 >= 0x800000)) {
temp1 >>= 16;
temp2 >>= 16;
*_GWS(myArg1) = (frac16)(SqrtF16(temp1 * temp1 + temp2 * temp2) << 16);
} else {
*_GWS(myArg1) = SqrtF16(SquareSF16(temp1) + SquareSF16(temp2)) << 8;
}
}
static void op_CRUNCH(Anim8 *myAnim8) {
frac16 myArg;
if (_GWS(myArg2)) {
myArg = imath_ranged_rand16(*_GWS(myArg1), *_GWS(myArg2));
} else if (_GWS(myArg1)) {
myArg = *_GWS(myArg1);
} else {
myArg = 0;
}
myAnim8->startTime = _GWS(ws_globals)[GLB_TIME];
if (myArg >= 0) {
myAnim8->switchTime = _GWS(ws_globals)[GLB_TIME] + (myArg >> 16);
} else {
myAnim8->switchTime = -1;
}
if (myAnim8->transTime <= 0x10000) {
myAnim8->flags &= ~((TAG_BEZ | TAG_TARGS) << 16);
}
_GWS(keepProcessing) = false;
}
static void op_BRANCH(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "check the CCR, arg1 is the branch type, arg2 is the PC offset");
}
const int32 myOffset = *_GWS(myArg2) >> 14;
switch (*_GWS(myArg1) >> 16) {
case BRANCH_BR:
myAnim8->pcOffset += myOffset;
break;
case BRANCH_BLT:
if (_GWS(compareCCR) < 0) myAnim8->pcOffset += myOffset;
break;
case BRANCH_BLE:
if (_GWS(compareCCR) <= 0) myAnim8->pcOffset += myOffset;
break;
case BRANCH_BE:
if (_GWS(compareCCR) == 0) myAnim8->pcOffset += myOffset;
break;
case BRANCH_BNE:
if (_GWS(compareCCR) != 0) myAnim8->pcOffset += myOffset;
break;
case BRANCH_BGE:
if (_GWS(compareCCR) >= 0) myAnim8->pcOffset += myOffset;
break;
case BRANCH_BGT:
if (_GWS(compareCCR) > 0) myAnim8->pcOffset += myOffset;
break;
default:
break;
}
}
static void op_SETCEL(Anim8 *myAnim8) {
int32 myIndex;
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "setcel(arg1, arg2) or setcel(arg1, rand(arg2, arg3))");
}
if (_GWS(myArg3)) {
myIndex = imath_ranged_rand(*_GWS(myArg2) >> 16, *_GWS(myArg3) >> 16);
} else if (_GWS(myArg2)) {
myIndex = *_GWS(myArg2) >> 16;
} else {
myIndex = (*_GWS(myArg1) & 0xff0000) >> 16;
}
if (!myAnim8->myCCB) {
// Allocate and initialize a CCB structure
if ((myAnim8->myCCB = (CCB *)mem_alloc(sizeof(CCB), "CCB")) == nullptr) {
ws_LogErrorMsg(FL, "Out of memory - mem requested: %d bytes.", sizeof(CCB));
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02fe, "setcel() failed.");
}
if (!InitCCB(myAnim8->myCCB)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025d, "setcel() failed.");
}
}
CCB *myCCB = myAnim8->myCCB;
if (myCCB->flags & CCB_DISC_STREAM) {
ws_CloseSSstream(myCCB);
}
ShowCCB(myCCB);
myCCB->flags |= CCB_SKIP;
if ((myAnim8->myCCB = GetWSAssetCEL((uint32)(*_GWS(myArg1)) >> 24, (uint32)myIndex, myCCB)) == nullptr) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025b, "setcel() failed.");
}
frac16 *myRegs = myAnim8->myRegs;
if (myRegs[IDX_W] < 0) {
myRegs[IDX_W] = -myCCB->source->w << 16;
} else {
myRegs[IDX_W] = myCCB->source->w << 16;
}
myRegs[IDX_H] = myCCB->source->h << 16;
_GWS(mapTheCel) = true;
}
static void op_SEQ_SEND_MSG(Anim8 *myAnim8) {
frac16 msgValue;
//_GWS(myArg1) is the recipient machine hash, _GWS(myArg2) is the msg hash, _GWS(myArg3) (if exists) is the msg value
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: send to machine arg1, the message arg2 or the message arg2, arg3");
}
if (_GWS(myArg3)) {
msgValue = *_GWS(myArg3);
} else {
msgValue = 0;
}
sendWSMessage(*_GWS(myArg2), msgValue, nullptr, (*_GWS(myArg1)) >> 16, nullptr, 1);
return;
}
static void op_PUSH(Anim8 *myAnim8) {
int32 numOfArgs;
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: push arg1 or start with arg1, and push a total of arg2 values");
}
int32 direction = 1;
if (_GWS(myArg2)) {
if (*_GWS(myArg2) > 0) numOfArgs = (*_GWS(myArg2)) >> 16;
else {
numOfArgs = -(int)(*_GWS(myArg2)) >> 16;
direction = -1;
}
} else {
numOfArgs = 1;
}
if (((byte *)_GWS(stackLimit) - (byte *)_GWS(stackTop)) < (numOfArgs << 2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0254, "overflow during push instruction");
return;
}
if (_GWS(myArg2)) {
uint32 *data = (uint32 *)_GWS(myArg1);
for (int32 i = 0; i < numOfArgs; i++) {
*_GWS(stackTop)++ = *data;
data += direction;
}
} else {
*_GWS(stackTop)++ = (uint32)(*_GWS(myArg1));
}
}
static void op_POP(Anim8 *myAnim8) {
int32 numOfArgs;
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: pop into arg1 or start with arg1, and pop a total of arg2 values");
}
int32 direction = 1;
if (_GWS(myArg2)) {
if (*_GWS(myArg2) > 0) numOfArgs = (*_GWS(myArg2)) >> 16;
else {
numOfArgs = -(int)(*_GWS(myArg2)) >> 16;
direction = -1;
}
} else
numOfArgs = 1;
if (((byte *)_GWS(stackTop) - (byte *)_GWS(stackBase)) < (numOfArgs << 2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0255, "underflow during pop instruction");
}
if (_GWS(myArg2)) {
uint32 *data = (uint32 *)_GWS(myArg1);
for (int32 i = 0; i < numOfArgs; i++) {
*data = *(--_GWS(stackTop));
data += direction;
}
} else {
*_GWS(myArg1) = (frac16)(*(--_GWS(stackTop)));
}
}
static void op_JSR(Anim8 *myAnim8) {
if (myAnim8->returnStackIndex >= JSR_STACK_MAX) {
ws_LogErrorMsg(FL, "Max number of nested jsr instructions is: %d", JSR_STACK_MAX);
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0256, "jsr() failed");
}
myAnim8->returnHashes[myAnim8->returnStackIndex] = myAnim8->sequHash;
myAnim8->returnOffsets[myAnim8->returnStackIndex] = myAnim8->pcOffset;
myAnim8->returnStackIndex++;
// Find the sequence
int32 dummy;
myAnim8->sequHandle = ws_GetSEQU((uint32)*_GWS(myArg1) >> 16, &dummy, &myAnim8->pcOffset);
if (myAnim8->sequHandle == nullptr) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "jsr() failed");
}
myAnim8->sequHash = (uint32)*_GWS(myArg1) >> 16;
dbg_LaunchSequence(myAnim8);
}
static void op_RETURN(Anim8 *myAnim8) {
if (myAnim8->returnStackIndex <= 0) {
op_END(myAnim8);
return;
}
myAnim8->returnStackIndex--;
const uint32 returnSequHash = myAnim8->returnHashes[myAnim8->returnStackIndex];
const uint32 returnOffset = myAnim8->returnOffsets[myAnim8->returnStackIndex];
// Find the sequence
int32 dummy, dummy2;
myAnim8->sequHandle = ws_GetSEQU((uint32)returnSequHash, &dummy, &dummy2);
if (myAnim8->sequHandle == nullptr) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "return() failed");
}
myAnim8->sequHash = returnSequHash;
myAnim8->pcOffset = returnOffset;
dbg_LaunchSequence(myAnim8);
}
static void op_GET_CELS_COUNT(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_count(arg2)");
}
*_GWS(myArg1) = GetWSAssetCELCount((uint32)(*_GWS(myArg2)) >> 24) << 16;
}
static void op_GET_CELS_FRAME_RATE(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_frame_rate(arg2)");
}
*_GWS(myArg1) = GetWSAssetCELFrameRate((uint32)(*_GWS(myArg2)) >> 24);
}
static void op_GET_CELS_PIX_SPEED(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_pix_speed(arg2)");
}
*_GWS(myArg1) = GetWSAssetCELPixSpeed((uint32)(*_GWS(myArg2)) >> 24);
}
static void op_SET_INDEX(Anim8 *myAnim8) {
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: index_reg = arg1");
}
_GWS(indexReg) = *_GWS(myArg1) >> 16;
}
static void op_SET_LAYER(Anim8 *myAnim8) {
Anim8 *tempAnim8;
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: set_layer(arg1)");
}
const int32 newLayer = *_GWS(myArg1) >> 16;
const int32 myLayer = myAnim8->myLayer;
if (myLayer == newLayer) {
return;
}
// If we are moving myAnim8 closer to the front (screen)
if ((newLayer < myLayer) && myAnim8->infront) {
//search "upward" to find the new position for myAnim8
tempAnim8 = myAnim8->infront;
while (tempAnim8 && (tempAnim8->myLayer > newLayer)) {
tempAnim8 = tempAnim8->infront;
}
// If myAnim8 is to be moved
if (tempAnim8 != myAnim8->infront) {
// Remove myAnim8 from the list
myAnim8->infront->behind = myAnim8->behind;
if (myAnim8->behind) {
myAnim8->behind->infront = myAnim8->infront;
} else {
_GWS(myCruncher)->backLayerAnim8 = myAnim8->infront;
}
// If it belongs at the top layer of the list
if (!tempAnim8) {
myAnim8->infront = nullptr;
myAnim8->behind = _GWS(myCruncher)->frontLayerAnim8;
_GWS(myCruncher)->frontLayerAnim8->infront = myAnim8;
_GWS(myCruncher)->frontLayerAnim8 = myAnim8;
}
// Else it belongs after tempAnim8
else {
myAnim8->infront = tempAnim8;
myAnim8->behind = tempAnim8->behind;
tempAnim8->behind->infront = myAnim8;
tempAnim8->behind = myAnim8;
}
}
}
// Else we are moving myAnim8 close to the back (further from the screen)
else if ((newLayer > myLayer) && myAnim8->behind) {
//search "downward" to find the new position for myAnim8
tempAnim8 = myAnim8->behind;
while (tempAnim8 && (tempAnim8->myLayer < newLayer)) {
tempAnim8 = tempAnim8->behind;
}
// If myAnim8 is to be moved
if (tempAnim8 != myAnim8->behind) {
// Remove it from the list
if (myAnim8->infront) {
myAnim8->infront->behind = myAnim8->behind;
} else {
_GWS(myCruncher)->frontLayerAnim8 = myAnim8->behind;
}
myAnim8->behind->infront = myAnim8->infront;
// If it belongs at the bottom of the list
if (!tempAnim8) {
myAnim8->infront = _GWS(myCruncher)->backLayerAnim8;
myAnim8->behind = nullptr;
_GWS(myCruncher)->backLayerAnim8->behind = myAnim8;
_GWS(myCruncher)->backLayerAnim8 = myAnim8;
}
// Else it belongs right before tempAnim8
else {
myAnim8->infront = tempAnim8->infront;
myAnim8->behind = tempAnim8;
tempAnim8->infront->behind = myAnim8;
tempAnim8->infront = myAnim8;
}
}
}
// Now, make sure the layer is set in both myAnim8, and the register
myAnim8->myLayer = newLayer;
myAnim8->myRegs[IDX_LAYER] = newLayer << 16;
}
static void op_SET_DEPTH(Anim8 *myAnim8) {
int32 myDepth;
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: set_depth(arg1)");
}
if (!_GWS(myDepthTable)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02ff, "op_SET_DEPTH() failed - no depth table.");
}
for (myDepth = 0; myDepth < 15; myDepth++) {
if (_GWS(myDepthTable)[myDepth + 1] < (int)(*_GWS(myArg1) >> 16)) break;
}
_GWS(dataArg1) = (myAnim8->myRegs[IDX_LAYER] & 0xffffff) + (myDepth << 24);
_GWS(myArg1) = &_GWS(dataArg1);
op_SET_LAYER(myAnim8);
}
static void op_SET_DATA(Anim8 *myAnim8) {
if (!_GWS(myArg2)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: set_data(arg1, arg2)");
}
if ((myAnim8->dataHandle = ws_GetDATA(*_GWS(myArg1) >> 16, *_GWS(myArg2) >> 16, &myAnim8->dataOffset)) == nullptr) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "set_data() failed.");
}
}
static void op_OPEN_STREAM_SS(Anim8 *myAnim8) {
if (!_GWS(myArg1)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: stream_series(arg1)");
}
if (!myAnim8->myCCB) {
// Allocate and initialize a CCB structure
if ((myAnim8->myCCB = (CCB *)mem_alloc(sizeof(CCB), "CCB")) == nullptr) {
ws_LogErrorMsg(FL, "Out of memory - mem requested: %d.", sizeof(CCB));
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02fe, "open_ss_stream() failed.");
}
if (!InitCCB(myAnim8->myCCB)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025d, "open_ss_stream() failed.");
}
}
CCB *myCCB = myAnim8->myCCB;
ShowCCB(myCCB);
myCCB->flags |= CCB_SKIP;
if (!ws_OpenSSstream((SysFile *)(*_GWS(myArg1)), myAnim8)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0258, "open_ss_stream() failed.");
}
if (myAnim8->myRegs[IDX_W] < 0)
myAnim8->myRegs[IDX_W] = -myCCB->source->w << 16;
else
myAnim8->myRegs[IDX_W] = myCCB->source->w << 16;
myAnim8->myRegs[IDX_H] = myCCB->source->h << 16;
_GWS(mapTheCel) = true;
}
static void op_NEXT_STREAM_SS(Anim8 *myAnim8) {
if (!myAnim8->myCCB) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0253, "next_ss_stream() failed.");
}
CCB *myCCB = myAnim8->myCCB;
myCCB->flags |= CCB_SKIP;
if (!ws_GetNextSSstreamCel(myAnim8)) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0259, "next_ss_stream() failed.");
}
if (myAnim8->myRegs[IDX_W] < 0) {
myAnim8->myRegs[IDX_W] = -myCCB->source->w << 16;
} else {
myAnim8->myRegs[IDX_W] = myCCB->source->w << 16;
}
myAnim8->myRegs[IDX_H] = myCCB->source->h << 16;
_GWS(mapTheCel) = true;
}
static void op_CLOSE_STREAM_SS(Anim8 *myAnim8) {
if (!myAnim8->myCCB) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02f3, "close_ss_stream() failed.");
}
CCB *myCCB = myAnim8->myCCB;
ws_CloseSSstream(myCCB);
HideCCB(myCCB);
}
void (*pCodeJmpTable[])(Anim8 *myAnim8) = {
&op_END, //0
&op_CLEAR, //1
&op_SET, //2
&op_COMPARE, //3
&op_ADD, //4
&op_SUB, //5
&op_MULT, //6
&op_DIV, //7
&op_AND, //8
&op_OR, //9
&op_NOT, //10
&op_SIN, //11
&op_COS, //12
&op_ABS, //13
&op_MIN, //14
&op_MAX, //15
&op_MOD, //16
&op_FLOOR, //17
&op_ROUND, //18
&op_CEIL, //19
&op_POINT, //20
&op_DIST2D, //21
&op_CRUNCH, //22
&op_BRANCH, //23
&op_SETCEL, //24
&op_SEQ_SEND_MSG, //25
&op_PUSH, //26
&op_POP, //27
&op_JSR, //28
&op_RETURN, //29
&op_GET_CELS_COUNT, //30
&op_GET_CELS_FRAME_RATE, //31
&op_GET_CELS_PIX_SPEED, //32
&op_SET_INDEX, //33
&op_SET_LAYER, //34
&op_SET_DEPTH, //35
&op_SET_DATA, //36
&op_OPEN_STREAM_SS, //37
&op_NEXT_STREAM_SS, //38
&op_CLOSE_STREAM_SS //39
};
// The guts of the engine. This proc executes an anim8s program.
bool CrunchAnim8(Anim8 *myAnim8) {
bool moveTheCel = false;
frac16 percentDist;
uint32 *myPC;
// Get the register set for myAnim8
frac16 *myRegs = myAnim8->myRegs;
// Initialize the globals (flags, mostly) used in processing op codes
_GWS(keepProcessing) = false;
_GWS(terminated) = false;
_GWS(mapTheCel) = false;
_GWS(compareCCR) = 0;
//store the old values, so we can tell if we need to remap the Sprite
const frac16 oldX = myRegs[IDX_X];
const frac16 oldY = myRegs[IDX_Y];
const frac16 oldS = myRegs[IDX_S];
const int32 oldW = myRegs[IDX_W] >> 16;
const int32 oldH = myRegs[IDX_H] >> 16;
const int32 oldR = myRegs[IDX_R] >> 16;
// Check to see if we are still in an execution loop, or if it is time to
// Interpret further instructions
if ((myAnim8->switchTime >= 0) && ((int)_GWS(ws_globals)[GLB_TIME] >= myAnim8->switchTime)) {
_GWS(keepProcessing) = true;
}
// Decrement the timer register
myRegs[IDX_TIMER] -= (_GWS(ws_globals)[GLB_WATCH_DOG] << 16);
// Interpret pCode instructions until we hit something signalling to stop
while (_GWS(keepProcessing)) {
dbg_SetCurrSequInstr(myAnim8, _GWS(compareCCR));
myPC = (uint32 *)((intptr)*(myAnim8->sequHandle) + myAnim8->pcOffset);
uint32 *oldPC = myPC;
_GWS(pcOffsetOld) = myAnim8->pcOffset;
dbg_SetCurrMachInstr(myAnim8->myMachine, myAnim8->pcOffset, true);
const int32 myInstruction = ws_PreProcessPcode(&myPC, myAnim8);
if (myInstruction < 0 || myInstruction > 39) {
ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025c, nullptr);
}
dbg_EndCurrMachInstr();
myAnim8->pcOffset += (intptr)myPC - (intptr)oldPC;
pCodeJmpTable[myInstruction](myAnim8);
}
if (_GWS(bailOut)) {
_GWS(bailOut) = false;
return true;
}
if (_GWS(terminated)) {
if (_GWS(mapTheCel) || (oldR != (int)(myRegs[IDX_R] >> 16)) ||
(oldW != (int)(myRegs[IDX_W] >> 16)) || (oldH != (int)(myRegs[IDX_H] >> 16)) ||
(oldS != (int)myRegs[IDX_S])) {
myAnim8->flags |= TAG_MAP_CEL;
} else if ((oldX != myRegs[IDX_X]) || (oldY != myRegs[IDX_Y])) {
myAnim8->flags |= TAG_MOVE_CEL;
}
return false;
}
if (myAnim8->flags) {
const frac16 timeElapsed = (_GWS(ws_globals)[GLB_TIME] - myAnim8->startTime) << 16;
// This must be checked before TAG_TARGS because a bez path can use a target scale and rotate
// And we don't want to accidentally set up a target x or y.
// NOTE: for both bez paths, and targets, the time for the anim8 to reach the target or traverse
// the path is stored in IDX_TRANS_TIME, however, in order to determine how far aint32 the
// path we should be, we normally compute the elapsed time divided by the trans time.
// therefore we eliminate a division if we store the reciprocated trans time.
// This also serves as an initialization flag - if the trans time is > 1.
if ((myAnim8->flags >> 16) &TAG_BEZ) {
if (myAnim8->transTime > 0x10000) {
myAnim8->transTime = RecipUF16(myAnim8->transTime);
GetBezCoeffs(&myRegs[IDX_BEZ_CTRL], &myRegs[IDX_BEZ_COEFF]);
if ((myAnim8->flags >> 16) &TAG_TARGS) {
myAnim8->start_s = myRegs[IDX_S];
myAnim8->start_r = myRegs[IDX_R];
}
}
percentDist = MulSF16(timeElapsed, myAnim8->transTime);
if (percentDist < 0x10000) {
GetBezPoint(&myRegs[IDX_X], &myRegs[IDX_Y], &myRegs[IDX_BEZ_COEFF], percentDist);
if ((myAnim8->flags >> 16) &TAG_TARGS) {
myRegs[IDX_S] = myAnim8->start_s + MulSF16(percentDist, myRegs[IDX_TARG_S] - myAnim8->start_s);
myRegs[IDX_R] = myAnim8->start_r + MulSF16(percentDist, myRegs[IDX_TARG_R] - myAnim8->start_r);
_GWS(mapTheCel) = true;
} else moveTheCel = true;
} else if ((myRegs[IDX_X] != myRegs[IDX_BEZ_CTRL + 6]) || (myRegs[IDX_Y] != myRegs[IDX_BEZ_CTRL + 7])) {
myRegs[IDX_X] = myRegs[IDX_BEZ_CTRL + 6];
myRegs[IDX_Y] = myRegs[IDX_BEZ_CTRL + 7];
if ((myAnim8->flags >> 16) &TAG_TARGS) {
myRegs[IDX_S] = myRegs[IDX_TARG_S];
myRegs[IDX_R] = myRegs[IDX_TARG_R];
_GWS(mapTheCel) = true;
} else moveTheCel = true;
}
}
// Vectors must be checked before deltas, since vectors are converted to deltas at
// Initialization.
else if (((myAnim8->flags >> 16) &TAG_VECTORS) && (timeElapsed == 0)) {
myAnim8->start_x = myRegs[IDX_X];
myAnim8->start_y = myRegs[IDX_Y];
if ((myAnim8->flags >> 16) &TAG_DELTAS) {
myAnim8->start_s = myRegs[IDX_S];
myAnim8->start_r = myRegs[IDX_R];
}
const int32 tempAngle = (myRegs[IDX_THETA] >> 16) & 0xff;
myRegs[IDX_DELTA_X] = MulSF16(myRegs[IDX_VELOCITY], sinTable[tempAngle]);
myRegs[IDX_DELTA_Y] = MulSF16(myRegs[IDX_VELOCITY], -(int)cosTable[tempAngle]);
myAnim8->flags |= (TAG_DELTAS << 16);
} else if ((myAnim8->flags >> 16) &TAG_DELTAS) {
if (timeElapsed == 0) {
myAnim8->start_x = myRegs[IDX_X];
myAnim8->start_y = myRegs[IDX_Y];
myAnim8->start_s = myRegs[IDX_S];
myAnim8->start_r = myRegs[IDX_R];
} else {
myRegs[IDX_X] = myAnim8->start_x + MulSF16(timeElapsed, myRegs[IDX_DELTA_X]);
myRegs[IDX_Y] = myAnim8->start_y + MulSF16(timeElapsed, myRegs[IDX_DELTA_Y]);
if (myRegs[IDX_DELTA_R] || myRegs[IDX_DELTA_S]) {
myRegs[IDX_S] = myAnim8->start_s + MulSF16(timeElapsed, myRegs[IDX_DELTA_S]);
myRegs[IDX_R] = myAnim8->start_r + MulSF16(timeElapsed, myRegs[IDX_DELTA_R]);
_GWS(mapTheCel) = true;
} else moveTheCel = true;
}
} else if ((myAnim8->flags >> 16) &TAG_TARGS) {
if (myAnim8->transTime > 0x10000) {
myAnim8->start_s = myRegs[IDX_S];
myAnim8->start_r = myRegs[IDX_R];
myAnim8->start_x = myRegs[IDX_X];
myAnim8->start_y = myRegs[IDX_Y];
myAnim8->transTime = RecipUF16(myAnim8->transTime);
}
percentDist = MulSF16(timeElapsed, myAnim8->transTime);
if (percentDist < 0x10000) {
myRegs[IDX_X] = myAnim8->start_x + MulSF16(percentDist, myRegs[IDX_TARG_X] - myAnim8->start_x);
myRegs[IDX_Y] = myAnim8->start_y + MulSF16(percentDist, myRegs[IDX_TARG_Y] - myAnim8->start_y);
if (myRegs[IDX_TARG_R] || (myRegs[IDX_TARG_S] != myRegs[IDX_S])) {
myRegs[IDX_S] = myAnim8->start_s + MulSF16(percentDist, myRegs[IDX_TARG_S] - myAnim8->start_s);
myRegs[IDX_R] = myAnim8->start_r + MulSF16(percentDist, myRegs[IDX_TARG_R] - myAnim8->start_r);
_GWS(mapTheCel) = true;
} else moveTheCel = true;
} else {
myRegs[IDX_X] = myRegs[IDX_TARG_X];
myRegs[IDX_Y] = myRegs[IDX_TARG_Y];
myRegs[IDX_S] = myRegs[IDX_TARG_S];
myRegs[IDX_R] = myRegs[IDX_TARG_R];
myAnim8->flags &= ~TAG_TARGS;
_GWS(mapTheCel) = true;
}
}
}
if (_GWS(mapTheCel) || (oldR != (int)(myRegs[IDX_R] >> 16)) || (oldW != (int)(myRegs[IDX_W] >> 16)) ||
(oldH != (int)(myRegs[IDX_H] >> 16)) || (oldS != (int)myRegs[IDX_S])) {
_GWS(mapTheCel) = true;
} else if ((oldX != myRegs[IDX_X]) || (oldY != myRegs[IDX_Y])) {
moveTheCel = true;
}
if (moveTheCel || _GWS(mapTheCel) || (myAnim8->flags & (TAG_MAP_CEL | TAG_MOVE_CEL))) {
Cel_msr(myAnim8);
}
return true;
}
void ws_CrunchAnim8s(int16 *depth_table) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_CrunchAnim8s()");
//set up some of the _GWS(ws_globals) vars used for processing
_GWS(myDepthTable) = depth_table;
_GWS(crunchNext) = nullptr;
Anim8 *currAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch;
while (currAnim8) {
_GWS(crunchNext) = currAnim8->next;
if (currAnim8->active) {
if (!CrunchAnim8(currAnim8)) {
// If false was returned, this implies that an "END" op has been reached.
// Remove from the active list. Note: the machine still points to the anim8.
currAnim8->active = false;
if (currAnim8->eosReqOffset >= 0) {
// If the above field has a value, this implies that an On end sequence
//signal has been requested. If so, report back to the machine.
EOSreq *tempEOSreq = (EOSreq *)mem_get_from_stash(_GWS(memtypeEOS), "+EOS");
if (tempEOSreq == nullptr)
return;
tempEOSreq->myAnim8 = currAnim8;
tempEOSreq->prev = nullptr;
tempEOSreq->next = _GWS(EOSreqList);
if (_GWS(EOSreqList)) {
_GWS(EOSreqList)->prev = tempEOSreq;
}
_GWS(EOSreqList) = tempEOSreq;
}
}
}
currAnim8 = _GWS(crunchNext);
}
_GWS(crunchNext) = nullptr;
}
void ws_CrunchEOSreqs(void) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_CrunchEOSreqs()");
// Loop through, and handle all the eos requests
EOSreq *tempEOSreq = _GWS(EOSreqList);
while (tempEOSreq) {
_GWS(EOSreqList) = _GWS(EOSreqList)->next;
if (_GWS(EOSreqList)) {
_GWS(EOSreqList)->prev = nullptr;
}
const int32 pcOffset = tempEOSreq->myAnim8->eosReqOffset;
const int32 pcCount = tempEOSreq->myAnim8->eosReqCount;
machine *myXM = tempEOSreq->myAnim8->myMachine;
tempEOSreq->myAnim8->eosReqOffset = -1;
mem_free_to_stash((void *)tempEOSreq, _GWS(memtypeEOS));
ws_StepWhile(myXM, pcOffset, pcCount);
tempEOSreq = _GWS(EOSreqList);
}
}
// This proc was designed to allow the state machine to issue an OnEndSeq request
// In which the cruncher will signal the state machine should an anim8 ever finish.
bool ws_OnEndSeqRequest(Anim8 *myAnim8, int32 pcOffset, int32 pcCount) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_OnEndSeqRequest()");
myAnim8->eosReqOffset = pcOffset;
myAnim8->eosReqCount = pcCount;
return true;
}
void ws_CancelOnEndSeq(Anim8 *myAnim8) {
// Make sure the cruncher has been initialized
VERIFY_INTIALIZED("ws_CancelOnEndSeq()");
myAnim8->eosReqOffset = -1;
}
} // End of namespace M4