// veccahmaaf
// Tue 28 Dec 21:43:45 UTC 2021
// accompanying circuit
//  12 output 12 wiggle 11 output 11 wiggle
#if 0

for wokwi Uno simulator:

    [ https://wokwi.com/arduino/new?template=arduino-uno ]

upstream code - primary author:

    [ https://github.com/CharleyShattuck/Feather-M0-interpreter ]


    Tiny interpreter, similar to myforth's Standalone Interpreter

    This example code is in the public domain



    ok 640 dump \ dumps the terminal input buffer area << outdated info

    test with:
    ok 199 198 197 196 640 dump cr cr .s cr cr

    (never type 'ok' yourself - it's the forth prompt)

    Note: the 'ok' word was just updated - did not see the ENTER keypress, prior.

#endif

#include <unistd.h>

/* print macro - hide the details: */

#define lcl_printf() \
    buf_ptr = * & buffer; \
    memcpy(buffering, buf_ptr, sizeof buffer); \
    print_buffer();

/* test program includes */

/* TODO suppress includes and test */

#include <stdio.h>
#include <stdint.h>
#include <string.h> // strlen only

/* Structure of a dictionary entry */
typedef struct {
    const char *name;
    void (*function)();
} entry;

/* Data stack for parameter passing
     This "stack" is circular,
     like a Green Arrays F18A data stack,
     so overflow and underflow are not possible
     Number of items must be a power of 2 */

const int STKSIZE = 8;
const int STKMASK = 7;
int stack[STKSIZE];
int p = 0;

/* TOS is Top Of Stack */
#define TOS stack[p]

/* NAMED creates a string in flash */
#define NAMED(x, y) const char x[]=y

/* Terminal Input Buffer for interpreter */
const byte maxtib = 16;
char tib[maxtib];

/* buffer required for strings read from flash */
char namebuf[maxtib];
byte pos;

/* push n to top of data stack */
void push(int n) {
    p = (p + 1)& STKMASK;
    TOS = n;
}

/* return top of stack */
int pop() {
    int n = TOS;
    p = (p - 1)& STKMASK;
    return n;
}

/* Global delay timer */
int spd = 15;

/* top of stack becomes current spd */
NAMED(_speed, "speed");
void speed() {
    spd = pop();
}

/* discard top of stack */
NAMED(_drop, "drop");
void drop() {
    pop();
}

/* recover dropped stack item */
NAMED(_back, "back");
void back() {
    for (int i = 1; i < STKSIZE; i++) drop();
}

/* copy top of stack */
NAMED(_dup, "dup");
void dup() {
    push(TOS);
}

/* exchange top two stack items */
NAMED(_swap, "swap");
void swap() {
    int a;
    int b;
    a = pop();
    b = pop();
    push(a);
    push(b);
}

/* copy second on stack to top */
NAMED(_over, "over");
void over() {
    int a;
    int b;
    a = pop();
    b = pop();
    push(b);
    push(a);
    push(b);
}
/* add top two items */
NAMED(_add, "+");
void add() {
    int a = pop();
    TOS = a + TOS;
}

/* bitwise and top two items */
NAMED(_and, "and");
void and_() {
    int a = pop();
    TOS = a & TOS;
}

/* inclusive or top two items */
NAMED(_or, "or");
void or_() {
    int a = pop();
    TOS = a | TOS;
}

/* exclusive or top two items */
NAMED(_xor, "xor");
void xor_() {
    int a = pop();
    TOS = a ^ TOS;
}

/* invert all bits in top of stack */
NAMED(_invert, "invert");
void invert() {
    TOS = ~(TOS);
}

/* negate top of stack */
NAMED(_negate, "negate");
void negate() {
    TOS = -(TOS);
}

/* print carriage return */
NAMED(_cr, "cr");
void cr() {
    Serial.print('\n');
}

/* destructively display top of stack, decimal */
NAMED(_dot, ".");
void dot() {
    Serial.print(pop());
    Serial.print(" ");
}

/* destructively display top of stack, hex */
NAMED(_dotHEX, ".h");
void dotHEX() {
    Serial.print(0xffff & pop(), HEX);
    Serial.print(" ");
}

/* display whole stack, hex */
NAMED(_dotShex, ".sh");
void dotShex() {
    for (int i = 0; i < STKSIZE; i++) dotHEX();
}

/* display whole stack, decimal */
NAMED(_dotS, ".s");
void dotS() {
    for (int i = 0; i < STKSIZE; i++) dot();
}

/* delay TOS # of milliseconds */
NAMED(_delay, "delay");
void del() {
    delay(pop());
}

/* Toggle pin at TOS and delay(spd), repeat... */
NAMED(_wiggle, "wiggle");
void wiggle() {
    int a = pop();
    pinMode(a, OUTPUT);
    for (int i = 0; i < 20; i++) {
        digitalWrite(a, HIGH);
        delay(20);
        digitalWrite(a, LOW);
        delay(400);
    }
}

/* TOS is pin number, set it HIGH */
NAMED(_high, "high");
void high() {
    digitalWrite(pop(), HIGH);
}

/* set TOS pin LOW */
NAMED(_low, "low");
void low() {
    digitalWrite(pop(), LOW);
}

/* read TOS pin */
NAMED(_in, "in");
void in() {
    TOS = digitalRead(TOS);
}

/* make TOS pin an input */
NAMED(_input, "input");
void input() {
    pinMode(pop(), INPUT);
}

/* make TOS pin an output */
NAMED(_output, "output");
void output() {
    pinMode(pop(), OUTPUT);
}

/* make TOS pin an input with weak pullup */
NAMED(_input_pullup, "input_pullup");
void input_pullup() {
    pinMode(pop(), INPUT_PULLUP);
}

/* dump 16 bytes of RAM in hex with ascii on the side */
void dumpRAM() {
    char buffer[5] = "";
    char *ram;
    int p = pop();
    ram = (char*)p;
    sprintf(buffer, "%4x", p);
    Serial.print(buffer);
    Serial.print("   ");
    for (int i = 0; i < 16; i++) {
        char c = *ram++;
        sprintf(buffer, " %2x", (c & 0xff));
        Serial.print(buffer);
    }
    ram = (char*)p;
    Serial.print("   ");
    for (int i = 0; i < 16; i++) {
        buffer[0] = *ram++;
        if (buffer[0] > 0x7f || buffer[0] < ' ') buffer[0] = '.';
        buffer[1] = '\0';
        Serial.print(buffer);
    }
    push(p + 16);
}

/* dump 256 bytes of RAM */
NAMED(_dumpr, "dump");
void rdumps() {
    for (int i = 0; i < 16; i++) {
        Serial.println();
        dumpRAM();
    }
}

/* End of Forth interpreter words */
/* ******************************************** */
/* Beginning of application words */

/* test program */

char buffering[64];

void print_buffer(void) {
    Serial.print(buffering);
}

void space_it(void) {
      sprintf(buffering, "%c", ' ');
      print_buffer();
}

void newline(void) {
      sprintf(buffering, "%c", '\n');
      print_buffer();
}


NAMED(_testpa, "testpa");
void testpa(void) {
      newline();
      space_it();
      
      char buffer[48]; // 32 also 64
      char* buf_ptr;

      buffer[0] = 'a';
      buffer[1] = 'b';
      buffer[2] = 'c';
      buffer[3] = '\000';

      buf_ptr = buffer;

      int buf_size, buf_ptr_size;

      buf_size = sizeof(buffer); // captures "abc\000" size
      buf_ptr_size = sizeof(buf_ptr);

      int buf_len = strlen(buffer);

      size_t gottem;

      sprintf(buffering, "%c", '\'');
      print_buffer();

      memcpy(buffering, buf_ptr, sizeof buffer);
      print_buffer();

      sprintf(buffering, "%c%c", '\'', ' ');
      print_buffer();

      sprintf(buffering, "%s ", " is the buffer contents");
      print_buffer();

      sprintf(buf_ptr, "\n         sizeof(buf_ptr) is  %d", buf_ptr_size);

      lcl_printf();

      sprintf(buf_ptr, "%s", "\n         sizeof(buffer)  is ");

      lcl_printf();

      buf_size = sizeof(buffer); // captures "abc\000" size

      sprintf(buf_ptr, "%d\n", buf_size); // related to string length, possibly
      lcl_printf();

      sprintf(buf_ptr, "%s", "         strlen(buffer)  is ");
      lcl_printf();

      sprintf(buf_ptr, " %d\n", buf_len); // related to string length, possibly
      lcl_printf();

      uint8_t adrs;
      adrs = (uint8_t) & buf_ptr;

      sprintf(buf_ptr, "%s", "adrs (& buf_ptr) in hex is        ");
      lcl_printf();

      // print the buffer's address in ram
      sprintf(buf_ptr, "0x%.8X\n", adrs);
      lcl_printf();

      sprintf(buf_ptr, "%s", "adrs (& buf_ptr) in decimal is  ");
      lcl_printf();

      sprintf(buf_ptr, "%c%.11u\n\n", ' ', adrs);
      lcl_printf();
}

/* End of application words */
/* ******************************************** */
/* Now build the dictionary */

/* empty words don't cause an error */
NAMED(_nop, " ");
void nop() { }

/* Forward declaration required here */
NAMED(_words, "words");
void words();

/* table of names and function addresses in flash */
const entry dictionary[] = {
    {_nop, nop},
    {_words, words},
    {_dup, dup},
    {_drop, drop},
    {_back, back},
    {_swap, swap},
    {_over, over},
    {_add, add},
    {_and, and_},
    {_or, or_},
    {_xor, xor_},
    {_invert, invert},
    {_negate, negate},
    {_cr, cr},
    {_dotS, dotS},
    {_dotShex, dotShex},
    {_dot, dot},
    {_dotHEX, dotHEX},
    {_delay, del},
    {_high, high},
    {_low, low},
    {_in, in},
    {_input, input},
    {_output, output},
    {_input_pullup, input_pullup},
    {_wiggle, wiggle},
    {_dumpr, rdumps},
    {_testpa, testpa},
    {_speed, speed}
};

/* Number of words in the dictionary */
const int entries = sizeof dictionary / sizeof dictionary[0];

/* Display all words in dictionary */
void words() {
    for (int i = entries - 1; i >= 0; i--) {
        strcpy(namebuf, dictionary[i].name);
        Serial.print(namebuf);
        Serial.print(" ");
    }
}

/* Find a word in the dictionary, returning its position */
int locate() {
    for (int i = entries; i >= 0; i--) {
        strcpy(namebuf, dictionary[i].name);
        if (!strcmp(tib, namebuf)) return i;
    }
    return 0;
}

/* Is the word in tib a number? */
int isNumber() {
    char *endptr;
    strtol(tib, &endptr, 0);
    if (endptr == tib) return 0;
    if (*endptr != '\0') return 0;
    return 1;
}

/* Convert number in tib */
int number() {
    char *endptr;
    return (int) strtol(tib, &endptr, 0);
}

char ch;

void ok() {
    if (ch == '\n') Serial.println("ok");
}

/* Incrementally read command line from serial port */
byte reading() {
    if (!Serial.available()) return 1;
    ch = Serial.read();
    if (ch == '\r') return 1;
    if (ch == '\n') return 0;
    if (ch == ' ') return 0;
    if (pos < maxtib) {
      tib[pos++] = ch;
      tib[pos] = 0;
    }
    return 1;
}

/* Block on reading the command line from serial port */
/* then echo each word */
void readword() {
    pos = 0;
    tib[0] = 0;
    while (reading());
    Serial.print(tib);
    Serial.print(" ");
}

/* Run a word via its name */
void runword() {
    int place = locate();
    if (place != 0) {
      dictionary[place].function();
      ok();
      return;
    }
    if (isNumber()) {
      push(number());
      ok();
      return;
    }
    Serial.println("?");
}


void setup() {
    Serial.begin(9600);
    while (!Serial);
    Serial.println ("Forth-like interpreter:");
    words();
    Serial.println(" ");
    Serial.println("NOT_READY");
    testpa(); // TEST PROGRAM
    Serial.println("READY");
}

void loop() {
    readword();
    runword();
}

/* TESTED EDIT */
/* END */