F256 port added. llvm-mos lld is not happy about it.

This commit is contained in:
Scott Duensing 2024-02-01 19:43:16 -06:00
parent 389592fc12
commit 75a8bc61bc
36 changed files with 806 additions and 275 deletions

8
.gitignore vendored
View file

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

View file

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

View file

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

36
include/lib.h Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -31,6 +31,7 @@
void opcodes_nop(void);
void opcodes_quit(void);
void opcodes_random(void);
void opcodes_verify(void);
#endif // OC_MISC_H

View file

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

View file

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

View file

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

63
ports/f256/CMakeLists.txt Normal file
View file

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

60
ports/f256/build.sh Executable file
View file

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

200
ports/f256/f256zip.c Normal file
View file

@ -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<storyStaticMemoryBaseAddress(); i++) {
ok &= (fread(&b, 1, 1, in) == 1);
ZPOKE(i, b);
}
// Read in stack.
ok &= (fread(__state.stack, sizeof(__state.stack[0]), __state.sp, in) == __state.sp);
fclose(in);
}
return ok;
}
bool portFileSave(void) {
FILE *out;
bool ok = false;
uint32_t i;
byte b;
out = fopen("save.dat", "wb");
if (out) {
ok = true;
// Write out PC.
ok &= (fwrite(&__state.pc, sizeof(__state.pc), 1, out) == 1);
// Write out SP.
ok &= (fwrite(&__state.sp, sizeof(__state.sp), 1, out) == 1);
// Write out BP.
ok &= (fwrite(&__state.bp, sizeof(__state.bp), 1, out) == 1);
// Write out dynamic game RAM.
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
b = ZPEEK(i);
ok &= (fwrite(&b, 1, 1, out) == 1);
}
// Write out stack.
ok &= (fwrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
fclose(out);
}
return ok;
}
void portInput(uint32_t ramAddr, uint8_t length) {
uint8_t i;
char c;
for (i=0; i<length; i++) {
c = getchar();
ZPOKE(ramAddr, c);
if ((c == 13) || (c == 10)) break;
}
}
uint16_t portRandomGet(int16_t range) {
// If range is zero, randomize with "best" random seed.
// If range is negative, use the positive value to seed.
if (range == 0) return 0;
if (range < 0) {
randomSeed(-range);
return 0;
}
return (randomRead() % range) + 1;
}
void portStoryLoad(void) {
// Later, we probably want to see if the user has a memory expansion
// installed. If they do, we can use it and then use the lower memory
// for graphics and sound.
// Currently no status bar.
ZPOKE(STORY_FLAG_V3, ZPEEK(STORY_FLAG_V3) | STORY_FLAG_V3_STATUS_LINE_NOT_AVAILABLE);
// Uncomment for status bar and window splitting.
//POKE(STORY_FLAG_V3, PEEK(STORY_FLAG_V3) & ~STORY_FLAG_V3_STATUS_LINE_NOT_AVAILABLE);
//POKE(STORY_FLAG_V3, PEEK(STORY_FLAG_V3) | STORY_FLAG_V3_SCREEN_SPLITTING);
}
uint16_t portWordGet(uint16_t address) {
return SWAP_UINT16(FAR_PEEKW(BASE_ADDRESS + address));
}
void portWordSet(uint16_t address, uint16_t value) {
FAR_POKEW(BASE_ADDRESS + address, SWAP_UINT16(value));
}
int main(void) {
f256Init();
textSetCursor(199);
stateReset();
portStoryLoad();
opcodesSetup();
storySetup();
interpreterRun();
return 0;
}

7
ports/f256/foenixmgr.ini Normal file
View file

@ -0,0 +1,7 @@
[DEFAULT]
port=/dev/ttyUSB1
labels=sample.lbl
flash_address=380000
chunk_size=1024
cpu=65c02
data_rate=6000000

26
ports/f256/run.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
#
# 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.
#
python ../../FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz zip.pgz

View file

@ -21,6 +21,8 @@
*/
#include "stddclmr.h"
#include "interpreter.h"
#include "portme.h"
#include "messages.h"

View file

@ -21,38 +21,30 @@
*/
/*
* 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 "lib.h"
#include "stddclmr.h"
void libMemSet(byte *start, byte val, uint32_t len) {
uint32_t i;
#include "memory.h"
#include "opcodes.h"
#include "state.h"
#include "interpreter.h"
#include "story.h"
for (i=0; i<len; i++) start[i] = val;
}
int main(void) {
char *libStrChr(char *haystack, char needle) {
uint32_t len = libStrLen(haystack);
uint32_t i;
memoryStartup();
stateReset();
opcodesSetup();
portStoryLoad();
storySetup();
interpreterRun();
memoryShutdown();
for (i=0; i<len; i++) {
if (haystack[i] == needle) return &haystack[i];
}
return 0;
}
uint32_t libStrLen(char *str) {
uint32_t l = 0;
while (str[l] != 0) l++;
return l;
}

View file

@ -21,40 +21,8 @@
*/
#include <stdlib.h>
#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) {

View file

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

View file

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

View file

@ -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<input+inputLen; i++) {
ch = ZPEEK(i);
if ((ch >= '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<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);
}

View file

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

View file

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

View file

@ -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<storyLength(); i++) checksum += ZPEEK(i);
i = storyChecksum();
interpreterDoBranch(checksum == storyChecksum() ? 1 : 0);
}

View file

