More bug fixes. Passes CZECH V3 but has issues somewhere.
This commit is contained in:
parent
75a8bc61bc
commit
e750c41ba6
23 changed files with 443 additions and 264 deletions
|
@ -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}
|
||||
#)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
|
||||
//#define DEBUGGING
|
||||
#define DEBUGGING
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
void opcodes_nop(void);
|
||||
void opcodes_quit(void);
|
||||
void opcodes_piracy(void);
|
||||
void opcodes_random(void);
|
||||
void opcodes_verify(void);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
43
ports/zip.c
Normal 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"
|
|
@ -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***
|
||||
}
|
||||
|
|
25
src/memory.c
25
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
160
src/oc_input.c
160
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<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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
41
src/portme.c
41
src/portme.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
22
src/story.c
22
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***
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue