Subversion Repositories Krakow BASIC

Rev

Rev 3 | Rev 6 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* $Id: basic.c 4 2024-09-03 22:40:08Z nishi $ */

/* Krakow BASIC - Multi-platform simple BASIC */

#if defined(PLATFORM_SHIROI)

#include "dri/text.h"
#include "dri/video.h"
#include "dri/math.h"

#include "mem.h"
#include "char.h"
#define PLATFORM "Shiroi"
#define NEWLINE "\r\n"
#define BREAKKEY

#elif defined(PLATFORM_UNIX) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_ARDUINO) || defined(PLATFORM_C64)

#if defined(PLATFORM_WINDOWS)
#define PLATFORM "Windows"
#elif defined(PLATFORM_UNIX)
#define PLATFORM "Unix"
#elif defined(PLATFORM_ARDUINO)
#define PLATFORM "Arduino"
#define NEWLINE "\r\n"
#define BREAKKEY
#elif defined(PLATFORM_C64)
#define PLATFORM "Commodore-64"
#define NEWLINE "\r\n"
#define BREAKKEY
#include <conio.h>
#endif

#define mull(x, y) ((x) * (y))
#define divl(x, y) ((x) / (y))
#define killcursor(x)
#define cursor(x)
#define strnum atoi

#ifndef NEWLINE
#define NEWLINE "\n"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#if defined(__MINGW32__)
#include <conio.h>
#include <windows.h>
#elif defined(PLATFORM_ARDUINO)
#define BAUD 9600
#include <avr/io.h>
#include <util/delay.h>
#include <util/setbaud.h>
#define BUFFER_SIZE (1024)
#define LINE_BUFFER_SIZE (128)
#define LINES (32)
#undef putchar
#define putchar uart_putchar
#elif defined(PLATFORM_UNIX)
#include <termios.h>
#elif defined(PLATFORM_C64)
#define BUFFER_SIZE (16 * 1024)
#undef killcursor
#undef cursor
#define killcursor(x) cursor(0)
#define cursor(x) cursor(1)
#endif

#if defined(PLATFORM_ARDUINO)
int uart_putchar(char c) {
        while(!(UCSR0A & _BV(UDRE0)))
                ;
        UDR0 = c;
        return 0;
}

void uart_init(void) {
        UBRR0H = UBRRH_VALUE;
        UBRR0L = UBRRL_VALUE;

        UCSR0B |= _BV(TXEN0) | _BV(RXEN0);

        UCSR0C |= _BV(UCSZ00) | _BV(UCSZ01);
}
#endif

#define agetch(x) oggetch(0)
char oggetch(char wait) {
        int c;
#if defined(PLATFORM_WINDOWS)
rescan:
        c = _getch();
        if(c == '\r') return '\n';
        if(c == '\n') goto rescan;
#elif defined(PLATFORM_UNIX)
        c = getchar();
        if(c == EOF) return -1;
        if(c == '\r') return '\n';
#elif defined(PLATFORM_ARDUINO)
rescan:
        if(wait){
                if(!(UCSR0A & _BV(RXC0))) return 0;
        }else{
                while(!(UCSR0A & _BV(RXC0)));
        }
        c = UDR0;
        if(c == '\r') return '\n';
        if(c == '\n') goto rescan;
        if(c == 3) return 1;
#elif defined(PLATFORM_C64)
        if(!wait){
                if(!kbhit()) return 0;
        }
        c = cgetc();
        if(c == EOF) return -1;
        if(c == '\r') return '\n';
        if(c == 20) return 8;
        if(c == 3) return 1;
#endif
        return c;
}

bool strcaseequ(const char* a, const char* b) { return strcasecmp(a, b) == 0; }

#if defined(PLATFORM_ARDUINO)
void putstr(const char* n) {
        int i;
        for(i = 0; n[i] != 0; i++) {
                uart_putchar(n[i]);
        }
}

void putnum(int n) {
        char buf[64];
        int incr = 63;
        buf[incr--] = 0;
        while(1) {
                buf[incr--] = (n % 10) + '0';
                n /= 10;
                if(n == 0) break;
        }
        putstr(buf + incr + 1);
}
#else
void putnum(int n) {
        printf("%d", n);
        fflush(stdout);
}