@ -80,7 +80,6 @@ void opcodes_get_next_prop(void) {
void opcodes_get_parent(void) {
uint16_t result = objectRelationGet(__state.operands[0], 4);
variableStore(ZPEEK(__state.pc++), result);
interpreterDoBranch(result != 0 ? 1 : 0);
}
@ -93,8 +92,10 @@ void opcodes_get_prop(void) {
if (ptr == 0) {
result = objectPropertyDefaultGet(propId);
} else {
} else if (size == 1) {
result = ZPEEK(ptr);
} else {
result = ZPEEKW(ptr);
}
variableStore(ZPEEK(__state.pc++), result);
@ -105,7 +106,7 @@ void opcodes_get_prop_addr(void) {
uint16_t objId = __state.operands[0];
uint16_t propId = __state.operands[1];
variableStore(ZPEEK(__state.pc++), objectPropertyGet(objId, propId, NULL));
variableStore(ZPEEK(__state.pc++), objectPropertyGet(objId, propId, 0));
}
@ -153,7 +154,7 @@ void opcodes_insert_obj(void) {
void opcodes_put_prop(void) {
uint8_t size = 0;
uint8_t ptr = objectPropertyGet(__state.operands[0], __state.operands[1], &size);
uint32_t ptr = objectPropertyGet(__state.operands[0], __state.operands[1], &size);
if (ptr == 0) portDie(MSG_OP_OBJ_MISSING_PROPERTY);

View file

@ -33,7 +33,7 @@
void opcodes_new_line(void) {
portCharsPrint("\n", 1);
portCharPrint('\n');
}
@ -48,32 +48,29 @@ void opcodes_print_addr(void) {
void opcodes_print_char(void) {
static char c[2] = { 0, 0 };
c[0] = zsciiDecodeChar(__state.operands[0]);
if (c[0]) portCharsPrint(c, 1);
char c = zsciiDecodeChar(__state.operands[0]);
if (c) portCharPrint(c);
}
static void opcodes_print_num_helper(int16_t val) {
static char c[2] = { 0, 0 };
if (val < 0) {
portCharsPrint("-", 1);
val = -val;
}
void opcodes_print_num_r(int32_t val) {
if (val > 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);
}

View file

@ -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<storyStaticMemoryBaseAddress(); i++) {
ok &= portFileByteWrite(&f, ZPEEK(i));
}
// Write out stack.
for (i=0; i<__state.sp; i++) {
b = (uint8_t *)&__state.stack[i];
ok &= portFileByteWrite(&f, b[0]);
ok &= portFileByteWrite(&f, b[1]);
}
portFileClose(&f);
interpreterDoBranch(ok ? 1 : 0);
interpreterDoBranch(portFileSave() ? 1 : 0);
}
void opcodes_restart(void) {
stateReset();
portStoryLoad();
opcodesSetup();
storySetup();
}
void opcodes_restore(void) {
uint32_t f;
bool ok;
uint16_t i;
uint8_t *b;
ok = portFileReadOpen(&f, "save.dat");
// Read PC.
b = (uint8_t *)&__state.pc;
b[0] = portFileByteRead(&f);
b[1] = portFileByteRead(&f);
b[2] = portFileByteRead(&f);
b[3] = portFileByteRead(&f);
// Read SP.
b = (uint8_t *)&__state.sp;
b[0] = portFileByteRead(&f);
b[1] = portFileByteRead(&f);
// Read BP.
b = (uint8_t *)&__state.bp;
b[0] = portFileByteRead(&f);
b[1] = portFileByteRead(&f);
// Read dynamic memory.
for (i=0; i<storyStaticMemoryBaseAddress(); i++) {
ZPOKE(i, portFileByteRead(&f));
}
// Read stack.
for (i=0; i<__state.sp; i++) {
b = (uint8_t *)&__state.stack[i];
b[0] = portFileByteRead(&f);
b[1] = portFileByteRead(&f);
}
portFileClose(&f);
interpreterDoBranch(ok ? 1 : 0);
interpreterDoBranch(portFileRestore() ? 1 : 0);
}

View file

@ -94,7 +94,7 @@ void opcodesBuiltInitialTable(void) {
OP(140, jump);
OP(141, print_paddr);
OP(142, load);
mOP(143, not);
OP(143, not);
// 0-operand instructions...
OP(176, rtrue);
@ -106,7 +106,7 @@ void opcodesBuiltInitialTable(void) {
OP(182, restore);
OP(183, restart);
OP(184, ret_popped);
mOP(185, pop);
OP(185, pop);
OP(186, quit);
OP(187, new_line);

View file

@ -28,8 +28,12 @@
#include "portme.h"
#include "story.h"
#include "memory.h"
#include "state.h"
#include "interpreter.h"
#include "zork1.h"
//#include "czech.z3.h"
// The F256 has 512k of RAM. We use everything except the lower 64k.
@ -46,11 +50,9 @@ void portByteSet(uint16_t address, uint8_t value) {
}
void portCharsPrint(char *chars, uint16_t len) {
uint16_t x;
for (x=0; x<len; x++) {
printf("%c", chars[x]);
}
void portCharPrint(char c) {
printf("%c", c);
fflush(stdout);
}
@ -67,28 +69,67 @@ void portDie(char *fmt, ...) {
}
void portFileClose(uint32_t *handle) {
bool portFileRestore(void) {
FILE *in;
bool ok = false;
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.
ok &= (fread(_RAM, storyStaticMemoryBaseAddress(), 1, in) == 1);
// Read in stack.
ok &= (fread(__state.stack, sizeof(__state.stack[0]), __state.sp, in) == __state.sp);
fclose(in);
}
return ok;
}
byte portFileByteRead(uint32_t *handle) {
return 0;
bool portFileSave(void) {
FILE *out;
bool ok = false;
out = fopen("save.dat", "wb");
if (out) {
ok = true;
// Write out PC.
ok &= (fwrite(&__state.pc, sizeof(__state.pc), 1, out) == 1);
// Write out SP.
ok &= (fwrite(&__state.sp, sizeof(__state.sp), 1, out) == 1);
// Write out BP.
ok &= (fwrite(&__state.bp, sizeof(__state.bp), 1, out) == 1);
// Write out dynamic game RAM.
ok &= (fwrite(_RAM, storyStaticMemoryBaseAddress(), 1, out) == 1);
// Write out stack.
ok &= (fwrite(__state.stack, sizeof(__state.stack[0]), __state.sp, out) == __state.sp);
fclose(out);
}
return ok;
}
bool portFileByteWrite(uint32_t *handle, byte value) {
return false;
}
void portInput(uint32_t ramAddr, uint8_t length) {
char input[256];
uint8_t i;
if (!fgets(input, length, stdin))
portDie("EOF or error on stdin during read");
bool portFileReadOpen(uint32_t *handle, char *name) {
return false;
}
bool portFileWriteOpen(uint32_t *handle, char *name) {
return false;
for (i=0; i<length; i++) _RAM[ramAddr] = input[i];
}
@ -104,6 +145,7 @@ void portStoryLoad(void) {
// For now, we just copy an embedded Zork 1 into RAM.
memcpy(_RAM, zork1, zork1_len);
//memcpy(_RAM, czech_z3, czech_z3_len);
// Later, we probably want to see if the user has a memory expansion
// installed. If they do, we can use it and then use the lower memory
@ -128,3 +170,14 @@ void portWordSet(uint16_t address, uint16_t value) {
_RAM[address + 1] = value & 0xff;
}
int main(void) {
stateReset();
portStoryLoad();
opcodesSetup();
storySetup();
interpreterRun();
return 0;
}

View file

@ -21,9 +21,8 @@
*/
#include <string.h>
#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));
}

View file

@ -26,8 +26,9 @@
uint32_t storyLength() {
uint32_t m = (uint32_t)portWordGet(0x1a);
uint32_t m = (uint32_t)storyFileSize();
if (m > 0) {
switch (storyVersion()) {
case 1:
case 2:
@ -42,6 +43,10 @@ uint32_t storyLength() {
m *= 8;
break;
}
} else {
// Some old stories do not have the size in the header.
//***TODO***
}
return m;
}

View file

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

View file

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