v0.70: disassembler, immediate mode VM ops
- VM now prompts for filename to load bytecode from - Added new EightBall disassembler (`disass`), removed disassembly printout from compiler - Fixes to interpreter / compiler error reporting. Should no longer report completely incorrect line numbers! - Added new immediate mode VM instructions for load (`LDAWI`, `LDABI`, `LDRWI`, `LDRBI`), store (`STAWI`, `STABI`, `STRWI`, `STRBI`), and flow control (`JSRI`, `JMPI`, `BRCI`). Modified compiler to use these new instructions. This significantly improves code density and VM execution speed.
This commit is contained in:
parent
13df8be29f
commit
6dbf45c39c
BIN
8ball20.prg
BIN
8ball20.prg
Binary file not shown.
BIN
8ball64.prg
BIN
8ball64.prg
Binary file not shown.
BIN
8ballvm20.prg
BIN
8ballvm20.prg
Binary file not shown.
BIN
8ballvm64.prg
BIN
8ballvm64.prg
Binary file not shown.
12
Makefile
12
Makefile
|
@ -10,7 +10,7 @@ CC65BINDIR = $(CC65DIR)/bin
|
|||
CC65LIBDIR = $(CC65DIR)/lib
|
||||
APPLECMDR = ~/Desktop/Apple2/AppleCommander-1.3.5.jar
|
||||
|
||||
all: eightball eightballvm 8ball20.prg 8ballvm20.prg 8ball64.prg 8ballvm64.prg eightball.system ebvm.system test.d64 test.dsk
|
||||
all: eightball eightballvm disass 8ball20.prg 8ballvm20.prg 8ball64.prg 8ballvm64.prg eightball.system ebvm.system test.d64 test.dsk
|
||||
|
||||
clean:
|
||||
rm -f eightball eightballvm *.o 8ball20.* 8ball64.* eightball*.s eightball.system test.d64 *.map *.vice
|
||||
|
@ -23,6 +23,10 @@ eightballvm.o: eightballvm.c eightballutils.h eightballvm.h
|
|||
# 32 bit so sizeof(int*) = sizeof(int) [I am lazy]
|
||||
gcc -m32 -Wall -Wextra -g -c -o eightballvm.o eightballvm.c -lm
|
||||
|
||||
disass.o: disass.c eightballutils.h eightballvm.h
|
||||
# 32 bit so sizeof(int*) = sizeof(int) [I am lazy]
|
||||
gcc -m32 -Wall -Wextra -g -c -o disass.o disass.c -lm
|
||||
|
||||
eightballutils.o: eightballutils.c eightballutils.h
|
||||
# 32 bit so sizeof(int*) = sizeof(int) [I am lazy]
|
||||
gcc -m32 -Wall -Wextra -g -c -o eightballutils.o eightballutils.c -lm
|
||||
|
@ -35,6 +39,10 @@ eightballvm: eightballvm.o eightballutils.o
|
|||
# 32 bit so sizeof(int*) = sizeof(int) [I am lazy]
|
||||
gcc -m32 -Wall -Wextra -g -o eightballvm eightballvm.o eightballutils.o -lm
|
||||
|
||||
disass: disass.o eightballutils.o
|
||||
# 32 bit so sizeof(int*) = sizeof(int) [I am lazy]
|
||||
gcc -m32 -Wall -Wextra -g -o disass disass.o eightballutils.o -lm
|
||||
|
||||
eightball_20.o: eightball.c eightballutils.h eightballvm.h
|
||||
$(CC65BINDIR)/cc65 -Or -t vic20 -D VIC20 -o eightball_20.s eightball.c
|
||||
$(CC65BINDIR)/ca65 -t vic20 eightball_20.s
|
||||
|
@ -90,7 +98,7 @@ ebvm.system: eightballvm_a2e.o eightballutils_a2e.o
|
|||
$(CC65BINDIR)/ld65 -m 8ballvma2e.map -o ebvm.system -C apple2enh-system.cfg eightballvm_a2e.o eightballutils_a2e.o apple2enh-iobuf-0800.o $(CC65LIBDIR)/apple2enh.lib
|
||||
|
||||
unittest.8bp: unittest.8b
|
||||
tr {} [] <unittest.8b | \\100-\\132 \\300-\\332 | tr \\140-\\172 \\100-\\132 > unittest.8bp # ASCII -> PETSCII
|
||||
tr {} [] <unittest.8b | tr \\100-\\132 \\300-\\332 | tr \\140-\\172 \\100-\\132 > unittest.8bp # ASCII -> PETSCII
|
||||
|
||||
sieve4.8bp: sieve4.8b
|
||||
tr {} [] <sieve4.8b | tr \\100-\\132 \\300-\\332 | tr \\140-\\172 \\100-\\132 > sieve4.8bp # ASCII -> PETSCII
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/**************************************************************************/
|
||||
/* EightBall Disassembler */
|
||||
/* */
|
||||
/* The Eight Bit Algorithmic Language */
|
||||
/* For Apple IIe/c/gs (64K), Commodore 64, VIC-20 +32K RAM expansion */
|
||||
/* (also builds for Linux as 32 bit executable (gcc -m32) only) */
|
||||
/* */
|
||||
/* Compiles with cc65 v2.15 for VIC-20, C64, Apple II */
|
||||
/* and gcc 7.3 for Linux */
|
||||
/* */
|
||||
/* cc65: Define symbol VIC20 to build for Commodore VIC-20 + 32K. */
|
||||
/* Define symbol C64 to build for Commodore 64. */
|
||||
/* Define symbol A2E to build for Apple //e. */
|
||||
/* */
|
||||
/* Copyright Bobbi Webber-Manners 2018 */
|
||||
/* EightBall VM disassembler */
|
||||
/* */
|
||||
/* Formatted with indent -kr -nut */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* GNU PUBLIC LICENCE v3 OR LATER */
|
||||
/* */
|
||||
/* This program is free software: you can redistribute it and/or modify */
|
||||
/* it under the terms of the GNU General Public License as published by */
|
||||
/* the Free Software Foundation, either version 3 of the License, or */
|
||||
/* (at your option) any later version. */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "eightballvm.h"
|
||||
#include "eightballutils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef A2E
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
/* Order must match enum bytecode */
|
||||
char *bytecodenames[] = {
|
||||
"END",
|
||||
"LDI",
|
||||
"LDAW",
|
||||
"LDAWI",
|
||||
"LDAB",
|
||||
"LDABI",
|
||||
"STAW",
|
||||
"STAWI",
|
||||
"STAB",
|
||||
"STABI",
|
||||
"LDRW",
|
||||
"LDRWI",
|
||||
"LDRB",
|
||||
"LDRBI",
|
||||
"STRW",
|
||||
"STRWI",
|
||||
"STRB",
|
||||
"STRBI",
|
||||
"SWP",
|
||||
"DUP",
|
||||
"DUP2",
|
||||
"DRP",
|
||||
"OVER",
|
||||
"PICK",
|
||||
"POPW",
|
||||
"POPB",
|
||||
"PSHW",
|
||||
"PSHB",
|
||||
"DISC",
|
||||
"SPFP",
|
||||
"FPSP",
|
||||
"ATOR",
|
||||
"RTOA",
|
||||
"INC",
|
||||
"DEC",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"NEG",
|
||||
"GT",
|
||||
"GTE",
|
||||
"LT",
|
||||
"LTE",
|
||||
"EQL",
|
||||
"NEQL",
|
||||
"AND",
|
||||
"OR",
|
||||
"NOT",
|
||||
"BAND",
|
||||
"BOR",
|
||||
"BXOR",
|
||||
"BNOT",
|
||||
"LSH",
|
||||
"RSH",
|
||||
"JMP",
|
||||
"JMPI",
|
||||
"BRC",
|
||||
"BRCI",
|
||||
"JSR",
|
||||
"JSRI",
|
||||
"RTS",
|
||||
"PRDEC",
|
||||
"PRHEX",
|
||||
"PRCH",
|
||||
"PRSTR",
|
||||
"PRMSG",
|
||||
"KBDCH",
|
||||
"KBDLN"
|
||||
};
|
||||
|
||||
/*
|
||||
* Call stack grows down from top of memory.
|
||||
* If it hits CALLSTACKLIM then VM will quit with error
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
#define MEMORYSZ (64 * 1024)
|
||||
#else
|
||||
/* Has to be 64K minus a byte otherwise winds up being zero! */
|
||||
#define MEMORYSZ (64 * 1024) - 1
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
unsigned char memory[MEMORYSZ];
|
||||
#else
|
||||
unsigned char *memory = 0;
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define UINT16 unsigned short
|
||||
#else
|
||||
#define UINT16 unsigned int
|
||||
#endif
|
||||
|
||||
UINT16 pc = RTPCSTART; /* Program counter */
|
||||
UINT16 lastpc;
|
||||
|
||||
/*
|
||||
* Print a value as hex
|
||||
*/
|
||||
void _printhexbyte(unsigned char val) {
|
||||
|
||||
printchar(hexval2char((val>>4) & 0x0f));
|
||||
printchar(hexval2char(val & 0x0f));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch, decode and disassemble a VM instruction, then advance the program counter.
|
||||
*/
|
||||
void disassemble_instruction()
|
||||
{
|
||||
printhex(pc);
|
||||
print(": ");
|
||||
_printhexbyte(memory[pc]);
|
||||
printchar(' ');
|
||||
switch(memory[pc++]) {
|
||||
case VM_LDIMM:
|
||||
case VM_LDAWORDIMM:
|
||||
case VM_LDABYTEIMM:
|
||||
case VM_LDRWORDIMM:
|
||||
case VM_LDRBYTEIMM:
|
||||
case VM_STAWORDIMM:
|
||||
case VM_STABYTEIMM:
|
||||
case VM_STRWORDIMM:
|
||||
case VM_STRBYTEIMM:
|
||||
case VM_JMPIMM:
|
||||
case VM_BRNCHIMM:
|
||||
case VM_JSRIMM:
|
||||
_printhexbyte(memory[pc++]);
|
||||
printchar(' ');
|
||||
_printhexbyte(memory[pc++]);
|
||||
print(" ");
|
||||
print(bytecodenames[memory[pc-3]]);
|
||||
printchar(' ');
|
||||
printhex(memory[pc-2] + (memory[pc-1] << 8));
|
||||
print(" (");
|
||||
printdec(memory[pc-2] + (memory[pc-1] << 8));
|
||||
print(")");
|
||||
break;
|
||||
case VM_PRMSG:
|
||||
print("...00 ");
|
||||
print(bytecodenames[memory[pc-1]]);
|
||||
print(" \"");
|
||||
while(memory[pc]) {
|
||||
printchar(memory[pc++]);
|
||||
}
|
||||
++pc;
|
||||
printchar('"');
|
||||
break;
|
||||
default:
|
||||
print(" ");
|
||||
if (memory[pc-1] <= VM_KBDLN) {
|
||||
print(bytecodenames[memory[pc-1]]);
|
||||
} else {
|
||||
print("**ILLEGAL**");
|
||||
}
|
||||
}
|
||||
#ifdef A2E
|
||||
printchar('\r');
|
||||
#else
|
||||
printchar('\n');
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Disassemble the program!
|
||||
*/
|
||||
void disassemble()
|
||||
{
|
||||
while (pc < lastpc) {
|
||||
disassemble_instruction();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load bytecode into memory[].
|
||||
*/
|
||||
void load()
|
||||
{
|
||||
FILE *fp;
|
||||
char ch;
|
||||
|
||||
pc = RTPCSTART;
|
||||
do {
|
||||
print("\nBytecode file (CR for default)>");
|
||||
getln((char*)memory, 15);
|
||||
if (strlen((char*)memory) == 0) {
|
||||
strcpy((char*)memory, "bytecode");
|
||||
}
|
||||
print("Loading '");
|
||||
print((char*)memory);
|
||||
print("'\n");
|
||||
fp = fopen((char*)memory, "r");
|
||||
} while (!fp);
|
||||
while (!feof(fp)) {
|
||||
ch = fgetc(fp);
|
||||
memory[pc++] = ch;
|
||||
/* Print dot for each page */
|
||||
if (pc%0xff == 0) {
|
||||
printchar('.');
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
lastpc = pc - 1;
|
||||
pc = RTPCSTART;
|
||||
#ifdef A2E
|
||||
printchar(7);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
print("EightBall Disassembler v" VERSIONSTR "\n");
|
||||
print("(c)Bobbi, 2018\n");
|
||||
print("Free Software.\n");
|
||||
print("Licenced under GPL.\n\n");
|
||||
|
||||
load();
|
||||
#ifdef __GNUC__
|
||||
print(" Done.\n\n");
|
||||
#endif
|
||||
#ifdef A2E
|
||||
videomode(VIDEOMODE_80COL);
|
||||
clrscr();
|
||||
#elif defined(CBM)
|
||||
printchar(147); /* Clear */
|
||||
#endif
|
||||
disassemble();
|
||||
return 0;
|
||||
}
|
BIN
ebvm.system
BIN
ebvm.system
Binary file not shown.
177
eightball.c
177
eightball.c
|
@ -1069,7 +1069,6 @@ unsigned char P()
|
|||
if (compile) {
|
||||
compiletimelookup = 1;
|
||||
if (getintvar(key, idx, &arg, &type, addressmode)) {
|
||||
error(ERR_VAR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1080,7 +1079,6 @@ unsigned char P()
|
|||
}
|
||||
|
||||
if (getintvar(key, idx, &arg, &type, addressmode)) {
|
||||
error(ERR_VAR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1237,7 +1235,7 @@ unsigned char *heap2PtrBttm; /* Arena 2: bottom-up heap */
|
|||
|
||||
//#define HEAP2TOP (char*)0x97ff
|
||||
#define HEAP2TOP (char*)0xa7ff
|
||||
#define HEAP2LIM (char*)0x8a00
|
||||
#define HEAP2LIM (char*)0x8900
|
||||
/* HEAP2LIM HAS TO BE ADJUSTED TO NOT
|
||||
* TRASH THE CODE, WHICH LOADS FROM $2000 UP
|
||||
* USE THE MAPFILE! */
|
||||
|
@ -1258,7 +1256,7 @@ unsigned char *heap2PtrBttm; /* Arena 2: bottom-up heap */
|
|||
#define HEAP1LIM (char*)0xa000
|
||||
|
||||
#define HEAP2TOP (char*)0x9fff - 0x0400 /* Leave $800 for the C stack */
|
||||
#define HEAP2LIM (char*)0x7100
|
||||
#define HEAP2LIM (char*)0x6f00
|
||||
/* HEAP2LIM HAS TO BE ADJUSTED TO NOT
|
||||
* TRASH THE CODE, WHICH LOADS FROM $0800 UP
|
||||
* USE THE MAPFILE! */
|
||||
|
@ -1282,12 +1280,12 @@ unsigned char *heap2PtrBttm; /* Arena 2: bottom-up heap */
|
|||
//#define HEAP1LIM (char*)0xa000
|
||||
|
||||
//#define HEAP2TOP (char*)0x7fff - 0x0400 /* Leave $400 for the C stack */
|
||||
//#define HEAP2LIM (char*)0x7c00 /* HEAP2LIM HAS TO BE ADJUSTED TO NOT
|
||||
//#define HEAP2LIM (char*)0x7600 /* HEAP2LIM HAS TO BE ADJUSTED TO NOT
|
||||
// * TRASH THE CODE, WHICH LOADS FROM $1200 UP
|
||||
// * USE THE MAPFILE! */
|
||||
|
||||
// Everything in BLK5 for now
|
||||
// BLK3 is totally full of code!
|
||||
// BLK3 is almost totally full of code!
|
||||
// Man ... we really need more memory!!
|
||||
#define HEAP1TOP (char*)0xbfff
|
||||
#define HEAP1LIM (char*)0xb000
|
||||
|
@ -1458,28 +1456,53 @@ int getfreespace2()
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Compiler: Emit bytecode (used for everything except VM_LDIMM)
|
||||
* Compiler: Emit simple one byte code
|
||||
* Used for everything except immediate mode opcodes
|
||||
* Stores using codeptr.
|
||||
*/
|
||||
void emit(enum bytecode code)
|
||||
{
|
||||
/*
|
||||
unsigned char c = code;
|
||||
*/
|
||||
|
||||
*codeptr = code;
|
||||
++codeptr;
|
||||
|
||||
/*
|
||||
printhex(rtPC);
|
||||
print(": ");
|
||||
printhexbyte(c);
|
||||
print(" : ");
|
||||
print(bytecodenames[c]);
|
||||
printchar('\n');
|
||||
*/
|
||||
++rtPC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compiler: Emit opcode and 16 bit word argument
|
||||
* Stores using codeptr.
|
||||
*/
|
||||
void emit_imm(enum bytecode code, int word)
|
||||
{
|
||||
unsigned char *p = (unsigned char *) &word;
|
||||
|
||||
*codeptr = code;
|
||||
++codeptr;
|
||||
*codeptr = *p;
|
||||
++codeptr;
|
||||
++p;
|
||||
*codeptr = *p;
|
||||
++codeptr;
|
||||
rtPC += 3;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compiler: Emit word argument (VM_LDIMM opcode)
|
||||
* Stores using codeptr.
|
||||
* TODO: Eventually we can remove this
|
||||
*/
|
||||
void emitldi(int word)
|
||||
{
|
||||
|
@ -1494,6 +1517,7 @@ void emitldi(int word)
|
|||
*codeptr = *p;
|
||||
++codeptr;
|
||||
|
||||
/*
|
||||
printhex(rtPC);
|
||||
print(": ");
|
||||
printhexbyte(c);
|
||||
|
@ -1504,6 +1528,7 @@ void emitldi(int word)
|
|||
printchar(' ');
|
||||
printhex(word);
|
||||
printchar('\n');
|
||||
*/
|
||||
rtPC += 3;
|
||||
}
|
||||
|
||||
|
@ -1517,18 +1542,23 @@ void emitprmsg(void)
|
|||
char *p = readbuf;
|
||||
emit(VM_PRMSG);
|
||||
++rtPC;
|
||||
/*
|
||||
printchar('"');
|
||||
*/
|
||||
while (*p) {
|
||||
*codeptr = *p;
|
||||
++rtPC;
|
||||
/*
|
||||
printchar(*p);
|
||||
*/
|
||||
++codeptr;
|
||||
++p;
|
||||
}
|
||||
*codeptr = 0;
|
||||
++codeptr;
|
||||
|
||||
/*
|
||||
print("\"\n");
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1547,10 +1577,12 @@ void emit_fixup(int address, int word)
|
|||
++p;
|
||||
*ptr = *p;
|
||||
|
||||
/*
|
||||
printhex(address);
|
||||
print(": ");
|
||||
printhex(word);
|
||||
print(" ; Fixup\n");
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1563,6 +1595,7 @@ void writebytecode()
|
|||
unsigned char *p;
|
||||
p = (unsigned char *) CODESTART;
|
||||
strcpy(readbuf, "bytecode");
|
||||
printchar('\n');
|
||||
openfile(1);
|
||||
print("...\n");
|
||||
while (p < end) {
|
||||
|
@ -2019,8 +2052,7 @@ unsigned char createintvar(char *name,
|
|||
emit((type == TYPE_WORD) ? VM_PSHWORD : VM_PSHBYTE);
|
||||
emitldi(0);
|
||||
emit(VM_NEQL);
|
||||
emitldi(rtPC - 10);
|
||||
emit(VM_BRNCH);
|
||||
emit_imm(VM_BRNCHIMM, rtPC - 10);
|
||||
emit(VM_DROP);
|
||||
|
||||
/*
|
||||
|
@ -2034,9 +2066,7 @@ unsigned char createintvar(char *name,
|
|||
for (i = 0; i < sz; ++i) {
|
||||
if (arrinitmode == STRG_INIT) {
|
||||
emitldi((*txtPtr == '"') ? 0 : *txtPtr);
|
||||
((type ==
|
||||
TYPE_WORD) ? civ_st_rel_word(i) :
|
||||
civ_st_rel_byte(i));
|
||||
((type == TYPE_WORD) ? civ_st_rel_word(i) : civ_st_rel_byte(i));
|
||||
if (*txtPtr == '"') {
|
||||
break;
|
||||
}
|
||||
|
@ -2228,6 +2258,22 @@ void siv_st_rel(unsigned char type)
|
|||
((type == TYPE_WORD) ? emit(VM_STRWORD) : emit(VM_STRBYTE));
|
||||
}
|
||||
|
||||
/* Factored out to save a few bytes
|
||||
* Used by setintvar() only.
|
||||
*/
|
||||
void siv_st_abs_imm(unsigned int addr, unsigned char type)
|
||||
{
|
||||
emit_imm(((type & 0x0f) == TYPE_WORD) ? VM_STAWORDIMM : VM_STABYTEIMM, addr);
|
||||
}
|
||||
|
||||
/* Factored out to save a few bytes
|
||||
* Used by setintvar() only.
|
||||
*/
|
||||
void siv_st_rel_imm(unsigned int addr, unsigned char type)
|
||||
{
|
||||
emit_imm(((type & 0x0f) == TYPE_WORD) ? VM_STRWORDIMM : VM_STRBYTEIMM, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set existing integer variable
|
||||
* name is the variable name
|
||||
|
@ -2277,11 +2323,10 @@ unsigned char setintvar(char *name, int idx, int value)
|
|||
* ABSOLUTE addressing, but locals are addressed RELATIVE
|
||||
* to the frame pointer.
|
||||
*/
|
||||
emitldi(*getptrtoscalarword(ptr));
|
||||
if (local && compilingsub) {
|
||||
siv_st_rel(type);
|
||||
siv_st_rel_imm(*getptrtoscalarword(ptr), type);
|
||||
} else {
|
||||
siv_st_abs(type);
|
||||
siv_st_abs_imm(*getptrtoscalarword(ptr), type);
|
||||
}
|
||||
} else {
|
||||
if (type == TYPE_WORD) {
|
||||
|
@ -2299,8 +2344,7 @@ unsigned char setintvar(char *name, int idx, int value)
|
|||
error(ERR_SUBSCR);
|
||||
return 1;
|
||||
}
|
||||
bodyptr =
|
||||
(void *) *(int *) ((unsigned char *) ptr + sizeof(var_t));
|
||||
bodyptr = (void *) *(int *) ((unsigned char *) ptr + sizeof(var_t));
|
||||
|
||||
if (compile) {
|
||||
/* *** Index is on the stack (X) */
|
||||
|
@ -2360,6 +2404,22 @@ void giv_ld_rel(unsigned char type)
|
|||
(((type & 0x0f) == TYPE_WORD) ? emit(VM_LDRWORD) : emit(VM_LDRBYTE));
|
||||
}
|
||||
|
||||
/* Factored out to save a few bytes
|
||||
* Used by getintvar() only.
|
||||
*/
|
||||
void giv_ld_abs_imm(unsigned int addr, unsigned char type)
|
||||
{
|
||||
emit_imm(((type & 0x0f) == TYPE_WORD) ? VM_LDAWORDIMM : VM_LDABYTEIMM, addr);
|
||||
}
|
||||
|
||||
/* Factored out to save a few bytes
|
||||
* Used by getintvar() only.
|
||||
*/
|
||||
void giv_ld_rel_imm(unsigned int addr, unsigned char type)
|
||||
{
|
||||
emit_imm(((type & 0x0f) == TYPE_WORD) ? VM_LDRWORDIMM : VM_LDRBYTEIMM, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get existing integer variable
|
||||
* name is the variable name
|
||||
|
@ -2423,11 +2483,10 @@ unsigned char getintvar(char *name,
|
|||
emit(VM_RTOA);
|
||||
}
|
||||
} else {
|
||||
emitldi(*getptrtoscalarword(ptr));
|
||||
if (local && compilingsub) {
|
||||
giv_ld_rel(*type);
|
||||
giv_ld_rel_imm(*getptrtoscalarword(ptr), *type);
|
||||
} else {
|
||||
giv_ld_abs(*type);
|
||||
giv_ld_abs_imm(*getptrtoscalarword(ptr), *type);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2565,8 +2624,7 @@ void doif(unsigned char arg)
|
|||
/* **** Value of IF expression is on the eval stack **** */
|
||||
emit(VM_NOT);
|
||||
push_return(rtPC + 1);
|
||||
emitldi(0xffff); /* To be filled in later */
|
||||
emit(VM_BRNCH);
|
||||
emit_imm(VM_BRNCHIMM, 0xffff); /* To be filled in later */
|
||||
push_return(0);
|
||||
} else {
|
||||
if (skipFlag) {
|
||||
|
@ -2599,8 +2657,7 @@ unsigned char doelse()
|
|||
* Code to jump over ELSE block when IF condition is true
|
||||
*/
|
||||
return_stack[returnSP + 1] = rtPC;
|
||||
emitldi(0xffff); /* To be filled in later */
|
||||
emit(VM_JMP);
|
||||
emit_imm(VM_JMPIMM, 0xffff); /* To be filled in later */
|
||||
|
||||
/*
|
||||
* Fixup the dummy destination address initialized by
|
||||
|
@ -2841,7 +2898,6 @@ unsigned char assignorcreate(unsigned char mode)
|
|||
compiletimelookup = 1;
|
||||
}
|
||||
if (getintvar(name, i, &j, &type, 1)) {
|
||||
error(ERR_VAR);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
|
@ -2933,27 +2989,27 @@ unsigned char doendfor()
|
|||
emit(VM_DUP);
|
||||
emit(VM_PSHWORD);
|
||||
|
||||
emitldi(return_stack[returnSP + 2]); /* Pointer to loop variable */
|
||||
//emitldi(return_stack[returnSP + 2]); /* Pointer to loop variable */
|
||||
if (return_stack[returnSP + 4]) { /* Rel or abs */
|
||||
emit((type == TYPE_WORD) ? VM_LDRWORD : VM_LDRBYTE);
|
||||
/* Pointer to loop var */
|
||||
emit_imm((type == TYPE_WORD) ? VM_LDRWORDIMM : VM_LDRBYTEIMM, return_stack[returnSP + 2]);
|
||||
} else {
|
||||
emit((type == TYPE_WORD) ? VM_LDAWORD : VM_LDABYTE);
|
||||
/* Pointer to loop var */
|
||||
emit_imm((type == TYPE_WORD) ? VM_LDAWORDIMM : VM_LDABYTEIMM, return_stack[returnSP + 2]);
|
||||
}
|
||||
|
||||
/* Increment and store loop variable */
|
||||
emit(VM_INC);
|
||||
emit(VM_DUP);
|
||||
emitldi(return_stack[returnSP + 2]);
|
||||
if (return_stack[returnSP + 4]) {
|
||||
emit((type == TYPE_WORD) ? VM_STRWORD : VM_STRBYTE);
|
||||
emit_imm((type == TYPE_WORD) ? VM_STRWORDIMM : VM_STRBYTEIMM, return_stack[returnSP + 2]);
|
||||
} else {
|
||||
emit((type == TYPE_WORD) ? VM_STAWORD : VM_STABYTE);
|
||||
emit_imm((type == TYPE_WORD) ? VM_STAWORDIMM : VM_STABYTEIMM, return_stack[returnSP + 2]);
|
||||
}
|
||||
|
||||
/* Compare with loop limit already on eval stack */
|
||||
emit(VM_GTE);
|
||||
emitldi(return_stack[returnSP + 3]); /* Branch destination */
|
||||
emit(VM_BRNCH);
|
||||
emit_imm(VM_BRNCHIMM, return_stack[returnSP + 3]); /* Branch destination */
|
||||
|
||||
/* Drop loop limit from call stack */
|
||||
emit(VM_POPWORD);
|
||||
|
@ -3027,8 +3083,7 @@ void dowhile(char *startTxtPtr, unsigned char arg)
|
|||
/* **** Value of WHILE expression is on the eval stack **** */
|
||||
emit(VM_NOT);
|
||||
push_return(rtPC + 1); /* Address of dummy 0xffff */
|
||||
emitldi(0xffff);
|
||||
emit(VM_BRNCH);
|
||||
emit_imm(VM_BRNCHIMM, 0xffff);
|
||||
push_return(0); /* Dummy */
|
||||
} else {
|
||||
if (skipFlag) {
|
||||
|
@ -3062,8 +3117,8 @@ unsigned char doendwhile()
|
|||
/*
|
||||
* Jump back and re-evaluate the WHILE argument.
|
||||
*/
|
||||
emitldi(return_stack[returnSP + 3]);
|
||||
emit(VM_JMP);
|
||||
emit_imm(VM_JMPIMM, return_stack[returnSP + 3]);
|
||||
|
||||
/*
|
||||
* Fixup the dummy destination address initialized by
|
||||
* dowhile() to point to the ENDWHILE.
|
||||
|
@ -3166,9 +3221,9 @@ unsigned char dosubr()
|
|||
|
||||
compilingsub = 1;
|
||||
|
||||
print("\nsub ");
|
||||
print("\n[");
|
||||
print(readbuf);
|
||||
print("\n");
|
||||
print("]");
|
||||
|
||||
/*
|
||||
* Create entry in subroutine table
|
||||
|
@ -3367,10 +3422,14 @@ unsigned char docall()
|
|||
strncpy(s->name, readbuf, SUBRNUMCHARS);
|
||||
}
|
||||
|
||||
counter = -1;
|
||||
if (!compile) {
|
||||
counter = -1;
|
||||
}
|
||||
while (l) {
|
||||
p = l->line;
|
||||
++counter;
|
||||
if (!compile) {
|
||||
++counter;
|
||||
}
|
||||
|
||||
skipFlag = 0;
|
||||
|
||||
|
@ -3408,6 +3467,7 @@ unsigned char docall()
|
|||
*/
|
||||
eatspace();
|
||||
if (expect('(')) {
|
||||
counter = origcounter;
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
|
@ -3512,10 +3572,12 @@ unsigned char docall()
|
|||
|
||||
/* If end of line, error */
|
||||
if (!(*txtPtr)) {
|
||||
counter = origcounter;
|
||||
error(ERR_ARG);
|
||||
return RET_ERROR;
|
||||
}
|
||||
if (*txtPtr == ')') {
|
||||
counter = origcounter;
|
||||
error(ERR_ARG);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
@ -3529,6 +3591,7 @@ unsigned char docall()
|
|||
}
|
||||
if (eval(0, &arg)) {
|
||||
/* No expression found */
|
||||
counter = origcounter;
|
||||
error(ERR_ARG);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
@ -3565,12 +3628,14 @@ unsigned char docall()
|
|||
varslocal = oldvarslocal;
|
||||
array = findintvar(name2, &local);
|
||||
if (!array) {
|
||||
counter = origcounter;
|
||||
error(ERR_VAR);
|
||||
return RET_ERROR;
|
||||
}
|
||||
/* j holds number of dimensions */
|
||||
j = (array->type & 0xf0) >> 4;
|
||||
if (((array->type & 0x0f) != type) || (j == 0)) {
|
||||
counter = origcounter;
|
||||
error(ERR_TYPE);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
@ -3585,6 +3650,7 @@ unsigned char docall()
|
|||
} else {
|
||||
if (eval(0, &arg)) {
|
||||
/* No expression found */
|
||||
counter = origcounter;
|
||||
error(ERR_ARG);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
@ -3614,11 +3680,14 @@ unsigned char docall()
|
|||
|
||||
eatspace();
|
||||
if (expect(')')) {
|
||||
counter = origcounter;
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
if (compile) {
|
||||
emitldi(0xffff);
|
||||
|
||||
emit_imm(VM_JSRIMM, 0xffff);
|
||||
|
||||
/*
|
||||
* Create entry in call table
|
||||
*/
|
||||
|
@ -3633,8 +3702,6 @@ unsigned char docall()
|
|||
callsbegin = s;
|
||||
}
|
||||
|
||||
emit(VM_JSR);
|
||||
|
||||
/* Caller must drop the arguments
|
||||
* pushed to call stack above */
|
||||
if (argbytes) {
|
||||
|
@ -3658,8 +3725,8 @@ unsigned char docall()
|
|||
}
|
||||
l = l->next;
|
||||
}
|
||||
error(ERR_NOSUB);
|
||||
counter = origcounter;
|
||||
error(ERR_NOSUB);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
|
@ -4204,8 +4271,7 @@ unsigned char parseline()
|
|||
emitldi(0x8000);
|
||||
emit(VM_BITAND);
|
||||
emit(VM_NOT);
|
||||
emitldi(rtPC + 9); /* Jump over printing of '-' */
|
||||
emit(VM_BRNCH);
|
||||
emit_imm(VM_BRNCHIMM, rtPC + 9); /* Jump over printing of '-' */
|
||||
emitldi('-');
|
||||
emit(VM_PRCH);
|
||||
emit(VM_NEG);
|
||||
|
@ -4315,13 +4381,15 @@ unsigned char parseline()
|
|||
callsbegin = callsend = NULL;
|
||||
CLEARRTCALLSTACK();
|
||||
run(0);
|
||||
emit(VM_END);
|
||||
linksubs();
|
||||
writebytecode();
|
||||
if (compile) {
|
||||
emit(VM_END);
|
||||
linksubs();
|
||||
writebytecode();
|
||||
compile = 0;
|
||||
}
|
||||
#ifndef __GNUC__
|
||||
CLEARHEAP2TOP(); /* Clear the linkage table */
|
||||
#endif
|
||||
compile = 0;
|
||||
break;
|
||||
case TOK_NEW:
|
||||
new();
|
||||
|
@ -4810,6 +4878,9 @@ void run(unsigned char cont)
|
|||
current = program;
|
||||
}
|
||||
while (current && !status) {
|
||||
if (compile) {
|
||||
printchar('.');
|
||||
}
|
||||
txtPtr = current->line;
|
||||
status = parseline();
|
||||
/* parseline() can set current to NULL when return is to
|
||||
|
@ -4827,6 +4898,7 @@ void run(unsigned char cont)
|
|||
printchar('\n');
|
||||
returnSP = (RETSTACKSZ - 1);
|
||||
skipFlag = 0;
|
||||
compile = 0;
|
||||
break;
|
||||
case 3:
|
||||
print("\nBrk at ");
|
||||
|
@ -4834,6 +4906,7 @@ void run(unsigned char cont)
|
|||
printchar('\n');
|
||||
returnSP = (RETSTACKSZ - 1);
|
||||
skipFlag = 0;
|
||||
compile = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
BIN
eightball.system
BIN
eightball.system
Binary file not shown.
|
@ -37,7 +37,7 @@
|
|||
/* */
|
||||
/**************************************************************************/
|
||||
|
||||
#define VERSIONSTR "0.69"
|
||||
#define VERSIONSTR "0.70"
|
||||
|
||||
void print(char *str);
|
||||
|
||||
|
|
BIN
eightballvm
BIN
eightballvm
Binary file not shown.
229
eightballvm.c
229
eightballvm.c
|
@ -62,6 +62,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef A2E
|
||||
#include <conio.h>
|
||||
|
@ -84,7 +85,7 @@
|
|||
#ifdef __GNUC__
|
||||
#define UINT16 unsigned short
|
||||
#else
|
||||
#define UINT16 unsigned int
|
||||
#define UINT16 unsigned short
|
||||
#endif
|
||||
|
||||
UINT16 pc = RTPCSTART; /* Program counter */
|
||||
|
@ -116,11 +117,11 @@ unsigned char *memory = 0;
|
|||
#define TREG evalstack[evalptr - 4] /* Only valid if evalptr >= 4 */
|
||||
|
||||
/*
|
||||
* Error checks are called through macros to make it easy to
|
||||
* disable them in production. We should not need these checks
|
||||
* in production (assuming no bugs in the compiler!) ... but they
|
||||
* are helpful for debugging!
|
||||
*/
|
||||
* Error checks are called through macros to make it easy to
|
||||
* disable them in production. We should not need these checks
|
||||
* in production (assuming no bugs in the compiler!) ... but they
|
||||
* are helpful for debugging!
|
||||
*/
|
||||
|
||||
#ifdef STACKCHECKS
|
||||
|
||||
|
@ -148,10 +149,11 @@ unsigned char *memory = 0;
|
|||
/* Handler for unsupported bytecode */
|
||||
#define UNSUPPORTED() unsupported()
|
||||
|
||||
#ifdef STACKCHECKS
|
||||
/*
|
||||
* Check for evaluation stack underflow.
|
||||
* level - Number of 16 bit operands required on eval stack.
|
||||
*/
|
||||
* Check for evaluation stack underflow.
|
||||
* level - Number of 16 bit operands required on eval stack.
|
||||
*/
|
||||
void checkunderflow(unsigned char level)
|
||||
{
|
||||
if (evalptr < level) {
|
||||
|
@ -163,9 +165,9 @@ void checkunderflow(unsigned char level)
|
|||
}
|
||||
|
||||
/*
|
||||
* Check evaluation stack is not going to overflow.
|
||||
* Assumes evalptr has already been advanced.
|
||||
*/
|
||||
* Check evaluation stack is not going to overflow.
|
||||
* Assumes evalptr has already been advanced.
|
||||
*/
|
||||
void checkoverflow()
|
||||
{
|
||||
if (evalptr > EVALSTACKSZ - 1) {
|
||||
|
@ -177,9 +179,9 @@ void checkoverflow()
|
|||
}
|
||||
|
||||
/*
|
||||
* Check call stack is not going to underflow.
|
||||
* bytes - Number of bytes required on call stack.
|
||||
*/
|
||||
* Check call stack is not going to underflow.
|
||||
* bytes - Number of bytes required on call stack.
|
||||
*/
|
||||
void checkstackunderflow(unsigned char bytes)
|
||||
{
|
||||
if ((MEMORYSZ - sp) < bytes) {
|
||||
|
@ -191,9 +193,9 @@ void checkstackunderflow(unsigned char bytes)
|
|||
}
|
||||
|
||||
/*
|
||||
* Check call stack is not going to overflow.
|
||||
* Assumes sp has already been advanced.
|
||||
*/
|
||||
* Check call stack is not going to overflow.
|
||||
* Assumes sp has already been advanced.
|
||||
*/
|
||||
void checkstackoverflow()
|
||||
{
|
||||
if (sp < CALLSTACKLIM + 1) {
|
||||
|
@ -203,10 +205,11 @@ void checkstackoverflow()
|
|||
while (1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handler for unsupported bytecodes
|
||||
*/
|
||||
* Handler for unsupported bytecodes
|
||||
*/
|
||||
void unsupported()
|
||||
{
|
||||
print("Unsupported instruction ");
|
||||
|
@ -218,19 +221,20 @@ void unsupported()
|
|||
}
|
||||
|
||||
/*
|
||||
* Fetch, decode and execute a VM instruction, then advance the program counter.
|
||||
*/
|
||||
* Fetch, decode and execute a VM instruction, then advance the program counter.
|
||||
*/
|
||||
void execute_instruction()
|
||||
{
|
||||
unsigned int tempword;
|
||||
unsigned short tempword;
|
||||
unsigned short *wordptr;
|
||||
unsigned char *byteptr;
|
||||
#ifndef __GNUC__
|
||||
unsigned int delay;
|
||||
unsigned short delay;
|
||||
#endif
|
||||
|
||||
//print("--->PC "); printhex(pc); print(" eval stk: "); printhex(evalptr); print("\n");
|
||||
#ifdef DEBUGREGS
|
||||
unsigned int i;
|
||||
unsigned short i;
|
||||
print("\n");
|
||||
print("--->PC ");
|
||||
printhex(pc);
|
||||
|
@ -266,9 +270,22 @@ void execute_instruction()
|
|||
printhex(pc);
|
||||
print(": ");
|
||||
print(bytecodenames[memory[pc]]);
|
||||
if (memory[pc] == VM_LDIMM) {
|
||||
/* TODO: Should encode immediate mode as one bit of opcode to make this more efficient */
|
||||
if ((memory[pc] == VM_LDIMM) ||
|
||||
(memory[pc] == VM_LDAWORDIMM) ||
|
||||
(memory[pc] == VM_LDABYTEIMM) ||
|
||||
(memory[pc] == VM_LDRWORDIMM) ||
|
||||
(memory[pc] == VM_LDRBYTEIMM) ||
|
||||
(memory[pc] == VM_STAWORDIMM) ||
|
||||
(memory[pc] == VM_STABYTEIMM) ||
|
||||
(memory[pc] == VM_STRWORDIMM) ||
|
||||
(memory[pc] == VM_STRBYTEIMM) ||
|
||||
(memory[pc] == VM_JMPIMM) ||
|
||||
(memory[pc] == VM_BRNCHIMM) ||
|
||||
(memory[pc] == VM_JSRIMM)) {
|
||||
printchar(' ');
|
||||
printhex(memory[pc + 1] + 256 * memory[pc + 2]);
|
||||
wordptr = (unsigned short *)&memory[pc + 1];
|
||||
printhex(*wordptr);
|
||||
printchar(' ');
|
||||
} else {
|
||||
print(" ");
|
||||
|
@ -324,10 +341,9 @@ void execute_instruction()
|
|||
case VM_LDIMM: /* Pushes the following 16 bit word to the evaluation stack */
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
/* Note: Word is stored in little endian format! */
|
||||
tempword = memory[++pc];
|
||||
tempword += memory[++pc] * 256;
|
||||
XREG = tempword;
|
||||
wordptr = (unsigned short *)&memory[++pc];
|
||||
++pc;
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
/*
|
||||
* Absolute addressing:
|
||||
|
@ -342,23 +358,56 @@ void execute_instruction()
|
|||
printchar('\n');
|
||||
#endif
|
||||
|
||||
XREG = memory[XREG] + 256 * memory[XREG + 1];
|
||||
wordptr = (unsigned short *)&memory[XREG];
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
case VM_LDAWORDIMM: /* Imm mode - push 16 bit value pointed to by addr after opcode */
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
wordptr = (unsigned short *)&memory[*wordptr]; /* Pointer to variable */
|
||||
++pc;
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
case VM_LDABYTE: /* Replaces X with 8 bit value pointed to by X. */
|
||||
CHECKUNDERFLOW(1);
|
||||
XREG = memory[XREG];
|
||||
break;
|
||||
case VM_STAWORD: /* Stores 16 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
case VM_LDABYTEIMM: /* Imm mode - push byte pointed to by addr after opcode */
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
byteptr = (unsigned char *)&memory[*wordptr]; /* Pointer to variable */
|
||||
++pc;
|
||||
XREG = *byteptr;
|
||||
break;
|
||||
case VM_STAWORD: /* Stores 16 bit value Y in addr pointed to by X. Drops X and Y.*/
|
||||
CHECKUNDERFLOW(2);
|
||||
memory[XREG] = YREG & 0x00ff;
|
||||
memory[XREG + 1] = (YREG & 0xff00) >> 8;
|
||||
wordptr = (unsigned short *)&memory[XREG];
|
||||
*wordptr = YREG;
|
||||
evalptr -= 2;
|
||||
break;
|
||||
case VM_STAWORDIMM: /* Imm mode - store 16 bit value X in addr after opcode. Drop X.*/
|
||||
CHECKUNDERFLOW(1);
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
wordptr = (unsigned short *)&memory[*wordptr]; /* Pointer to variable */
|
||||
++pc;
|
||||
*wordptr = XREG;
|
||||
--evalptr;
|
||||
break;
|
||||
case VM_STABYTE: /* Stores 8 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
CHECKUNDERFLOW(2);
|
||||
memory[XREG] = YREG;
|
||||
evalptr -= 2;
|
||||
break;
|
||||
case VM_STABYTEIMM: /* Imm mode - store 8 bit value X in addr after opcode. Drop X.*/
|
||||
CHECKUNDERFLOW(1);
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
byteptr = (unsigned char *)&memory[*wordptr]; /* Pointer to variable */
|
||||
++pc;
|
||||
*byteptr = XREG;
|
||||
--evalptr;
|
||||
break;
|
||||
/*
|
||||
* Relative to Frame Pointer addressing:
|
||||
* XREG points to address in system memory relative to the frame pointer.
|
||||
|
@ -376,9 +425,16 @@ void execute_instruction()
|
|||
printchar('\n');
|
||||
#endif
|
||||
|
||||
XREG =
|
||||
memory[(XREG + fp + 1) & 0xffff] +
|
||||
256 * memory[(XREG + fp + 2) & 0xffff];
|
||||
wordptr = (unsigned short *)&memory[(XREG + fp + 1) & 0xffff];
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
case VM_LDRWORDIMM: /* Imm mode - push 16 bit value pointed to by addr after opcode */
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
wordptr = (unsigned short *)&memory[(*wordptr + fp + 1) & 0xffff]; /* Pointer to variable */
|
||||
++pc;
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
case VM_LDRBYTE: /* Replaces X with 8 bit value pointed to by X. */
|
||||
CHECKUNDERFLOW(1);
|
||||
|
@ -395,17 +451,41 @@ void execute_instruction()
|
|||
|
||||
XREG = memory[(XREG + fp + 1) & 0xffff];
|
||||
break;
|
||||
case VM_LDRBYTEIMM: /* Imm mode - push byte pointed to by addr after opcode */
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
byteptr = (unsigned char *)&memory[(*wordptr + fp + 1) & 0xffff]; /* Pointer to variable */
|
||||
++pc;
|
||||
XREG = *byteptr;
|
||||
break;
|
||||
case VM_STRWORD: /* Stores 16 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
CHECKUNDERFLOW(2);
|
||||
memory[(XREG + fp + 1) & 0xffff] = YREG & 0x00ff;
|
||||
memory[(XREG + fp + 2) & 0xffff] = (YREG & 0xff00) >> 8;
|
||||
wordptr = (unsigned short *)&memory[(XREG + fp + 1) & 0xffff];
|
||||
*wordptr = YREG;
|
||||
evalptr -= 2;
|
||||
break;
|
||||
case VM_STRWORDIMM: /* Imm mode - store 16 bit value X in addr after opcode. Drop X.*/
|
||||
CHECKUNDERFLOW(1);
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
wordptr = (unsigned short *)&memory[(*wordptr + fp + 1) & 0xffff]; /* Pointer to variable */
|
||||
++pc;
|
||||
*wordptr = XREG;
|
||||
--evalptr;
|
||||
break;
|
||||
case VM_STRBYTE: /* Stores 8 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
CHECKUNDERFLOW(2);
|
||||
memory[(XREG + fp + 1) & 0xffff] = YREG;
|
||||
evalptr -= 2;
|
||||
break;
|
||||
case VM_STRBYTEIMM: /* Imm mode - store 8 bit value X in addr after opcode. Drop X.*/
|
||||
CHECKUNDERFLOW(1);
|
||||
wordptr = (unsigned short *)&memory[++pc]; /* Pointer to operand */
|
||||
byteptr = (unsigned char *)&memory[(*wordptr + fp + 1) & 0xffff]; /* Pointer to variable */
|
||||
++pc;
|
||||
*byteptr = XREG;
|
||||
--evalptr;
|
||||
break;
|
||||
/*
|
||||
* Manipulate evaluation stack
|
||||
*/
|
||||
|
@ -450,7 +530,8 @@ void execute_instruction()
|
|||
sp += 2;
|
||||
++evalptr;
|
||||
CHECKOVERFLOW();
|
||||
XREG = memory[sp - 1] + 256 * memory[sp];
|
||||
wordptr = (unsigned short *)&memory[sp - 1];
|
||||
XREG = *wordptr;
|
||||
break;
|
||||
case VM_POPBYTE: /* Pop 8 bit value from call stack, push onto eval stack [X] */
|
||||
CHECKSTACKUNDERFLOW(1);
|
||||
|
@ -467,11 +548,11 @@ void execute_instruction()
|
|||
printhex(sp - 1);
|
||||
printchar('\n');
|
||||
#endif
|
||||
|
||||
memory[sp] = (XREG & 0xff00) >> 8;
|
||||
byteptr = (unsigned char *)&XREG;
|
||||
memory[sp] = *(byteptr + 1);
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
memory[sp] = XREG & 0x00ff;
|
||||
memory[sp] = *byteptr;
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
--evalptr;
|
||||
|
@ -506,10 +587,11 @@ void execute_instruction()
|
|||
#endif
|
||||
|
||||
/* Push old FP to stack */
|
||||
memory[sp] = (fp & 0xff00) >> 8;
|
||||
byteptr = (unsigned char *)&fp;
|
||||
memory[sp] = *(byteptr + 1);
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
memory[sp] = fp & 0x00ff;
|
||||
memory[sp] = *byteptr;
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
|
||||
|
@ -531,7 +613,8 @@ void execute_instruction()
|
|||
CHECKSTACKUNDERFLOW(2);
|
||||
sp += 2;
|
||||
CHECKOVERFLOW();
|
||||
fp = memory[sp - 1] + 256 * memory[sp];
|
||||
wordptr = (unsigned short *)&memory[sp - 1];
|
||||
fp = *wordptr;
|
||||
|
||||
#ifdef DEBUGSTACK
|
||||
print(" Recovered FP ");
|
||||
|
@ -676,6 +759,11 @@ void execute_instruction()
|
|||
pc = XREG;
|
||||
--evalptr;
|
||||
return; /* Do not advance program counter */
|
||||
case VM_JMPIMM: /* Imm mode - jump to 16 bit word following opcode */
|
||||
wordptr = (unsigned short *)&memory[++pc];
|
||||
++pc;
|
||||
pc = *wordptr;
|
||||
return;
|
||||
case VM_BRNCH: /* If Y!= 0, jump to address X. Drop X, Y. */
|
||||
CHECKUNDERFLOW(2);
|
||||
if (YREG) {
|
||||
|
@ -685,22 +773,46 @@ void execute_instruction()
|
|||
}
|
||||
evalptr -= 2;
|
||||
return; /* Do not advance program counter */
|
||||
case VM_BRNCHIMM: /* Imm mode - if X!=0 branch to 16 bit word following opcode */
|
||||
wordptr = (unsigned short *)&memory[++pc];
|
||||
++pc;
|
||||
CHECKUNDERFLOW(1);
|
||||
if (XREG) {
|
||||
pc = *wordptr;
|
||||
} else {
|
||||
++pc;
|
||||
}
|
||||
--evalptr;
|
||||
return; /* Do not advance program counter */
|
||||
case VM_JSR: /* Push PC to call stack. Jump to address X. Drop X. */
|
||||
CHECKUNDERFLOW(1);
|
||||
byteptr = (unsigned char *) &pc;
|
||||
memory[sp] = *byteptr;
|
||||
memory[sp] = *(byteptr + 1);
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
memory[sp] = *(byteptr + 1);
|
||||
memory[sp] = *byteptr;
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
pc = XREG;
|
||||
--evalptr;
|
||||
return; /* Do not advance program counter */
|
||||
case VM_JSRIMM: /* Imm mode - push PC to calls stack, jump to 16 bit word */
|
||||
wordptr = (unsigned short *)&memory[++pc];
|
||||
++pc;
|
||||
byteptr = (unsigned char *) &pc;
|
||||
memory[sp] = *(byteptr + 1);
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
memory[sp] = *byteptr;
|
||||
--sp;
|
||||
CHECKSTACKOVERFLOW();
|
||||
pc = *wordptr;
|
||||
return; /* Do not advance program counter */
|
||||
case VM_RTS: /* Pop call stack, jump to the address popped. */
|
||||
CHECKSTACKUNDERFLOW(2);
|
||||
++sp;
|
||||
pc = 256 * memory[sp] + memory[sp + 1];
|
||||
wordptr = (unsigned short *)&memory[sp];
|
||||
pc = *wordptr;
|
||||
++sp;
|
||||
break;
|
||||
/*
|
||||
|
@ -775,14 +887,25 @@ void execute()
|
|||
|
||||
/*
|
||||
* Load bytecode into memory[].
|
||||
* TODO: This is POSIX-only at the moment. Need to add CBM support.
|
||||
*/
|
||||
void load()
|
||||
{
|
||||
FILE *fp;
|
||||
char ch;
|
||||
char *p = (char*)&memory[RTPCSTART];
|
||||
|
||||
pc = RTPCSTART;
|
||||
fp = fopen("bytecode", "r");
|
||||
do {
|
||||
print("\nBytecode file (CR for default)>");
|
||||
getln(p, 15);
|
||||
if (strlen(p) == 0) {
|
||||
strcpy(p, "bytecode");
|
||||
}
|
||||
print("Loading '");
|
||||
print(p);
|
||||
print("'\n");
|
||||
fp = fopen(p, "r");
|
||||
} while (!fp);
|
||||
while (!feof(fp)) {
|
||||
ch = fgetc(fp);
|
||||
memory[pc++] = ch;
|
||||
|
@ -807,7 +930,7 @@ int main()
|
|||
print("(c)Bobbi, 2018\n");
|
||||
print("Free Software.\n");
|
||||
print("Licenced under GPL.\n\n");
|
||||
print("Loading bytecode: ");
|
||||
|
||||
load();
|
||||
#ifdef __GNUC__
|
||||
print(" Done.\n\n");
|
||||
|
|
|
@ -74,14 +74,22 @@ enum bytecode {
|
|||
VM_LDIMM, /* Pushes the following 16 bit word to the evaluation stack */
|
||||
/* Absolute addressing: */
|
||||
VM_LDAWORD, /* Replaces X with 16 bit value pointed to by X. */
|
||||
VM_LDAWORDIMM, /* Imm mode - push 16 bit value pointed to by addr after opcode */
|
||||
VM_LDABYTE, /* Replaces X with 8 bit value pointed to by X. */
|
||||
VM_LDABYTEIMM, /* Imm mode - push byte pointed to by addr after opcode */
|
||||
VM_STAWORD, /* Stores 16 bit value Y in addr pointed to by X. Drops X and Y.*/
|
||||
VM_STAWORDIMM, /* Imm mode - store 16 bit value X in addr after opcode. Drop X.*/
|
||||
VM_STABYTE, /* Stores 8 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
VM_STABYTEIMM, /* Imm mode - store 8 bit value X in addr after opcode. Drop X. */
|
||||
/* Relative to Frame Pointer addressing: */
|
||||
VM_LDRWORD, /* Replaces X with 16 bit value pointed to by X. */
|
||||
VM_LDRWORDIMM, /* Imm mode - push 16 bit value pointed to by addr after opcode */
|
||||
VM_LDRBYTE, /* Replaces X with 8 bit value pointed to by X. */
|
||||
VM_LDRBYTEIMM, /* Imm mode - push byte pointed to by addr after opcode */
|
||||
VM_STRWORD, /* Stores 16 bit value Y in addr pointed to by X. Drops X and Y.*/
|
||||
VM_STRWORDIMM, /* Imm mode - store 16 bit value X in addr after opcode. Drop X.*/
|
||||
VM_STRBYTE, /* Stores 8 bit value Y in addr pointed to by X. Drops X and Y. */
|
||||
VM_STRBYTEIMM, /* Imm mode - store 16 bit value X in addr after opcode. Drop X.*/
|
||||
/**** Manipulate evaluation stack ***********************************************************/
|
||||
VM_SWAP, /* Swaps X and Y */
|
||||
VM_DUP, /* Duplicates X -> X, Y */
|
||||
|
@ -128,8 +136,11 @@ enum bytecode {
|
|||
VM_RSH, /* X = Y>>X. Y is dropped. */
|
||||
/**** Flow control **************************************************************************/
|
||||
VM_JMP, /* Jump to address X. Drop X. */
|
||||
VM_JMPIMM, /* Imm mode - jump to 16 bit word following opcode */
|
||||
VM_BRNCH, /* If Y!= 0, jump to address X. Drop X, Y. */
|
||||
VM_BRNCHIMM, /* Imm mode - if X!=0 branch to 16 bit word following opcode */
|
||||
VM_JSR, /* Push PC to call stack. Jump to address X. Drop X. */
|
||||
VM_JSRIMM, /* Imm mode - push PC to call stack, jump to 16 bit word */
|
||||
VM_RTS, /* Pop call stack, jump to the address popped. */
|
||||
/**** Input / Output ************************************************************************/
|
||||
VM_PRDEC, /* Print 16 bit decimal in X. Drop X */
|
||||
|
@ -143,69 +154,6 @@ enum bytecode {
|
|||
/********************************************************************************************/
|
||||
};
|
||||
|
||||
/* Order must match enum bytecode */
|
||||
char *bytecodenames[] = {
|
||||
"END",
|
||||
"LDI",
|
||||
"LDAW",
|
||||
"LDAB",
|
||||
"STAW",
|
||||
"STAB",
|
||||
"LDRW",
|
||||
"LDRB",
|
||||
"STRW",
|
||||
"STRB",
|
||||
"SWP",
|
||||
"DUP",
|
||||
"DUP2",
|
||||
"DRP",
|
||||
"OVER",
|
||||
"PICK",
|
||||
"POPW",
|
||||
"POPB",
|
||||
"PSHW",
|
||||
"PSHB",
|
||||
"DISC",
|
||||
"SPFP",
|
||||
"FPSP",
|
||||
"ATOR",
|
||||
"RTOA",
|
||||
"INC",
|
||||
"DEC",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"NEG",
|
||||
"GT",
|
||||
"GTE",
|
||||
"LT",
|
||||
"LTE",
|
||||
"EQL",
|
||||
"NEQL",
|
||||
"AND",
|
||||
"OR",
|
||||
"NOT",
|
||||
"BAND",
|
||||
"BOR",
|
||||
"BXOR",
|
||||
"BNOT",
|
||||
"LSH",
|
||||
"RSH",
|
||||
"JMP",
|
||||
"BRC",
|
||||
"JSR",
|
||||
"RTS",
|
||||
"PRDEC",
|
||||
"PRHEX",
|
||||
"PRCH",
|
||||
"PRSTR",
|
||||
"PRMSG",
|
||||
"KBDCH",
|
||||
"KBDLN"
|
||||
};
|
||||
|
||||
#ifdef A2E
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue