/**************************************************************************/ /* EightBall Virtual Machine */ /* */ /* 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 */ /* */ /* Note that this code assumes that sizeof(int) = sizeof(int*), which is */ /* true for 6502 (16 bits each) and i686 (32 bits each) - but not amd64 */ /* */ /* 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 */ /* Reference implementation of EightBall Virtual Machine. */ /* */ /* This is not intended to be optimized for speed. I plan to implement */ /* an optimized version in 6502 assembler later. */ /* */ /**************************************************************************/ /**************************************************************************/ /* 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 . */ /* */ /**************************************************************************/ /* * EIGHTBALL VIRTUAL MACHINE DEFINITION - Compilation Target * * Simple stack based VM with EVALUATION STACK of 16 bit integer values. * It doesn't need to be too big - 16 words is a reasonable size for this * stack. * * For convenience define this shorthand: * [ X ] Top of stack * [ Y ] Second * [ Z ] Third * [ T ] Fourth * * There is a separate CALL STACK which is used for argument passing, * subroutine call and return and for local variables. This stack can grow * very large, especially if you allocate big local arrays. The call stack * allows either 16 bit words or bytes to be pushed or popped. * * The virtual machine also has the following three 16 bit registers: * Program Counter (Referred to as rtPC in the compiler code) * Stack Pointer (Referred to as rtSP in the compiler code) * Points to free memory one 'above' the top of the call stack. * Frame Pointer (points to stack pointer on entry to subroutine. Used to make * it easier to refer to local variables allocated on the call * stack, and also to release the locals on return from sub.) */ enum bytecode { /**** Miscellaneous *************************************************************************/ VM_END, /* Terminate execution */ /**** Load Immediate ************************************************************************/ 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 */ VM_DUP2, /* Duplicates X -> X,Z; Y -> Y,T */ VM_DROP, /* Drops X */ VM_OVER, /* Duplicates Y -> X,Z */ VM_PICK, /* Duplicates stack level specified in X+1 -> X */ /**** Manipulate call stack *****************************************************************/ VM_POPWORD, /* Pop 16 bit value from call stack, push onto eval stack [X] */ VM_POPBYTE, /* Pop 8 bit value from call stack, push onto eval stack [X] */ VM_PSHWORD, /* Push 16 bit value in X onto call stack. Drop X. */ VM_PSHBYTE, /* Push 8 bit value in X onto call stack. Drop X. */ VM_DISCARD, /* Discard X bytes from call stack. Drop X. */ VM_SPTOFP, /* Copy stack pointer to frame pointer. (Enter function scope) */ VM_FPTOSP, /* Copy frame pointer to stack pointer. (Release local vars) */ VM_ATOR, /* Convert absolute address in X to relative address */ VM_RTOA, /* Convert relative address in X to absolute address */ /**** Integer math **************************************************************************/ VM_INC, /* X = X+1. */ VM_DEC, /* X = X-1. */ VM_ADD, /* X = Y+X. Y is dropped. */ VM_SUB, /* X = Y-X. Y is dropped. */ VM_MUL, /* X = Y*X. Y is dropped. */ VM_DIV, /* X = Y/X. Y is dropped. */ VM_MOD, /* X = Y%X. Y is dropped . */ VM_NEG, /* X = -X */ /**** Comparisons ***************************************************************************/ VM_GT, /* X = Y>X. Y is dropped. */ VM_GTE, /* X = Y>=X. Y is dropped. */ VM_LT, /* 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 */ VM_PRHEX, /* Print 16 bit hex in X. Drop X */ VM_PRCH, /* Print character in X. Drop X */ VM_PRSTR, /* Print null terminated string pointed to by X. Drop X */ VM_PRMSG, /* Print literal string at PC (null terminated) */ VM_KBDCH, /* Push character from keyboard onto eval stack */ VM_KBDLN /* Obtain line from keyboard and write to memory pointed to by */ /* Y. X contains the max number of bytes in buf. Drop X, Y. */ /********************************************************************************************/ }; #ifdef A2E /* * Apple II Enhanced */ #define RTCALLSTACKTOP 0xb7ff #define RTCALLSTACKLIM 0x9800 #define RTPCSTART 0x5000 /* TBC */ #elif defined(C64) /* * C64 */ #define RTCALLSTACKTOP 0xbfff #define RTCALLSTACKLIM 0xa000 #define RTPCSTART 0x3000 /* TBC */ #elif defined(VIC20) /* * VIC-20: */ #define RTCALLSTACKTOP 0xbfff #define RTCALLSTACKLIM 0xa000 #define RTPCSTART 0x4000 /* TBC */ #elif defined(__GNUC__) /* * Linux */ //#define RTCALLSTACKTOP 64 * 1024 - 1 //#define RTCALLSTACKTOP 48 * 1024 - 1 // FOR TESTING #define RTCALLSTACKTOP 0xb7ff //#define RTCALLSTACKLIM 32 * 1024 #define RTCALLSTACKLIM 0x9800 //#define RTPCSTART 0 #define RTPCSTART 0x5000 // SO THINGS WORK ON APPLE II :) #endif