void putstr(const char* n) {
        printf("%s", n);
        fflush(stdout);
}
#endif

void change_color(int a) {
#if defined(PLATFORM_ARDUINO) || defined(PLATFORM_UNIX)
        int fg = (a >> 4) & 0xf;
        int bg = (a & 0xf);
        char color[2];
        if(!(0 <= fg && fg <= 15)) return;
        if(!(0 <= bg && bg <= 15)) return;
        color[1] = 0;
        if(bg < 8) {
                color[0] = bg + '0';
                putstr("\x1b[4");
        } else {
                color[0] = (bg - 8) + '0';
                putstr("\x1b[10");
        }
        putstr(color);
        putstr("m");
        if(fg < 8) {
                color[0] = fg + '0';
                putstr("\x1b[3");
        } else {
                color[0] = (fg - 8) + '0';
                putstr("\x1b[9");
        }
        putstr(color);
        putstr("m");
        putstr("\x1b[2J\x1b[1;1H");
#elif defined(PLATFORM_C64)
        int fg = (a >> 4) & 0xf;
        int bg = (a & 0xf);
        bgcolor(bg);
        textcolor(fg);
#endif
}

void clear(void) {
#if defined(PLATFORM_WINDOWS)
        system("cls");
#elif defined(PLATFORM_UNIX) || defined(PLATFORM_ARDUINO)
        putstr("\x1b[0m\x1b[2J\x1b[1;1H");
#elif defined(PLATFORM_C64)
        clrscr();
#endif
}

void basic(void);

int main() {
#if defined(PLATFORM_WINDOWS)
        HANDLE winstdout = GetStdHandle(STD_OUTPUT_HANDLE);
        DWORD mode = 0;
        GetConsoleMode(winstdout, &mode);
        const DWORD origmode = mode;
        mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(winstdout, mode);
#elif defined(PLATFORM_UNIX)
        struct termios old, new;
        tcgetattr(0, &old);
        new = old;
        new.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &new);
#elif defined(PLATFORM_ARDUINO)
        uart_init();
        DDRB |= _BV(DDB5);
        PORTB |= _BV(PORT5);
#endif
#if defined(PLATFORM_C64)
        change_color((1 << 4) | 0);
#endif
        basic();
#if defined(PLATFORM_WINDOWS)
        SetConsoleMode(winstdout, origmode);
#elif defined(PLATFORM_UNIX)
        tcsetattr(0, TCSANOW, &old);
#endif
}

#else
#error "Define PLATFORM_*"
#endif

#define VERSION "0.0"

#ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE (512)
#endif

#ifndef BUFFER_SIZE
#define BUFFER_SIZE (1024 * 24)
#endif

#ifndef LINES
#define LINES (1024)
#endif

unsigned char basicbuffer[BUFFER_SIZE];
char linebuf[LINE_BUFFER_SIZE];

int pexpr(char* expr, char* buffer, int* number) {
        char ownbuf[128];
        int i;
        int start = 0;
        int br = 0;
        int result = 0;
        int stack[32];
        int sp = 0;
        char put = 0;
        for(i = 0; expr[i] != 0; i++) ownbuf[i] = expr[i];
        ownbuf[i] = 0;
        for(i = 0; i < 32; i++) stack[i] = 0;
        for(i = 0;; i++) {
                if(ownbuf[i] == 0) {
                        break;
                } else if('0' <= ownbuf[i] && ownbuf[i] <= '9') {
                        stack[sp] *= 10;
                        stack[sp] += ownbuf[i] - '0';
                        put = 1;
                } else if(ownbuf[i] == '+' || ownbuf[i] == '-' || ownbuf[i] == '*' || ownbuf[i] == '/') {
                        put = 0;
                        if(sp < 2) {
                                return -1;
                        } else {
                                int top = stack[--sp];
                                int bottom = stack[--sp];
                                int value = 0;
                                if(ownbuf[i] == '+') {
                                        value = top + bottom;
                                } else if(ownbuf[i] == '-') {
                                        value = bottom + top;
                                } else if(ownbuf[i] == '*') {
                                        value = mull(top, bottom);
                                } else if(ownbuf[i] == '/') {
                                        value = divl(bottom, top);
                                }
                                stack[sp++] = value;
                        }
                        stack[sp] = 0;
                } else if(ownbuf[i] == ' ' && put == 1) {
                        stack[++sp] = 0;
                }
        }
        result = stack[0];
        *number = result;
        return 1;
}

