F256 port added. llvm-mos lld is not happy about it.
This commit is contained in:
parent
389592fc12
commit
75a8bc61bc
36 changed files with 806 additions and 275 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
|
||||
#define DEBUGGING
|
||||
//#define DEBUGGING
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
|
36
include/lib.h
Normal file
36
include/lib.h
Normal 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
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
void opcodes_nop(void);
|
||||
void opcodes_quit(void);
|
||||
void opcodes_random(void);
|
||||
void opcodes_verify(void);
|
||||
|
||||
|
||||
#endif // OC_MISC_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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
63
ports/f256/CMakeLists.txt
Normal 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
60
ports/f256/build.sh
Executable 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
200
ports/f256/f256zip.c
Normal 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
7
ports/f256/foenixmgr.ini
Normal 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
26
ports/f256/run.sh
Executable 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
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "stddclmr.h"
|
||||
|
||||
#include "interpreter.h"
|
||||
#include "portme.h"
|
||||
#include "messages.h"
|
||||
|
|
|
@ -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;
|
||||
}
|
32
src/memory.c
32
src/memory.c
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
176
src/oc_input.c
176
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<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);
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -152,8 +153,8 @@ 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);
|
||||
uint8_t size = 0;
|
||||
uint32_t ptr = objectPropertyGet(__state.operands[0], __state.operands[1], &size);
|
||||
|
||||
if (ptr == 0) portDie(MSG_OP_OBJ_MISSING_PROPERTY);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
89
src/portme.c
89
src/portme.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
33
src/story.c
33
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
70
src/zscii.c
70
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue