More bug fixes. Passes CZECH V3 but has issues somewhere.

This commit is contained in:
Scott Duensing 2024-02-02 23:05:38 -06:00
parent 75a8bc61bc
commit e750c41ba6
23 changed files with 443 additions and 264 deletions

View file

@ -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}
#)

View file

@ -28,7 +28,7 @@
#include <stdint.h>
//#define DEBUGGING
#define DEBUGGING
#ifdef DEBUGGING

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -30,6 +30,7 @@
void opcodes_nop(void);
void opcodes_quit(void);
void opcodes_piracy(void);
void opcodes_random(void);
void opcodes_verify(void);

View file

@ -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;

View file

@ -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?

View file

@ -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

View file

@ -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<length; i++) {
c = getchar();
ZPOKE(ramAddr, c);
if ((c == 13) || (c == 10)) break;
c[0] = getchar();
// Delete/Backspace.
if ((c[0] == 8) || (c[0] == 127)) {
if (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);

View file

@ -23,4 +23,4 @@
#
python ../../FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz zip.pgz
python ../../../f256/FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz f256zip.pgz

43
ports/zip.c Normal file
View file

@ -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"

View file

@ -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<numSeps; i++) {
if (ch == ZPEEK(seps + i)) {
isSep = true;
break;
}
}
} // isSep
if (isSep) {
encoded[0] = 0;
encoded[1] = 0;
encoded[2] = 0;
tokLen = ptr - strStart;
if (tokLen == 0) break; // Ran out of string.
zChIdx = 0;
for (i=0; i<tokLen; i++) {
ch = ZPEEK(strStart + i);
if ((ch >= '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<numEntries; i++) {
zscii1 = ZPEEKW(dictPtr); dictPtr += 2;
zscii2 = ZPEEKW(dictPtr); dictPtr += 2;
if ((encoded[0] == zscii1) && (encoded[1] == zscii2)) {
dictPtr -= 4;
break;
}
dictPtr += (entryLen - 4);
}
} else { // V3
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10;
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5;
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0;
encoded[2] |= 0x8000;
for (i=0; i<numEntries; i++) {
zscii1 = ZPEEKW(dictPtr); dictPtr += 2;
zscii2 = ZPEEKW(dictPtr); dictPtr += 2;
zscii3 = ZPEEKW(dictPtr); dictPtr += 2;
if ((encoded[0] == zscii1) && (encoded[1] == zscii2) && (encoded[2] == zscii3)) {
dictPtr -= 6;
break;
}
dictPtr += (entryLen - 6);
}
} // V3
if (i == numEntries) dictPtr = 0; // Not Found.
ZPOKEW(parse, dictPtr); parse += 2;
ZPOKE(parse++, tokLen);
ZPOKE(parse++, (strStart - input) + 1);
numToks++;
if (numToks >= 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***
}

View file

@ -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;
}

View file

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

View file

@ -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;

View file

@ -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<input+inputLen; i++) {
ch = ZPEEK(i);
if ((ch == '\n') || (ch == '\r') || (ch == 0)) break;
printf("%c", ch);
}
printf("]\n");
#endif
// Tidy up input.
@ -86,7 +64,7 @@ void opcodes_read(void) {
// Make lowercase.
ch -= 'A' - 'a';
ZPOKE(i, ch);
} else if ((ch == '\n') || (ch == '\r')) {
} else if ((ch == '\n') || (ch == '\r') || (ch == 0)) {
// End of line, end of input.
ch = 0;
ZPOKE(i, ch);
@ -94,113 +72,5 @@ void opcodes_read(void) {
}
}
// Tokenize input.
strStart = input;
ptr = input;
while (1) {
isSep = false;
ch = ZPEEK(ptr);
if ((ch == ' ') || (ch == 0)) {
isSep = true;
} else {
for (i=0; i<numSeps; i++) {
if (ch == ZPEEK(seps + i)) {
isSep = true;
break;
}
}
} // isSep
if (isSep) {
encoded[0] = 0;
encoded[1] = 0;
encoded[2] = 0;
tokLen = ptr - strStart;
if (tokLen == 0) break; // Ran out of string.
zChIdx = 0;
for (i=0; i<tokLen; i++) {
ch = ZPEEK(strStart + i);
if ((ch >= '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<numEntries; i++) {
zscii1 = ZPEEKW(dictPtr); dictPtr += 2;
zscii2 = ZPEEKW(dictPtr); dictPtr += 2;
if ((encoded[0] == zscii1) && (encoded[1] == zscii2)) {
dictPtr -= 4;
break;
}
dictPtr += (entryLen - 4);
}
} else { // V3
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 10;
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 5;
encoded[2] |= ((pos < zChIdx) ? zChars[pos++] : 5) << 0;
encoded[2] |= 0x8000;
for (i=0; i<numEntries; i++) {
zscii1 = ZPEEKW(dictPtr); dictPtr += 2;
zscii2 = ZPEEKW(dictPtr); dictPtr += 2;
zscii3 = ZPEEKW(dictPtr); dictPtr += 2;
if ((encoded[0] == zscii1) && (encoded[1] == zscii2) && (encoded[2] == zscii3)) {
dictPtr -= 6;
break;
}
dictPtr += (entryLen - 6);
}
} // V3
if (i == numEntries) dictPtr = 0; // Not Found.
ZPOKEW(parse, dictPtr);
parse += 2;
ZPOKE(parse++, tokLen);
ZPOKE(parse++, strStart - input + 1);
numToks++;
if (numToks >= 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();
}

View file

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

View file

@ -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<storyLength(); i++) checksum += ZPEEK(i);
i = storyChecksum();
checksum %= 0x10000;
#ifdef DEBUGGING
printf("\n\nChecksum = %u Header = %d\n\n", checksum, storyChecksum());
#endif
interpreterDoBranch(checksum == storyChecksum() ? 1 : 0);
*/
// We lie. Everything passes.
interpreterDoBranch(1);
}

View file

@ -20,8 +20,6 @@
* SOFTWARE.
*/
#include <stdint.h>
#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);

View file

@ -25,6 +25,8 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#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<length; i++) _RAM[ramAddr] = input[i];
for (i=0; i<length; i++) {
c = getch();
// Delete/Backspace.
if ((c == 8) || (c == 127)) {
if (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);
}
}

View file

@ -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***
}

View file

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