/* arr gets sorted, arr2 index gets replaced */
void sort(int* arr, int* arr2, int size) {
        int i;
redo:
        for(i = 1; i < size; i++) {
                if(arr[i - 1] > arr[i]) {
                        int tmp = arr[i];
                        arr[i] = arr[i - 1];
                        arr[i - 1] = tmp;
                        tmp = arr2[i];
                        arr2[i] = arr2[i - 1];
                        arr2[i - 1] = tmp;
                }
        }
        for(i = 1; i < size; i++) {
                if(arr[i - 1] > arr[i]) {
                        goto redo;
                }
        }
}

int run(char* cmd, int linenum, char num, int* lgoto) {
        char line[LINE_BUFFER_SIZE];
        char rcmd[32];
        int i;
        char* arg;
        int incr = 0;
#ifdef BREAKKEY
        if(agetch() == 1) return -1;
#endif
        if(lgoto != 0) *lgoto = 0;
        for(i = 0; cmd[i] != 0; i++) line[i] = cmd[i];
        line[i] = 0;
        rcmd[0] = 0;
        for(i = 0;; i++) {
                if(line[i] == ' ' || line[i] == '\t' || line[i] == 0 || line[i] == '"') {
                        break;
                } else {
                        rcmd[incr++] = line[i];
                        if(incr == 32) {
                                putstr("! Command too long");
                                if(linenum != -1) {
                                        putstr(" in line ");
                                        putnum(linenum);
                                }
                                putstr(NEWLINE);
                                return 1;
                        }
                        rcmd[incr] = 0;
                }
        }
        arg = line + 1 + strlen(rcmd);
        if(strcaseequ(rcmd, "COLOR")) {
                int argc = 0;
                char* farg = 0;
                char* sarg = 0;
                int fgcolor, bgcolor, ret0, ret1;
                if(arg[0] != 0) argc++;
                for(i = 0; arg[i] != 0; i++) {
                        if(arg[i] == ',') {
                                arg[i] = 0;
                                farg = arg;
                                sarg = arg + i + 1;
                                for(; *sarg != 0 && (*sarg == '\t' || *sarg == ' '); sarg++)
                                        ;
                                argc++;
                        }
                }
                if(argc != 2) {
                        putstr("! Invalid argument");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                }
                bgcolor = 0;
                fgcolor = 0;
                ret0 = pexpr(farg, 0, &bgcolor);
                ret1 = pexpr(sarg, 0, &fgcolor);
                if(ret0 == 0) {
                        putstr("! Invalid argument");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                } else if(ret0 == -1) {
                        putstr("! Syntax error");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                }
                if(ret1 == 1) {
                        change_color((fgcolor << 4) | bgcolor);
                } else if(ret1 == 0) {
                        putstr("! Invalid argument");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                } else if(ret1 == -1) {
                        putstr("! Syntax error");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                }
        } else if(num == 0 && strcaseequ(rcmd, "LIST")) {
                int addr = BUFFER_SIZE - 1;
                int saddr = 0;
                int lbuf[LINES];
                int shift[LINES];
                int cnt = 0;
                int i;
                while(1) {
                        unsigned long ln = 0;
                        for(i = 0; i < 4; i++) {
                                ln >>= 8;
                                ln |= (unsigned long)basicbuffer[addr--] << 24;
                        }
                        if(ln == 0) break;
                        lbuf[cnt] = ln;
                        shift[cnt++] = saddr;
                        saddr += strlen(basicbuffer + saddr) + 1;
                }
                sort(lbuf, shift, cnt);
                for(i = 0; i < cnt; i++) {
                        putnum(lbuf[i]);
                        putstr(" ");
                        putstr(basicbuffer + shift[i]);
                        putstr(NEWLINE);
                }
        } else if(num == 1 && strcaseequ(rcmd, "GOTO")) {
                int ret = pexpr(arg, 0, lgoto);
                if(ret == 0) {
                        putstr("! Invalid argument");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                } else if(ret == -1) {
                        putstr("! Syntax error");
                        if(linenum != -1) {
                                putstr(" in line ");
                                putnum(linenum);
                        }
                        putstr(NEWLINE);
                        return 1;
                }
        } else if(num == 0 && strcaseequ(rcmd, "RUN")) {
                int addr = BUFFER_SIZE - 1;
                int saddr = 0;
                int lbuf[LINES];
                int shift[LINES];
                int cnt = 0;
                int gt, i;
                while(1) {
                        unsigned long ln = 0;
                        for(i = 0; i < 4; i++) {
                                ln >>= 8;
                                ln |= (unsigned long)basicbuffer[addr--] << 24;
                        }
                        if(ln == 0) break;
                        lbuf[cnt] = ln;
                        shift[cnt++] = saddr;
                        saddr += strlen(basicbuffer + saddr) + 1;
                }
                sort(lbuf, shift, cnt);
                for(i = 0; i < cnt; i++) {
                        int ret = run(basicbuffer + shift[i], lbuf[i], 1, &gt);
                        if(ret != 0) return ret;
                        if(gt != 0) {
                                char found;
                                for(i = 0; i < cnt; i++) {
                                        if(lbuf[i] == gt) {
                                                found = 1;
                                                break;
                                        }
                                }
                                if(found) {
                                        i--;
                                } else {
                                        putstr("! GOTO no such line");
                                        if(linenum != -1) {
                                                putstr(" in line ");
                                                putnum(linenum);
                                        }
                                        putstr(NEWLINE);
                                        return 1;
                                }
                        }
                }
        } else {
                putstr("! Unknown command");
                if(linenum != -1) {
                        putstr(" in line ");
                        putnum(linenum);
                }
                putstr(NEWLINE);
                return 1;
        }
        return 0;
}

