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