muddle/src/interpreter.c
2024-02-01 19:43:16 -06:00

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();
}
}