From 4d65fcb9a8b6c7c6fd5a3390c46a96d11b6a80d4 Mon Sep 17 00:00:00 2001 From: Alex AUVOLAT Date: Sat, 8 Jun 2013 23:09:52 +0200 Subject: All FWIK is deleted. YOSH is now pure C. Not-working KBASIC included. --- Makefile | 10 +- src/common.make | 2 +- src/include/errno.h | 2 +- src/include/tce/syscalls.h | 5 + src/include/tce/vfs.h | 2 - src/kernel/config.h | 4 +- src/kernel/core/sys.cpp | 8 +- src/kernel/task/task.cpp | 10 +- src/kernel/task/task.h | 6 +- src/kernel/ui/vt.cpp | 41 +-- src/kernel/ui/vt.h | 2 +- src/kernel/vfs/node.h | 3 + src/user/app/fwik.make | 6 - src/user/app/init/main.c | 4 +- src/user/app/kbasic/Makefile | 9 + src/user/app/kbasic/basic.h | 75 +++++ src/user/app/kbasic/commands.c | 103 +++++++ src/user/app/kbasic/control.c | 511 ++++++++++++++++++++++++++++++++ src/user/app/kbasic/expr.c | 153 ++++++++++ src/user/app/kbasic/kbasic.txt | 19 ++ src/user/app/kbasic/lex.c | 197 ++++++++++++ src/user/app/kbasic/main.c | 55 ++++ src/user/app/kbasic/pppg.bas | 14 + src/user/app/kbasic/tables.bas | 15 + src/user/app/kbasic/var.c | 19 ++ src/user/app/led/main.c | 8 +- src/user/app/prime/main.c | 2 +- src/user/app/test/main.c | 2 +- src/user/app/yosh/Makefile | 2 +- src/user/app/yosh/main.c | 292 ++++++++++++++++++ src/user/app/yosh/main.cpp | 271 ----------------- src/user/lib/fwik/Makefile | 11 - src/user/lib/fwik/String.cpp | 176 ----------- src/user/lib/fwik/include/IO/Dir.h | 24 -- src/user/lib/fwik/include/IO/IOStream.h | 39 --- src/user/lib/fwik/include/IO/Node.h | 32 -- src/user/lib/fwik/include/IO/Term.h | 33 --- src/user/lib/fwik/include/String.h | 42 --- src/user/lib/fwik/include/cpp.h | 16 - src/user/lib/fwik/io/Dir.cpp | 37 --- src/user/lib/fwik/io/IOStream.cpp | 19 -- src/user/lib/fwik/io/Node.cpp | 118 -------- src/user/lib/fwik/io/Term.cpp | 62 ---- src/user/lib/fwik/main.cpp | 37 --- src/user/lib/libc/Makefile | 3 +- src/user/lib/libc/include/ctype.h | 24 ++ src/user/lib/libc/include/readline.h | 4 +- src/user/lib/libc/include/setjmp.h | 25 ++ src/user/lib/libc/include/stdio.h | 53 +++- src/user/lib/libc/include/stdlib.h | 15 +- src/user/lib/libc/include/string.h | 5 + src/user/lib/libc/include/tce/syscall.h | 16 +- src/user/lib/libc/start.c | 18 +- src/user/lib/libc/std/ctype.c | 28 ++ src/user/lib/libc/std/readline.c | 19 +- src/user/lib/libc/std/setjmp.asm | 37 +++ src/user/lib/libc/std/stdio.c | 245 ++++++++++++++- src/user/lib/libc/std/stdlib.c | 32 +- src/user/lib/libc/std/string.c | 113 ++++++- src/user/lib/libc/tce/syscall.c | 28 +- src/user/link.ld | 2 +- 61 files changed, 2116 insertions(+), 1049 deletions(-) delete mode 100644 src/user/app/fwik.make create mode 100644 src/user/app/kbasic/Makefile create mode 100644 src/user/app/kbasic/basic.h create mode 100644 src/user/app/kbasic/commands.c create mode 100644 src/user/app/kbasic/control.c create mode 100644 src/user/app/kbasic/expr.c create mode 100644 src/user/app/kbasic/kbasic.txt create mode 100644 src/user/app/kbasic/lex.c create mode 100644 src/user/app/kbasic/main.c create mode 100644 src/user/app/kbasic/pppg.bas create mode 100644 src/user/app/kbasic/tables.bas create mode 100644 src/user/app/kbasic/var.c create mode 100644 src/user/app/yosh/main.c delete mode 100644 src/user/app/yosh/main.cpp delete mode 100644 src/user/lib/fwik/Makefile delete mode 100644 src/user/lib/fwik/String.cpp delete mode 100644 src/user/lib/fwik/include/IO/Dir.h delete mode 100644 src/user/lib/fwik/include/IO/IOStream.h delete mode 100644 src/user/lib/fwik/include/IO/Node.h delete mode 100644 src/user/lib/fwik/include/IO/Term.h delete mode 100644 src/user/lib/fwik/include/String.h delete mode 100644 src/user/lib/fwik/include/cpp.h delete mode 100644 src/user/lib/fwik/io/Dir.cpp delete mode 100644 src/user/lib/fwik/io/IOStream.cpp delete mode 100644 src/user/lib/fwik/io/Node.cpp delete mode 100644 src/user/lib/fwik/io/Term.cpp delete mode 100644 src/user/lib/fwik/main.cpp create mode 100644 src/user/lib/libc/include/ctype.h create mode 100644 src/user/lib/libc/include/setjmp.h create mode 100644 src/user/lib/libc/std/ctype.c create mode 100644 src/user/lib/libc/std/setjmp.asm diff --git a/Makefile b/Makefile index 947874a..c44c64f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ .PHONY: clean, mrproper, Init.rfs, floppy, commit -Projects = tools/makeinitrd kernel user/lib/libc user/lib/fwik user/app/init user/app/test user/app/yosh user/app/prime user/app/led +Projects = tools/makeinitrd kernel +Projects += user/lib/libc +Projects += user/app/init user/app/test user/app/prime user/app/led +Projects += user/app/kbasic +Projects += user/app/yosh QemuCmd = qemu-system-i386 BasePath = $(shell pwd) @@ -36,7 +40,7 @@ commit: mrproper git commit -a; exit 0 git push origin -$(Cdrom): menu_cdrom.lst src/kernel/kernel.elf src/user/app/test/test.elf src/user/app/init/init.elf src/user/app/yosh/yosh.elf src/user/app/prime/prime.elf src/user/app/led/led.elf +$(Cdrom): menu_cdrom.lst src/kernel/kernel.elf src/user/app/test/test.elf src/user/app/init/init.elf src/user/app/yosh/yosh.elf src/user/app/prime/prime.elf src/user/app/led/led.elf src/user/app/kbasic/kbasic.elf mkdir -p cdrom/boot/grub if [ ! -e cdrom/boot/grub/stage2_eltorito ]; then \ echo "Please copy grub's stage2_eltorito to cdrom/boot/grub."; \ @@ -49,11 +53,13 @@ $(Cdrom): menu_cdrom.lst src/kernel/kernel.elf src/user/app/test/test.elf src/us cp src/user/app/yosh/yosh.elf cdrom; strip cdrom/yosh.elf cp src/user/app/prime/prime.elf cdrom; strip cdrom/prime.elf cp src/user/app/led/led.elf cdrom; strip cdrom/led.elf + cp src/user/app/kbasic/kbasic.elf cdrom; strip cdrom/kbasic.elf src/tools/makeinitrd/makeinitrd cdrom/initrd \ cdrom/test.elf:/bin/test \ cdrom/yosh.elf:/bin/yosh \ cdrom/prime.elf:/bin/prime \ cdrom/led.elf:/bin/led \ + cdrom/kbasic.elf:/bin/kbasic \ README:/readme genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 \ -boot-info-table -input-charset ascii -A TCE -o $(Cdrom) cdrom diff --git a/src/common.make b/src/common.make index 4ffb6c6..be30752 100644 --- a/src/common.make +++ b/src/common.make @@ -3,7 +3,7 @@ # ============== ENVIRONMENT VARIABLES CC = i586-elf-gcc -CFLAGS = -m32 -nostdlib -nostdinc -nostartfiles -nodefaultlibs -fno-builtin -ffreestanding -fno-stack-protector -Wall -Werror -g -Wno-error=main +CFLAGS = -m32 -nostdlib -nostdinc -nostartfiles -nodefaultlibs -fno-builtin -ffreestanding -fno-stack-protector -Wall -Werror -Wno-error=main CCFLAGS = CXX = i586-elf-g++ CXXFLAGS = -fno-rtti -fno-exceptions -Wno-write-strings -Wno-main -Wno-error=unused-parameter diff --git a/src/include/errno.h b/src/include/errno.h index fe60d81..098bcc1 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -8,7 +8,7 @@ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* No such process */ #define EINTR 4 /* Interrupted system call */ -#define EIO 5 /* I/O error */ +#define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Argument list too long */ #define ENOEXEC 8 /* Exec format error */ diff --git a/src/include/tce/syscalls.h b/src/include/tce/syscalls.h index c82cab7..f32c225 100644 --- a/src/include/tce/syscalls.h +++ b/src/include/tce/syscalls.h @@ -29,6 +29,11 @@ #define SC_WRITE 27 #define SC_LINK 28 +#define EX_PAGEFAULT 4000 +#define EX_EXCEPTION 4001 +#define EX_NOTHREADS 4002 +#define EX_INVALID 4003 + // ERRORS #define E_NOT_IMPLEMENTED -1 // could also mean that your request is nonsense diff --git a/src/include/tce/vfs.h b/src/include/tce/vfs.h index 8af3514..36e391f 100644 --- a/src/include/tce/vfs.h +++ b/src/include/tce/vfs.h @@ -3,8 +3,6 @@ #include -typedef int FILE; - typedef struct _file_info { uint32_t type; uint32_t dev_type; diff --git a/src/kernel/config.h b/src/kernel/config.h index 74d0933..b7f6a17 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -2,7 +2,7 @@ #define DEF_CONFIG_H #define K_OS_NAME "T/CE" -#define K_OS_VER "0.1.3" -#define K_OS_CODENAME "FWIcKet" +#define K_OS_VER "0.1.4" +#define K_OS_CODENAME "VivaElC" #endif diff --git a/src/kernel/core/sys.cpp b/src/kernel/core/sys.cpp index 32ed988..01a3c20 100644 --- a/src/kernel/core/sys.cpp +++ b/src/kernel/core/sys.cpp @@ -32,7 +32,7 @@ void stack_trace(size_t bp) { uint32_t *stack = (uint32_t*)bp, i; for (i = 0; i < 5 && (size_t)stack > K_HIGHHALF_ADDR && (size_t)stack < (bp + 0x8000); i++) { *ke_vt << " | " << (size_t)stack; - *ke_vt << "\tnext:" << stack[0] << "\t\tret:" << stack[1] << "\n"; + *ke_vt << "\tnext:" << stack[0] << "\t\tret:" << stack[1] << " \n"; stack = (uint32_t*)stack[0]; } } @@ -40,8 +40,8 @@ void stack_trace(size_t bp) { /* For internal use only. Used by panic and panic_assert. */ static void panic_do(char* file, int line) { asm volatile("cli;"); - *ke_vt << "\n File:\t\t" << file << ":" << line; - *ke_vt << "\nTrace:\n"; + *ke_vt << " \n File:\t\t" << file << ":" << line; + *ke_vt << " \nTrace: \n"; size_t bp; asm volatile("mov %%ebp,%0" : "=r"(bp)); stack_trace(bp); *ke_vt << "\n\t\tSystem halted -_-'"; asm volatile("hlt"); @@ -52,6 +52,7 @@ void panic(char* message, char* file, int line) { ke_vt->fgcolor = TC_WHITE; ke_vt->bgcolor = TC_BLUE; ke_vt->outputTo(text_display); + *ke_vt << " * * * * * * * * * * * * * * * * * * * * * * * * * * "; *ke_vt << "\nPANIC:\t" << message; panic_do(file, line); } @@ -60,6 +61,7 @@ void panic_assert(char* assertion, char* file, int line) { ke_vt->fgcolor = TC_WHITE; ke_vt->bgcolor = TC_RED; ke_vt->outputTo(text_display); + *ke_vt << " * * * * * * * * * * * * * * * * * * * * * * * * * * "; *ke_vt << "\nASSERT FAILED:\t" << assertion; panic_do(file, line); } diff --git a/src/kernel/task/task.cpp b/src/kernel/task/task.cpp index 4a802b8..91f802d 100644 --- a/src/kernel/task/task.cpp +++ b/src/kernel/task/task.cpp @@ -119,10 +119,10 @@ uint32_t tasking_handleException(registers *regs) { *ke_vt << "\n (PID " << current_thread->process->pid << ") "; if (regs->int_no == 14) { *ke_vt << ">>> Process exiting.\n"; - thread_exit_stackJmp(EX_PR_EXCEPTION); + thread_exit_stackJmp(EX_PAGEFAULT); } else { *ke_vt << ">>> Thread exiting.\n"; - thread_exit_stackJmp(EX_TH_EXCEPTION); + thread_exit_stackJmp(EX_EXCEPTION); } PANIC("This should never have happened. Please report this."); return 0; @@ -160,7 +160,7 @@ void thread_exit2(uint32_t reason) { //See EX_TH_* defines in task.h process* pr; if (th == 0 || th->process == 0) goto retrn; pr = th->process; - if ((reason == EX_TH_NORMAL || reason == EX_TH_EXCEPTION) && pr->thread_count > 1) { + if ((reason == EX_NOTHREADS || reason == EX_EXCEPTION) && pr->thread_count > 1) { delete th; } else { pr->finish(reason); @@ -188,12 +188,12 @@ void thread_exit_stackJmp(uint32_t reason) { /* System call. Exit the current thread. */ void thread_exit() { - thread_exit_stackJmp(EX_TH_NORMAL); + thread_exit_stackJmp(EX_NOTHREADS); } /* System call. Exit the current process. */ void process_exit(size_t retval) { - if (retval == EX_TH_NORMAL || retval == EX_TH_EXCEPTION) retval = EX_PR_EXCEPTION; + if (retval == EX_EXCEPTION || retval == EX_NOTHREADS || retval == EX_PAGEFAULT) retval = EX_INVALID; thread_exit_stackJmp(retval); } diff --git a/src/kernel/task/task.h b/src/kernel/task/task.h index fa748c2..5cd3fb2 100644 --- a/src/kernel/task/task.h +++ b/src/kernel/task/task.h @@ -15,14 +15,12 @@ #define PL_USER 1 #define PL_KERNEL 0 -#define EX_TH_NORMAL 0x10000 //ERROR code : just one thread exits, because it has to -#define EX_TH_EXCEPTION 0x10001 //ERROR code : just one thread exits, because of an unhandled exception -#define EX_PR_EXCEPTION 0x10002 //ERROR code : all process finishes, because of an unhandled exception - #define USER_STACK_SIZE 0x10000 //64k, but pages will be mapped one by one as they are used typedef void (*thread_entry)(void*); +typedef int FILE; // DUPLICATE FROM vfs/node.h + class thread; class node; class process { diff --git a/src/kernel/ui/vt.cpp b/src/kernel/ui/vt.cpp index 3bed897..6876806 100644 --- a/src/kernel/ui/vt.cpp +++ b/src/kernel/ui/vt.cpp @@ -18,7 +18,6 @@ vt::vt(node* parent, int ww, int hh) : node(parent, FT_TERMINAL) { bgcolor = TC_BLACK; output = 0; cursor_visible = true; - keyboard_echo = false; csr_l = csr_c = 0; kbd_buffer_filled = 0; @@ -194,9 +193,12 @@ void vt::keyboardInput(keypress kp, keyboard* from) { if ((kp.command == 0 || kp.command == KB_ENTER || kp.command == KB_TAB || kp.command == KB_BACKSPACE) && kp.character != 0 && kp.pressed) { b[n++] = (char)kp.character; - } else if (kp.command == KB_CMD_ALT && kp.character >= 64 && kp.character <= 126 && kp.pressed) { + } else if ((kp.command & KB_CMD_ALT) && kp.character >= 64 && kp.character <= 126 && kp.pressed) { b[n++] = 27; b[n++] = kp.character; + } else if ((kp.command & KB_CMD_CTRL) && kp.character >= 'A' && kp.character <= 'z' && kp.pressed) { + if (kp.character >= 'A' && kp.character <= 'Z') b[n++] = 1 + kp.character - 'A'; + if (kp.character >= 'a' && kp.character <= 'z') b[n++] = 1 + kp.character - 'a'; } else if (kb_cmd_esc[kp.command & 0x3F] != 0 && kp.pressed) { int i; for (i = 0; kb_cmd_esc[kp.command & 0x3F][i] != 0; i++) { @@ -330,10 +332,6 @@ int vt::write(size_t offset, size_t len, char* buffer) { } else if (buffer[i] == 'p') { // restore cursor position csr_l = save_csr_l; csr_c = save_csr_c; - } else if (buffer[i] == 'e') { // CUSTOM : enable keyboard echo - keyboard_echo = true; - } else if (buffer[i] == 'h') { // CUSTOM : disable keyboard echo - keyboard_echo = false; } else if (buffer[i] == '?') { if (buffer[++i] == '2' && buffer[++i] == '5') { if (buffer[++i] == 'l') cursor_visible = false; @@ -361,38 +359,17 @@ int vt::read(size_t offset, size_t len, char* buffer) { unsigned c = 0; while (true) { - bool end = false; if (kbd_buffer_filled > 0) { int r = 0; - if (keyboard_echo) { - for (; r < kbd_buffer_filled; r++) { - if (kbd_buffer[r] == '\b') { - if (c > 0) { - c--; - put('\b'); - } - } else if (kbd_buffer[r] == '\n') { - buffer[c++] = '\n'; - c++; - put('\n'); - end = true; - break; - } else if (kbd_buffer[r] >= ' ') { - buffer[c++] = kbd_buffer[r]; - put(buffer[c]); - } - if (c == len) break; - } - } else { - while (r < kbd_buffer_filled && c < len) { - buffer[c++] = kbd_buffer[r++]; - } - end = true; + while (r < kbd_buffer_filled && c < len) { + buffer[c++] = kbd_buffer[r++]; } + memcpy(kbd_buffer + r, kbd_buffer, (kbd_buffer_filled - r)); kbd_buffer_filled -= r; + + return c; } - if (end || c == len) return c; if (kbd_waiter != 0) return c; kbd_waiter = current_thread; diff --git a/src/kernel/ui/vt.h b/src/kernel/ui/vt.h index a29716f..8a169b4 100644 --- a/src/kernel/ui/vt.h +++ b/src/kernel/ui/vt.h @@ -19,7 +19,7 @@ class vt : public node { vt_char *text; void put_at(int l, int c, int ch); - bool cursor_visible, keyboard_echo; + bool cursor_visible; int kbd_buffer_filled; char kbd_buffer[KBD_BUFFER_SIZE]; diff --git a/src/kernel/vfs/node.h b/src/kernel/vfs/node.h index 2ebf93b..3b1c700 100644 --- a/src/kernel/vfs/node.h +++ b/src/kernel/vfs/node.h @@ -4,6 +4,7 @@ #include #include + #include class display; @@ -38,6 +39,8 @@ node* vfs_find(node* root, char* filename); extern node *root, *dot_dev, *dot_ui; +typedef int FILE; // file descriptor type, DIFFERENT FROM FILE TYPE IN USERLAND !!! + // syscall interface FILE open(char* filename, int mode); FILE open_relative(FILE root, char* filename, int mode); diff --git a/src/user/app/fwik.make b/src/user/app/fwik.make deleted file mode 100644 index 292440d..0000000 --- a/src/user/app/fwik.make +++ /dev/null @@ -1,6 +0,0 @@ -ExtObj = $(SrcPath)/user/lib/fwik/_fwik.o - -include $(SrcPath)/common.make - -CFLAGS += -I $(SrcPath)/include -I $(SrcPath)/user/lib/libc/include -I $(SrcPath)/user/lib/fwik/include -LDFLAGS += -T $(SrcPath)/user/link.ld diff --git a/src/user/app/init/main.c b/src/user/app/init/main.c index 92fb8f7..bd0b3bf 100644 --- a/src/user/app/init/main.c +++ b/src/user/app/init/main.c @@ -1,8 +1,8 @@ #include #include -int main(char **args) { - FILE home_term = open("/.ui/home", 0); +int main(int argc, char **args) { + int home_term = open("/.ui/home", 0); int i, pid; if (home_term < 0) return -1; diff --git a/src/user/app/kbasic/Makefile b/src/user/app/kbasic/Makefile new file mode 100644 index 0000000..22bf991 --- /dev/null +++ b/src/user/app/kbasic/Makefile @@ -0,0 +1,9 @@ +Obj = main.o lex.o control.o commands.o var.o expr.o +Out = kbasic.elf + + +include $(SrcPath)/user/app/common.make + +LDFLAGS += -Map kbasic.map +CFLAGS += -Wno-error=missing-braces + diff --git a/src/user/app/kbasic/basic.h b/src/user/app/kbasic/basic.h new file mode 100644 index 0000000..b3491da --- /dev/null +++ b/src/user/app/kbasic/basic.h @@ -0,0 +1,75 @@ +/* A tiny BASIC interpreter */ + +/* + Thank the Internet for providing me with this program. + Please report any bugs/security holes to: katchup@adnab.fr.nf +*/ + +#ifndef DEF_BASIC_H +#define DEF_BASIC_H + + +// Token types +#define DELIMITER 1 +#define VARIABLE 2 +#define NUMBER 3 +#define COMMAND 4 +#define STRING 5 +#define QUOTE 6 + +// Keywords +#define EOL 98 // Special characters +#define FINISHED 99 +#define IF 1 // Control structures +#define THEN 2 +#define FOR 3 +#define NEXT 4 +#define TO 5 +#define GOTO 6 +#define GOSUB 7 +#define RETURN 8 +#define END 9 +#define EXEC 10 +#define LOAD 11 +#define RUN 12 +#define LIST 13 +#define PRINT 20 // Built-in commands +#define INPUT 21 +#define RND 50 // Built-in functions + +// Buffer size definitions +#define TOK_LEN 80 +#define LAB_LEN 10 // Control structures +#define NUM_LAB 100 +#define FOR_NEST 25 +#define SUB_NEST 25 + +// Lexer +extern char *program; // Data for program +extern char *prog_ip; // Program instructino pointer +extern char token[TOK_LEN]; +extern int token_type, tok; +int get_token(); +void putback(); +void find_eol(); +char *load_file(char *fname); + +// Data structures +int* find_var(char *s); // returns a pointer to the variable + +// Program structure +void init(); +extern char *interp_name; /* given by command line argument 0 */ +void load_program(char *); // load and scan labels +void insert_line(char *l, int n); // add line to program +int start(char *entry); // start execution at given point +void serror(int); + +// BASIC commands +void exec_print(), input(); +void assignment(); +void get_exp(int*); + + +#endif + diff --git a/src/user/app/kbasic/commands.c b/src/user/app/kbasic/commands.c new file mode 100644 index 0000000..f60f32a --- /dev/null +++ b/src/user/app/kbasic/commands.c @@ -0,0 +1,103 @@ +#include +#include +#include + +#include "basic.h" + +/* Assign a variable a value. */ +void assignment() +{ + int *var; + int value; + + /* get the variable name */ + get_token(); + if(!isalpha(*token)) { + serror(4); + return; + } + + var = find_var(token); + + /* get the equals sign */ + get_token(); + if(*token!='=') { + serror(3); + return; + } + + /* get the value to assign to var */ + get_exp(&value); + + /* assign the value */ + *var = value; +} + + + +/* Execute a simple version of the BASIC PRINT statement */ +void exec_print() +{ + int answer; + int len=0, spaces; + char last_delim; + + do { + get_token(); /* get next list item */ + if(tok==EOL || tok==FINISHED) break; + if(token_type==QUOTE) { /* is string */ + printf("%s", token); + len += strlen(token); + get_token(); + } + else { /* is expression */ + putback(); + get_exp(&answer); + get_token(); + len += printf("%d", answer); + } + last_delim = *token; + + if(*token==';') { + /* compute number of spaces to move to next tab */ + spaces = 8 - (len % 8); + len += spaces; /* add in the tabbing position */ + while(spaces) { + printf(" "); + spaces--; + } + } + else if(*token==',') /* do nothing */; + else if(tok!=EOL && tok!=FINISHED) serror(0); + } while (*token==';' || *token==','); + + if(tok==EOL || tok==FINISHED) { + if(last_delim != ';' && last_delim!=',') printf("\n"); + } + else serror(0); /* error is not , or ; */ + +} + +/* Execute a simple form of the BASIC INPUT command */ +void input() +{ + int *var; + int tmp; + + get_token(); /* see if prompt string is present */ + if(token_type==QUOTE) { + printf("%s", token); /* if so, print it and check for comma */ + get_token(); + if(*token!=',') serror(1); + get_token(); + } + else printf("? "); /* otherwise, prompt with / */ + + var = find_var(token); /* get the input var */ + + scanf("%d\n", var); /* read input */ + + /* flush input */ + while ((tmp = getchar()) != '\n' && tmp != EOF); +} + diff --git a/src/user/app/kbasic/control.c b/src/user/app/kbasic/control.c new file mode 100644 index 0000000..1ab22ff --- /dev/null +++ b/src/user/app/kbasic/control.c @@ -0,0 +1,511 @@ +#include +#include +// #include +// #include +#include +#include +#include + +#include "basic.h" + +struct label { + char name[LAB_LEN]; + char *p; /* points to place to go in source file*/ +}; +struct for_stack { + int *var; /* counter variable */ + int target; /* target value */ + char *loc; +}; + +struct label label_table[NUM_LAB]; + +char *gstack[SUB_NEST]; /* stack for gosub */ +struct for_stack fstack[FOR_NEST]; /* stack for FOR/NEXT loop */ + +int ftos; /* index to top of FOR stack */ +int gtos; /* index to top of GOSUB stack */ + +char *program, *prog_ip; /* holds expression to be analyzed */ + +char *interp_name; + +jmp_buf e_buf; /* hold environment for longjmp() */ + +char *find_label(); +void scan_labels(); +struct for_stack fpop(); +char *gpop(); +int get_next_label(char *); +void fpush(struct for_stack); +void gosub(), greturn(), gpush(char*), label_init(); +void exec_if(), exec_for(), next(), exec_goto(); +void exec_exec(), exec_load(); +void exec_list(); + +/* initialize variables */ +void init() { + program = NULL; + prog_ip = NULL; + label_init(); +} + +/* Load program and scans labels */ +void load_program (char *p_buf) { + if (program != NULL) { + free(program); + } + + program = p_buf; + scan_labels(); /* find the labels in the program */ +} + +/* Insert line at the right place of program */ +void insert_line(char *l, int n) { + char *a, *b, *p; + int i_n, t, k; + + + if (program == NULL) { + load_program(strdup(l)); + } else { + int prog_l = strlen(program); + + // lookup insert position + // find smallest label above line number + a = (n == 0 ? program : program + prog_l); + i_n = -1; b = program + prog_l; + + for (t = 0; t < NUM_LAB; t++) { + if (label_table[t].name[0]) { + k = atoi(label_table[t].name); + if (k == n) { + a = label_table[t].p; + while (*a != '\n' && a > program) a--; // back to beginning of line + if (*a == '\n') a++; + } + if (k > n && (k < i_n || i_n == -1)) { + i_n = k; + b = label_table[t].p; + while (*b != '\n' && b > program) b--; // back to beginning of line + if (*b == '\n') b++; + if (b < a) a = b; + } + } + } + + p = malloc(prog_l + strlen(l) + 3); + + strncpy(p, program, (a - program)); // copy [0, a[ + p[a - program] = 0; + // if l is an empty line, do not copy. + for (i_n = 0; l[i_n]; i_n++) { + if (!isdigit(l[i_n]) && l[i_n] != '\n' && l[i_n] != '\r') { + strcpy(p + (a - program), l); // copy l + break; + } + } + strcat(p + (a - program), b); // copy [b, end[ + + load_program(p); // reparse labels + } +} + +/* start program execution at given entry point (not necessarily in loaded program) + return values : + - 0 : return on end of input + - 1 : return on error + - 2 : return on "END" statement + - 3 : return on "LOAD" statement +*/ +int start(char *entry) { + ftos = 0; /* initialize the FOR stack index */ + gtos = 0; /* initialize the GOSUB stack index */ + + prog_ip = entry; + if(setjmp(e_buf)) return 1; /* initialize the long jump buffer */ + + do { + token_type = get_token(); + /* check for assignment statement */ + if(token_type==VARIABLE) { + putback(); /* return the var to the input stream */ + assignment(); /* must be assignment statement */ + } + else /* is command */ + switch(tok) { + case LIST: + exec_list(); + break; + case PRINT: + exec_print(); + break; + case GOTO: + exec_goto(); + break; + case IF: + exec_if(); + break; + case FOR: + exec_for(); + break; + case NEXT: + next(); + break; + case INPUT: + input(); + break; + case GOSUB: + gosub(); + break; + case RETURN: + greturn(); + break; + case EXEC: + exec_exec(); + break; + case LOAD: + exec_load(); + return 3; + case RUN: // (re)start program at begining + ftos = gtos = 0; + prog_ip = program; + break; + case END: // end program execution + return 2; + } + } while (prog_ip != NULL && tok != FINISHED); + + return 0; +} + +/* display an error message */ +void serror(int error) +{ + static char *e[]= { + "syntax error", + "unbalanced parentheses", + "no expression present", + "equals sign expected", + "not a variable", + "Label table full", + "duplicate label", + "undefined label", + "THEN expected", + "TO expected", + "too many nested FOR loops", + "NEXT without FOR", + "too many nested GOSUBs", + "RETURN without GOSUB" + }; + printf ("[near '%s' %d %d] E: %s\n", token, tok, token_type, e[error]); + + longjmp(e_buf, 1); /* return to save point */ + exit(0); + +} + +/* Find all labels. */ +void scan_labels() +{ + int addr; + + label_init(); /* zero all labels */ + + if (program == 0) return; /* if no program is loaded, nothing to do. */ + + prog_ip = program; /* prog_ip will go through the program */ + + /* if the first token in the file is a label */ + get_token(); + if(token_type==NUMBER) { + strcpy(label_table[0].name,token); + label_table[0].p=prog_ip; + } + + find_eol(); + do { + get_token(); + if(token_type==NUMBER) { + addr = get_next_label(token); + if(addr==-1 || addr==-2) { + (addr==-1) ?serror(5):serror(6); + } + strncpy(label_table[addr].name, token, LAB_LEN); + label_table[addr].name[LAB_LEN-1] = 0; // null terminate + label_table[addr].p = prog_ip; /* current point in program */ + } + /* if not on a blank line, find next line */ + if(tok!=EOL) find_eol(); + } while(tok!=FINISHED); +} + +/* Return index of next free position in label array. + A -1 is returned if the array is full. + A -2 is returned when duplicate label is found. + */ +int get_next_label(char *s) +{ + register int t; + + for(t=0;t", *token)) { + serror(0); /* not a legal operator */ + return; + } + op=*token; + + get_exp(&y); /* get right expression */ + + /* determine the outcome */ + cond = 0; + switch(op) { + case '<': + if(x': + if(x>y) cond=1; + break; + case '=': + if(x==y) cond=1; + break; + } + if(cond) { /* is true so process target of IF */ + get_token(); + if(tok!=THEN) { + serror(8); + return; + }/* else program execution starts on next line */ + } + else find_eol(); /* find start of next line */ +} + +/* Execute a FOR loop. */ +void exec_for() +{ + struct for_stack i; + int value; + + get_token(); /* read the control variable */ + if(!isalpha(*token)) { + serror(4); + return; + } + + i.var=find_var(token); /* save its index */ + + get_token(); /* read the equals sign */ + if(*token!='=') { + serror(3); + return; + } + + get_exp(&value); /* get initial value */ + + *(i.var) = value; + + get_token(); + if(tok!=TO) serror(9); /* read and discard the TO */ + + get_exp(&i.target); /* get target value */ + + /* if loop can execute at least once, push info on stack */ + if(value>=*(i.var)) { + i.loc = prog_ip; + fpush(i); + } + else /* otherwise, skip loop code altogether */ + while(tok!=NEXT) get_token(); +} + +/* Execute a NEXT statement. */ +void next() +{ + struct for_stack i; + + i = fpop(); /* read the loop info */ + + (*(i.var))++; + if(*(i.var)>i.target) return; /* all done */ + fpush(i); /* otherwise, restore the info */ + prog_ip = i.loc; /* loop */ +} + +/* Push function for the FOR stack. */ +void fpush(struct for_stack i) +{ + if(ftos>FOR_NEST) + serror(10); + + fstack[ftos]=i; + ftos++; +} + +struct for_stack fpop() +{ + ftos--; + if(ftos<0) serror(11); + return(fstack[ftos]); +} + +/* Execute a GOSUB command. */ +void gosub() +{ + char *loc; + + get_token(); + /* find the label to call */ + loc = find_label(token); + if(loc=='\0') + serror(7); /* label not defined */ + else { + gpush(prog_ip); /* save place to return to */ + prog_ip = loc; /* start program running at that loc */ + } +} + +/* Return from GOSUB. */ +void greturn() +{ + prog_ip = gpop(); +} + +/* GOSUB stack push function. */ +void gpush(char *s) +{ + gtos++; + + if(gtos==SUB_NEST) { + serror(12); + return; + } + + gstack[gtos]=s; + +} + +/* GOSUB stack pop function. */ +char *gpop() +{ + if(gtos==0) { + serror(13); + return 0; + } + + return(gstack[gtos--]); +} + + +/* EXEC function + Forks and runs another instance of the interpreter + */ +void exec_exec() { + + /* TODO + int pid; + + get_token(); + if (token_type==QUOTE) { + pid = fork(); + if (pid < 0) { + printf("Error: could not fork.\n"); + } else { + if (pid == 0) { + execlp(interp_name, interp_name, token, 0); + printf("Error: could not exec.\n"); + exit(0); + } else { + wait(0); + } + } + get_token(); + } else { + serror(0); + } + */ + printf("EXEC not available.\n"); +} + +/* LOAD function + loads a program into interpreter and ends anything currently executing */ +void exec_load() { + char *p_buf; + + get_token(); + if (token_type==QUOTE) { + if (!(p_buf = load_file(token))) { + printf("Error: could not load file %s\n", token); + } else { + load_program(p_buf); + } + } else { + serror(0); + } +} + +/* LIST function + dumps program source text */ +void exec_list() { + get_token(); + + if (program == NULL) { + printf("No program loaded.\n"); + } else { + printf("%s", program); + } +} diff --git a/src/user/app/kbasic/expr.c b/src/user/app/kbasic/expr.c new file mode 100644 index 0000000..3b8d558 --- /dev/null +++ b/src/user/app/kbasic/expr.c @@ -0,0 +1,153 @@ +#include + +#include "basic.h" + +void level2(int*), level3(int*), level4(int*), level5(int*), level6(int*), primitive(int*); +void unary(char, int*), arith(char, int*, int*); + +/* Entry point into parser. */ +void get_exp(int *result) +{ + get_token(); + if(!*token) { + serror(2); + return; + } + level2(result); + putback(); /* return last token read to input stream */ +} + +/* Add or subtract two terms. */ +void level2(int *result) +{ + register char op; + int hold; + + level3(result); + while((op = *token) == '+' || op == '-') { + get_token(); + level3(&hold); + arith(op, result, &hold); + } +} + +/* Multiply or divide two factors. */ +void level3(int *result) +{ + register char op; + int hold; + + level4(result); + while((op = *token) == '*' || op == '/' || op == '%') { + get_token(); + level4(&hold); + arith(op, result, &hold); + } +} + +/* Process integer exponent. */ +void level4(int *result) +{ + int hold; + + level5(result); + if(*token== '^') { + get_token(); + level4(&hold); + arith('^', result, &hold); + } +} + +/* Is a unary + or -. */ +void level5(int *result) +{ + register char op; + + op = 0; + if((token_type==DELIMITER) && (*token=='+' || *token=='-')) { + op = *token; + get_token(); + } + level6(result); + if(op) + unary(op, result); +} + +/* Process parenthesized expression. */ +void level6(int *result) +{ + if((*token == '(') && (token_type == DELIMITER)) { + get_token(); + level2(result); + if(*token != ')') + serror(1); + get_token(); + } + else + primitive(result); +} + +/* Find value of number or variable. */ +void primitive(int *result) +{ + + switch(token_type) { + case VARIABLE: + *result = *find_var(token); + get_token(); + return; + case NUMBER: + *result = atoi(token); + get_token(); + return; + case COMMAND: + switch(tok) { + case RND: + *result = rand(); + get_token(); + return; + } + default: + serror(0); + } +} + +/* Perform the specified arithmetic. */ +void arith(char o, int *r, int *h) +{ + register int t, ex; + + switch(o) { + case '-': + *r = *r-*h; + break; + case '+': + *r = *r+*h; + break; + case '*': + *r = *r * *h; + break; + case '/': + *r = (*r)/(*h); + break; + case '%': + t = (*r)/(*h); + *r = *r-(t*(*h)); + break; + case '^': + ex = *r; + if(*h==0) { + *r = 1; + break; + } + for(t=*h-1; t>0; --t) *r = (*r) * ex; + break; + } +} + +/* Reverse the sign. */ +void unary(char o, int *r) +{ + if(o=='-') *r = -(*r); +} + diff --git a/src/user/app/kbasic/kbasic.txt b/src/user/app/kbasic/kbasic.txt new file mode 100644 index 0000000..19131ed --- /dev/null +++ b/src/user/app/kbasic/kbasic.txt @@ -0,0 +1,19 @@ +KBASIC commands + +EXEC "" fork and run file in a new process +LOAD "" reset interpreter context and load file, ends all execution +RUN start loaded program at begining +END end execution of program +LIST list program text + +GOTO