int execute(int linenum, char* cmd, char num) {
        int i;
        char line[LINE_BUFFER_SIZE];
        int incr = 0;
        char dq = 0;
        for(i = 0; cmd[i] != 0; i++) {
                if(cmd[i] == ' ' || cmd[i] == '\t') {
                        if(!dq) {
                                for(; cmd[i] != 0 && (cmd[i] == ' ' || cmd[i] == '\t'); i++)
                                        ;
                                i--;
                        }
                        line[incr++] = cmd[i];
                } else if(cmd[i] == '"') {
                        line[incr++] = '"';
                        dq = dq == 0 ? 1 : 0;
                } else {
                        line[incr++] = dq == 0 ? toupper(cmd[i]) : cmd[i];
                }
        }
        line[incr] = 0;
        if(num == 0) {
                int ret = run(line, -1, 0, 0);
                putstr("Ready\r\n");
                return ret;
        } else {
                int addr = BUFFER_SIZE - 1;
                int i;
                int shf = 0;
                int cnt = 0;
                int len;
                while(1) {
                        unsigned long ln = 0;
                        for(i = 0; i < 4; i++) {
                                ln >>= 8;
                                ln |= (unsigned long)basicbuffer[addr--] << 24;
                        }
                        cnt++;
                        if(ln == linenum) shf = cnt;
                        if(shf != 0) {
                                addr += 4;
                                for(i = 0; i < 4; i++) {
                                        basicbuffer[addr] = basicbuffer[addr - 4 * shf];
                                        addr--;
                                }
                                addr += 4;
                                ln = 0;
                                for(i = 0; i < 4; i++) {
                                        ln >>= 8;
                                        ln |= (unsigned long)basicbuffer[addr--] << 24;
                                }
                        }
                        if(ln == 0) break;
                }
                if(line[0] != 0) {
                        addr += 4;
                        for(i = 0; i < 4; i++) {
                                basicbuffer[addr--] = linenum & 0xff;
                                linenum >>= 8;
                        }
                }
                len = 0;
                cnt = 0;
                while(1) {
                        int slen = strlen(basicbuffer + len);
                        if(slen == 0) break;
                        len += slen + 1;
                        cnt++;
                }
                if(line[0] != 0) {
                        for(i = 0; line[i] != 0; i++) {
                                basicbuffer[len + i] = line[i];
                        }
                        basicbuffer[len + i] = 0;
                        basicbuffer[len + i + 1] = 0;
                }
                if(shf != 0) {
                        cnt = 0;
                        len = 0;
                        while(1) {
                                int slen = strlen(basicbuffer + len);
                                if(slen == 0) break;

                                len += slen + 1;
                                cnt++;

                                if(cnt == shf) {
                                        int i;
                                        int nc = 0;
                                        len -= slen + 1;
                                        for(i = len;; i++) {
                                                basicbuffer[i] = basicbuffer[i + slen + 1];
                                                if(basicbuffer[i] == 0) {
                                                        nc++;
                                                        if(nc == 2) break;
                                                } else {
                                                        nc = 0;
                                                }
                                        }
                                        break;
                                }
                        }
                }
                return 0;
        }
}

