diff --git a/8ball20.prg b/8ball20.prg index 719cda9..beb50df 100644 Binary files a/8ball20.prg and b/8ball20.prg differ diff --git a/8ball64.prg b/8ball64.prg index aa16f05..8145304 100644 Binary files a/8ball64.prg and b/8ball64.prg differ diff --git a/8ballvm20.prg b/8ballvm20.prg index ddc45af..0ca0e93 100644 Binary files a/8ballvm20.prg and b/8ballvm20.prg differ diff --git a/8ballvm64.prg b/8ballvm64.prg index c176f6f..d3aff7b 100644 Binary files a/8ballvm64.prg and b/8ballvm64.prg differ diff --git a/Makefile b/Makefile index d48f74d..484bc54 100644 --- a/Makefile +++ b/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.8bp # ASCII -> PETSCII + tr {} [] unittest.8bp # ASCII -> PETSCII sieve4.8bp: sieve4.8b tr {} [] sieve4.8bp # ASCII -> PETSCII diff --git a/disass b/disass new file mode 100644 index 0000000..549501f Binary files /dev/null and b/disass differ diff --git a/disass.c b/disass.c new file mode 100644 index 0000000..b0c9652 --- /dev/null +++ b/disass.c @@ -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 . */ +/* */ +/**************************************************************************/ + +#include "eightballvm.h" +#include "eightballutils.h" + +#include +#include +#include + +#ifdef A2E +#include +#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; +} diff --git a/ebvm.system b/ebvm.system index f7e3d79..5a0d27a 100644 Binary files a/ebvm.system and b/ebvm.system differ diff --git a/eightball b/eightball index 6d0625e..7328ff8 100644 Binary files a/eightball and b/eightball differ diff --git a/eightball.c b/eightball.c index 7080a53..3ce3333 100644 --- a/eightball.c +++ b/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; } } diff --git a/eightball.system b/eightball.system index db056e0..1e57418 100644 Binary files a/eightball.system and b/eightball.system differ diff --git a/eightballutils.h b/eightballutils.h index c54370e..37215a3 100644 --- a/eightballutils.h +++ b/eightballutils.h @@ -37,7 +37,7 @@ /* */ /**************************************************************************/ -#define VERSIONSTR "0.69" +#define VERSIONSTR "0.70" void print(char *str); diff --git a/eightballvm b/eightballvm index c3b5a07..0067626 100644 Binary files a/eightballvm and b/eightballvm differ diff --git a/eightballvm.c b/eightballvm.c index 77deb57..40e43a8 100644 --- a/eightballvm.c +++ b/eightballvm.c @@ -62,6 +62,7 @@ #include #include +#include #ifdef A2E #include @@ -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"); diff --git a/eightballvm.h b/eightballvm.h index 2cd31c6..1477c29 100644 --- a/eightballvm.h +++ b/eightballvm.h @@ -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 /* diff --git a/test.d64 b/test.d64 index e607c3a..06ed87e 100644 Binary files a/test.d64 and b/test.d64 differ diff --git a/test.dsk b/test.dsk index 6c75c4a..b363707 100644 Binary files a/test.dsk and b/test.dsk differ