diff --git a/CMakeLists.txt b/CMakeLists.txt index e33c76c..df63640 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,3 +61,6 @@ add_executable(${CMAKE_PROJECT_NAME} ) target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +#target_link_libraries(${CMAKE_PROJECT_NAME} +#) diff --git a/include/common.h b/include/common.h index ad56ef6..0fe1a8a 100644 --- a/include/common.h +++ b/include/common.h @@ -28,7 +28,7 @@ #include -//#define DEBUGGING +#define DEBUGGING #ifdef DEBUGGING diff --git a/include/interpreter.h b/include/interpreter.h index b55e338..71e54b9 100644 --- a/include/interpreter.h +++ b/include/interpreter.h @@ -33,6 +33,8 @@ void interpreterDoReturn(uint16_t r); bool interpreterParseOperand(uint8_t opType, uint8_t operandId); uint8_t interpreterParseVariableOperands(uint8_t index); void interpreterRun(void); +void interpreterTokenizeInput(void); +void interpreterUpdateStatusBar(void); #endif // INTERPRETER_H diff --git a/include/memory.h b/include/memory.h index f71d4a9..ef44cad 100644 --- a/include/memory.h +++ b/include/memory.h @@ -40,7 +40,7 @@ #define ZPOKEW(a,v) portWordSet(a,v) -uint32_t memoryUnpackAddress(uint16_t address, uint8_t type); +uint32_t memoryUnpackAddress(uint32_t address, uint8_t type); #endif // MEMORY_H diff --git a/include/messages.h b/include/messages.h index 420132e..37c089e 100644 --- a/include/messages.h +++ b/include/messages.h @@ -29,39 +29,41 @@ #ifdef DEBUGGING -#define MSG_UNIMPLEMENTED "Unimplemented." -#define MSG_INT_INVALID_EXT_OPCODE "Invalid extended opcode!" -#define MSG_INT_NO_ABBR "No abbreviations in abbreviations!" -#define MSG_INT_OBJECT0_REF "Object 0 referenced!" -#define MSG_INT_OBJECT_BAD_ID "Invalid object ID!" -#define MSG_INT_REF_UNALLOCATED_LOCAL "Referenced unallocated local variable!" -#define MSG_INT_STACK_OVERFLOW "Stack overflow!" -#define MSG_INT_STACK_UNDERFLOW "Stack underflow!" -#define MSG_INT_UNIMPLEMENTED_OPCODE "Unimplemented opcode! %s %d" -#define MSG_INT_V12_SHIFT "Add V1/V2 shifting." -#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking." -#define MSG_MEM_BUFFER "Unable to allocate memory buffer." -#define MSG_OP_INP_BUFFER_TOO_SMALL "Text buffer too small for reading." -#define MSG_OP_OBJ_MISSING_PROPERTY "Missing object property." -#define MSG_OP_VAR_TOO_MANY_LOCALS "Too many local variables!" -#define MSG_OP_VAR_STACK_OVERFLOW "Stack overflow!" +#define MSG_UNIMPLEMENTED "Unimplemented." +#define MSG_INT_INVALID_EXT_OPCODE "Invalid extended opcode!" +#define MSG_INT_NO_ABBR "No abbreviations in abbreviations!" +#define MSG_INT_OBJECT0_REF "Object 0 referenced!" +#define MSG_INT_OBJECT_BAD_ID "Invalid object ID!" +#define MSG_INT_PARSE_BUFFER_TOO_SMALL "Parse buffer too small for reading." +#define MSG_INT_REF_UNALLOCATED_LOCAL "Referenced unallocated local variable!" +#define MSG_INT_STACK_OVERFLOW "Stack overflow!" +#define MSG_INT_STACK_UNDERFLOW "Stack underflow!" +#define MSG_INT_UNIMPLEMENTED_OPCODE "Unimplemented opcode! %s %d" +#define MSG_INT_V12_SHIFT "Add V1/V2 shifting." +#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking." +#define MSG_MEM_BUFFER "Unable to allocate memory buffer." +#define MSG_OP_CALL_TOO_MANY_LOCALS "Too many local variables! (%d)" +#define MSG_OP_CALL_STACK_OVERFLOW "Stack overflow!" +#define MSG_OP_INPUT_BUFFER_TOO_SMALL "Text buffer too small for reading." +#define MSG_OP_OBJ_MISSING_PROPERTY "Missing object property." #else // DEBUGGING -#define MSG_UNIMPLEMENTED "" -#define MSG_INT_INVALID_EXT_OPCODE "" -#define MSG_INT_NO_ABBR "" -#define MSG_INT_OBJECT0_REF "" -#define MSG_INT_OBJECT_BAD_ID "" -#define MSG_INT_REF_UNALLOCATED_LOCAL "" -#define MSG_INT_STACK_OVERFLOW "" -#define MSG_INT_STACK_UNDERFLOW "" -#define MSG_INT_UNIMPLEMENTED_OPCODE "" -#define MSG_INT_V12_SHIFT "" -#define MSG_INT_V12_SHIFT_LOCK "" -#define MSG_MEM_BUFFER "" -#define MSG_OP_INP_BUFFER_TOO_SMALL "" -#define MSG_OP_OBJ_MISSING_PROPERTY "" -#define MSG_OP_VAR_TOO_MANY_LOCALS "" -#define MSG_OP_VAR_STACK_OVERFLOW "" +#define MSG_UNIMPLEMENTED "" +#define MSG_INT_INVALID_EXT_OPCODE "" +#define MSG_INT_NO_ABBR "" +#define MSG_INT_OBJECT0_REF "" +#define MSG_INT_OBJECT_BAD_ID "" +#define MSG_INT_PARSE_BUFFER_TOO_SMALL "" +#define MSG_INT_REF_UNALLOCATED_LOCAL "" +#define MSG_INT_STACK_OVERFLOW "" +#define MSG_INT_STACK_UNDERFLOW "" +#define MSG_INT_UNIMPLEMENTED_OPCODE "" +#define MSG_INT_V12_SHIFT "" +#define MSG_INT_V12_SHIFT_LOCK "" +#define MSG_MEM_BUFFER "" +#define MSG_OP_CALL_TOO_MANY_LOCALS "" +#define MSG_OP_CALL_STACK_OVERFLOW "" +#define MSG_OP_INPUT_BUFFER_TOO_SMALL "" +#define MSG_OP_OBJ_MISSING_PROPERTY "" #endif // DEBUGGING diff --git a/include/oc_misc.h b/include/oc_misc.h index ab38e24..39841bf 100644 --- a/include/oc_misc.h +++ b/include/oc_misc.h @@ -30,6 +30,7 @@ void opcodes_nop(void); void opcodes_quit(void); +void opcodes_piracy(void); void opcodes_random(void); void opcodes_verify(void); diff --git a/include/state.h b/include/state.h index 9323774..449ef0d 100644 --- a/include/state.h +++ b/include/state.h @@ -30,19 +30,20 @@ typedef struct stateS { - uint16_t stack[2048]; //***TODO*** How big does this really need to be? Old games are 1024, new...? - bool quit; - uint32_t pc; // Program Counter - uint16_t sp; // Stack Pointer - uint16_t bp; // Base Pointer - opcodeT opcodes[256]; - opcodeT extOpcodes[30]; - uint8_t operandCount; - uint16_t operands[8]; - char alphabetTable[78]; + uint16_t stack[2048]; //***TODO*** How big does this really need to be? Old games are 1024, new...? + bool quit; + uint32_t pc; // Program Counter + uint16_t sp; // Stack Pointer + uint16_t bp; // Base Pointer + opcodeT opcodes[256]; + opcodeT extOpcodes[30]; + uint8_t operandCount; + uint16_t operands[8]; + char alphabetTable[78]; #ifdef DEBUGGING - char *opcodesName[256]; - char *extOpcodesName[30]; + char *opcodesName[256]; + char *extOpcodesName[30]; + uint32_t instructionsRun; #endif } stateT; diff --git a/include/story.h b/include/story.h index c9f1b2d..f1d4e12 100644 --- a/include/story.h +++ b/include/story.h @@ -60,6 +60,9 @@ #define STORY_SCREEN_HEIGHT_INFINITE 255 +#define STORY_SCREEN_HEIGHT_LINES 0x20 +#define STORY_SCREEN_WIDTH_CHARACTERS 0x21 + #define STORY_INTERPRETER_NUMBER_DECSYSTEM_20 1 #define STORY_INTERPRETER_NUMBER_APPLE_IIE 2 #define STORY_INTERPRETER_NUMBER_MACINTOSH 3 @@ -89,8 +92,8 @@ #define storyChecksum() portWordGet(0x1c) #define storyInterpreterNumber() portByteGet(0x1e) #define storyInterpreterVersion() portByteGet(0x1f) -#define storyScreenHeightLines() portByteGet(0x20) -#define storyScreenWidthCharacters() portByteGet(0x21) +#define storyScreenHeightLines() portByteGet(STORY_SCREEN_HEIGHT_LINES) +#define storyScreenWidthCharacters() portByteGet(STORY_SCREEN_WIDTH_CHARACTERS) #define storyScreenHeightUnits() portWordGet(0x22) #define storyScreenWidthUnits() portWordGet(0x24) #define storyFontHeightUnits() (storyVersion() == 5 ? portByteGet(0x27) ? portByteGet(0x26)) // WTF Infocom? diff --git a/ports/f256/build.sh b/ports/f256/build.sh index 88493f1..43d37fe 100755 --- a/ports/f256/build.sh +++ b/ports/f256/build.sh @@ -40,18 +40,19 @@ mkdir -p .builddir pushd .builddir ${CLANG} -c ${F256}/f256lib/f256.c -${CLANG} -c ../../../src/{interpreter,lib,memory,object,oc_call,oc_compare,oc_input,oc_math,oc_memory,oc_misc,oc_object,oc_output,oc_save,oc_window,opcodes,state,story,variable,zscii}.c +${CLANG} -c ../../zip.c ${CLANG} -c ../${PROJECT}.c -${CLANG} -o ${PROJECT} {interpreter,lib,memory,object,oc_call,oc_compare,oc_input,oc_math,oc_memory,oc_misc,oc_object,oc_output,oc_save,oc_window,opcodes,state,story,variable,zscii}.o f256.o ${PROJECT}.o +${CLANG} -o ${PROJECT} zip.o f256.o ${PROJECT}.o -mv -f ${PROJECT} zip.bin +mv -f ${PROJECT} ${PROJECT}.bin ${F256}/header \ pgz 24 \ ../${PROJECT}.pgz \ ${START} \ ${PROJECT}.bin ${START} \ - ../../../tests/testers/czech/czech.z3 + ../../../stories/zork1-r119-s880429.z3 0x10000 +# ../../../tests/testers/czech/czech.z3 0x10000 #llvm-nm ${PROJECT}.elf > ${PROJECT}.lst #llvm-objdump -d --print-imm-hex ${PROJECT}.elf > ${PROJECT}.lst diff --git a/ports/f256/f256zip.c b/ports/f256/f256zip.c index 61ce9f3..c534cf2 100644 --- a/ports/f256/f256zip.c +++ b/ports/f256/f256zip.c @@ -65,6 +65,9 @@ void portCharPrint(char c) { void portDie(char *fmt, ...) { +#ifdef DEBUGGING + printf("%s\n", fmt); // Yeah, this isn't right. +#endif exit(1); } @@ -134,12 +137,36 @@ bool portFileSave(void) { void portInput(uint32_t ramAddr, uint8_t length) { uint8_t i; - char c; + char c[2] = { 0, 0 }; + byte x; + byte y; for (i=0; i 0) { + textGetXY(&x, &y); + if (x > 0) { + x--; + } else { + x = 0; + y--; + } + textGotoXY(x, y); + textPrint(" "); + textGotoXY(x, y); + i--; + } + } else { + ZPOKE(ramAddr + i, c[0]); + // Enter/Return. + if ((c[0] == 13) || (c[0] == 10)) { + textPrint("\n"); + break; + } + textPrint(c); + } } } @@ -165,6 +192,9 @@ void portStoryLoad(void) { // installed. If they do, we can use it and then use the lower memory // for graphics and sound. + ZPOKE(STORY_SCREEN_WIDTH_CHARACTERS, 80); + ZPOKE(STORY_SCREEN_HEIGHT_LINES, 30); + // Currently no status bar. ZPOKE(STORY_FLAG_V3, ZPEEK(STORY_FLAG_V3) | STORY_FLAG_V3_STATUS_LINE_NOT_AVAILABLE); diff --git a/ports/f256/run.sh b/ports/f256/run.sh index f4ccdad..43cafbc 100755 --- a/ports/f256/run.sh +++ b/ports/f256/run.sh @@ -23,4 +23,4 @@ # -python ../../FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz zip.pgz +python ../../../f256/FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz f256zip.pgz diff --git a/ports/zip.c b/ports/zip.c new file mode 100644 index 0000000..4ce399b --- /dev/null +++ b/ports/zip.c @@ -0,0 +1,43 @@ +/* + * 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. + */ + + +// Everything but the platform-specific file to make ports easier to build. +#include "../src/interpreter.c" +#include "../src/lib.c" +#include "../src/memory.c" +#include "../src/object.c" +#include "../src/oc_call.c" +#include "../src/oc_compare.c" +#include "../src/oc_input.c" +#include "../src/oc_math.c" +#include "../src/oc_memory.c" +#include "../src/oc_misc.c" +#include "../src/oc_object.c" +#include "../src/oc_output.c" +#include "../src/oc_save.c" +#include "../src/oc_window.c" +#include "../src/opcodes.c" +#include "../src/state.c" +#include "../src/story.c" +#include "../src/variable.c" +#include "../src/zscii.c" diff --git a/src/interpreter.c b/src/interpreter.c index 6d29d22..2ba54e5 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -30,6 +30,7 @@ #include "state.h" #include "variable.h" #include "memory.h" +#include "lib.h" void interpreterDoBranch(int32_t truth) { @@ -64,7 +65,7 @@ void interpreterDoReturn(uint16_t r) { 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); + printf("Returning: initial pc=%X, bp=%u, sp=%u\n", (unsigned int)__state.pc, __state.bp, __state.sp); #endif // Dump all locals and data pushed on stack during the routine. @@ -82,7 +83,7 @@ void interpreterDoReturn(uint16_t r) { 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); + printf("Returning: new pc=%X, bp=%u, sp=%u\n", (unsigned int)__state.pc, __state.bp, __state.sp); #endif // Store the routine result. @@ -183,7 +184,14 @@ void interpreterRun(void) { 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]); + printf("ins=%u pc=%X sp=%u bp=%u %sopcode=%u ('%s') [", + __state.instructionsRun, + (unsigned int)__state.pc, + __state.sp, + __state.bp, + extended ? "ext " : "", + opcode, + extended ? __state.extOpcodesName[opcode] : __state.opcodesName[opcode]); if (__state.operandCount) { uint8_t i; for (i=0; i<__state.operandCount - 1; i++) @@ -191,9 +199,172 @@ void interpreterRun(void) { printf("%X", (unsigned int)__state.operands[i]); } printf("]\n"); + + if (__state.instructionsRun == 261) { + printf("\n*** BREAKING ***\n"); + fflush(stdout); + printf(" "); // Set breakpoint here. + } #endif op(); + +#ifdef DEBUGGING + __state.instructionsRun++; +#endif } } + +void interpreterTokenizeInput(void) { + static char tableA2v1[] = "0123456789.,!?_#\'\"/\\<-:()"; + static char tableA2v2plus[] = "\n0123456789.,!?_#\'\"/\\-:()"; + char *tableA2 = (storyVersion() <= 1) ? tableA2v1 : tableA2v2plus; + uint32_t input = __state.operands[0]; + uint32_t parse = __state.operands[1]; + byte parseLen = ZPEEK(parse++); + uint32_t seps = storyDictionaryAddress(); + byte numSeps = ZPEEK(seps++); + uint32_t dict = seps + numSeps; + byte entryLen = ZPEEK(dict++); + uint16_t numEntries = ZPEEKW(dict); dict += 2; + byte numToks = 0; + uint32_t strStart; + uint32_t ptr; + byte *ptr2; + bool isSep; + byte ch; + uint32_t i; + uint16_t encoded[3]; + byte tokLen; + byte zChars[12]; + byte zChIdx; + byte pos; + uint32_t dictPtr; + uint16_t zscii1; + uint16_t zscii2; + uint16_t zscii3; + +#ifdef DEBUGGING + printf("Max parse: %u\n", parseLen); +#endif + + if (parseLen == 0) portDie(MSG_INT_PARSE_BUFFER_TOO_SMALL); + + input++; // Skip inputLen byte. + parse++; // Skip where we put final token count. + + // Tokenize input. + strStart = input; + ptr = input; + while (1) { + isSep = false; + ch = ZPEEK(ptr); + + if ((ch == ' ') || (ch == 0)) { + isSep = true; + } else { + for (i=0; i= 'a') && (ch <= 'z')) { + zChars[zChIdx++] = ((ch - 'a') + 6); + } else { + ptr2 = (byte *)libStrChr(tableA2, ch); + if (ptr2) { + // Command char to shift to table A2 for just the next char. + zChars[zChIdx++] = 3; + // +1 because the first table entry is a different piece of magic. + zChars[zChIdx++] = ((ptr2 - (byte *)tableA2) + 1) + 6; + } + } + if (zChIdx >= (sizeof(zChars) / sizeof(zChars[0]))) break; + } // for + + pos = 0; + encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10; + encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5; + encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0; + encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10; + encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5; + encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0; + + dictPtr = dict; + if (storyVersion() <= 3) { + + encoded[1] |= 0x8000; + for (i=0; i= parseLen) break; // Ran out of space. + + strStart = ptr + 1; + } // if isSep + + if (ch == 0) break; // End of string. + + ptr++; + + } // while + +#ifdef DEBUGGING + printf("Tokenized %u tokens\n", (unsigned int)numToks); +#endif + + ZPOKE(__state.operands[1] + 1, numToks); +} + + +void interpreterUpdateStatusBar(void) { + //***TODO*** +} diff --git a/src/memory.c b/src/memory.c index e47aba4..525f905 100644 --- a/src/memory.c +++ b/src/memory.c @@ -25,24 +25,11 @@ #include "story.h" -uint32_t memoryUnpackAddress(uint16_t address, uint8_t type) { - switch (storyVersion()) { - case 1: - case 2: - case 3: - return (uint32_t)address * 2; - case 4: - case 5: - return (uint32_t)address * 4; - case 6: - if (type == MEMORY_ROUTINE) { - return ((uint32_t)address * 4) + storyRoutinesOffset(); - } else { - return ((uint32_t)address * 4) + storyStringsOffset(); - } - case 8: - return (uint32_t)address * 8; - } +uint32_t memoryUnpackAddress(uint32_t address, uint8_t type) { - return ((uint16_t)ZPEEK(address) << 8) | ((uint16_t)ZPEEK(address + 1)); + if (storyVersion() <= 3) return address * 2; + if (storyVersion() <= 5) return (address * 4) + (type == MEMORY_ROUTINE ? storyRoutinesOffset() : storyStringsOffset()); + if (storyVersion() <= 6) return address * 8; + + return 0; } diff --git a/src/object.c b/src/object.c index 015231d..72175fe 100644 --- a/src/object.c +++ b/src/object.c @@ -129,10 +129,19 @@ void objectUnparent(uint16_t objectId) { if (parentPtr != 0) { if (storyVersion() <= 3) { ptr = parentPtr + 6; // 4 to skip attrs, 2 to skip to child. +#ifdef DEBUGGING + printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId); +#endif while (ZPEEK(ptr) != objectId) { // If not direct child, look through sibling list.. ptr = objectPointerGet(ZPEEK(ptr)) + 5; +#ifdef DEBUGGING + printf("Checking %X (%d) for %d\n", (unsigned int)ptr, ZPEEK(ptr), objectId); +#endif } ZPOKE(ptr, ZPEEK(objPtr + 5)); // obj sibling takes obj's place. +#ifdef DEBUGGING + printf("Setting %X to %d from %X\n", (unsigned int)ptr, ZPEEK(objPtr + 5), objPtr + 5); +#endif } else { portDie(MSG_UNIMPLEMENTED); } diff --git a/src/oc_call.c b/src/oc_call.c index dc0e868..a59a8f6 100644 --- a/src/oc_call.c +++ b/src/oc_call.c @@ -40,15 +40,18 @@ void opcodes_call(void) { uint16_t src; uint8_t dst; - if ((args == 0) && (__state.operands[0] == 0)) { + if ((args == 0) || (__state.operands[0] == 0)) { // Legal no-op; store 0 to return value and bounce. - ZPOKEW(variableAddress(storeId, 1), 0); + variableStore(storeId, 0); } else { routine = memoryUnpackAddress(__state.operands[0], MEMORY_ROUTINE); numLocals = ZPEEK(routine++); - if (numLocals > 15) portDie(MSG_OP_VAR_TOO_MANY_LOCALS); +#ifdef DEBUGGING + printf("op0=%u routine=%X, numLocals=%d\n", __state.operands[0], (unsigned int)routine, numLocals); +#endif + if (numLocals > 15) portDie(MSG_OP_CALL_TOO_MANY_LOCALS, numLocals); - if (__state.sp + 5 + numLocals >= sizeof(__state.stack)) portDie(MSG_OP_VAR_STACK_OVERFLOW); + if (__state.sp + 5 + numLocals >= sizeof(__state.stack)) portDie(MSG_OP_CALL_STACK_OVERFLOW); // Save where we should store the call's result. __state.stack[__state.sp++] = storeId; diff --git a/src/oc_input.c b/src/oc_input.c index 62889da..345df83 100644 --- a/src/oc_input.c +++ b/src/oc_input.c @@ -26,57 +26,35 @@ #include "state.h" #include "memory.h" #include "messages.h" -#include "story.h" -#include "lib.h" +#include "interpreter.h" void opcodes_read(void) { - static char tableA2v1[] = "0123456789.,!?_#\'\"/\\<-:()"; - static char tableA2v2plus[] = "\n0123456789.,!?_#\'\"/\\-:()"; - uint32_t input = __state.operands[0]; - byte inputLen = ZPEEK(input++); - uint32_t parse = __state.operands[1]; - byte parseLen = ZPEEK(parse++); - char *tableA2 = (storyVersion() <= 1) ? tableA2v1 : tableA2v2plus; - uint32_t seps = storyDictionaryAddress(); - byte numSeps = ZPEEK(seps++); - uint32_t dict = seps + numSeps; - byte entryLen = ZPEEK(dict++); - uint16_t numEntries = ZPEEKW(dict); - byte numToks = 0; - uint32_t strStart; - uint32_t ptr; - byte *ptr2; - bool isSep; + uint32_t input = __state.operands[0]; + byte inputLen = ZPEEK(input++); byte ch; - byte i; - uint16_t encoded[3]; - byte tokLen; - byte zChars[12]; - byte zChIdx; - byte pos; - uint32_t dictPtr; - uint16_t zscii1; - uint16_t zscii2; - uint16_t zscii3; - - dict += 2; + uint32_t i; #ifdef DEBUGGING printf("Read from input stream: text-buffer=%X parse-buffer=%X\n", (unsigned int)__state.operands[0], (unsigned int)__state.operands[1]); printf("Max input: %u\n", inputLen); #endif - if (inputLen < 3) portDie(MSG_OP_INP_BUFFER_TOO_SMALL); + if (inputLen == 0) portDie(MSG_OP_INPUT_BUFFER_TOO_SMALL); - //***TODO*** - //interpreterUpdateStatusBar(); + interpreterUpdateStatusBar(); // Read input from user. portInput(input, inputLen); #ifdef DEBUGGING - printf("Input string from user is '%s'\n", __memoryBuffer); + printf("Input: ["); + for (i=input; i= 'a') && (ch <= 'z')) { - zChars[zChIdx++] = ((ch - 'a') + 6); - } else { - ptr2 = (byte *)libStrChr(tableA2, ch); - if (ptr2) { - // Command char to shift to table A2 for just the next char. - zChars[zChIdx++] = 3; - // +1 because the first table entry is a different piece of magic. - zChars[zChIdx++] = ((ptr2 - (byte *)tableA2) + 1) + 6; - } - } - if (zChIdx >= (sizeof(zChars) / sizeof(zChars[0]))) break; - } // for - - pos = 0; - encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10; - encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5; - encoded[0] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0; - encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10; - encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5; - encoded[1] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0; - - dictPtr = dict; - if (storyVersion() <= 3) { - - encoded[1] |= 0x8000; - for (i=0; i= parseLen) break; // Ran out of space. - - strStart = ptr + 1; - } // if isSep - - if (ch == 0) break; // End of string. - - ptr++; - - } // while - -#ifdef DEBUGGING - printf("Tokenized %u tokens\n", (unsigned int)numToks); -#endif - - ZPOKE(__state.operands[1] + 1, numToks); + interpreterTokenizeInput(); } diff --git a/src/oc_memory.c b/src/oc_memory.c index 0070d60..5a45336 100644 --- a/src/oc_memory.c +++ b/src/oc_memory.c @@ -30,7 +30,15 @@ void opcodes_load(void) { uint8_t dst = ZPEEK(__state.pc++); uint8_t src = (uint8_t)(__state.operands[0] & 0xFF); - int16_t val = variableLoad(src); + int16_t val; + + if (src == 0) { + // Indirect access. Do not pull, just read. + val = __state.stack[__state.sp - 1]; + } else { + val = variableLoad(src); + } + variableStore(dst, val); } @@ -53,7 +61,15 @@ void opcodes_pop(void) { void opcodes_pull(void) { - variableStore((__state.operands[0] & 0xFF), variableLoad(0)); + byte dst = (__state.operands[0] & 0xFF); + uint16_t val = variableLoad(0); + + if (dst == 0) { + // Indirect access. Do not push, just write. + __state.stack[__state.sp - 1] = val; + } else { + variableStore(dst, val); + } } @@ -63,7 +79,15 @@ void opcodes_push(void) { void opcodes_store(void) { - variableStore((__state.operands[0] & 0xFF), __state.operands[1]); + byte dst = (__state.operands[0] & 0xFF); + uint16_t val = __state.operands[1]; + + if (dst == 0) { + // Indirect access. Do not push, just write. + __state.stack[__state.sp - 1] = val; + } else { + variableStore(dst, val); + } } diff --git a/src/oc_misc.c b/src/oc_misc.c index d1c2138..ed664df 100644 --- a/src/oc_misc.c +++ b/src/oc_misc.c @@ -40,17 +40,33 @@ void opcodes_quit(void) { } +void opcodes_piracy(void) { + // Sure. This is an original game disk. + interpreterDoBranch(1); +} + + void opcodes_random(void) { variableStore((__state.pc++), portRandomGet((int16_t)__state.operands[0])); } void opcodes_verify(void) { + /* uint16_t checksum = 0; uint32_t i; for (i=64; i - #include "story.h" #include "state.h" #include "opcodes.h" @@ -171,7 +169,7 @@ void opcodesBuiltInitialTable(void) { mOP(185, catch); - mOP(191, piracy); + OP(191, piracy); mOP(228, aread); diff --git a/src/portme.c b/src/portme.c index 472b28e..543e4c8 100644 --- a/src/portme.c +++ b/src/portme.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "portme.h" #include "story.h" @@ -122,14 +124,43 @@ bool portFileSave(void) { } +/* reads from keypress, doesn't echo */ +int getch(void) { + struct termios oldattr, newattr; + int ch; + tcgetattr( STDIN_FILENO, &oldattr ); + newattr = oldattr; + newattr.c_lflag &= ~( ICANON | ECHO ); + tcsetattr( STDIN_FILENO, TCSANOW, &newattr ); + ch = getchar(); + tcsetattr( STDIN_FILENO, TCSANOW, &oldattr ); + return ch; +} + + void portInput(uint32_t ramAddr, uint8_t length) { - char input[256]; uint8_t i; + char c; - if (!fgets(input, length, stdin)) - portDie("EOF or error on stdin during read"); - - for (i=0; i 0) { + printf("\b \b"); + i--; + } + } else { + ZPOKE(ramAddr + i, c); + // Enter/Return. + if ((c == 13) || (c == 10)) { + printf("\n"); + break; + } + printf("%c", c); + } + fflush(stdout); + } } diff --git a/src/story.c b/src/story.c index 6fa4d27..aa4e70d 100644 --- a/src/story.c +++ b/src/story.c @@ -26,24 +26,12 @@ uint32_t storyLength() { - uint32_t m = (uint32_t)storyFileSize(); + uint32_t m = (uint32_t)storyFileSize() * 2; - if (m > 0) { - switch (storyVersion()) { - case 1: - case 2: - case 3: - m *= 2; - break; - case 4: - case 5: - m *= 4; - break; - case 6: - m *= 8; - break; - } - } else { + if (storyVersion() >= 4) m *= 2; + if (storyVersion() >= 6) m *= 2; + + if (m == 0) { // Some old stories do not have the size in the header. //***TODO*** } diff --git a/src/variable.c b/src/variable.c index b878603..ebcddf4 100644 --- a/src/variable.c +++ b/src/variable.c @@ -36,23 +36,19 @@ uint32_t variableAddressGlobal(uint16_t var) { uint16_t variableLoad(uint16_t var) { // Is it on the stack? - if (var <= 16) return __state.stack[variableAddress(var, 0)]; + if (var <= 16) return __state.stack[variableAddress(var, false)]; // Nope, global. return ZPEEKW(variableAddressGlobal(var)); } void variableStore(uint16_t var, uint16_t value) { - uint32_t addr; - if (var <= 16) { // On stack. - addr = variableAddress(var, 1); - __state.stack[addr] = value; + __state.stack[variableAddress(var, true)] = value; } else { // Global. - addr = variableAddressGlobal(var); - ZPOKEW(addr, value); + ZPOKEW(variableAddressGlobal(var), value); } }