void basic(void) {
        int i;
        int lineind;
        clear();

#ifdef SMALL
        putstr("Krakow BASIC  Ver. ");
        putstr(VERSION);
        putstr(NEWLINE);
        putstr(NEWLINE);
#else
        putstr(PLATFORM);
        putstr("   Krakow BASIC V");
        putstr(VERSION);
        putstr("\r\n");
        putstr("Copyright 2024 by: Nishi.\r\n");
        putstr("                   penguin2233.");
        putstr(NEWLINE);
        putstr(NEWLINE);
        putstr(" Max ");
        putnum(LINE_BUFFER_SIZE);
        putstr(" characters per line");
        putstr(NEWLINE);
        putstr(" Max ");
        putnum(LINES);
        putstr(" lines");
        putstr(NEWLINE);
        putstr(" ");
#endif
        putnum(BUFFER_SIZE);
        putstr(" bytes free");
        putstr(NEWLINE);
        putstr(NEWLINE);

        for(i = 0; i < BUFFER_SIZE; i++) {
                basicbuffer[i] = 0;
        }
        putstr("Ready");
        putstr(NEWLINE);

        cursor();
        linebuf[0] = 0;
        lineind = 0;
        while(1) {
                char c;
#if defined(PLATFORM_C64)
                c = oggetch(1);
#else
                c = agetch();
#endif
                if(c != 0) killcursor();
                if(c == 1) {
                        putstr("Break");
                        putstr(NEWLINE);
                        lineind = 0;
                } else if(c == '\n') {
                        int i;
                        char num = 1;
                        char* cmd = linebuf;

                        linebuf[lineind] = 0;
                        putstr(NEWLINE);
                        if(lineind == 0) goto skip;

                        for(i = 0; linebuf[i] != 0; i++) {
                                if(linebuf[i] == ' ') {
                                        linebuf[i] = 0;
                                        cmd = linebuf + i + 1;
                                        break;
                                } else if(!('0' <= linebuf[i] && linebuf[i] <= '9')) {
                                        num = 0;
                                        break;
                                }
                        }

                        if(num == 1 && strnum(linebuf) == 0) {
                                putstr("! Line number 0 is illegal");
                                putstr(NEWLINE);
                        } else {
                                int ret = execute(num == 1 ? strnum(linebuf) : -1, (num == 1 && cmd == linebuf) ? "" : cmd, num);
                                if(ret == -1) {
                                        putstr("! Break");
                                        putstr(NEWLINE);
                                }
                        }

                skip:
                        lineind = 0;
                } else if(c == 0x8 || c == 0x7f) {
                        if(lineind > 0) {
                                putstr("\x08 \x08");
                                linebuf[--lineind] = 0;
                        }
                } else if(c == -1) {
                        break;
                } else if(c != 0) {
                        putchar(c);
                        linebuf[lineind++] = c;
                }
                cursor();
        }
}