199 lines
5.5 KiB
C
199 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "stddclmr.h"
|
|
|
|
#include "interpreter.h"
|
|
#include "portme.h"
|
|
#include "messages.h"
|
|
#include "story.h"
|
|
#include "state.h"
|
|
#include "variable.h"
|
|
#include "memory.h"
|
|
|
|
|
|
void interpreterDoBranch(int32_t truth) {
|
|
uint8_t branch = ZPEEK(__state.pc++);
|
|
int32_t farJump = (branch & (1 << 6)) == 0;
|
|
int32_t onTruth = (branch & (1 << 7)) ? 1 : 0;
|
|
uint8_t byte2 = farJump ? ZPEEK(__state.pc++) : 0;
|
|
int16_t offset;
|
|
|
|
if (onTruth == truth) {
|
|
offset = (int16_t)(branch & 0x3f);
|
|
if (farJump) {
|
|
if (offset & (1 << 5)) offset |= 0xc0;
|
|
offset = (offset << 8) | ((int16_t)byte2);
|
|
}
|
|
if (offset == 0) {
|
|
interpreterDoReturn(0);
|
|
} else if (offset == 1) {
|
|
interpreterDoReturn(1);
|
|
} else {
|
|
__state.pc = (__state.pc + offset) - 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void interpreterDoReturn(uint16_t r) {
|
|
uint8_t storeId;
|
|
|
|
//***TODO*** Newer versions start in a real routine, but still aren't allowed to return from it.
|
|
|
|
if (__state.bp == 0) portDie(MSG_INT_STACK_UNDERFLOW);
|
|
|
|
#ifdef DEBUGGING
|
|
printf("Returning: initial pc=%X, bp=%u, sp=%u\n", __state.pc, __state.bp, __state.sp);
|
|
#endif
|
|
|
|
// Dump all locals and data pushed on stack during the routine.
|
|
__state.sp = __state.bp;
|
|
// Dump our copy of numLocals.
|
|
__state.sp--;
|
|
// Restore previous frame's base pointer.
|
|
__state.bp = __state.stack[--__state.sp];
|
|
|
|
// Point to start of saved program counter;
|
|
__state.sp -= 2;
|
|
__state.pc = ((uint32_t)__state.stack[__state.sp]) | (((uint32_t)__state.stack[__state.sp + 1]) << 16);
|
|
|
|
// Pop the result storage location.
|
|
storeId = (uint8_t)__state.stack[--__state.sp];
|
|
|
|
#ifdef DEBUGGING
|
|
printf("Returning: new pc=%X, bp=%u, sp=%u\n", __state.pc, __state.bp, __state.sp);
|
|
#endif
|
|
|
|
// Store the routine result.
|
|
variableStore(storeId, r);
|
|
}
|
|
|
|
|
|
bool interpreterParseOperand(uint8_t opType, uint8_t operandId) {
|
|
switch(opType) {
|
|
// Large constant.
|
|
case 0:
|
|
__state.operands[operandId] = ZPEEKW(__state.pc);
|
|
__state.pc += 2;
|
|
return true;
|
|
|
|
// Small constant.
|
|
case 1:
|
|
__state.operands[operandId] = ZPEEK(__state.pc++);
|
|
return true;
|
|
|
|
// Variable.
|
|
case 2:
|
|
__state.operands[operandId] = variableLoad(ZPEEK(__state.pc++));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
uint8_t interpreterParseVariableOperands(uint8_t index) {
|
|
uint8_t operandTypes = ZPEEK(__state.pc++);
|
|
uint8_t shifter = 6;
|
|
uint8_t i;
|
|
uint8_t opType;
|
|
|
|
for (i=0; i<4; i++) {
|
|
opType = (operandTypes >> shifter) & 0x3;
|
|
shifter -= 2;
|
|
if (!interpreterParseOperand(opType, index + i)) break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
void interpreterRun(void) {
|
|
uint8_t opcode;
|
|
bool extended;
|
|
opcodeT op;
|
|
bool eight;
|
|
|
|
while (__state.quit == 0) {
|
|
opcode = ZPEEK(__state.pc++);
|
|
extended = ((opcode == 190) && (storyVersion() >= 5)) ? true : false;
|
|
|
|
if (extended) {
|
|
|
|
opcode = ZPEEK(__state.pc++);
|
|
if (opcode >= (sizeof(__state.extOpcodes) / sizeof(__state.extOpcodes[0]))) portDie(MSG_INT_INVALID_EXT_OPCODE);
|
|
__state.operandCount = interpreterParseVariableOperands(0);
|
|
op = __state.extOpcodes[opcode];
|
|
|
|
} else {
|
|
|
|
if (opcode <= 127) {
|
|
// 2OP
|
|
__state.operandCount = 2;
|
|
interpreterParseOperand(((opcode >> 6) & 0x1) ? 2 : 1, 0);
|
|
interpreterParseOperand(((opcode >> 5) & 0x1) ? 2 : 1, 1);
|
|
} else if (opcode <= 175) {
|
|
// 1OP
|
|
__state.operandCount = 1;
|
|
interpreterParseOperand((opcode >> 4) & 0x3, 0);
|
|
} else if (opcode <= 191) {
|
|
// 0OP
|
|
__state.operandCount = 0;
|
|
} else if (opcode > 191) {
|
|
// Variable Opcodes
|
|
eight = (opcode == 236) || (opcode == 250);
|
|
if (!eight) {
|
|
// call_vs2 and call_vn2 take up to EIGHT arguments!
|
|
__state.operandCount = interpreterParseVariableOperands(0);
|
|
} else {
|
|
__state.operandCount = interpreterParseVariableOperands(0);
|
|
if (__state.operandCount == 4) {
|
|
__state.operandCount += interpreterParseVariableOperands(4);
|
|
} else {
|
|
__state.pc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
op = __state.opcodes[opcode];
|
|
|
|
} // extended
|
|
|
|
if (op == 0) portDie(MSG_INT_UNIMPLEMENTED_OPCODE, extended ? "ext" : "", opcode);
|
|
|
|
#ifdef DEBUGGING
|
|
printf("pc=%X %sopcode=%u ('%s') [", (unsigned int)__state.pc, extended ? "ext " : "", opcode, extended ? __state.extOpcodesName[opcode] : __state.opcodesName[opcode]);
|
|
if (__state.operandCount) {
|
|
uint8_t i;
|
|
for (i=0; i<__state.operandCount - 1; i++)
|
|
printf("%X,", (unsigned int)__state.operands[i]);
|
|
printf("%X", (unsigned int)__state.operands[i]);
|
|
}
|
|
printf("]\n");
|
|
#endif
|
|
|
|
op();
|
|
}
|
|
}
|
|
|