diff --git a/.gitignore b/.gitignore index eb236cd..c8350c0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,17 @@ # Stuff I've downloaded stuff/ +# ZIP tests. +tests/ + # Story files stories/ +# Build files. +.builddir/ +*.o +*.pgz + # Dumb QtCreator junk build-*/ *.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c83290..e33c76c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(zip LANGUAGES C) set(HEADERS common.h interpreter.h + lib.h memory.h messages.h object.h @@ -25,13 +26,14 @@ set(HEADERS story.h variable.h zscii.h +# czech.z3.h zork1.h ) list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/include/") set(SOURCE interpreter.c - main.c + lib.c memory.c object.c oc_call.c diff --git a/include/common.h b/include/common.h index 0fe1a8a..ad56ef6 100644 --- a/include/common.h +++ b/include/common.h @@ -28,7 +28,7 @@ #include -#define DEBUGGING +//#define DEBUGGING #ifdef DEBUGGING diff --git a/include/lib.h b/include/lib.h new file mode 100644 index 0000000..5154b9e --- /dev/null +++ b/include/lib.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + + +#ifndef LIB_H +#define LIB_H + + +#include "common.h" + + +void libMemSet(byte *start, byte val, uint32_t len); +char *libStrChr(char *haystack, char needle); +uint32_t libStrLen(char *str); + + +#endif // LIB_H diff --git a/include/memory.h b/include/memory.h index ac2a5f5..f71d4a9 100644 --- a/include/memory.h +++ b/include/memory.h @@ -40,13 +40,6 @@ #define ZPOKEW(a,v) portWordSet(a,v) -extern char *__memoryBuffer; -extern uint16_t __memoryBufferLen; - - -void memoryEnsureBuffer(uint32_t newSize); -void memoryShutdown(void); -void memoryStartup(void); uint32_t memoryUnpackAddress(uint16_t address, uint8_t type); diff --git a/include/messages.h b/include/messages.h index 0da85ed..420132e 100644 --- a/include/messages.h +++ b/include/messages.h @@ -41,6 +41,7 @@ #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!" @@ -57,7 +58,8 @@ #define MSG_INT_V12_SHIFT "" #define MSG_INT_V12_SHIFT_LOCK "" #define MSG_MEM_BUFFER "" -#define MSG_OP_VAR_MISSING_PROPERTY "" +#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 "" #endif // DEBUGGING diff --git a/include/object.h b/include/object.h index ee732d3..0469acd 100644 --- a/include/object.h +++ b/include/object.h @@ -31,7 +31,7 @@ uint32_t objectPointerGet(uint16_t objectId); uint32_t objectPointerParentGet(uint32_t objectPointer); uint16_t objectPropertyDefaultGet(uint32_t propertyId); -uint8_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size); +uint32_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size); uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship); void objectUnparent(uint16_t objectId); diff --git a/include/oc_math.h b/include/oc_math.h index 4373934..6d753b1 100644 --- a/include/oc_math.h +++ b/include/oc_math.h @@ -35,6 +35,7 @@ void opcodes_div(void); void opcodes_inc(void); void opcodes_mod(void); void opcodes_mul(void); +void opcodes_not(void); void opcodes_or(void); void opcodes_sub(void); diff --git a/include/oc_memory.h b/include/oc_memory.h index 78699ff..3870db0 100644 --- a/include/oc_memory.h +++ b/include/oc_memory.h @@ -31,6 +31,9 @@ void opcodes_load(void); void opcodes_loadb(void); void opcodes_loadw(void); +void opcodes_pop(void); +void opcodes_pull(void); +void opcodes_push(void); void opcodes_store(void); void opcodes_storeb(void); void opcodes_storew(void); diff --git a/include/oc_misc.h b/include/oc_misc.h index 34259d4..ab38e24 100644 --- a/include/oc_misc.h +++ b/include/oc_misc.h @@ -31,6 +31,7 @@ void opcodes_nop(void); void opcodes_quit(void); void opcodes_random(void); +void opcodes_verify(void); #endif // OC_MISC_H diff --git a/include/portme.h b/include/portme.h index 7e8fde5..e797acb 100644 --- a/include/portme.h +++ b/include/portme.h @@ -30,13 +30,11 @@ uint8_t portByteGet(uint16_t address); void portByteSet(uint16_t address, uint8_t value); -void portCharsPrint(char *chars, uint16_t len); +void portCharPrint(char c); void portDie(char *fmt, ...); -void portFileClose(uint32_t *handle); -byte portFileByteRead(uint32_t *handle); -bool portFileByteWrite(uint32_t *handle, byte value); -bool portFileReadOpen(uint32_t *handle, char *name); -bool portFileWriteOpen(uint32_t *handle, char *name); +bool portFileRestore(void); +bool portFileSave(void); +void portInput(uint32_t ramAddr, uint8_t length); uint16_t portRandomGet(int16_t range); void portStoryLoad(void); uint16_t portWordGet(uint16_t address); diff --git a/include/story.h b/include/story.h index d708f7c..c9f1b2d 100644 --- a/include/story.h +++ b/include/story.h @@ -85,6 +85,7 @@ #define storyStaticMemoryBaseAddress() portWordGet(0xe) #define storyFlags2() portWordGet(0x10) #define storyAbbreviationTableAddress() portWordGet(0x18) +#define storyFileSize() portWordGet(0x1a) #define storyChecksum() portWordGet(0x1c) #define storyInterpreterNumber() portByteGet(0x1e) #define storyInterpreterVersion() portByteGet(0x1f) diff --git a/include/zscii.h b/include/zscii.h index 71b7726..5d0677d 100644 --- a/include/zscii.h +++ b/include/zscii.h @@ -28,7 +28,6 @@ #include "common.h" -uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP); char zsciiDecodeChar(uint16_t z); uint32_t zsciiPrint(uint32_t pc, bool abbr); diff --git a/ports/f256/CMakeLists.txt b/ports/f256/CMakeLists.txt new file mode 100644 index 0000000..16d9759 --- /dev/null +++ b/ports/f256/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.12) + +project(zip LANGUAGES C) + +set(HEADERS + common.h + interpreter.h + lib.h + memory.h + messages.h + object.h + oc_call.h + oc_compare.h + oc_input.h + oc_math.h + oc_memory.h + oc_misc.h + oc_object.h + oc_output.h + oc_save.h + oc_window.h + opcodes.h + portme.h + state.h + stddclmr.h + story.h + variable.h + zscii.h +# czech.z3.h +# zork1.h +) +list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/../../include/") + +set(SOURCE + interpreter.c + lib.c + memory.c + object.c + oc_call.c + oc_compare.c + oc_input.c + oc_math.c + oc_memory.c + oc_misc.c + oc_object.c + oc_output.c + oc_save.c + oc_window.c + opcodes.c + state.c + story.c + variable.c + zscii.c +) +list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/../../src/") + +add_executable(${CMAKE_PROJECT_NAME} + ${HEADERS} + ${SOURCE} + f256zip.c +) + +target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../include) diff --git a/ports/f256/build.sh b/ports/f256/build.sh new file mode 100755 index 0000000..88493f1 --- /dev/null +++ b/ports/f256/build.sh @@ -0,0 +1,60 @@ +#!/bin/bash -ex + +# +# 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. +# + + +PROJECT=f256zip +START=0x200 + +F256=$(pwd)/../../../f256 +LLVM=${F256}/llvm-mos +SETTINGS=${LLVM}/mos-platform/f256k/lib/settings.ld +PATH=${LLVM}/bin:${PATH} + +echo "__f256_start = ${START};" > ${SETTINGS} + +CLANG="mos-f256k-clang -I../../../include -I${F256}/include -I${F256}/f256lib -Os" + +[[ -d .builddir ]] && rm -rf .builddir +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 ../${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 + +mv -f ${PROJECT} zip.bin + +${F256}/header \ + pgz 24 \ + ../${PROJECT}.pgz \ + ${START} \ + ${PROJECT}.bin ${START} \ + ../../../tests/testers/czech/czech.z3 + +#llvm-nm ${PROJECT}.elf > ${PROJECT}.lst +#llvm-objdump -d --print-imm-hex ${PROJECT}.elf > ${PROJECT}.lst +#hexdump -C ../${PROJECT}.pgz > ${PROJECT}.hex + +popd diff --git a/ports/f256/f256zip.c b/ports/f256/f256zip.c new file mode 100644 index 0000000..61ce9f3 --- /dev/null +++ b/ports/f256/f256zip.c @@ -0,0 +1,200 @@ +/* + * 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. + */ + + +/* + * Z-Machine for the Foenix F256 computers. + * + * F256 machines are 65c02 based systems with 512k of banked RAM. + * As such, this ZIP is going to load the story entirely into "high" + * memory (above the 64k accessable to the CPU) and page things in + * and out as needed. + * + * To keep library code at a minimum, we'll also statically allocate + * as much as possible and do other "bad" things now frowned upon in + * modern development. :-) + */ + + +#include "f256.h" + +#include "portme.h" +#include "story.h" +#include "memory.h" +#include "state.h" +#include "interpreter.h" + + +#define BASE_ADDRESS 0x10000 + + +uint8_t portByteGet(uint16_t address) { + return FAR_PEEK(BASE_ADDRESS + address); +} + + +void portByteSet(uint16_t address, uint8_t value) { + FAR_POKE(BASE_ADDRESS + address, value); +} + + +void portCharPrint(char c) { + static char ch[2] = { 0, 0 }; + ch[0] = c; + textPrint(ch); +} + + +void portDie(char *fmt, ...) { + exit(1); +} + + +bool portFileRestore(void) { + + FILE *in; + bool ok = false; + uint32_t i; + byte b; + + in = fopen("save.dat", "rb"); + if (in) { + ok = true; + + // Read in PC. + ok &= (fread(&__state.pc, sizeof(__state.pc), 1, in) == 1); + // Read in SP. + ok &= (fread(&__state.sp, sizeof(__state.sp), 1, in) == 1); + // Read in BP. + ok &= (fread(&__state.bp, sizeof(__state.bp), 1, in) == 1); + // Read in dynamic game RAM. + for (i=0; i - #include "memory.h" #include "story.h" -#include "portme.h" -#include "messages.h" - - -// Generic buffer for whatever. -char *__memoryBuffer = 0; -uint16_t __memoryBufferLen = 0; - - -void memoryEnsureBuffer(uint32_t newSize) { - if (__memoryBufferLen >= newSize) return; - free(__memoryBuffer); - while (__memoryBufferLen < newSize) { - __memoryBufferLen += 512; - } - __memoryBuffer = (char *)malloc(__memoryBufferLen); - if (__memoryBuffer == 0) portDie(MSG_MEM_BUFFER); -} - - -void memoryShutdown(void) { - free(__memoryBuffer); - __memoryBuffer = 0; - __memoryBufferLen = 0; -} - - -void memoryStartup(void) { - memoryEnsureBuffer(1024); -} uint32_t memoryUnpackAddress(uint16_t address, uint8_t type) { diff --git a/src/object.c b/src/object.c index 7d08001..015231d 100644 --- a/src/object.c +++ b/src/object.c @@ -71,7 +71,7 @@ uint16_t objectPropertyDefaultGet(uint32_t propertyId) { } -uint8_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) { +uint32_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) { uint32_t ptr = objectPointerGet(objectId); uint16_t addr; uint8_t info; @@ -112,7 +112,7 @@ uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship) { uint16_t result = 0; if (storyVersion() <= 3) { - result = ZPEEKW(objPtr + relationship); + result = ZPEEK(objPtr + relationship); } else { portDie(MSG_UNIMPLEMENTED); } diff --git a/src/oc_compare.c b/src/oc_compare.c index 1bf5227..844c6fd 100644 --- a/src/oc_compare.c +++ b/src/oc_compare.c @@ -77,7 +77,7 @@ void opcodes_jin(void) { if (objPtr == 0) return; if (storyVersion() <= 3) { - interpreterDoBranch((ZPEEKW(objPtr + 4) == parentId) ? 1 : 0); + interpreterDoBranch((ZPEEK(objPtr + 4) == parentId) ? 1 : 0); } else { portDie(MSG_UNIMPLEMENTED); } @@ -100,6 +100,6 @@ void opcodes_jz(void) { void opcodes_test(void) { - interpreterDoBranch(((__state.operands[0] & __state.operands[1]) == __state.operands[0]) ? 1 : 0); + interpreterDoBranch(((__state.operands[0] & __state.operands[1]) == __state.operands[1]) ? 1 : 0); } diff --git a/src/oc_input.c b/src/oc_input.c index 14d479e..62889da 100644 --- a/src/oc_input.c +++ b/src/oc_input.c @@ -23,10 +23,184 @@ #include "oc_input.h" #include "portme.h" +#include "state.h" +#include "memory.h" #include "messages.h" +#include "story.h" +#include "lib.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; + 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; + +#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); + //***TODO*** - portDie(MSG_UNIMPLEMENTED); + //interpreterUpdateStatusBar(); + + // Read input from user. + portInput(input, inputLen); + +#ifdef DEBUGGING + printf("Input string from user is '%s'\n", __memoryBuffer); +#endif + + // Tidy up input. + for (i=input; i= 'A') && (ch <= 'Z')) { + // Make lowercase. + ch -= 'A' - 'a'; + ZPOKE(i, ch); + } else if ((ch == '\n') || (ch == '\r')) { + // End of line, end of input. + ch = 0; + ZPOKE(i, ch); + break; + } + } + + // 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); } diff --git a/src/oc_math.c b/src/oc_math.c index 9a92422..23038ac 100644 --- a/src/oc_math.c +++ b/src/oc_math.c @@ -62,6 +62,11 @@ void opcodes_mul(void) { } +void opcodes_not(void) { + variableStore(ZPEEK(__state.pc++), ~__state.operands[0]); +} + + void opcodes_or(void) { variableStore(ZPEEK(__state.pc++), __state.operands[0] | __state.operands[1]); } diff --git a/src/oc_memory.c b/src/oc_memory.c index 3dca653..0070d60 100644 --- a/src/oc_memory.c +++ b/src/oc_memory.c @@ -28,7 +28,10 @@ void opcodes_load(void) { - variableStore(ZPEEK(__state.pc++), ZPEEK((__state.operands[0] & 0xFF))); + uint8_t dst = ZPEEK(__state.pc++); + uint8_t src = (uint8_t)(__state.operands[0] & 0xFF); + int16_t val = variableLoad(src); + variableStore(dst, val); } @@ -44,6 +47,21 @@ void opcodes_loadw(void) { } +void opcodes_pop(void) { + variableAddress(0, 0); // Causes a pop. +} + + +void opcodes_pull(void) { + variableStore((__state.operands[0] & 0xFF), variableLoad(0)); +} + + +void opcodes_push(void) { + variableStore(0, __state.operands[0]); +} + + void opcodes_store(void) { variableStore((__state.operands[0] & 0xFF), __state.operands[1]); } diff --git a/src/oc_misc.c b/src/oc_misc.c index 3460a3b..d1c2138 100644 --- a/src/oc_misc.c +++ b/src/oc_misc.c @@ -25,6 +25,9 @@ #include "state.h" #include "variable.h" #include "portme.h" +#include "story.h" +#include "memory.h" +#include "interpreter.h" void opcodes_nop(void) { @@ -40,3 +43,14 @@ void opcodes_quit(void) { 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 9) { - opcodes_print_num_helper(val / 10); + opcodes_print_num_r(val / 10); } - c[0] = '0' + (val % 10); - portCharsPrint(c, 1); + portCharPrint('0' + (val % 10)); } void opcodes_print_num(void) { - opcodes_print_num_helper(__state.operands[0]); + int32_t n = (int16_t)__state.operands[0]; + + if (n < 0) { + portCharPrint('-'); + n = -n; + } + opcodes_print_num_r(n); } @@ -100,6 +97,6 @@ void opcodes_print_paddr(void) { void opcodes_print_ret(void) { __state.pc += zsciiPrint(__state.pc, 0); - portCharsPrint("\n", 1); + portCharPrint('\n'); interpreterDoReturn(1); } diff --git a/src/oc_save.c b/src/oc_save.c index 79046e2..f82e068 100644 --- a/src/oc_save.c +++ b/src/oc_save.c @@ -26,97 +26,22 @@ #include "state.h" #include "portme.h" #include "story.h" -#include "memory.h" +#include "opcodes.h" void opcodes_save(void) { - uint32_t f; - bool ok; - uint16_t i; - uint8_t *b; - - ok = portFileWriteOpen(&f, "save.dat"); - - // Write out PC. - b = (uint8_t *)&__state.pc; - ok &= portFileByteWrite(&f, b[0]); - ok &= portFileByteWrite(&f, b[1]); - ok &= portFileByteWrite(&f, b[2]); - ok &= portFileByteWrite(&f, b[3]); - - // Write out SP. - b = (uint8_t *)&__state.sp; - ok &= portFileByteWrite(&f, b[0]); - ok &= portFileByteWrite(&f, b[1]); - - // Write out BP. - b = (uint8_t *)&__state.sp; - ok &= portFileByteWrite(&f, b[0]); - ok &= portFileByteWrite(&f, b[1]); - - // Write out dynamic memory. - for (i=0; i - #include "state.h" +#include "lib.h" stateT __state; @@ -31,5 +30,5 @@ stateT __state; void stateReset(void) { // Zero out all state data. - memset(&__state, 0, sizeof(stateT)); + libMemSet((byte *)&__state, 0, sizeof(stateT)); } diff --git a/src/story.c b/src/story.c index 94e09af..6fa4d27 100644 --- a/src/story.c +++ b/src/story.c @@ -26,21 +26,26 @@ uint32_t storyLength() { - uint32_t m = (uint32_t)portWordGet(0x1a); + uint32_t m = (uint32_t)storyFileSize(); - switch (storyVersion()) { - case 1: - case 2: - case 3: - m *= 2; - break; - case 4: - case 5: - m *= 4; - break; - case 6: - m *= 8; - break; + 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 { + // Some old stories do not have the size in the header. + //***TODO*** } return m; diff --git a/src/variable.c b/src/variable.c index 391b505..b878603 100644 --- a/src/variable.c +++ b/src/variable.c @@ -30,13 +30,13 @@ uint32_t variableAddressGlobal(uint16_t var) { - return storyGlobalVariableTableAddress() + ((var - 0x10) * 2); + return storyGlobalVariableTableAddress() + ((var - 16) * 2); } 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, 0)]; // Nope, global. return ZPEEKW(variableAddressGlobal(var)); } @@ -45,7 +45,7 @@ uint16_t variableLoad(uint16_t var) { void variableStore(uint16_t var, uint16_t value) { uint32_t addr; - if (var < 16) { + if (var <= 16) { // On stack. addr = variableAddress(var, 1); __state.stack[addr] = value; @@ -60,14 +60,18 @@ void variableStore(uint16_t var, uint16_t value) { uint8_t variableAddress(uint8_t var, bool writing) { uint16_t numLocals; - if (var == 0) { // top of stack + // Top of stack. + if (var == 0) { if (writing) { + if (__state.sp >= sizeof(__state.stack)) portDie(MSG_INT_STACK_OVERFLOW); #ifdef DEBUGGING printf("Push stack\n"); #endif return __state.sp++; + } else { + if (__state.sp == 0) portDie(MSG_INT_STACK_UNDERFLOW); numLocals = __state.bp ? __state.stack[__state.bp - 1] : 0; if ((__state.bp + numLocals) >= sizeof(__state.stack)) portDie(MSG_INT_STACK_UNDERFLOW); @@ -75,13 +79,15 @@ uint8_t variableAddress(uint8_t var, bool writing) { printf("Pop stack\n"); #endif return --__state.sp; + } // writing } // var - if ((var >= 0x1) && (var <= 0xf)) { // Local var. + // Local var. + if ((var >= 0x1) && (var <= 0xf)) { if (__state.stack[__state.bp - 1] <= (var - 1)) portDie(MSG_INT_REF_UNALLOCATED_LOCAL); return __state.bp + (var - 1); } - return storyGlobalVariableTableAddress() + ((var - 0x10) * 2); + return variableAddressGlobal(var); } diff --git a/src/zscii.c b/src/zscii.c index 2aed1b2..e3cc9d2 100644 --- a/src/zscii.c +++ b/src/zscii.c @@ -29,8 +29,24 @@ #include "memory.h" -uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP) { - uint32_t bufLen = *bufLenP; +char zsciiDecodeChar(uint16_t z) { + char c = 0; + + //***TODO*** V6+ has more codes. + + if ((z >= 32) && (z <= 126)) { + c = z; // ASCII + } else if (z == 13) { + c = '\n'; + } else if ((z >= 155) && (z <= 251)) { + c = '?'; //***TODO*** Extended ZSCII + } + + return c; +} + + +uint32_t zsciiPrint(uint32_t zstr, bool abbr) { uint16_t code = 0; uint8_t alphabet = 0; uint8_t useAbbrTable = 0; @@ -71,10 +87,7 @@ uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP) { printVal = zsciiDecodeChar(zsciiCode); if (printVal) { decodedChars++; - if (bufLen) { - *(buf++) = printVal; - bufLen--; - } + portCharPrint(printVal); } alphabet = 0; useAbbrTable = 0; @@ -92,11 +105,7 @@ uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP) { abbrAddr = ZPEEKW(ptr); ptr += 2; - abbrDecodedChars = bufLen; - zsciiDecode(abbrAddr * 2, 1, buf, &abbrDecodedChars); - decodedChars += abbrDecodedChars; - buf += (bufLen < abbrDecodedChars) ? bufLen : abbrDecodedChars; - bufLen = (bufLen < abbrDecodedChars) ? 0 : (bufLen - abbrDecodedChars); + decodedChars += zsciiPrint(abbrAddr * 2, true); useAbbrTable = 0; alphabet = 0; //***FIXME*** Version 3+ has no shift-lock but V1 needs it. @@ -148,10 +157,7 @@ uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP) { if (printVal) { decodedChars++; - if (bufLen) { - *(buf++) = printVal; - bufLen--; - } + portCharPrint(printVal); } if (alphabet && !newShift) alphabet = 0; @@ -161,39 +167,5 @@ uint32_t zsciiDecode(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLenP) { // There is no NULL terminator, you look for a word with the top bit set. } while ((code & (1 << 15)) == 0); - *bufLenP = decodedChars; - return pc - zstr; } - - -char zsciiDecodeChar(uint16_t z) { - char c = 0; - - //***TODO*** V6+ has more codes. - - if ((z >= 32) && (z <= 126)) { - c = z; // ASCII - } else if (z == 13) { - c = '\n'; - } else if ((z >= 155) && (z <= 251)) { - c = '?'; //***TODO*** Extended ZSCII - } - - return c; -} - - -uint32_t zsciiPrint(uint32_t pc, bool abbr) { - uint32_t decodedChars = __memoryBufferLen; - uint32_t ret = zsciiDecode(pc, abbr, __memoryBuffer, &decodedChars); - - if (decodedChars > __memoryBufferLen) { - memoryEnsureBuffer(decodedChars); - ret = zsciiDecode(pc, abbr, __memoryBuffer, &decodedChars); - } - - portCharsPrint(__memoryBuffer, decodedChars); - - return ret; -}