Starting to run code.
This commit is contained in:
parent
730c3ec441
commit
3a055f96f4
17 changed files with 844 additions and 56 deletions
|
@ -3,22 +3,25 @@ cmake_minimum_required(VERSION 3.12)
|
||||||
project(zip LANGUAGES C)
|
project(zip LANGUAGES C)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
interpreter.h
|
||||||
memory.h
|
memory.h
|
||||||
|
messages.h
|
||||||
oc_0op.h
|
oc_0op.h
|
||||||
oc_1op.h
|
oc_1op.h
|
||||||
oc_2op.h
|
oc_2op.h
|
||||||
oc_var_op.h
|
oc_var_op.h
|
||||||
oc_ext.h
|
oc_ext.h
|
||||||
opcodes.h
|
opcodes.h
|
||||||
|
portme.h
|
||||||
state.h
|
state.h
|
||||||
stddclmr.h
|
stddclmr.h
|
||||||
story.h
|
story.h
|
||||||
text.h
|
|
||||||
zork1.h
|
zork1.h
|
||||||
)
|
)
|
||||||
list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/include/")
|
list(TRANSFORM HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/include/")
|
||||||
|
|
||||||
set(SOURCE
|
set(SOURCE
|
||||||
|
interpreter.c
|
||||||
main.c
|
main.c
|
||||||
memory.c
|
memory.c
|
||||||
oc_0op.c
|
oc_0op.c
|
||||||
|
@ -27,9 +30,9 @@ set(SOURCE
|
||||||
oc_var_op.c
|
oc_var_op.c
|
||||||
oc_ext.c
|
oc_ext.c
|
||||||
opcodes.c
|
opcodes.c
|
||||||
|
portme.c
|
||||||
state.c
|
state.c
|
||||||
story.c
|
story.c
|
||||||
text.c
|
|
||||||
)
|
)
|
||||||
list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/")
|
list(TRANSFORM SOURCE PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/src/")
|
||||||
|
|
||||||
|
|
46
include/interpreter.h
Normal file
46
include/interpreter.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 INTERPRETER_H
|
||||||
|
#define INTERPRETER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t interpreterDecodeZSCII(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLen);
|
||||||
|
char interpreterDecodeZSCIIChar(uint16_t z);
|
||||||
|
void interpreterDoBranch(int32_t b);
|
||||||
|
void interpreterDoReturn(uint16_t r);
|
||||||
|
uint8_t interpreterGetObjectPointer(uint16_t objectId);
|
||||||
|
uint8_t interpreterGetObjectProperty(uint16_t objectId, uint32_t propertyId, uint8_t *size);
|
||||||
|
bool interpreterParseOperand(uint8_t opType, uint8_t operandId);
|
||||||
|
uint8_t interpreterParseVariableOperands(uint8_t index);
|
||||||
|
void interpreterPrintChars(char *chars, uint16_t len);
|
||||||
|
uint32_t interpreterPrintZSCII(uint32_t pc, bool abbr);
|
||||||
|
void interpreterRun(void);
|
||||||
|
uint8_t interpreterVariableAddress(uint8_t var, bool writing);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INTERPRETER_H
|
|
@ -34,22 +34,33 @@
|
||||||
#define true 1
|
#define true 1
|
||||||
#define false 0
|
#define false 0
|
||||||
|
|
||||||
|
// Memory access.
|
||||||
#define PEEK(a) memoryByte(a)
|
#define PEEK(a) memoryByte(a)
|
||||||
#define POKE(a,v) memorySetByte(a,v)
|
#define POKE(a,v) memorySetByte(a,v)
|
||||||
#define PEEKW(a) memoryWord(a)
|
#define PEEKW(a) memoryWord(a)
|
||||||
#define POKEW(a,v) memorySetWord(a,v)
|
#define POKEW(a,v) memorySetWord(a,v)
|
||||||
|
|
||||||
|
// Stack access.
|
||||||
|
#define SPEEKD(a) ((uint32_t)*(uint32_t *)&__state.stack[a])
|
||||||
|
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
typedef unsigned char bool;
|
typedef unsigned char bool;
|
||||||
|
|
||||||
|
|
||||||
|
extern char *__memoryBuffer;
|
||||||
|
extern uint16_t __memoryBufferLen;
|
||||||
|
|
||||||
|
|
||||||
uint8_t memoryByte(uint16_t address);
|
uint8_t memoryByte(uint16_t address);
|
||||||
|
void memoryEnsureBuffer(uint32_t newSize);
|
||||||
|
void memoryLoadStory(void);
|
||||||
void memorySetByte(uint16_t address, uint8_t value);
|
void memorySetByte(uint16_t address, uint8_t value);
|
||||||
void memorySetWord(uint16_t address, uint16_t value);
|
void memorySetWord(uint16_t address, uint16_t value);
|
||||||
|
void memoryShutdown(void);
|
||||||
|
void memoryStartup(void);
|
||||||
uint32_t memoryUnpackAddress(uint16_t address, uint8_t type);
|
uint32_t memoryUnpackAddress(uint16_t address, uint8_t type);
|
||||||
uint16_t memoryWord(uint16_t address);
|
uint16_t memoryWord(uint16_t address);
|
||||||
void memoryLoadStory(void);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // MEMORY_H
|
#endif // MEMORY_H
|
||||||
|
|
65
include/messages.h
Normal file
65
include/messages.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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 MESSAGES_H
|
||||||
|
#define MESSAGES_H
|
||||||
|
|
||||||
|
#define MSG_USE_MESSAGES // Undef this to save memory.
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MSG_USE_MESSAGES
|
||||||
|
#define MSG_UNIMPLEMENTED "Unimplemented."
|
||||||
|
#define MSG_INT_INVALID_EXT_OPCODE "Invalid extended opcode!"
|
||||||
|
#define MSG_INT_NO_ABBR "No abbreviations in abbreviations!"
|
||||||
|
#define MSG_INT_OBJECT0_REF "Object 0 referenced!"
|
||||||
|
#define MSG_INT_OBJECT_BAD_ID "Invalid object ID!"
|
||||||
|
#define MSG_INT_REF_UNALLOCATED_LOCAL "Referenced unallocated local variable!"
|
||||||
|
#define MSG_INT_STACK_OVERFLOW "Stack overflow!"
|
||||||
|
#define MSG_INT_STACK_UNDERFLOW "Stack underflow!"
|
||||||
|
#define MSG_INT_UNIMPLEMENTED_OPCODE "Unimplemented opcode! %s %d"
|
||||||
|
#define MSG_INT_V12_SHIFT "Add V1/V2 shifting."
|
||||||
|
#define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking."
|
||||||
|
#define MSG_MEM_BUFFER "Unable to allocate memory buffer."
|
||||||
|
#define MSG_OP_VAR_MISSING_PROPERTY "Missing object property."
|
||||||
|
#define MSG_OP_VAR_TOO_MANY_LOCALS "Too many local variables!"
|
||||||
|
#define MSG_OP_VAR_STACK_OVERFLOW "Stack overflow!"
|
||||||
|
#else // MSG_USE_MESSAGES
|
||||||
|
#define MSG_UNIMPLEMENTED ""
|
||||||
|
#define MSG_INT_INVALID_EXT_OPCODE ""
|
||||||
|
#define MSG_INT_NO_ABBR ""
|
||||||
|
#define MSG_INT_OBJECT0_REF ""
|
||||||
|
#define MSG_INT_OBJECT_BAD_ID ""
|
||||||
|
#define MSG_INT_REF_UNALLOCATED_LOCAL ""
|
||||||
|
#define MSG_INT_STACK_OVERFLOW ""
|
||||||
|
#define MSG_INT_STACK_UNDERFLOW ""
|
||||||
|
#define MSG_INT_UNIMPLEMENTED_OPCODE ""
|
||||||
|
#define MSG_INT_V12_SHIFT ""
|
||||||
|
#define MSG_INT_V12_SHIFT_LOCK ""
|
||||||
|
#define MSG_MEM_BUFFER ""
|
||||||
|
#define MSG_OP_VAR_MISSING_PROPERTY ""
|
||||||
|
#define MSG_OP_VAR_TOO_MANY_LOCALS ""
|
||||||
|
#define MSG_OP_VAR_STACK_OVERFLOW ""
|
||||||
|
#endif // MSG_USE_MESSAGES
|
||||||
|
|
||||||
|
|
||||||
|
#endif // MESSAGES_H
|
|
@ -28,6 +28,7 @@
|
||||||
typedef void (*opcodeT)(void);
|
typedef void (*opcodeT)(void);
|
||||||
|
|
||||||
|
|
||||||
|
void opcodesBuiltInitialTable(void);
|
||||||
void opcodesSetup(void);
|
void opcodesSetup(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef TEXT_H
|
#ifndef PORTME_H
|
||||||
#define TEXT_H
|
#define PORTME_H
|
||||||
|
|
||||||
|
|
||||||
void textPrint(char *what);
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
#endif // TEXT_H
|
void portDie(char *fmt, ...);
|
||||||
|
void portPrintChars(char *chars, uint16_t len);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PORTME_H
|
|
@ -29,17 +29,31 @@
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DEBUGGING
|
||||||
|
|
||||||
|
|
||||||
typedef struct stateS {
|
typedef struct stateS {
|
||||||
byte stack[2048]; //***TODO*** How big does this really need to be? Old games are 1024, new...?
|
uint16_t stack[2048]; //***TODO*** How big does this really need to be? Old games are 1024, new...?
|
||||||
bool quit;
|
bool quit;
|
||||||
uint16_t pc;
|
uint32_t pc; // Program Counter
|
||||||
uint16_t sp;
|
uint16_t sp; // Stack Pointer
|
||||||
|
uint16_t bp; // Base Pointer
|
||||||
opcodeT opcodes[256];
|
opcodeT opcodes[256];
|
||||||
opcodeT extOpcodes[30];
|
opcodeT extOpcodes[30];
|
||||||
|
uint8_t operandCount;
|
||||||
|
uint16_t operands[8];
|
||||||
|
char alphabetTable[78];
|
||||||
|
#ifdef DEBUGGING
|
||||||
|
char *opcodesName[256];
|
||||||
|
char *extOpcodesName[30];
|
||||||
|
#endif
|
||||||
} stateT;
|
} stateT;
|
||||||
|
|
||||||
|
|
||||||
extern stateT __state;
|
extern stateT __state;
|
||||||
|
|
||||||
|
|
||||||
|
void stateReset(void);
|
||||||
|
|
||||||
|
|
||||||
#endif // STATE_H
|
#endif // STATE_H
|
||||||
|
|
437
src/interpreter.c
Normal file
437
src/interpreter.c
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "story.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
#ifdef DEBUGGING
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t interpreterDecodeZSCII(uint32_t zstr, bool abbr, char *buf, uint32_t *bufLen) {
|
||||||
|
uint16_t code = 0;
|
||||||
|
uint8_t alphabet = 0;
|
||||||
|
uint8_t useAbbrTable = 0;
|
||||||
|
uint8_t zsciiCollector = 0;
|
||||||
|
uint16_t zsciiCode = 0;
|
||||||
|
uint32_t decodedChars = 0;
|
||||||
|
uint32_t bufPtr = 0;
|
||||||
|
uint32_t pc = zstr;
|
||||||
|
char printVal = 0;
|
||||||
|
uint32_t bufLenTemp;
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t ptr;
|
||||||
|
uint16_t abbrAddr;
|
||||||
|
uint32_t abbrDecodedChars;
|
||||||
|
int8_t i;
|
||||||
|
uint8_t ch;
|
||||||
|
int16_t newShift;
|
||||||
|
|
||||||
|
do {
|
||||||
|
code = PEEKW(pc);
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
// Characters are 5 bits each, packed three to a 16-bit word.
|
||||||
|
for (i=10; i>=0; i-=5) {
|
||||||
|
ch = ((code >> 1) & 0x1f);
|
||||||
|
|
||||||
|
if (zsciiCollector) {
|
||||||
|
if (zsciiCollector == 2) {
|
||||||
|
zsciiCode |= ((uint16_t)ch) << 5;
|
||||||
|
} else {
|
||||||
|
zsciiCode |= ((uint16_t)ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
zsciiCollector--;
|
||||||
|
|
||||||
|
if (!zsciiCollector) {
|
||||||
|
printVal = interpreterDecodeZSCIIChar(zsciiCode);
|
||||||
|
if (printVal) {
|
||||||
|
decodedChars++;
|
||||||
|
if (bufPtr < *bufLen) {
|
||||||
|
buf[bufPtr++] = printVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alphabet = 0;
|
||||||
|
useAbbrTable = 0;
|
||||||
|
zsciiCode = 0;
|
||||||
|
} // !zsciiCollector
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
} else if (useAbbrTable) { // zsciiCollector
|
||||||
|
|
||||||
|
if (abbr) portDie(MSG_INT_NO_ABBR);
|
||||||
|
|
||||||
|
index = ((32 * (((uint32_t)useAbbrTable) - 1)) + (uint32_t)ch);
|
||||||
|
ptr = storyAbbreviationTableAddress() + (index * 2);
|
||||||
|
abbrAddr = PEEKW(ptr);
|
||||||
|
|
||||||
|
abbrDecodedChars = *bufLen - bufPtr;
|
||||||
|
interpreterDecodeZSCII(abbrAddr * 2, 1, buf, &abbrDecodedChars);
|
||||||
|
decodedChars += abbrDecodedChars;
|
||||||
|
|
||||||
|
bufLenTemp = *bufLen - bufPtr;
|
||||||
|
bufPtr += (bufLenTemp < abbrDecodedChars) ? bufLenTemp : abbrDecodedChars;
|
||||||
|
bufLenTemp = (bufLenTemp < abbrDecodedChars) ? 0 : (bufLenTemp - abbrDecodedChars);
|
||||||
|
*bufLen = *bufLen - bufLenTemp;
|
||||||
|
|
||||||
|
useAbbrTable = 0;
|
||||||
|
alphabet = 0; //***FIXME*** Version 3+ has no shift-lock but V1 needs it.
|
||||||
|
|
||||||
|
} // useAbbrTable
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case 0:
|
||||||
|
printVal = ' ';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if (storyVersion() == 1) {
|
||||||
|
printVal = '\n';
|
||||||
|
} else {
|
||||||
|
useAbbrTable = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if (storyVersion() <= 2) {
|
||||||
|
portDie(MSG_INT_V12_SHIFT);
|
||||||
|
} else {
|
||||||
|
useAbbrTable = ch;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
if (storyVersion() <= 2) {
|
||||||
|
portDie(MSG_INT_V12_SHIFT_LOCK);
|
||||||
|
} else {
|
||||||
|
newShift = 1;
|
||||||
|
alphabet = ch - 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if ((ch == 6) && (alphabet == 2)) {
|
||||||
|
zsciiCollector = 2;
|
||||||
|
} else {
|
||||||
|
printVal = __state.alphabetTable[(alphabet * 26) + (ch - 6)];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (printVal) {
|
||||||
|
decodedChars++;
|
||||||
|
if (bufPtr < *bufLen) {
|
||||||
|
buf[bufPtr++] = printVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alphabet && !newShift) alphabet = 0;
|
||||||
|
|
||||||
|
} // for
|
||||||
|
|
||||||
|
// There is no NULL terminator, you look for a word with the top bit set.
|
||||||
|
} while ((code & (1 << 15)) == 0);
|
||||||
|
|
||||||
|
*bufLen = decodedChars;
|
||||||
|
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char interpreterDecodeZSCIIChar(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void interpreterDoBranch(int32_t b) {
|
||||||
|
uint8_t branch = __state.pc++;
|
||||||
|
int32_t farJump = (branch & (1 << 6)) == 0;
|
||||||
|
int32_t onTruth = (branch & (1 << 7)) ? 1 : 0;
|
||||||
|
uint8_t byte2 = farJump ? __state.pc++ : 0;
|
||||||
|
int16_t offset;
|
||||||
|
|
||||||
|
if (onTruth == b) {
|
||||||
|
offset = (int16_t)(branch & 0x3f);
|
||||||
|
if (farJump) {
|
||||||
|
if (offset & (1 << 5)) offset |= 0xc0;
|
||||||
|
offset = (offset << 8) | ((int16_t)byte2);
|
||||||
|
}
|
||||||
|
if (offset == 0) {
|
||||||
|
interpreterDoReturn(0);
|
||||||
|
} else if (offset == 1) {
|
||||||
|
interpreterDoReturn(1);
|
||||||
|
} else {
|
||||||
|
__state.pc = (__state.pc + offset) - 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void interpreterDoReturn(uint16_t r) {
|
||||||
|
uint8_t storeId;
|
||||||
|
uint8_t store;
|
||||||
|
|
||||||
|
// Newer versions start in a real routine, but still aren't allowed to return from it.
|
||||||
|
if (__state.bp == 0) portDie(MSG_INT_STACK_UNDERFLOW);
|
||||||
|
|
||||||
|
// Dump all locals and data pushed on stack during the routine.
|
||||||
|
__state.sp = __state.bp;
|
||||||
|
// Dump our copy of numLocals.
|
||||||
|
__state.sp--;
|
||||||
|
// Restore previous frame's base pointer.
|
||||||
|
__state.bp = __state.stack[--__state.sp];
|
||||||
|
|
||||||
|
// Point to start of saved program counter;
|
||||||
|
__state.sp -= 2;
|
||||||
|
__state.pc = ((uint32_t)__state.stack[__state.sp]) | (((uint32_t)__state.stack[__state.sp + 1]) << 16);
|
||||||
|
|
||||||
|
// Pop the result storage location.
|
||||||
|
storeId = (uint8_t)__state.stack[--__state.sp];
|
||||||
|
// Store the routine result.
|
||||||
|
store = interpreterVariableAddress(storeId, 1);
|
||||||
|
POKEW(store, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t interpreterGetObjectPointer(uint16_t objectId) {
|
||||||
|
uint8_t ptr;
|
||||||
|
|
||||||
|
if (objectId == 0) portDie(MSG_INT_OBJECT0_REF);
|
||||||
|
if ((storyVersion() <= 3) && (objectId > 255)) portDie(MSG_INT_OBJECT_BAD_ID);
|
||||||
|
|
||||||
|
ptr = storyObjectTableAddress();
|
||||||
|
ptr += 31 * 2; // Skip properties defaults table.
|
||||||
|
ptr += 9 * (objectId - 1); // Find object in table.
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t interpreterGetObjectProperty(uint16_t objectId, uint32_t propertyId, uint8_t *size) {
|
||||||
|
uint8_t ptr = interpreterGetObjectPointer(objectId);
|
||||||
|
uint16_t addr;
|
||||||
|
uint8_t info;
|
||||||
|
uint16_t num;
|
||||||
|
uint8_t s;
|
||||||
|
|
||||||
|
if (storyVersion() <= 3) {
|
||||||
|
ptr += 7; // Skip to properties address field.
|
||||||
|
addr = PEEKW(ptr);
|
||||||
|
ptr = addr;
|
||||||
|
ptr += PEEK(ptr) * 2 + 1; // Skip object name to start of properties.
|
||||||
|
while (1) {
|
||||||
|
info = PEEK(ptr++);
|
||||||
|
num = (info & 0x1f); // 5 bits for property ID.
|
||||||
|
s = ((info >> 5) & 0x7) + 1; // 3 bits for property size.
|
||||||
|
|
||||||
|
// These go in descending numeric order, and should fail the interpreter if missing.
|
||||||
|
// We use 0xFFFFFFFF internally to mean "first property".
|
||||||
|
if ((num == propertyId) || (propertyId == 0xFFFFFFFF)) { // found it?
|
||||||
|
if (size) *size = s;
|
||||||
|
return ptr;
|
||||||
|
} else if (num < propertyId) // we're past it.
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Try the next property.
|
||||||
|
ptr += s;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
portDie(MSG_UNIMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool interpreterParseOperand(uint8_t opType, uint8_t operandId) {
|
||||||
|
uint8_t addr;
|
||||||
|
|
||||||
|
switch(opType) {
|
||||||
|
// Large constant.
|
||||||
|
case 0:
|
||||||
|
__state.operands[operandId] = PEEKW(__state.pc);
|
||||||
|
__state.pc += 2;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Small constant.
|
||||||
|
case 1:
|
||||||
|
__state.operands[operandId] = PEEK(__state.pc++);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Variable.
|
||||||
|
case 2:
|
||||||
|
addr = interpreterVariableAddress(PEEK(__state.pc++), 0);
|
||||||
|
__state.operands[operandId] = PEEKW(addr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t interpreterParseVariableOperands(uint8_t index) {
|
||||||
|
uint8_t operandTypes = PEEK(__state.pc++);
|
||||||
|
uint8_t shifter = 6;
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t opType;
|
||||||
|
|
||||||
|
for (i=0; i<4; i++) {
|
||||||
|
opType = (operandTypes >> shifter) & 0x3;
|
||||||
|
shifter -= 2;
|
||||||
|
if (!interpreterParseOperand(opType, index + i)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void interpreterPrintChars(char *chars, uint16_t len) {
|
||||||
|
//***TODO*** This is where we need to deal with windows and screen
|
||||||
|
// splits and other stuff.
|
||||||
|
portPrintChars(chars, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t interpreterPrintZSCII(uint32_t pc, bool abbr) {
|
||||||
|
uint32_t decodedChars = __memoryBufferLen;
|
||||||
|
uint32_t ret = interpreterDecodeZSCII(pc, abbr, __memoryBuffer, &decodedChars);
|
||||||
|
|
||||||
|
if (decodedChars > __memoryBufferLen) {
|
||||||
|
memoryEnsureBuffer(decodedChars);
|
||||||
|
ret = interpreterDecodeZSCII(pc, abbr, __memoryBuffer, &decodedChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
interpreterPrintChars(__memoryBuffer, decodedChars);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void interpreterRun(void) {
|
||||||
|
uint8_t opcode;
|
||||||
|
bool extended;
|
||||||
|
opcodeT op;
|
||||||
|
|
||||||
|
while (__state.quit == 0) {
|
||||||
|
opcode = PEEK(__state.pc++);
|
||||||
|
extended = ((opcode == 190) && (storyVersion() >= 5)) ? true : false;
|
||||||
|
|
||||||
|
if (extended) {
|
||||||
|
|
||||||
|
opcode = PEEK(__state.pc++);
|
||||||
|
if (opcode >= (sizeof(__state.extOpcodes) / sizeof(__state.extOpcodes[0]))) portDie(MSG_INT_INVALID_EXT_OPCODE);
|
||||||
|
__state.operandCount = interpreterParseVariableOperands(0);
|
||||||
|
op = __state.extOpcodes[opcode];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (opcode <= 127) {
|
||||||
|
// 2OP
|
||||||
|
__state.operandCount = 2;
|
||||||
|
interpreterParseOperand(((opcode >> 6) & 0x1) ? 2 : 1, 0);
|
||||||
|
interpreterParseOperand(((opcode >> 5) & 0x1) ? 2 : 1, 1);
|
||||||
|
} else if (opcode <= 175) {
|
||||||
|
// 1OP
|
||||||
|
__state.operandCount = 1;
|
||||||
|
interpreterParseOperand((opcode >> 4) & 0x3, 0);
|
||||||
|
} else if (opcode <= 191) {
|
||||||
|
// 0OP
|
||||||
|
__state.operandCount = 0;
|
||||||
|
} else if (opcode > 191) {
|
||||||
|
// Variable Opcodes
|
||||||
|
if ((opcode == 236) || (opcode == 250)) {
|
||||||
|
// call_vs2 and call_vn2 take up to EIGHT arguments!
|
||||||
|
__state.operandCount = interpreterParseVariableOperands(0);
|
||||||
|
} else {
|
||||||
|
__state.operandCount = interpreterParseVariableOperands(0);
|
||||||
|
if (__state.operandCount == 4) {
|
||||||
|
__state.operandCount += interpreterParseVariableOperands(4);
|
||||||
|
} else {
|
||||||
|
__state.pc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
op = __state.opcodes[opcode];
|
||||||
|
|
||||||
|
} // extended
|
||||||
|
|
||||||
|
if (op == 0) portDie(MSG_INT_UNIMPLEMENTED_OPCODE, extended ? "ext" : "", opcode);
|
||||||
|
|
||||||
|
#ifdef DEBUGGING
|
||||||
|
printf("pc=%X %sopcode=%u ('%s') [", (unsigned int)__state.pc, extended ? "ext " : "", opcode, extended ? __state.extOpcodesName[opcode] : __state.opcodesName[opcode]);
|
||||||
|
if (__state.operandCount) {
|
||||||
|
uint8_t i;
|
||||||
|
for (i=0; i<__state.operandCount - 1; i++)
|
||||||
|
printf("%X,", (unsigned int)__state.operands[i]);
|
||||||
|
printf("%X", (unsigned int)__state.operands[i]);
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
op();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t interpreterVariableAddress(uint8_t var, bool writing) {
|
||||||
|
uint16_t numLocals;
|
||||||
|
|
||||||
|
if (var == 0) { // top of stack
|
||||||
|
if (writing) {
|
||||||
|
if (__state.sp >= sizeof(__state.stack)) portDie(MSG_INT_STACK_OVERFLOW);
|
||||||
|
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);
|
||||||
|
return --__state.sp;
|
||||||
|
} // writing
|
||||||
|
} // var
|
||||||
|
|
||||||
|
if ((var >= 0x1) && (var <= 0xf)) { // Local var.
|
||||||
|
if (__state.stack[__state.bp - 1] <= (var - 1)) portDie(MSG_INT_REF_UNALLOCATED_LOCAL);
|
||||||
|
return __state.bp + (var - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return storyGlobalVariableTableAddress() + ((var - 0x10) * 2);
|
||||||
|
}
|
12
src/main.c
12
src/main.c
|
@ -35,20 +35,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "stddclmr.h"
|
#include "stddclmr.h"
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "story.h"
|
|
||||||
#include "state.h"
|
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
memoryLoadStory();
|
memoryStartup();
|
||||||
|
stateReset();
|
||||||
opcodesSetup();
|
opcodesSetup();
|
||||||
|
memoryLoadStory();
|
||||||
|
interpreterRun();
|
||||||
|
memoryShutdown();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
105
src/memory.c
105
src/memory.c
|
@ -22,14 +22,22 @@
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "story.h"
|
#include "story.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "messages.h"
|
||||||
|
|
||||||
#include "zork1.h"
|
#include "zork1.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Generic buffer for whatever.
|
||||||
|
char *__memoryBuffer = 0;
|
||||||
|
uint16_t __memoryBufferLen = 0;
|
||||||
|
|
||||||
|
|
||||||
// The F256 has 512k of RAM. We use everything except the lower 64k.
|
// The F256 has 512k of RAM. We use everything except the lower 64k.
|
||||||
static uint8_t _RAM[1024 * (512 - 64)];
|
static uint8_t _RAM[1024 * (512 - 64)];
|
||||||
|
|
||||||
|
@ -39,17 +47,88 @@ uint8_t memoryByte(uint16_t address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 memoryLoadStory(void) {
|
||||||
|
char *p;
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
// For now, we just copy an embedded Zork 1 into RAM.
|
||||||
|
memcpy(_RAM, zork1, zork1_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
|
||||||
|
// for graphics and sound.
|
||||||
|
|
||||||
|
// Currently no status bar.
|
||||||
|
POKE(STORY_FLAG_V3, PEEK(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);
|
||||||
|
|
||||||
|
// V6+ this is the address of main(), not just a raw starting address.
|
||||||
|
__state.pc = storyInitialPC();
|
||||||
|
__state.sp = 0;
|
||||||
|
|
||||||
|
// Set up alphabet tables. ***TODO*** V5+ has alternate tables in header.
|
||||||
|
p = __state.alphabetTable;
|
||||||
|
for (i=0; i<26; i++) *(p++) = 'a' + i; // Alphabet A0
|
||||||
|
for (i=0; i<26; i++) *(p++) = 'A' + i; // Alphabet A1
|
||||||
|
// Alphabet A2
|
||||||
|
*(p++) = 0;
|
||||||
|
if (storyVersion() != 1) *(p++) = '\n';
|
||||||
|
for (i=0; i<10; i++) *(p++) = 'o' + i;
|
||||||
|
*(p++) = '.';
|
||||||
|
*(p++) = ',';
|
||||||
|
*(p++) = '!';
|
||||||
|
*(p++) = '?';
|
||||||
|
*(p++) = '_';
|
||||||
|
*(p++) = '#';
|
||||||
|
*(p++) = '\'';
|
||||||
|
*(p++) = '"';
|
||||||
|
*(p++) = '/';
|
||||||
|
*(p++) = '\\';
|
||||||
|
if (storyVersion() == 1) *(p++) = '<';
|
||||||
|
*(p++) = '-';
|
||||||
|
*(p++) = ':';
|
||||||
|
*(p++) = '(';
|
||||||
|
*(p++) = ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void memorySetByte(uint16_t address, uint8_t value) {
|
void memorySetByte(uint16_t address, uint8_t value) {
|
||||||
_RAM[address] = value;
|
_RAM[address] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void memorySetWord(uint16_t address, uint16_t value) {
|
void memorySetWord(uint16_t address, uint16_t value) {
|
||||||
_RAM[address] = (value & 0xff00) << 8; // MSB first.
|
_RAM[address] = (value & 0xff00) >> 8; // MSB first.
|
||||||
_RAM[address + 1] = value & 0x00ff;
|
_RAM[address + 1] = value & 0x00ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void memoryShutdown(void) {
|
||||||
|
free(__memoryBuffer);
|
||||||
|
__memoryBuffer = 0;
|
||||||
|
__memoryBufferLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void memoryStartup(void) {
|
||||||
|
memoryEnsureBuffer(512);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t memoryUnpackAddress(uint16_t address, uint8_t type) {
|
uint32_t memoryUnpackAddress(uint16_t address, uint8_t type) {
|
||||||
switch (storyVersion()) {
|
switch (storyVersion()) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -76,27 +155,3 @@ uint32_t memoryUnpackAddress(uint16_t address, uint8_t type) {
|
||||||
uint16_t memoryWord(uint16_t address) {
|
uint16_t memoryWord(uint16_t address) {
|
||||||
return ((uint16_t)_RAM[address] << 8) | ((uint16_t)_RAM[address + 1]);
|
return ((uint16_t)_RAM[address] << 8) | ((uint16_t)_RAM[address + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void memoryLoadStory(void) {
|
|
||||||
// For now, we just copy an embedded Zork 1 into RAM.
|
|
||||||
memcpy(_RAM, zork1, zork1_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
|
|
||||||
// for graphics and sound.
|
|
||||||
|
|
||||||
// Zero out all state data.
|
|
||||||
memset(&__state, 0, sizeof(stateT));
|
|
||||||
|
|
||||||
// Currently no status bar.
|
|
||||||
POKE(STORY_FLAG_V3, PEEK(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);
|
|
||||||
|
|
||||||
// V6+ this is the address of main(), not just a raw starting address.
|
|
||||||
__state.pc = storyInitialPC();
|
|
||||||
__state.sp = 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -22,14 +22,15 @@
|
||||||
|
|
||||||
|
|
||||||
#include "oc_0op.h"
|
#include "oc_0op.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
|
||||||
void opcodes_new_line(void) {
|
void opcodes_new_line(void) {
|
||||||
|
interpreterPrintChars("\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void opcodes_print(void) {
|
void opcodes_print(void) {
|
||||||
|
__state.pc += interpreterPrintZSCII(__state.pc, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,20 +22,22 @@
|
||||||
|
|
||||||
|
|
||||||
#include "oc_1op.h"
|
#include "oc_1op.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
|
||||||
void opcodes_jump(void) {
|
void opcodes_jump(void) {
|
||||||
|
__state.pc = __state.pc + ((int16_t)__state.operands[0]) - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_jz(void) {
|
void opcodes_jz(void) {
|
||||||
|
interpreterDoBranch((__state.operands[0] == 0) ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_ret(void) {
|
void opcodes_ret(void) {
|
||||||
|
interpreterDoReturn(__state.operands[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
35
src/oc_2op.c
35
src/oc_2op.c
|
@ -22,33 +22,60 @@
|
||||||
|
|
||||||
|
|
||||||
#include "oc_2op.h"
|
#include "oc_2op.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "story.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "messages.h"
|
||||||
|
|
||||||
|
|
||||||
void opcodes_add(void) {
|
void opcodes_add(void) {
|
||||||
|
uint8_t store = interpreterVariableAddress(PEEK(__state.pc++), 1);
|
||||||
|
POKEW(store, ((int16_t)__state.operands[0]) + ((int16_t)__state.operands[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_je(void) {
|
void opcodes_je(void) {
|
||||||
|
uint16_t a = __state.operands[0];
|
||||||
|
int8_t i;
|
||||||
|
|
||||||
|
for (i=1; i<__state.operandCount; i++) {
|
||||||
|
if (a == __state.operands[i]) interpreterDoBranch(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interpreterDoBranch(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_loadw(void) {
|
void opcodes_loadw(void) {
|
||||||
|
uint16_t store = interpreterVariableAddress(PEEK(__state.pc++), 1);
|
||||||
|
uint16_t offset = __state.operands[0] + (__state.operands[1] * 2);
|
||||||
|
POKEW(store, PEEKW(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_store(void) {
|
void opcodes_store(void) {
|
||||||
|
uint8_t store = interpreterVariableAddress((uint8_t)(__state.operands[0] & 0xFF), 1);
|
||||||
|
POKEW(store, __state.operands[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_sub(void) {
|
void opcodes_sub(void) {
|
||||||
|
uint8_t store = interpreterVariableAddress(PEEK(__state.pc++), 1);
|
||||||
|
POKEW(store, ((int16_t)__state.operands[0]) - ((int16_t)__state.operands[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_test_attr(void) {
|
void opcodes_test_attr(void) {
|
||||||
|
uint8_t ptr = interpreterGetObjectPointer(__state.operands[0]);
|
||||||
|
uint16_t attrId = __state.operands[1];
|
||||||
|
|
||||||
|
if (storyVersion() <= 3) {
|
||||||
|
ptr += (attrId / 8);
|
||||||
|
interpreterDoBranch((ptr & (0x80 >> (attrId & 7))) ? 1 : 0);
|
||||||
|
} else {
|
||||||
|
portDie(MSG_UNIMPLEMENTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,18 +22,87 @@
|
||||||
|
|
||||||
|
|
||||||
#include "oc_var_op.h"
|
#include "oc_var_op.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "portme.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
#include "story.h"
|
||||||
|
|
||||||
|
|
||||||
void opcodes_call(void) {
|
void opcodes_call(void) {
|
||||||
|
uint8_t args = __state.operandCount;
|
||||||
|
uint8_t storeId = PEEK(__state.pc++);
|
||||||
|
uint32_t routine;
|
||||||
|
uint8_t numLocals;
|
||||||
|
int8_t i;
|
||||||
|
uint16_t src;
|
||||||
|
uint8_t dst;
|
||||||
|
|
||||||
|
if ((args == 0) && (__state.operands[0] == 0)) {
|
||||||
|
// Legal no-op; store 0 to return value and bounce.
|
||||||
|
POKEW(interpreterVariableAddress(storeId, 1), 0);
|
||||||
|
} else {
|
||||||
|
routine = memoryUnpackAddress(__state.operands[0], MEMORY_ROUTINE);
|
||||||
|
numLocals = PEEK(routine++);
|
||||||
|
if (numLocals > 15) portDie(MSG_OP_VAR_TOO_MANY_LOCALS);
|
||||||
|
|
||||||
|
if (__state.sp + 5 + numLocals >= sizeof(__state.stack)) portDie(MSG_OP_VAR_STACK_OVERFLOW);
|
||||||
|
|
||||||
|
// Save where we should store the call's result.
|
||||||
|
__state.stack[__state.sp++] = storeId;
|
||||||
|
|
||||||
|
// Next instruction to run upon return.
|
||||||
|
__state.stack[__state.sp++] = __state.pc & 0xffff;
|
||||||
|
__state.stack[__state.sp++] = ((__state.pc >> 16) & 0xffff);
|
||||||
|
|
||||||
|
// Current base pointer.
|
||||||
|
__state.stack[__state.sp++] = __state.bp;
|
||||||
|
|
||||||
|
// Number of locals.
|
||||||
|
__state.stack[__state.sp++] = numLocals;
|
||||||
|
|
||||||
|
// New base pointer.
|
||||||
|
__state.bp = __state.sp;
|
||||||
|
|
||||||
|
if (storyVersion() <= 4) {
|
||||||
|
for (i=0; i<numLocals; i++, routine += 2) __state.stack[__state.sp++] = PEEKW(routine);
|
||||||
|
} else {
|
||||||
|
for (i=0; i<numLocals; i++) __state.stack[__state.sp++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove return address from argument count.
|
||||||
|
args--;
|
||||||
|
// It's legal to have more arguments than locals. Toss extras.
|
||||||
|
if (args > numLocals) args = numLocals;
|
||||||
|
|
||||||
|
// Copy arguments into local variables on stack.
|
||||||
|
src = 1;
|
||||||
|
dst = __state.bp;
|
||||||
|
for (i=0; i<args; i++) {
|
||||||
|
__state.stack[dst++] = __state.operands[src++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move PC to called routine.
|
||||||
|
__state.pc = routine;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_put_prop(void) {
|
void opcodes_put_prop(void) {
|
||||||
|
uint8_t size = 0;
|
||||||
|
uint8_t ptr = interpreterGetObjectProperty(__state.operands[0], __state.operands[1], &size);
|
||||||
|
|
||||||
|
if (ptr == 0) portDie(MSG_OP_VAR_MISSING_PROPERTY);
|
||||||
|
|
||||||
|
if (size == 1) {
|
||||||
|
POKE(ptr, (__state.operands[2] & 0xff));
|
||||||
|
} else {
|
||||||
|
POKEW(ptr, __state.operands[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void opcodes_storew(void) {
|
void opcodes_storew(void) {
|
||||||
|
POKEW(__state.operands[0] + (__state.operands[1] * 2), __state.operands[2]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,17 @@
|
||||||
|
|
||||||
|
|
||||||
// Macros to declare implemented opcodes.
|
// Macros to declare implemented opcodes.
|
||||||
|
#ifdef DEBUGGING
|
||||||
|
#define OP(name) __state.opcodes[_nextOpcode] = opcodes_##name; __state.opcodesName[_nextOpcode++] = #name
|
||||||
|
#define OPn(num, name) __state.opcodes[num] = opcodes_##name; __state.opcodesName[num] = #name
|
||||||
|
#define OPX(name) __state.extOpcodes[_nextOpcode] = opcodes_ext_##name; __state.extOpcodesName[_nextOpcode++] = #name
|
||||||
|
#define OPXn(num, name) __state.extOpcodes[num] = opcodes_ext_##name; __state.extOpcodes[num] = #name
|
||||||
|
#else
|
||||||
#define OP(name) __state.opcodes[_nextOpcode++] = opcodes_##name
|
#define OP(name) __state.opcodes[_nextOpcode++] = opcodes_##name
|
||||||
#define OPn(num, name) __state.opcodes[num] = opcodes_##name
|
#define OPn(num, name) __state.opcodes[num] = opcodes_##name
|
||||||
#define OPX(name) __state.extOpcodes[_nextOpcode++] = opcodes_ext_##name
|
#define OPX(name) __state.extOpcodes[_nextOpcode++] = opcodes_ext_##name
|
||||||
#define OPXn(num, name) __state.extOpcodes[num] = opcodes_ext_##name
|
#define OPXn(num, name) __state.extOpcodes[num] = opcodes_ext_##name
|
||||||
|
#endif
|
||||||
|
|
||||||
// Macros to declare un-implemented opcodes.
|
// Macros to declare un-implemented opcodes.
|
||||||
#define mOP(name) _nextOpcode++
|
#define mOP(name) _nextOpcode++
|
||||||
|
@ -48,8 +55,7 @@
|
||||||
int16_t _nextOpcode;
|
int16_t _nextOpcode;
|
||||||
|
|
||||||
|
|
||||||
void opcodesSetup(void) {
|
void opcodesBuiltInitialTable(void) {
|
||||||
|
|
||||||
// 2-operand instructions...
|
// 2-operand instructions...
|
||||||
_nextOpcode = 1;
|
_nextOpcode = 1;
|
||||||
OP(je);
|
OP(je);
|
||||||
|
@ -251,3 +257,21 @@ void opcodesSetup(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void opcodesSetup(void) {
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
opcodesBuiltInitialTable();
|
||||||
|
|
||||||
|
// 2OP opcodes repeating with different operand forms.
|
||||||
|
for (i=32; i<=127; i++) __state.opcodes[i] = __state.opcodes[i % 32];
|
||||||
|
// 1OP opcodes repeating with different operand forms.
|
||||||
|
for (i=144; i<=175; i++) __state.opcodes[i] = __state.opcodes[128 + (i % 16)];
|
||||||
|
// 2OP opcodes repeating with VAR operand forms.
|
||||||
|
for (i=192; i<=223; i++) __state.opcodes[i] = __state.opcodes[i % 32];
|
||||||
|
|
||||||
|
#ifdef DEBUGGING
|
||||||
|
for (i=32; i<=127; i++) __state.opcodesName[i] = __state.opcodesName[i % 32];
|
||||||
|
for (i=144; i<=175; i++) __state.opcodesName[i] = __state.opcodesName[128 + (i % 16)];
|
||||||
|
for (i=192; i<=223; i++) __state.opcodesName[i] = __state.opcodesName[i % 32];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -21,10 +21,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "text.h"
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "portme.h"
|
||||||
|
|
||||||
|
|
||||||
void textPrint(char *what) {
|
void portDie(char *fmt, ...) {
|
||||||
printf("%s", what);
|
va_list ap;
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void portPrintChars(char *chars, uint16_t len) {
|
||||||
|
uint16_t x;
|
||||||
|
for (x=0; x<len; x++) {
|
||||||
|
printf("%c", chars[x]);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,7 +21,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
|
|
||||||
stateT __state;
|
stateT __state;
|
||||||
|
|
||||||
|
|
||||||
|
void stateReset(void) {
|
||||||
|
// Zero out all state data.
|
||||||
|
memset(&__state, 0, sizeof(stateT));
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue