/*
+ * msp4th
*
- * TODO:
- * - use enum for VM opcodes
+ * forth-like interpreter for the msp430
+ *
+ * Source originally from Mark Bauer, beginning life in the z80 and earlier.
+ * Nathan Schemm used and modified for use in his series of msp430-compatible
+ * processors.
*
- * X speed up pop/pushMathStack (need bounds check??)
- * only bounds check is for mathStack underflow
+ * This version by Dan White (2013). Cleaned-up, expanded, and given
+ * capabilities to allow live re-configuration and directly calling user C
+ * functions.
*
- * X UART usage is blocking, convert to interrupt-based
- * user may provide msp4th_{putchar,getchar} function pointers
- * the default one remains blocking
+ * * Used in Dan's "atoi" chip loaded from flash into RAM.
+ * * Fabbed in ROM as part of the Gharzai/Schmitz "piranha" imager chip.
*
- * X allow configurable user-code space
- * mathStack[], addrStack[]
- * prog[], cmdList[], progOpcodes[]
- * array locations come from a vector table instead of hard-coded
- * locations in bss space. init_msp4th() populates the pointers
- * with values from the table (which can be user-written to effect
- * changing user code sizes.
+ * If cpp symbol "MSP430" is not defined, it compiles to a version for testing
+ * on PC as a console program via the setup and main() in "test430.c".
+ *
+ * TODO:
+ * - use enum/symbols for VM opcodes (?builtins tagged as negative numbers?)
*
*/
#if defined(MSP430)
#include "ns430.h"
#include "ns430-atoi.h"
+
// our "special" pointer, direct word access to all absolute address space
#define dirMemory ((int16_t *) 0)
#else
// mixins to test msp4th on PC
#include <stdint.h>
-
-// the OS does not like direct memory addressing, something about
-// segfaulting... just create a sham space for testing
+// the OS does not like direct, absolute, memory addressing...
+// just create a sham space for testing
int16_t dirMemory[65536];
+
#endif
#include "msp4th.h"
+/****************************************************************************
+ *
+ * Module-level global variables (in RAM)
+ *
+ ***************************************************************************/
+int16_t xit; // set to 1 to kill program
+int16_t echo; // boolean: false -> no interactive echo/prompt
+uint16_t progCounter; // value determines builtin/user and opcode to execute
-/*
- * Local function prototypes
+int16_t lineBufferIdx; // input line buffer index
+int16_t progIdx; // next open space for user opcodes
+int16_t cmdListIdx; // next open space for user word strings
+
+/* The following allow re-configuring the location/size of all configurable
+ * arrays. Then the stack sizes and user program space sizes can be
+ * (re-)specified by changing the table and calling init_msp4th() again. See
+ * test430.c for a configuration example.
+ *
+ * THE ONLY BOUNDARY CHECKS ARE:
+ * - math stack underflow
+ * - line buffer overflow
+ * - word buffer overflow
+ *
+ * The user is therefore responsible for:
+ * - keeping stack depths in range
+ * - not underflowing the address stack
+ * - allocating sufficient space in prog[], progOpcodes[], cmdList[]
*/
-void msp4th_puts(uint8_t *s);
-uint8_t getKeyB(void);
-void getLine(void);
-uint8_t nextPrintableChar(void);
-uint8_t skipStackComment(void);
-void getWord(void);
-void listFunction(void);
-int16_t popMathStack(void);
-void pushMathStack(int16_t n);
-int16_t popAddrStack(void);
-void pushAddrStack(int16_t n);
-void ndropFunc(void);
-int16_t lookupToken(uint8_t *x, uint8_t *l);
-void luFunc(void);
-void numFunc(void);
-void ifFunc(int16_t x);
-void loopFunc(int16_t n);
-void rollFunc(int16_t n);
-void pushnFunc(void);
-void overFunc(void);
-void dfnFunc(void);
-void printNumber(int16_t n);
-void printHexChar(int16_t n);
-void printHexByte(int16_t n);
-void printHexWord(int16_t n);
-void execN(int16_t n);
-void execFunc(void);
+#if defined(MSP430)
+/* Register notes.
+ * r0-3 are special (pc, sp, sr, cg)
+ * In mspgcc:
+ * r4-5 are (sometimes) used as frame and argument pointers.
+ * r12-15 are call clobbered.
+ * * Function arguments are allocated left-to-right assigned r15--r12, others
+ * are passed on the stack.
+ * * Return values are passed in r15, r15:r14, r15:r14:r13, or r15:r14:r13:r12
+ * depending on size.
+ */
+int16_t register *mathStackPtr asm("r6"); // used enough to keep in registers
+int16_t register *addrStackPtr asm("r7");
+#else
+int16_t *mathStackPtr;
+int16_t *addrStackPtr;
+#endif
+
+int16_t *mathStackStart; // original locations for calculating stack depth
+int16_t *addrStackStart;
+
+int16_t *prog; // user programs (opcodes) are placed here
+
+int16_t *progOpcodes; // mapping between cmdList word index and program
+ // opcodes start index into prog[]
+
+uint8_t *cmdList; // string containing user-defined words
+
+uint8_t *lineBuffer; // where interactive inputs are buffered
+int16_t lineBufferLength;// to prevent overflow
+
+uint8_t *wordBuffer; // the currently-parsed word
+int16_t wordBufferLength;
+
+void (*msp4th_putchar)(uint8_t c); // the sole interactive output avenue
+uint8_t (*msp4th_getchar)(void); // the sole interactive input avenue
+void (*msp4th_puts)(uint8_t *s); // the sole interactive output avenue
+
+
/****************************************************************************
"call2 call3 call4 ndrop swpb " // 61 -> 65
"+! roll pick tuck max " // 66 -> 70
"min s. sh. neg echo " // 71 -> 75
+ "init " // 76
};
-#define LAST_PREDEFINED 75 // update this when we add commands to the built in list
+#define LAST_PREDEFINED 76 // update this when we add commands to the built in list
// these commands are interps
};
+
/****************************************************************************
*
- * Module-level global variables (in RAM)
+ * Local function prototypes
*
***************************************************************************/
-int16_t xit; // set to 1 to kill program
-int16_t echo; // boolean: false -> no interactive echo/prompt
-
-uint16_t progCounter;
+uint8_t getKeyB(void);
+void getLine(void);
+uint8_t nextPrintableChar(void);
+uint8_t skipStackComment(void);
+void getWord(void);
+void listFunction(void);
+int16_t popMathStack(void);
+void pushMathStack(int16_t n);
+int16_t popAddrStack(void);
+void pushAddrStack(int16_t n);
+void ndropFunc(void);
+int16_t lookupToken(uint8_t *x, uint8_t *l);
+void luFunc(void);
+void numFunc(void);
+void ifFunc(int16_t x);
+void loopFunc(int16_t n);
+void rollFunc(int16_t n);
+void pushnFunc(void);
+void overFunc(void);
+void dfnFunc(void);
+void printNumber(int16_t n);
+void printHexChar(int16_t n);
+void printHexByte(int16_t n);
+void printHexWord(int16_t n);
+void execN(int16_t n);
+void execFunc(void);
-int16_t lineBufferIdx; /* input line buffer index */
-int16_t progIdx; // next open space for user opcodes
-int16_t cmdListIdx; // next open space for user word strings
+/****************************************************************************
+ *
+ * Public functions
+ *
+ ***************************************************************************/
-/* The following utilize a vector table to allow re-configuring the
- * location/size of these arrays. Then the stack sizes and user program space
- * sizes can be (re-)specified by changing the table and calling init_msp4th()
- * again.
- */
-#if defined(MSP430)
-int16_t register *mathStackPtr asm("r6");
-int16_t register *addrStackPtr asm("r7");
-#else
-int16_t *mathStackPtr;
-int16_t *addrStackPtr;
-#endif
+void msp4th_init(struct msp4th_config *c)
+{
+ /*
+ * Get addresses of user-configurable arrays from the pre-known vector
+ * table locations.
+ *
+ * Changing the values in the msp4th_* locations and calling
+ * init_msp4th() again restarts the interpreter with the new layout;
+ */
+ mathStackPtr = c->mathStackStart;
+ addrStackPtr = c->addrStackStart;
+ mathStackStart = c->mathStackStart;
+ addrStackStart = c->addrStackStart;
+ prog = c->prog;
+ progOpcodes = c->progOpcodes;
+ cmdList = c->cmdList;
+ lineBuffer = c->lineBuffer;
+ lineBufferLength = c->lineBufferLength;
+ wordBuffer = c->wordBuffer;
+ wordBufferLength = c->wordBufferLength;
+ msp4th_putchar = c->putchar;
+ msp4th_getchar = c->getchar;
+ msp4th_puts = c->puts;
-int16_t *mathStackStart;
-int16_t *addrStackStart;
-int16_t *prog; // user programs (opcodes) are placed here
+ xit = 0;
+ echo = 1;
+ progCounter = 10000;
+ progIdx = 1; // this will be the first opcode
+ cmdListIdx = 0;
-int16_t *progOpcodes; // mapping between user word index and program opcodes
- // start index into prog[]
+ lineBufferIdx = 0;
+ msp4th_puts((uint8_t *)"msp4th!");
+}
-uint8_t *cmdList;
-uint8_t *lineBuffer;
-int16_t lineBufferLength;
-uint8_t *wordBuffer;
-int16_t wordBufferLength;
-void (*msp4th_putchar)(uint8_t c);
-uint8_t (*msp4th_getchar)(void);
+void msp4th_processLoop() // this processes the forth opcodes.
+{
+ uint16_t opcode;
+ uint16_t tmp;
+ while(xit == 0){
+ if(progCounter > 9999){
+ tmp = progCounter - 10000;
+ opcode = progBi[tmp];
+ } else {
+ opcode = prog[progCounter];
+ }
+ progCounter = progCounter + 1;
+ if(opcode > 19999){
+ // this is a built in opcode
+ execN(opcode - 20000);
+ } else {
+ pushAddrStack(progCounter);
+ progCounter = progOpcodes[opcode];
+ }
+ } // while ()
+}
-void msp4th_puts(uint8_t *s)
-{
- uint16_t i = 0;
- uint8_t c = 1;
- while (c != 0) {
- c = s[i++];
- msp4th_putchar(c);
- }
- msp4th_putchar('\r');
- msp4th_putchar('\n');
-}
uint8_t getKeyB()
} else if ( ((c == 255) || (c == '\ 4')) && (lineBufferIdx == 0)) {
xit = 1;
waiting = 0;
+ // add a sham word so we make it back to processLoop() to exit
lineBuffer[lineBufferIdx++] = 'x';
lineBuffer[lineBufferIdx] = ' ';
} else {
}
-#define ATOS (addrStackPtr)
+#define ATOS (*addrStackPtr)
#define ANOS (*(addrStackPtr + 1))
#define ASTACK(n) (*(addrStackPtr + n))
void loopFunc(int16_t n)
{
- int16_t j, k, m;
-
- j = popAddrStack(); // loop address
- k = popAddrStack(); // count
- m = popAddrStack(); // limit
- k = k + n; // up the count
-
- if ( ! ( ((n > 0) && (k >= m))
- || ((n < 0) && (k <= m)))) {
- // put it all back and loop
- pushAddrStack(m);
- pushAddrStack(k);
- pushAddrStack(j);
+#define j ATOS // loop address
+#define k ANOS // count
+#define m ASTACK(2) // limit
+
+ ANOS += n; // inc/dec the count
+
+ if ( ! ( ((n >= 0) && (k >= m))
+ || ((n <= 0) && (k <= m)))) {
+ // loop
progCounter = j;
+ } else {
+ // done, cleanup
+ popAddrStack();
+ popAddrStack();
+ popAddrStack();
}
+#undef j
+#undef k
+#undef m
}
"mov r14, 0(%[ms])\n"
: /* outputs */
: [ms] "r" (mathStackPtr) /* inputs */
- : /* clobbers */
+ : /* clobbers */ "r10","r11","r12","r13","r14"
);
#else
i = NOS;
case 16: // abs ( a -- |a| ) \ -32768 is unchanged
if (TOS < 0) {
- TOS = ~TOS + 1;
+ TOS = -TOS;
}
break;
MPYS = popMathStack();
OP2 = NOS;
x = (((int32_t)RESHI << 16) | RESLO);
+ asm("eint");
x = (x / TOS);
popMathStack();
TOS = (int16_t)(x & 0xffff);
- asm("eint");
#else
i = popMathStack();
j = TOS;
case 75: // echo ( bool -- ) \ ?echo prompts and terminal input?
echo = popMathStack();
+ break;
+
+ case 76: // init ( &config -- ) \ clears buffers and calls msp4th_init
+#if defined(MSP430)
+ //saves 2 words of ROM
+ asm("mov.w #0, %[b](r3)\n"
+ : /* outputs */
+ : [b] "m" (lineBuffer) /* inputs */
+ : /* clobbers */
+ );
+#else
+ *lineBuffer = 0;
+#endif
+ msp4th_init((struct msp4th_config *)popMathStack());
+ break;
default:
break;
-/*
- * Public function prototypes
- */
-
-void msp4th_init(struct msp4th_config *c)
-{
- /*
- * Get addresses of user-configurable arrays from the pre-known vector
- * table locations.
- *
- * Changing the values in the msp4th_* locations and calling
- * init_msp4th() again restarts the interpreter with the new layout;
- */
- mathStackPtr = c->mathStackStart;
- addrStackPtr = c->addrStackStart;
- mathStackStart = c->mathStackStart;
- addrStackStart = c->addrStackStart;
- prog = c->prog;
- progOpcodes = c->progOpcodes;
- cmdList = c->cmdList;
- lineBuffer = c->lineBuffer;
- lineBufferLength = c->lineBufferLength;
- wordBuffer = c->wordBuffer;
- wordBufferLength = c->wordBufferLength;
- msp4th_putchar = c->putchar;
- msp4th_getchar = c->getchar;
-
-
- xit = 0;
- echo = 1;
- progCounter = 10000;
- progIdx = 1; // this will be the first opcode
- cmdListIdx = 0;
-
- lineBufferIdx = 0;
- msp4th_puts((uint8_t *)"msp4th!");
-}
-
-
-void msp4th_processLoop() // this processes the forth opcodes.
-{
- uint16_t opcode;
- uint16_t tmp;
-
- while(xit == 0){
- if(progCounter > 9999){
- tmp = progCounter - 10000;
- opcode = progBi[tmp];
- } else {
- opcode = prog[progCounter];
- }
-
- progCounter = progCounter + 1;
-
- if(opcode > 19999){
- // this is a built in opcode
- execN(opcode - 20000);
- } else {
- pushAddrStack(progCounter);
- progCounter = progOpcodes[opcode];
- }
- } // while ()
-}
-