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