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:
Bobbi Webber-Manners 2018-05-21 13:58:41 -04:00 committed by GitHub
parent 13df8be29f
commit 6dbf45c39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 605 additions and 171 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

BIN
disass Normal file

Binary file not shown.

282
disass.c Normal file
View File

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

Binary file not shown.

BIN
eightball

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -37,7 +37,7 @@
/* */
/**************************************************************************/
#define VERSIONSTR "0.69"
#define VERSIONSTR "0.70"
void print(char *str);

Binary file not shown.

View File

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

View File

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

BIN
test.d64

Binary file not shown.

BIN
test.dsk

Binary file not shown.