diff --git a/.gitignore b/.gitignore index 2ea588d..6dbd7b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.a *.o sprit +msp430 diff --git a/compat.txt b/compat.txt new file mode 100644 index 0000000..3a06bbb --- /dev/null +++ b/compat.txt @@ -0,0 +1,133 @@ +yes 6.1.0010 ! + 6.1.0030 # + 6.1.0040 #> + 6.1.0050 #S +yes 6.1.0070 ' +yes 6.1.0080 ( +yes 6.1.0090 * + 6.1.0100 */ + 6.1.0110 */MOD +yes 6.1.0120 + +yes 6.1.0130 +! +yes 6.1.0140 +LOOP +yes 6.1.0150 , +yes 6.1.0160 - +yes 6.1.0180 . +yes 6.1.0190 ." +yes 6.1.0230 / + 6.1.0240 /MOD +yes 6.1.0250 0< +yes 6.1.0270 0= +yes 6.1.0290 1+ +yes 6.1.0300 1- +yes 6.1.0310 2! +yes 6.1.0320 2* +yes 6.1.0330 2/ +yes 6.1.0350 2@ +yes 6.1.0370 2DROP +yes 6.1.0380 2DUP +yes 6.1.0400 2OVER +yes 6.1.0430 2SWAP +yes 6.1.0450 : +yes 6.1.0460 ; +yes 6.1.0480 < + 6.1.0490 <# +yes 6.1.0530 = +yes 6.1.0540 > + 6.1.0550 >BODY + 6.1.0560 >IN + 6.1.0570 >NUMBER +yes 6.1.0580 >R +yes 6.1.0630 ?DUP +yes 6.1.0650 @ + 6.1.0670 ABORT + 6.1.0680 ABORT" +yes 6.1.0690 ABS +yes 6.1.0695 ACCEPT +yes 6.1.0705 ALIGN +yes 6.1.0706 ALIGNED +yes 6.1.0710 ALLOT +yes 6.1.0720 AND + 6.1.0750 BASE +yes 6.1.0760 BEGIN +yes 6.1.0770 BL +yes 6.1.0850 C! +yes 6.1.0860 C, +yes 6.1.0870 C@ +yes 6.1.0880 CELL+ +yes 6.1.0890 CELLS +yes 6.1.0895 CHAR +yes 6.1.0897 CHAR+ +yes 6.1.0898 CHARS +yes 6.1.0950 CONSTANT +yes 6.1.0980 COUNT +yes 6.1.0990 CR +yes 6.1.1000 CREATE + 6.1.1170 DECIMAL + 6.1.1200 DEPTH +yes 6.1.1240 DO +yes 6.1.1250 DOES> +yes 6.1.1260 DROP +yes 6.1.1290 DUP +yes 6.1.1310 ELSE +yes 6.1.1320 EMIT +yes 6.1.1345 ENVIRONMENT? + 6.1.1360 EVALUATE +yes 6.1.1370 EXECUTE +yes 6.1.1380 EXIT +yes 6.1.1540 FILL + 6.1.1550 FIND + 6.1.1561 FM/MOD +yes 6.1.1650 HERE + 6.1.1670 HOLD +yes 6.1.1680 I +yes 6.1.1700 IF +yes 6.1.1710 IMMEDIATE +yes 6.1.1720 INVERT +yes 6.1.1730 J +yes 6.1.1750 KEY +yes 6.1.1760 LEAVE +yes 6.1.1780 LITERAL +yes 6.1.1800 LOOP +yes 6.1.1805 LSHIFT + 6.1.1810 M* +yes 6.1.1870 MAX +yes 6.1.1880 MIN +yes 6.1.1890 MOD +yes 6.1.1900 MOVE +yes 6.1.1910 NEGATE +yes 6.1.1980 OR +yes 6.1.1990 OVER +yes 6.1.2033 POSTPONE + 6.1.2050 QUIT +yes 6.1.2060 R> +yes 6.1.2070 R@ +yes 6.1.2120 RECURSE +yes 6.1.2140 REPEAT +yes 6.1.2160 ROT +yes 6.1.2162 RSHIFT +yes 6.1.2165 S" + 6.1.2170 S>D + 6.1.2210 SIGN + 6.1.2214 SM/REM + 6.1.2216 SOURCE +yes 6.1.2220 SPACE +yes 6.1.2230 SPACES +yes 6.1.2250 STATE +yes 6.1.2260 SWAP +yes 6.1.2270 THEN +yes 6.1.2310 TYPE + 6.1.2320 U. + 6.1.2340 U< + 6.1.2360 UM* + 6.1.2370 UM/MOD +yes 6.1.2380 UNLOOP +yes 6.1.2390 UNTIL +yes 6.1.2410 VARIABLE +yes 6.1.2430 WHILE +yes 6.1.2450 WORD +yes 6.1.2490 XOR +yes 6.1.2500 [ +yes 6.1.2510 ['] +yes 6.1.2520 [CHAR] +yes 6.1.2540 ] diff --git a/msp430.cpp b/msp430.cpp new file mode 100644 index 0000000..6c92f47 --- /dev/null +++ b/msp430.cpp @@ -0,0 +1,353 @@ +// sprit-forth: A portable subroutine-threaded Forth. +// Copyright (C) 2023 Clyne Sullivan +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Library General Public License as published by +// the Free Software Foundation; either version 2 of the License, or (at your +// option) any later version. +// +// This library 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 Library General Public License for +// more details. +// +// You should have received a copy of the GNU Library General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +#include + +#include + +#include "core.hpp" +#include "parse.hpp" +#include "state.hpp" +#include "types.hpp" + +using DoubleCell = Cell; + +static char strbuf[80]; + +static void serput(int c); +static void serputs(const char *s); +static void printint(DoubleCell n, int base); + +static void initMCU(); +static void initGPIO(); +static void initClock(); +static void initUART(); +static void Software_Trim(); +#define MCLK_FREQ_MHZ (16) + +static void doparse(); + +// TODO: +// sys m* _/ _% _' depth _rdepth _in _ev find _uma u< um/mod + +static void peek() { *sp() = *(Cell *)(*sp()); } +static void commaSP() { comma(pop()); } +static void discard() { auto v = pop(); (void)v; } +static void tobool() { if (*sp()) *sp() = -1; } + +constexpr WordSet words ( + Word("[", WordWrap<[] { STATE = 0; }>).markImmediate(), + Word("]", WordWrap<[] { STATE = -1; }>), + Word("@", WordWrap), + Word("c@", WordWrap), + Word("!", WordWrap<[] { auto a = (Cell *)pop(); *a = pop(); }>), + Word("c!", WordWrap<[] { auto a = (char *)pop(); *a = pop(); }>), + Word("_d", WordWrap<[] { *sp() += (Cell)DICT.data(); }>), + Word("_jmp", WordWrap<[] { jump((FuncList)*++IP); }>), + Word("_jmp0", WordWrap<[] { + ++IP; + if (pop() == 0) + jump((FuncList)*IP); + }>), + Word(",", WordWrap), + Word("emit", WordWrap<[] { serput(pop()); }>), + Word("key", WordWrap<[] { push(key()); }>), + Word("key?", WordWrap<[] { push(haskey()); }, tobool>), + Word("execute", WordWrap<[] { (void)executor((FuncList *)pop()); }>), + Word(":", WordWrap), + Word(";", WordWrap).markImmediate(), + Word("exit", fexit), + Word("drop", WordWrap), + Word("dup", WordWrap<[] { push(*sp()); }>), + Word("swap", WordWrap<[] { std::swap(*sp(), *(sp() - 1)); }>), + Word("pick", WordWrap<[] { auto t = *(sp() - *sp() - 1); *sp() = t; }>), + Word("cells", WordWrap<[] { *sp() *= sizeof(Cell); }>), + Word("+", WordWrap<[] { *(sp() - 1) += *sp(); }, discard>), + Word("-", WordWrap<[] { *(sp() - 1) -= *sp(); }, discard>), + Word("*", WordWrap<[] { *(sp() - 1) *= *sp(); }, discard>), + Word("/", WordWrap<[] { *(sp() - 1) /= *sp(); }, discard>), + Word("mod", WordWrap<[] { *(sp() - 1) %= *sp(); }, discard>), + Word("=", WordWrap<[] { *(sp() - 1) = *(sp() - 1) == *sp(); }, discard, tobool>), + Word("<", WordWrap<[] { *(sp() - 1) = *(sp() - 1) < *sp(); }, discard, tobool>), + Word("or", WordWrap<[] { *(sp() - 1) |= *sp(); }, discard>), + Word("and", WordWrap<[] { *(sp() - 1) &= *sp(); }, discard>), + Word("xor", WordWrap<[] { *(sp() - 1) ^= *sp(); }, discard>), + Word("lshift", WordWrap<[] { *(sp() - 1) <<= *sp(); }, discard>), + Word("rshift", WordWrap<[] { *(sp() - 1) >>= *sp(); }, discard>), + Word(">r", WordWrap<[] { rpush(pop()); }>), + Word("r>", WordWrap<[] { push(rpop()); }>), + Word("immediate", WordWrap<[] { ((Word *)LATEST)->markImmediate(); }>), + Word("aligned", WordWrap<[] { *sp() = aligned(*sp()); }>), + Word("align", WordWrap), + Word("literal", WordWrap<[] { if (STATE) compileliteral(); }>).markImmediate(), + Word("\'", WordWrap), + Word("_i", WordWrap<[] { *sp() = ((Word *)*sp())->immediate(); }, tobool>), + Word("[']", WordWrap).markImmediate(), + Word("compile,", WordWrap), + Word("word", WordWrap), + //Word("_b", WordWrap<[] { + // serput('#'); // Gives a good breakpoint spot for gdb + //}>), + Word(".", WordWrap<[] { printint(pop(), BASE); }>) +); + +int main() +{ + initMCU(); + initialize(words); + + serputs("alee forth\n\r"); + + while (1) { + doparse(); + serputs("\n\r"); + } +} + +void getinput() +{ + auto ptr = strbuf; + + while (1) { + if (UCA0IFG & UCRXIFG) { + auto c = static_cast(UCA0RXBUF); + serput(c); + + if (c == '\r') { + do { + addkey(*--ptr); + } while (ptr != strbuf); + + serputs("\n\r"); + return; + } else if (c == '\b') { + if (ptr > strbuf) + --ptr; + } else if (ptr < strbuf + sizeof(strbuf)) { + if (c >= 'A' && c <= 'Z') + c += 32; + *ptr++ = c; + } + } + } +} + +void doparse() +{ + auto result = parse(); + + if (result == Error::none) { + serputs(STATE ? "compiled" : "ok"); + } else { + serputs("error "); + printint(static_cast(result), BASE); + } +} + +void serput(int c) +{ + while (!(UCA0IFG & UCTXIFG)); + UCA0TXBUF = static_cast(c); +} + +void serputs(const char *s) +{ + while (*s) + serput(*s++); +} + +void printint(DoubleCell n, int base) +{ + static const char digit[] = "0123456789ABCDEF"; + + char *ptr = strbuf; + bool neg = n < 0; + + if (neg) + n = -n; + + do { + *ptr++ = digit[n % base]; + } while ((n /= base)); + + if (neg) + serput('-'); + + do { + serput(*--ptr); + } while (ptr > strbuf); + serput(' '); +} + +void initMCU() +{ + WDTCTL = WDTPW | WDTHOLD; + initGPIO(); + initClock(); + initUART(); + SYSCFG0 = FRWPPW; +} + +void initGPIO() +{ + // Unnecessary, but done by TI example + P1DIR = 0xFF; P2DIR = 0xFF; + P1REN = 0xFF; P2REN = 0xFF; + P1OUT = 0x00; P2OUT = 0x00; + + // Set LED pins to outputs + P6DIR |= BIT0 | BIT1 | BIT2; + P6OUT |= BIT0 | BIT1 | BIT2; + P5DIR |= BIT5 | BIT6 | BIT7; + P5OUT |= BIT5 | BIT6 | BIT7; + + // Setup buttons w/ pullups + P3DIR &= ~BIT4; P3REN |= BIT4; P3OUT |= BIT4; + P2DIR &= ~BIT3; P2REN |= BIT3; P2OUT |= BIT3; + + // Allow GPIO configurations to be applied + PM5CTL0 &= ~LOCKLPM5; + + // Safety measure, prevent unwarranted interrupts + P5IFG = 0; + P6IFG = 0; +} + +void initClock() +{ + static_assert(MCLK_FREQ_MHZ == 16); + + // Configure one FRAM waitstate as required by the device datasheet for MCLK + // operation beyond 8MHz _before_ configuring the clock system. + FRCTL0 = FRCTLPW | NWAITS_1; + + P2SEL0 |= BIT0 | BIT1; // P2.0~P2.1: crystal pins + do + { + CSCTL7 &= ~(XT1OFFG | DCOFFG); // Clear XT1 and DCO fault flag + SFRIFG1 &= ~OFIFG; + } while (SFRIFG1 & OFIFG); // Test oscillator fault flag + + __bis_SR_register(SCG0); // disable FLL + CSCTL3 |= SELREF__XT1CLK; // Set XT1 as FLL reference source + CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_5;// DCOFTRIM=5, DCO Range = 16MHz + CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz + __delay_cycles(3); + __bic_SR_register(SCG0); // enable FLL + Software_Trim(); // Software Trim to get the best DCOFTRIM value + + CSCTL4 = SELMS__DCOCLKDIV | SELA__XT1CLK; // set XT1 (~32768Hz) as ACLK source, ACLK = 32768Hz + // default DCOCLKDIV as MCLK and SMCLK source + +} + +void initUART() +{ + // Configure UART pins + P5SEL0 |= BIT1 | BIT2; + SYSCFG3 |= USCIA0RMP; // Set the remapping source + + UCA0CTLW0 |= UCSWRST; + UCA0CTLW0 |= UCSSEL__SMCLK; // 16 MHz + + // Baud Rate calculation + // N = 16MHz / 115200 = 138.888 + // OS16 = 1, UCBRx = INT(N/16) = 8(.6806) + // UCBRFx = INT( ((N/16) - UCBRx) * 16) = 10(.8896) + UCA0BRW = 8; + UCA0MCTLW = 0xD600 | 0x00A0 | UCOS16; + + UCA0CTLW0 &= ~UCSWRST; // Initialize eUSCI +} + +void Software_Trim() +{ + unsigned int oldDcoTap = 0xffff; + unsigned int newDcoTap = 0xffff; + unsigned int newDcoDelta = 0xffff; + unsigned int bestDcoDelta = 0xffff; + unsigned int csCtl0Copy = 0; + unsigned int csCtl1Copy = 0; + unsigned int csCtl0Read = 0; + unsigned int csCtl1Read = 0; + unsigned int dcoFreqTrim = 3; + unsigned char endLoop = 0; + + do + { + CSCTL0 = 0x100; // DCO Tap = 256 + do + { + CSCTL7 &= ~DCOFFG; // Clear DCO fault flag + }while (CSCTL7 & DCOFFG); // Test DCO fault flag + + __delay_cycles((unsigned int)3000 * MCLK_FREQ_MHZ);// Wait FLL lock status (FLLUNLOCK) to be stable + // Suggest to wait 24 cycles of divided FLL reference clock + while((CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)) && ((CSCTL7 & DCOFFG) == 0)); + + csCtl0Read = CSCTL0; // Read CSCTL0 + csCtl1Read = CSCTL1; // Read CSCTL1 + + oldDcoTap = newDcoTap; // Record DCOTAP value of last time + newDcoTap = csCtl0Read & 0x01ff; // Get DCOTAP value of this time + dcoFreqTrim = (csCtl1Read & 0x0070)>>4;// Get DCOFTRIM value + + if(newDcoTap < 256) // DCOTAP < 256 + { + newDcoDelta = 256 - newDcoTap; // Delta value between DCPTAP and 256 + if((oldDcoTap != 0xffff) && (oldDcoTap >= 256)) // DCOTAP cross 256 + endLoop = 1; // Stop while loop + else + { + dcoFreqTrim--; + CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4); + } + } + else // DCOTAP >= 256 + { + newDcoDelta = newDcoTap - 256; // Delta value between DCPTAP and 256 + if(oldDcoTap < 256) // DCOTAP cross 256 + endLoop = 1; // Stop while loop + else + { + dcoFreqTrim++; + CSCTL1 = (csCtl1Read & (~DCOFTRIM)) | (dcoFreqTrim<<4); + } + } + + if(newDcoDelta < bestDcoDelta) // Record DCOTAP closest to 256 + { + csCtl0Copy = csCtl0Read; + csCtl1Copy = csCtl1Read; + bestDcoDelta = newDcoDelta; + } + + }while(endLoop == 0); // Poll until endLoop == 1 + + CSCTL0 = csCtl0Copy; // Reload locked DCOTAP + CSCTL1 = csCtl1Copy; // Reload locked DCOFTRIM + while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // Poll until FLL is locked +} + +// disables the watchdog between the __start() and the __crt_0init() +extern "C" +__attribute__((naked, section(".crt_0010init"))) +void __gcc_disable_watchdog() +{ + WDTCTL = WDTPW + WDTHOLD; +} +