// tbasic.c /* -------------------------------------------------------------------- tbasic.c Basicinterpreter fuer STM8S Controller. Ausgangsdateien von T. Suzuki Modifikationen und Anpassungen an STM8 von R. Seelig "Wettstreit um den billigsten Basic-Computer" Hardware : STM8S103 / S105 19.12.2023 R. Seelig GNU General Public License ( macht also was ihr wollt damit! ) -------------------------------------------------------------------- */ /* ################################################################## I/O Mapping fuer STM8S103F3 ################################################################## ------------ (PD4) I/O 9 | 1 20 | I/O 8 (PD3) UART_TX | 2 19 | (PD2) UART_RX | 3 18 | SWIM NRST | 4 17 | I/O 7 (PC7) (PA1) Autorun | 5 16 | I/O 6 (PC6) | 6 15 | I/O 5 (PC5) Vss (GND) | 7 14 | I/O 4 (PC4) VCAP (*1) | 8 13 | I/O 3 (PC3) Vdd (+Ub) | 9 12 | I/O 1 (PB4) (PA3) I/O 0 | 10 11 | I/O 2 (PB5) ------------ *1 : Ist mit min. 1uF gegen GND zu verschalten */ /* 01.01.2024 A. Kratzsch All warnings removed Include stdarg.h removed Duplicate defines removed Meaningful messages Changes to shrink the code size: - Function newline: uart_putstring - Function error: duplicate newline - New function uart_get (duplicate code) - New function basic_run (dupicate code) - New function load_save_prog (duplicate code) - New array portmap - Function toktoi I_REM: Loop modified - Function ioutput: if removed - Message ERR_COM removed - Function sstyle removed - Define getrnd - Function ivalue: function pointer array - Function iexe: function pointer array - Function iexp: function pointer array - New function: io_adr New keywords: AND, OR, XOR, <> ADC() DELAY(ms) BYTEOUT(pin, value) PEEK(addr) POKE addr, value EEPROM(32) */ #include #include #include #include "stm8s.h" #include "stm8_init.h" #include "stm8_gpio.h" #include "uart.h" #define autorun_init() PA1_input_init() #define is_autorun() !is_PA1() enum { SIZE_LINE = 40 }; // Eingabespeicher einer Zeile + NULL-Byte enum { SIZE_IBUF = SIZE_LINE }; enum { SIZE_LIST = 512 }; // Basic-Listing Speicher (Programmspeicher) enum { SIZE_ARRY = 32 }; // max. Array-Groesse enum { SIZE_GSTK = 3 * 2 }; // GOSUB stack size (2 / nest) enum { SIZE_LSTK = 3 * 5 }; // FOR stack size (5 / nest) enum { IO_MAX = 10 }; enum { BAUDRATE = 19200 }; enum { ADC_PIN = 4 }; // PD3 = AIN4 = PIN8 uint16_t portddr= 0; // dient als Flagregister wie die Ausgangspins // PA3, PB4, PB5, PC3, PC4, PC5, PC6, PC7, PD3, PD4 const uint8_t pinmap [IO_MAX] = { 3, 4, 5, 3, 4, 5, 6, 7, 3, 4 }; const uint8_t portmap[IO_MAX] = { 0, 1, 1, 2, 2, 2, 2, 2, 3, 3 }; const char *title = "STM8S103F3P6 --TBasic v1.02 -- 19200Bd 8n1\n\r"; enum { return_key = 0x0d }; enum { return_alt = 0x0a }; typedef struct { uint8_t p; uint8_t b; } PB; #define getrnd(value) ((rand() % value) + 1) // Basic - Prototypen int16_t ivalue(void); int16_t iexp(void); void sysfunc(int16_t v1, int16_t v2); const char *kwtbl[] = { // Operator "*", "/", ">=", "#", "<>", ">", "=", "<=", "<", "AND", "OR", "XOR", // Operator and Value "-", "+", // Value "(", "RND", "ABS", "SIZE", "IN", " ", "PEEK", "ADC", " ", "EEPROM", "@", // Exec "GOTO", "GOSUB", "RETURN", "FOR", "NEXT", "IF", "REM", "INPUT", "PRINT", "LET", ";", "OUT", "FUNC", "DELAY", "POKE", "BYTEOUT", // Others "LIST", "RUN", "NEW", "SAVE", "LOAD", " ", " ", "TO", "STEP", "STOP", ",", ")", }; // Interpreterzwischencode enum { // Operator I_MUL, I_DIV, I_GTE, I_SHARP, I_NEQ, I_GT, I_EQ, I_LTE, I_LT, I_AND, I_OR, I_XOR, // Operator and Value I_MINUS, I_PLUS, // Value I_OPEN, I_RND, I_ABS, I_SIZE, I_IN, I_NUM, I_PEEK, I_ADC, I_VAR, I_EEPROM, I_ARRAY, // Exec I_GOTO, I_GOSUB, I_RETURN, I_FOR, I_NEXT, I_IF, I_REM, I_INPUT, I_PRINT, I_LET, I_SEMI, I_OUT, I_FUNC, I_DELAY, I_POKE, I_BYTEOUT, // Oters I_LIST, I_RUN, I_NEW, I_SAVE, I_LOAD, I_STR, I_EOL, I_TO, I_STEP, I_STOP, I_COMMA, I_CLOSE, }; // Schluesselwort zaehlen enum { SIZE_KWTBL = sizeof(kwtbl) / sizeof(kwtbl[0]) }; // Textformatierung // (F_NSA: kein Leerzeichen nach Funktion) // (F_NSB: und auch keines davor) enum { F_NONE = 0, F_NSA = 1, F_NSB = 2 }; const uint8_t i_nsf[] = { F_NSA | F_NSB, // I_MUL F_NSA | F_NSB, // I_DIV F_NSA | F_NSB, // I_GTE F_NSA | F_NSB, // I_SHARP F_NSA | F_NSB, // I_NEQ F_NSA | F_NSB, // I_GT F_NSA | F_NSB, // I_EQ F_NSA | F_NSB, // I_LTE F_NSA | F_NSB, // I_LT F_NSA | F_NSB, // I_AND F_NSA | F_NSB, // I_OR F_NSA | F_NSB, // I_XOR F_NSA | F_NSB, // I_MINUS F_NSA | F_NSB, // I_PLUS F_NSA | F_NSB, // I_OPEN F_NSA, // I_RND F_NSA, // I_ABS F_NSA, // I_SIZE F_NSA, // I_IN F_NONE, // I_NUM F_NONE, // I_PEEK F_NSA, // I_ADC F_NONE, // I_VAR F_NSA, // I_EEPROM F_NSA, // I_ARRAY F_NONE, // I_GOTO F_NONE, // I_GOSUB F_NSA, // I_RETURN F_NONE, // I_FOR F_NONE, // I_NEXT F_NONE, // I_IF F_NSA, // I_REM F_NONE, // I_INPUT F_NONE, // I_PRINT F_NONE, // I_LET F_NSB, // I_SEMI F_NSA, // I_OUT F_NONE, // I_FUNC F_NONE, // I_DELAY F_NONE, // I_POKE F_NONE, // I_BYTEOUT F_NONE, // I_LIST F_NONE, // I_RUN F_NONE, // I_NEW F_NONE, // I_SAVE F_NONE, // I_LOAD F_NONE, // I_STR F_NSB, // I_EOL F_NONE, // I_TO F_NONE, // I_STEP F_NSA, // I_STOP F_NSA | F_NSB, // I_COMMA F_NSA | F_NSB, // I_CLOSE }; // Macrosuchfunktionen #define nospacea(c) (i_nsf[c] & F_NSA) #define nospaceb(c) (i_nsf[c] & F_NSB) // Fehlermeldungen const char* errmsg[] = { "OK", "Div by zero", "O-flow", "Out of range", "Buffer full", "List full", "GOSUB too deep", "Stack underflow", "FOR too deep", "NEXT no FOR", "NEXT no counter", "NEXT mismatch FOR", "FOR no var", "FOR no TO", "LET no var", "IF no condition", "Undef line no.", "\'(\' or \')\' expected", "\'=\' expected", "Syntax error", "Intern error", "Abort by [ESC]", "No I/O number", "I/O value to large", "Value expected" }; // Fehlercodes enum { ERR_OK, ERR_DIVBY0, ERR_VOF, ERR_SOR, ERR_IBUFOF, ERR_LBUFOF, ERR_GSTKOF, ERR_GSTKUF, ERR_LSTKOF, ERR_LSTKUF, ERR_NEXTWOV, ERR_NEXTUM, ERR_FORWOV, ERR_FORWOTO, ERR_LETWOV, ERR_IFWOC, ERR_ULN, ERR_PAREN, ERR_VWOEQ, ERR_SYNTAX, ERR_SYS, ERR_ESC, ERR_NOIO, ERR_IOERR, ERR_NOVAL, }; // RAM mapping char lbuf[SIZE_LINE]; // Buffer Eingabezeile uint8_t ibuf[SIZE_IBUF]; // i-code Konvertierungsspeicher int16_t var[26]; // max. 26 Variable int16_t arr[SIZE_ARRY]; // Speichergroesse Array uint8_t listbuf[SIZE_LIST]; // Programmspeicher uint8_t* clp; // Zeiger auf aktuelle Zeile uint8_t* cip; // Zeiger auf aktuellen i-Code uint8_t* gstk[SIZE_GSTK]; // GOSUB stack uint8_t gstki; // GOSUB stack index uint8_t* lstk[SIZE_LSTK]; // FOR stack uint8_t lstki; // FOR stack index uint8_t err; // index Fehlermeldung #define bitset16(reg, nr) ( reg |= 1 << nr ) #define bitclr16(reg, nr) ( reg &= ~(1 << nr) ) #define testbit16(reg, nr) ( (reg & (1 << nr)) >> nr ) void newline(void) { uart_putstring("\r\n"); } // Register Offset der GPIO Ports #define P_ODR 0 // ( Output Register ) #define P_IDR 1 // ( Input Register ) #define P_DDR 2 // ( Direction Register ) #define P_CR1 3 // ( Controll Register 1 ) #define P_CR2 4 // ( Controll Register 2 ) void eeprom_unlock(void) { if (!(FLASH_IAPSR & FLASH_IAPSR_DUL)) FLASH_DUKR= 0xae; FLASH_DUKR= 0x56; } #define eeprom_lock() (FLASH_IAPSR &= ~(FLASH_IAPSR_DUL)) /* -------------------------------------------------- eeprom_write schreibt den Wert value an die Adresse addr im internen EEPROM -------------------------------------------------- */ uint8_t eeprom_write(uint16_t addr, uint8_t value) { uint8_t *address = (char *) EEPROM_BASE_ADDR + addr; if (addr > 0x027f) return 0; // 0x27f = 639 = hoechste verfuegbare // Speicherzelle *address = (uint8_t) value; return 1; } /* -------------------------------------------------- eeprom_read liest den Inhalt der Speicheradresse addr des internen EEPROM aus und gibt dieses als Argument zurueck -------------------------------------------------- */ uint8_t eeprom_read(uint16_t addr) { uint8_t value; uint8_t *address = (char *) EEPROM_BASE_ADDR + addr; if (addr > 0x027f) return 0xff; // 0x27f = 639 = hoechste verfuegbare // Speicherzelle value= *address; return value; } void load_save_prog(uint8_t load) { uint16_t adr; eeprom_unlock(); for (adr = 0; adr < SIZE_LIST; adr++) { if (load) listbuf[adr] = eeprom_read(adr); else eeprom_write(adr, listbuf[adr]); } eeprom_lock(); uart_putstring("\n\rDone..."); } void get_port(PB *pb, uint8_t index) { pb->p = portmap[index]; pb->b = pinmap[index]; } uint8_t *io_adr(uint8_t ioport) { return (uint8_t *)0x5000 + (ioport * 5); } /* ---------------------------------------------------- io_set setzt oder loescht ein Bit im angegebenen Port Bsp.: io_set(1,5,1); 1 = PB 5 = Bit 5 1 = auf 1 setzen ---------------------------------------------------- */ void io_set(uint8_t ioport, uint8_t iono, uint8_t value) { uint8_t *ioadr = io_adr(ioport); uint8_t b = 1 << iono; if (value) *(ioadr + P_ODR) |= b; else *(ioadr + P_ODR) &= ~(b); } /* ---------------------------------------------------- io_get liest einen Eingangspin ein ---------------------------------------------------- */ uint8_t io_get(uint8_t ioport, uint8_t iono) { uint8_t *ioadr = io_adr(ioport); uint8_t b = ( *(ioadr + P_IDR) & (1 << iono) ) >> iono; return b; } /* ---------------------------------------------------- io_outputinit initialisiert einen einzelnen GPIO-Pin als Ausgang. Verbraucht bei nur einem verwendeten I/O Port MEHR Speicher als in stm8_init.h, jedoch wenn mehrere oder gar alle I/Os benoetigt werden weniger Hierbei gilt: ioport == 0 => PortA 1 => PortB 2 => PortC 3 => PortD iono => Bitposition im Port I/O Adressen der Ports fangen bei Adresse 0x5000 an und belegen jeweils 4 Bytes Speicher Adressraum PortA = 0x5000, PortB = 0x5005 etc. Offset 0 = ODR ( Output Register ) 1 = IDR ( Input Register ) 2 = DDR ( Direction Register ) 3 = CR1 ( Controll Register 1 ) 4 = CR2 ( Controll Register 2 ) ---------------------------------------------------- */ void io_outputinit(uint8_t ioport, uint8_t iono) { uint8_t *ioadr = io_adr(ioport); uint8_t b = 1 << iono; *(ioadr + P_DDR) |= b; // Output Register *(ioadr + P_CR1) |= b; // Controll Register 1 *(ioadr + P_CR2) &= ~(b); // Controll Register 2 } /* ---------------------------------------------------- io_inputinit initialisiert einen einzelnen GPIO-Pin als Eingang. ---------------------------------------------------- */ void io_inputinit(uint8_t ioport, uint8_t iono) { uint8_t *ioadr = io_adr(ioport); uint8_t b = 1 << iono; *(ioadr + P_DDR) &= ~(b); // Output Register *(ioadr + P_CR1) |= b; // Controll Register 1 } void adc_init(uint8_t pin) { // Enable the ADC clock CLK_PCKENR2 |= (1 << 3); // Set the channel selection to channel 0 ADC_CSR = pin & 0x0F; // Set the conversion mode to single conversion // Set the ADC clock frequency to 2MHz, assuming a 16 MHz frequency ADC_CR1 = ADC_CR1_SPSEL2; // Set the align to right ADC_CR2 = (1 << 3); // Turn on the ADC ADC_CR1 |= ADC_CR1_ADON; } int16_t adc_get(uint8_t pin) { pin; // unused parameter uint8_t adcH, adcL; // Start the conversion ADC_CR1 |= ADC_CR1_ADON; // Wait for the ADC to finish conversion while (!(ADC_CSR & ADC_CSR_EOX)); // Read the lower byte adcL = ADC_DRL; // Read the upper byte adcH = ADC_DRH; // Clear the EOC flag ADC_CSR &= ~ADC_CSR_EOX; return adcL | (adcH << 8); } char c_toupper(char c) // gleiche Funktion wie C - Bibliothek { return(c <= 'z' && c >= 'a' ? c - 32 : c); } char c_isprint(char c) { return(c >= 32 && c <= 126); } char c_isspace(char c) { return(c == ' ' || (c <= 13 && c >= 9)); } char c_isdigit(char c) { return(c <= '9' && c >= '0'); } char c_isalpha(char c) { return ((c <= 'z' && c >= 'a') || (c <= 'Z' && c >= 'A')); } char uart_readkey(void) { char c; c= uart_getchar(); if (c== return_alt) return return_key; return c; } uint8_t uart_get(char fn) { char c; char ok; uint8_t len = 0; while ((c = uart_readkey()) != return_key) { if (c == 9) c = ' '; // TAB durch Leerzeichen ersetzen if (((c == 8) || (c == 127)) && (len > 0)) // letztes Zeichen loeschen { len--; uart_putstring("\b \b"); } else { ok = fn ? ((len == 0 && (c == '+' || c == '-')) || (len < 6 && c_isdigit(c))) : (c_isprint(c) && (len < (SIZE_LINE - 1))); if (ok) { lbuf[len++] = c; uart_putchar(c); } } } newline(); lbuf[len] = 0; return len; } void uart_getstring(void) { uint8_t len = uart_get(0); if (len > 0) { while (c_isspace(lbuf[--len])); // Leerzeichen uebergehen lbuf[++len] = 0; } } void putnum(int16_t value, int16_t d) { uint16_t dig; uint8_t sign; if (value < 0) { sign = 1; value = -value; } else { sign = 0; } lbuf[6] = 0; dig = 6; do { lbuf[--dig] = (value % 10) + '0'; value /= 10; }while (value > 0); if (sign) lbuf[--dig] = '-'; while (6 - dig < d) { uart_putchar(' '); d--; } uart_putstring(&lbuf[dig]); } int16_t getnum(void) { int16_t value, tmp; uint8_t sign; uint8_t len = uart_get(1); sign = (lbuf[0] == '-'); len = (lbuf[0] == '-' || lbuf[0] == '+'); value = 0; tmp = 0; while (lbuf[len]) { tmp = 10 * value + lbuf[len++] - '0'; if (value > tmp) // Werteueberlauf { err = ERR_VOF; } value = tmp; } return sign ? -value : value; } uint8_t toktoi(void) // Konvertierung Token zu I-Code, Rueckgabe Laenge oder 0 { uint8_t i; uint8_t len = 0; char *pkw = 0; char *ptok; char *s = lbuf; char c; int16_t value; int16_t tmp; while (*s) { while (c_isspace(*s)) s++; // Leerzeichen ueberlesen // Schluesselwortuebersetzung for (i = 0; i < SIZE_KWTBL; i++) { pkw = (char *)kwtbl[i]; // Schluesselworttabelle ptok = s; // Schluesselwortvergleich while ((*pkw != 0) && (*pkw == c_toupper(*ptok))) { pkw++; ptok++; } if (*pkw == 0) // gefunden { if (len >= SIZE_IBUF - 1) { err = ERR_IBUFOF; return 0; } // I-Code gefunden ibuf[len++] = i; s = ptok; break; } } // Case statement benoetigt Argument fuer Nummern, Variable oder strings if (i == I_REM) { uint8_t len_p = len++; ptok = s; while (*ptok) // String kopieren { if (len >= SIZE_IBUF - 1) { err = ERR_IBUFOF; return 0; } ibuf[len++] = *ptok++; } ibuf[len_p] = len - len_p - 1; // Laenge setzen s = ptok; break; } if (*pkw == 0) continue; ptok = s; // Zahlenkonvertierung if (c_isdigit(*ptok)) { value = 0; tmp = 0; do { tmp = 10 * value + *ptok++ - '0'; if (value > tmp) { err = ERR_VOF; return 0; } value = tmp; } while (c_isdigit(*ptok)); if (len >= SIZE_IBUF - 3) { err = ERR_IBUFOF; return 0; } s = ptok; ibuf[len++] = I_NUM; ibuf[len++] = value & 255; ibuf[len++] = value >> 8; } else { if (*s == '\"' || *s == '\'') { c = *s++; ptok = s; for (i = 0; (*ptok != c) && c_isprint(*ptok); i++) ptok++; if (len >= SIZE_IBUF - 1 - i) { err = ERR_IBUFOF; return 0; } ibuf[len++] = I_STR; ibuf[len++] = i; while (i--) { ibuf[len++] = *s++; } if (*s == c) s++; } else { if (c_isalpha(*ptok)) { if (len >= SIZE_IBUF - 2) { err = ERR_IBUFOF; return 0; } if (len >= 4 && ibuf[len - 2] == I_VAR && ibuf[len - 4] == I_VAR) { err = ERR_SYNTAX; return 0; } ibuf[len++] = I_VAR; ibuf[len++] = c_toupper(*ptok) - 'A'; s++; } else { err = ERR_SYNTAX; return 0; } } } } ibuf[len++] = I_EOL; return len; } int16_t getlineno(uint8_t *lp) { if (*lp == 0) return 32767; // hoechste Zeilennummer return *(lp + 1) | *(lp + 2) << 8; } uint8_t* getlp(int16_t lineno) // Zeilennummer suchen { uint8_t *lp; for (lp = listbuf; *lp; lp += *lp) { if (getlineno(lp) >= lineno) break; } return lp; } int16_t getsize(void) // freien Speicher ermitteln { uint8_t* lp; for (lp = listbuf; *lp; lp += *lp); return (int16_t)((listbuf - lp) + SIZE_LIST - 1); } void inslist(void) // I-Code zum Programmlisting hinzufuegen { uint8_t *insp; uint8_t *p1, *p2; int16_t len; if (getsize() < *ibuf) { err = ERR_LBUFOF; // List Speicher overflow return; } insp = getlp(getlineno(ibuf)); if (getlineno(insp) == getlineno(ibuf)) // Zeilennummer { p1 = insp; p2 = p1 + *p1; while ((len = *p2) != 0) { while (len--) *p1++ = *p2++; } *p1 = 0; } if (*ibuf == 4) return; // Platz schaffen for (p1 = insp; *p1; p1 += *p1); len = (int16_t)(p1 - insp + 1); p2 = p1 + *ibuf; while (len--) *p2-- = *p1--; // und einfuegen len = *ibuf; p1 = insp; p2 = ibuf; while (len--) *p1++ = *p2++; } void putlist(uint8_t* ip) { uint8_t i; while (*ip != I_EOL) { // Schluesselwort if (kwtbl[*ip][0] != ' ') { uart_putstring((char *)kwtbl[*ip]); if (!nospacea(*ip)) uart_putchar(' '); if (*ip == I_REM) { ip++; i = *ip++; while (i--) { uart_putchar(*ip++); } return; } ip++; } // Zahl else if (*ip == I_NUM) { ip++; putnum(*ip | *(ip + 1) << 8, 0); ip += 2; if (!nospaceb(*ip)) uart_putchar(' '); } // Variable else if (*ip == I_VAR) { ip++; uart_putchar(*ip++ + 'A'); if (!nospaceb(*ip)) uart_putchar(' '); } // String else if (*ip == I_STR) { char c; c = '\"'; ip++; for (i = *ip; i; i--) { if (*(ip + i) == '\"') { c = '\''; break; } } uart_putchar(c); i = *ip++; while (i--) { uart_putchar(*ip++); } uart_putchar(c); if (*ip == I_VAR) uart_putchar(' '); } // nichts gefunden, eigentlich darf hierher nicht gelangt werden else { err = ERR_SYS; return; } } } int16_t getparam(void) // extrahiere Parameter { int16_t value; if (*cip != I_OPEN) { err = ERR_PAREN; return 0; } cip++; value = iexp(); if (err) return 0; if (*cip != I_CLOSE) { err = ERR_PAREN; return 0; } cip++; return value; } /* -------------------------------------------------------- iioin Eingangportpins lesen. Ist ein Portportpin nicht als Eingang definiert, geschieht es hier und bleibt so- lange ein Eingang, bis er ueber ioutput als Ausgang definiert wird. -------------------------------------------------------- */ int16_t iioin(uint8_t index) { PB pb; get_port(&pb, index); // Portbelegung siehe ioutput if (testbit16(portddr, index)) { bitclr16(portddr, index); io_inputinit(pb.p, pb.b); } return io_get(pb.p, pb.b); } /* -------------------------------------------------------- ioutput Ausgangspins schalten. Ist ein Portpin nicht als Ausgang definiert, geschieht es hier und bleibt so- lange ein Ausgang, bis er ueber iioin als Eingang definiert wird. -------------------------------------------------------- */ void ioutput(void) // I/O Funktion : Output { int16_t value; int16_t index; index = getparam(); if (err) return; if (index >= IO_MAX) { err = ERR_NOIO; return; } if (*cip != I_EQ) { err = ERR_NOIO; return; } cip++; value = iexp(); if (err) return; // index ist die Portnummer, value der Wert // Registerbits: // mapping I/O BitNr 0 PA3 // mapping I/O BitNr 1 PB4 // mapping I/O BitNr 2 PB5 // mapping I/O BitNr 3 PC3 // mapping I/O BitNr 4 PC4 // mapping I/O BitNr 5 PC5 // mapping I/O BitNr 6 PC6 // mapping I/O BitNr 7 PC7 // mapping I/O BitNr 8 PD3 // mapping I/O BitNr 9 PD4 PB pb; get_port(&pb, (uint8_t)index); if (!(testbit16(portddr, index))) { bitset16(portddr, index); io_outputinit(pb.p, pb.b); } io_set(pb.p, pb.b, (value != 0)); } /////////////////////////////////////////////////////////////// int16_t i_minus0(void) { return -ivalue(); } int16_t i_plus0(void) { return ivalue(); } int16_t i_open(void) { cip--; return getparam(); } int16_t i_array(void) { int16_t value = getparam(); if (err) return -1; if (value >= SIZE_ARRY) { err = ERR_SOR; return -1; } return arr[value]; } // Read data from EEPROM int16_t i_eeprom(void) { int16_t adr = getparam(); if (err) return -1; if (adr >= SIZE_ARRY) { err = ERR_SOR; return -1; } eeprom_unlock(); uint8_t v = eeprom_read(SIZE_LIST + adr); eeprom_lock(); return v; } int16_t i_rnd(void) { int16_t value = getparam(); if (err) return -1; return getrnd(value); } int16_t i_abs(void) { int16_t value = getparam(); if (err) return -1; return (value < 0) ? -value : value; } int16_t i_size(void) { if ((*cip != I_OPEN) || (*(cip + 1) != I_CLOSE)) { err = ERR_PAREN; return -1; } cip += 2; return getsize(); } int16_t i_in(void) { int16_t value = getparam(); if (err) return -1; if (value >= IO_MAX) { err = ERR_NOIO; return -1; } return iioin((uint8_t)value); } int16_t i_num(void) { int16_t value = *cip | *(cip + 1) << 8; cip += 2; return value; } int16_t i_var(void) { return var[*cip++]; } int16_t i_adc(void) { if ((*cip != I_OPEN) || (*(cip + 1) != I_CLOSE)) { err = ERR_PAREN; return 0; } cip += 2; adc_init(ADC_PIN); int32_t val32 = 0; for (uint8_t i= 0; i< 16; i++) { val32 += adc_get(ADC_PIN); } return val32 / 16; } // PEEK (addr) int16_t i_peek(void) { if (*cip != I_OPEN) { err = ERR_PAREN; return 0; } cip++; uint16_t value = getparam(); if (err) return -1; if (*cip != I_CLOSE) { err = ERR_PAREN; return 0; } cip++; return *((uint8_t *)value); } typedef int16_t(*OP0_FP)(void); const OP0_FP op0_fp[] = { // Operator and Value i_minus0, i_plus0, // Value i_open, i_rnd, i_abs, i_size, i_in, i_num, i_peek, i_adc, i_var, i_eeprom, i_array, }; #define OP2(fn, op) int16_t fn(int16_t a, int16_t b) { return a op b; } OP2(i_plus, +) OP2(i_minus, -) OP2(i_mul, *) OP2(i_eq, ==) OP2(i_neq, !=) OP2(i_lt, <) OP2(i_lte, <=) OP2(i_gt, >) OP2(i_gte, >=) OP2(i_and, &) OP2(i_or, |) OP2(i_xor, ^) #undef OP2 int16_t i_div(int16_t a, int16_t b) { if (b == 0) { err = ERR_DIVBY0; return -1; } return a / b; } typedef int16_t(*OP2_FP)(int16_t a, int16_t b); const OP2_FP op2_fp[] = { // Operator i_mul, i_div, i_gte, i_neq, i_neq, i_gt, i_eq, i_lte, i_lt, i_and, i_or, i_xor, // Operator and Value i_minus, i_plus, }; enum { level_max = 6 }; const uint8_t op2_level[] = { // Operator 1, // I_MUL 1, // I_DIV 3, // I_GTE 3, // I_SHARP 3, // I_NEQ 3, // I_GT 3, // I_EQ 3, // I_LTE 3, // I_LT 4, // I_AND 5, // I_OR 6, // I_XOR // Operator and Value 2, // I_MINUS 2, // I_PLUS }; int16_t ivalue(void) { uint8_t i = *cip; if (i > I_ARRAY || i < I_MINUS) { err = ERR_SYNTAX; return -1; } cip++; return op0_fp[i - I_MINUS](); } /* ---------------------------------------------------------------- iexp Der Parser ---------------------------------------------------------------- */ int16_t iexp_(uint8_t level) { if (!level) return ivalue(); int16_t value = iexp_(level - 1); if (err) return -1; while (1) { uint8_t op = *cip; if (op > I_PLUS /*|| op < I_MUL*/) return value; else if (op2_level[op] != level) return value; cip++; int16_t tmp = iexp_(level - 1); if (err) return -1; value = op2_fp[op](value, tmp); } } int16_t iexp(void) { return iexp_(level_max); } /////////////////////////////////////////////////////////////// void iprint(void) { int16_t value; int16_t len; uint8_t i; len = 0; while (*cip != I_SEMI && *cip != I_EOL) { switch (*cip) { case I_STR: { cip++; i = *cip++; while (i--) uart_putchar(*cip++); break; } case I_SHARP: { cip++; len = iexp(); if (err) return; break; } default: { value = iexp(); if (err) return; putnum(value, len); break; } } if (*cip == I_COMMA) { cip++; if (*cip == I_SEMI || *cip == I_EOL) return; } else { if (*cip != I_SEMI && *cip != I_EOL) { err = ERR_SYNTAX; return; } } } newline(); } void ioutbyte(uint8_t value) // I/O Funktion : Output { // Outbyte, gibt ein gesamtes Byte auf den I/O Leitungen 0..7 aus uint8_t b, v; for (b= 0; b< 8; b++) { PB pb; get_port(&pb, b); io_outputinit(pb.p, pb.b); v = testbit16(value, b); io_set(pb.p, pb.b, v); } } void iinput(void) { int16_t value; int16_t index; uint8_t i; uint8_t prompt; while (1) { prompt = 1; if (*cip == I_STR) { cip++; i = *cip++; while (i--) uart_putchar(*cip++); prompt = 0; } switch (*cip) { case I_VAR: // Variablenwert einlesen { cip++; if (prompt) { // uart_putchar(*cip + 'A'); // hier wird die einzulesende Variable angezeigt // uart_putchar(':'); uart_putstring(": "); } value = getnum(); if (err) return; var[*cip++] = value; break; } case I_ARRAY: case I_EEPROM: { char is_array = *cip == I_ARRAY; cip++; index = getparam(); if (err) return; if (index >= SIZE_ARRY) { err = ERR_SOR; return; } if (prompt) { uart_putstring(is_array ? "@(" : "EEPROM("); putnum(index, 0); uart_putstring("):"); } value = getnum(); if (err) return; if (is_array) arr[index] = value; else { eeprom_unlock(); eeprom_write(SIZE_LIST + index, (uint8_t)value); eeprom_lock(); } break; } default: { err = ERR_SYNTAX; return; } } switch (*cip) { case I_COMMA: { cip++; break; } case I_SEMI: case I_EOL: return; default: { err = ERR_SYNTAX; return; } } } } // Variable assignment handler void ivar(void) { int16_t value; uint8_t index; index = *cip++; if (*cip != I_EQ) { err = ERR_VWOEQ; return; } cip++; value = iexp(); if (err) return; var[index] = value; } void iarray_(char is_array) { int16_t value; int16_t index; index = getparam(); if (err) return; if (index >= SIZE_ARRY) { err = ERR_SOR; return; } if (*cip != I_EQ) { err = ERR_VWOEQ; return; } cip++; value = iexp(); if (err) return; if (is_array) arr[index] = value; else ; // Todo } void iarray(void) { iarray_(1); } void ieeprom(void) { iarray_(0); } void ilet(void) { switch (*cip) { case I_VAR: { cip++; ivar(); // Variable assignment break; } case I_ARRAY: { cip++; iarray(); // Array assignment break; } case I_EEPROM: { cip++; ieeprom(); // EEPROM assignment break; } default: { err = ERR_LETWOV; break; } } } ///////////////////////////////////////////////////////////////// void i_func(void) { uint16_t arg1, arg2; // Parameter fuer func if (*cip != I_NUM) { err= ERR_NOVAL; return; } cip++; arg1 = *cip; cip++; arg1 += (*cip << 8); cip++; // Komma abfragen if (*cip != I_COMMA) { err= ERR_NOVAL; return; } cip++; if ((*cip != I_NUM) && (*cip != I_VAR)) { err= ERR_NOVAL; return; } if (*cip == I_NUM) { cip++; arg2= *cip; cip++; arg2 += (*cip << 8); } else { cip++; arg2= var[*cip]; } cip++; sysfunc(arg1, arg2); } void i_delay(void) { int16_t value = iexp(); if (err) return; delay(value); } void i_byteout(void) { int16_t value = iexp(); if (err) return; if (value > 255) { err = ERR_IOERR; return; } ioutbyte((uint8_t)value); } void i_goto(void) { int16_t lineno; // Zeilennummer uint8_t* lp; // Zeiger auf Zeile lineno = iexp(); if (err) return; lp = getlp(lineno); // Zeilennummer suchen if (lineno != getlineno(lp)) // wenn nicht gefunden { err = ERR_ULN; return; } clp = lp; cip = clp + 3; } void i_gosub(void) { int16_t lineno; // Zeilennummer uint8_t* lp; // Zeiger auf Zeile lineno = iexp(); if (err) return; lp = getlp(lineno); if (lineno != getlineno(lp)) { err = ERR_ULN; return; } // push pointers if (gstki > SIZE_GSTK - 2) // stack overflow ? { err = ERR_GSTKOF; return; } gstk[gstki++] = clp; gstk[gstki++] = cip; clp = lp; cip = clp + 3; } void i_return(void) { if (gstki < 2) // stack underflow ? { err = ERR_GSTKUF; return; } cip = gstk[--gstki]; clp = gstk[--gstki]; } void i_for(void) { int16_t index, vto, vstep; // FOR-NEXT items if (*cip++ != I_VAR) { err = ERR_FORWOV; return; } index = *cip; ivar(); if (err) return; if (*cip == I_TO) { cip++; vto = iexp(); } else { err = ERR_FORWOTO; return; } if (*cip == I_STEP) { cip++; vstep = iexp(); } else { vstep = 1; } // overflow check if (((vstep < 0) && (-32767 - vstep > vto)) || ((vstep > 0) && (32767 - vstep < vto))) { err = ERR_VOF; return; } // push pointers if (lstki > SIZE_LSTK - 5) // stack overflow ? { err = ERR_LSTKOF; return; } lstk[lstki++] = clp; lstk[lstki++] = cip; lstk[lstki++] = (uint8_t*)(uintptr_t)vto; lstk[lstki++] = (uint8_t*)(uintptr_t)vstep; lstk[lstki++] = (uint8_t*)(uintptr_t)index; } void i_next(void) { int16_t index, vto, vstep; // FOR-NEXT items if (lstki < 5) // leerer Stack { err = ERR_LSTKUF; return; } index = (int16_t)(uintptr_t)lstk[lstki - 1]; if (*cip++ != I_VAR) { err = ERR_NEXTWOV; return; } if (*cip++ != index) { err = ERR_NEXTUM; return; } vstep = (int16_t)(uintptr_t)lstk[lstki - 2]; var[index] += vstep; vto = (int16_t)(uintptr_t)lstk[lstki - 3]; // loop end if (((vstep < 0) && (var[index] < vto)) || ((vstep > 0) && (var[index] > vto))) { lstki -= 5; return; } // loop cip = lstk[lstki - 4]; clp = lstk[lstki - 5]; } void i_if(void) { int16_t condition; // IF condition condition = iexp(); if (err) { err = ERR_IFWOC; return; } if (condition) return; } void i_rem(void) { while (*cip != I_EOL) cip++; } void i_semi(void) { } // POKE addr, value void i_poke(void) { uint16_t addr = getparam(); if (err) return; if (*cip != I_COMMA) { err = ERR_SYNTAX; return; } cip++; int16_t value = getparam(); if (err) return; *((uint8_t*)addr) = (uint8_t)value; } typedef void(*EXE_FP)(void); const EXE_FP exe_fp[] = { // Value ivar, ieeprom, iarray, // Others i_goto, i_gosub, i_return, i_for, i_next, i_if, i_rem, iinput, iprint, ilet, i_semi, ioutput, i_func, i_delay, i_poke, i_byteout, }; uint8_t *iexe(void) // I-Code ausfuehren { while (*cip != I_EOL) { if (uart_ischar()) { if (uart_readkey() == 27) // ESC { err = ERR_ESC; return NULL; } } if (*cip == I_STOP) { while (*clp) clp += *clp; return clp; } EXE_FP fp = (*cip <= I_BYTEOUT && *cip >= I_VAR) ? exe_fp[(*cip) - I_VAR] : NULL; if (fp) { cip++; fp(); if (err) break; } else { err = ERR_SYNTAX; break; } if (err) return NULL; } return clp + *clp; } ///////////////////////////////////////////////////////////////// void irun(void) { uint8_t* lp; gstki = 0; lstki = 0; clp = listbuf; while (*clp) { cip = clp + 3; lp = iexe(); if (err) return; clp = lp; } } void ilist(void) { int16_t lineno; lineno = (*cip == I_NUM) ? getlineno(cip) : 0; for (clp = listbuf; *clp && (getlineno(clp) < lineno); clp += *clp); { while (*clp) { putnum(getlineno(clp), 0); uart_putchar(' '); putlist(clp + 3); if (err) break; newline(); clp += *clp; } } } void inew(void) { uint8_t i; for (i = 0; i < 26; i++) var[i] = 0; for (i = 0; i < SIZE_ARRY; i++) arr[i] = 0; gstki = 0; lstki = 0; *listbuf = 0; clp = listbuf; } void icom(void) // Kommandoprozessor { // int i; cip = ibuf; switch (*cip) { case I_SAVE: { load_save_prog(0); break; } case I_LOAD: { load_save_prog(1); break; } case I_NEW: { cip++; if (*cip == I_EOL) inew(); else err = ERR_SYNTAX; break; } case I_LIST: { cip++; if (*cip == I_EOL || *(cip + 3) == I_EOL) ilist(); else err = ERR_SYNTAX; break; } case I_RUN: { cip++; irun(); break; } default: { iexe(); break; } } } void error(void) // gibt OK oder Fehlermeldung aus { if (err) { newline(); if (cip >= listbuf && cip < listbuf + SIZE_LIST && *clp) { uart_putstring("LINE:"); putnum(getlineno(clp), 0); uart_putchar(' '); putlist(clp + 3); } else { uart_putstring("YOU TYPE: "); uart_putstring(lbuf); } } newline(); /* if (err == ERR_OK) uart_putstring("OK"); else if (err == ERR_ESC) uart_putstring("ESC Break"); else { uart_putstring("Err"); putnum(err, 0); } */ uart_putstring((char *)errmsg[err]); newline(); err = 0; } /* ----------------------------------------------------------------------- basic Hauptprogramm und USER-Interface zur Interaktion ----------------------------------------------------------------------- */ void basic_run(const char *msg, const char *code) { uint8_t len; uart_putstring((char *)msg); strcpy(lbuf, code); len = toktoi(); icom(); error(); // Fehlerflag loeschen, OK als Prompt } void basic(void) { uint8_t len; basic_run(title, "print \" \",size(),\" Bytes free\""); if (is_autorun()) { basic_run("\n\rAutoload...", "load"); basic_run("\n\rProgram running\n\r", "run"); } // Input 1 line and execute // sequentiell eine Zeile einlesen und ausfuehren while (1) { uart_putstring("> "); uart_getstring(); len = toktoi(); if (err) { error(); continue; } if (*ibuf == I_NUM) { *ibuf = len; inslist(); if (err) error(); continue; } icom(); // direkte Kommandoausfuehrung error(); // Fehler loeschen, OK ausgeben } } void sysfunc(int16_t v1, int16_t v2) { switch (v1) { // chr: gibt ein Byte als Asciizeichen aus case 1 : { uart_putchar(v2 & 0xff); break; } default : break; } } /* --------------------------------------------------------------------- M-A-I-N --------------------------------------------------------------------- */ int main(void) { sysclock_init(0); uart_init(BAUDRATE); autorun_init(); inew(); basic(); return 0; }