Compare commits
10 Commits
ee10cc6eb5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 40a2de796d | |||
| d5d96676b1 | |||
| 10eb159ef8 | |||
| 683b47f245 | |||
| f2cab38cf2 | |||
| 0826d1946b | |||
| 06d110e28b | |||
| bd9399e060 | |||
| 5b4e0e7a0f | |||
| 559ff15dc0 |
7
Makefile
7
Makefile
@@ -4,11 +4,14 @@ endif
|
|||||||
|
|
||||||
all: boot.img kernel.bin
|
all: boot.img kernel.bin
|
||||||
|
|
||||||
|
src/%.S: src/%.c
|
||||||
|
~/bin/bcc -ansi -0 -S -d -t $@ $<
|
||||||
|
|
||||||
src/%.o: src/%.c
|
src/%.o: src/%.c
|
||||||
bcc -ansi -0 -c -o $@ $<
|
~/bin/bcc -ansi -0 -c -o $@ $<
|
||||||
|
|
||||||
kernel.bin: src/screen.o src/conio.o src/string.o src/stdlib.o src/basic.o src/kernel.o
|
kernel.bin: src/screen.o src/conio.o src/string.o src/stdlib.o src/basic.o src/kernel.o
|
||||||
ld86 -d -M -o $@ $^ | tee ld86.out
|
~/bin/ld86 -d -M -L/home/andrew/lib/bcc -lbcc -o $@ $^ | tee ld86.out
|
||||||
|
|
||||||
asm/kernel_syms.S: kernel.bin
|
asm/kernel_syms.S: kernel.bin
|
||||||
cat ld86.out | \
|
cat ld86.out | \
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ Because it's fun. Don't you like to have fun? Ogre.
|
|||||||
What does it do?
|
What does it do?
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Right now, not much of anything at all. It boots from a 1.44mB floppy disk, and enters into a BASIC interpreter, just like your favorite home computers of the 70s/80s!
|
Right now, not much. It boots from a 1.44mB floppy disk, and enters into a BASIC interpreter, just like your favorite home computers of the 70s/80s!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Currently the BASIC only understands simple arithmetic expressions. But this will soon change; I intend to implement at least as many features as uBASIC, maybe QuickBASIC eventually.
|
Currently the BASIC only understands a few simple basic commands: `PRINT`, `LIST`, `GOTO` and `RUN`. The arithmetic parser was yanked out as part of a refactor that is still ongoing so it can't do math right now. The eventual target is for this to be fully MS BASIC-80 compliant.
|
||||||
|
|
||||||
|
Beware, this is a work in progress and is probably in some state of brokenness. The last couple of git log entries should be relied upon to understand the current state.
|
||||||
|
|
||||||
How can I run it?
|
How can I run it?
|
||||||
=====
|
=====
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
433
src/basic.c
433
src/basic.c
@@ -10,20 +10,21 @@
|
|||||||
struct basic_expr math_expressions[32];
|
struct basic_expr math_expressions[32];
|
||||||
int basic_errno;
|
int basic_errno;
|
||||||
int basic_last_stored_lineno;
|
int basic_last_stored_lineno;
|
||||||
|
int basic_run_ip = 0;
|
||||||
|
int basic_repl_mode = 0; /* 0 = REPL mode, 1 = RUN mode */
|
||||||
|
|
||||||
char _tokenizer_value[BASIC_TOKENIZER_MAX_LENGTH];
|
char _tokenizer_value[BASIC_TOKENIZER_MAX_LENGTH];
|
||||||
char *_tokenizer_prev;
|
char *_tokenizer_prev;
|
||||||
char *_tokenizer_prev_next;
|
char *_tokenizer_prev_next;
|
||||||
char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH];
|
char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH];
|
||||||
|
char const_string_test[255];
|
||||||
|
|
||||||
void basic_report_error()
|
void basic_report_error(char *prefix)
|
||||||
{
|
{
|
||||||
char decimal[2];
|
_cputs(prefix);
|
||||||
decimal[0] = 0;
|
memset(decimal, 0x00, 32);
|
||||||
decimal[1] = 0;
|
itoa(basic_errno, decimal);
|
||||||
_cputs("Error: ");
|
_cputs(decimal);
|
||||||
decimal[0] = dtoa(basic_errno);
|
|
||||||
_cputs((char *)&decimal);
|
|
||||||
_cputs("\n");
|
_cputs("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,40 +39,35 @@ void basic_cmd_rem(void *expr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void basic_cmd_goto(void *data)
|
||||||
|
{
|
||||||
|
int lineno;
|
||||||
|
if ( data == NULL ) {
|
||||||
|
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(decimal, 0x00, 32);
|
||||||
|
lineno = atoi(lstripseek(data));
|
||||||
|
basic_run_ip = lineno-1;
|
||||||
|
}
|
||||||
|
|
||||||
void basic_cmd_list(void *data)
|
void basic_cmd_list(void *data)
|
||||||
{
|
{
|
||||||
basic_expr *expr;
|
|
||||||
char decimal[32];
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int x = 0;
|
||||||
int limit = basic_last_stored_lineno;
|
int limit = basic_last_stored_lineno;
|
||||||
|
char *lineptr = NULL;
|
||||||
|
|
||||||
if ( data == NULL ) {
|
i = 0;
|
||||||
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
/* I don't know why, but somehow, this memcpy fixes the bug where _cputs and _cputsf stops properly processing
|
||||||
return;
|
* constant character strings passed as the format string. I've tried lots and lots of things and I still don't
|
||||||
}
|
* know why this fixes the bug.
|
||||||
|
|
||||||
expr = basic_parse_expr((char *)data, 0);
|
|
||||||
if ( expr == NULL ) {
|
|
||||||
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* The default state of the lval integer argument is zero. Nothing special happens in the parsing
|
|
||||||
* of a "LIST" command with no integer argument that ensures this, that's just the default state.
|
|
||||||
* So the only time expr->lval.i will be non-zero is if user said "LIST NNNN". We're relying on that here.
|
|
||||||
*/
|
*/
|
||||||
if ( expr->lval.i != 0 ) {
|
memcpy(const_string_test, (char *)" \0", strlen(" \0"));
|
||||||
limit = expr->lval.i;
|
for ( i = 0; i <= limit ; i++ ) {
|
||||||
}
|
if ( basic_memory_lines[i][0] != 0 ) {
|
||||||
|
_cputsf("%d ", (char *)(i+1));
|
||||||
memset((char *)&decimal, 0x00, 32);
|
_cputsf("%s\n", basic_memory_lines[i]);
|
||||||
for ( i = 0; i < limit ; i++ ) {
|
|
||||||
if ( basic_memory_lines[i][0] != 0x00 ) {
|
|
||||||
itoa(i, (char *)&decimal);
|
|
||||||
_cputs((char *)&decimal);
|
|
||||||
/* this funky pointer dereferencing is to make bcc happy, it can't
|
|
||||||
* manage indexing the array and dereferencing that */
|
|
||||||
_cputs(((char *)&basic_memory_lines)+i);
|
|
||||||
_cputs("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,18 +81,26 @@ void basic_cmd_print(void *data)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expr = basic_parse_expr((char *)data, 0);
|
_cputs((char *)data);
|
||||||
if ( expr == NULL ) {
|
}
|
||||||
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
|
||||||
|
void basic_cmd_run(void *data)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
char *lineptr;
|
||||||
|
for ( basic_run_ip = 0; basic_run_ip <= basic_last_stored_lineno ; basic_run_ip++ ) {
|
||||||
|
basic_errno = 0;
|
||||||
|
if ( basic_memory_lines[basic_run_ip][0] != 0x00 ) {
|
||||||
|
rc = basic_run_line_v2((char *)basic_memory_lines[basic_run_ip], 1);
|
||||||
|
if ( rc != 0 || basic_errno != 0 ) {
|
||||||
|
_cputs("Error at Line ");
|
||||||
|
itoa(basic_run_ip+1, (char *)&decimal);
|
||||||
|
_cputs(": ");
|
||||||
|
_cputs((char *)&decimal);
|
||||||
|
_cputs("\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
memset((void *)&result, 0x00, sizeof(struct basic_variable));
|
|
||||||
basic_solve_expr(expr, &result);
|
|
||||||
if ( basic_errno != 0 ) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
basic_print_var(&result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,15 +135,10 @@ char *_tokenize(char *ptr, char *token)
|
|||||||
orig = ptr;
|
orig = ptr;
|
||||||
numtokens = strlen(token);
|
numtokens = strlen(token);
|
||||||
|
|
||||||
memset(&_tokenizer_value, 0x00, BASIC_TOKENIZER_MAX_LENGTH);
|
memset(_tokenizer_value, 0x00, BASIC_TOKENIZER_MAX_LENGTH);
|
||||||
while ( *ptr != 0x0 ) {
|
while ( *ptr != 0x0 ) {
|
||||||
tokenptr = token;
|
for ( tokenptr = token ; *tokenptr != 0x00; tokenptr += 1) {
|
||||||
for ( i = 0 ; i < numtokens; i++) {
|
if ( *ptr == *tokenptr) {
|
||||||
if ( *ptr == *(tokenptr + i)) {
|
|
||||||
if ( len == 0 ) {
|
|
||||||
len = 1;
|
|
||||||
ptr += 1;
|
|
||||||
}
|
|
||||||
goto _tokenize_copy;
|
goto _tokenize_copy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,21 +146,22 @@ char *_tokenize(char *ptr, char *token)
|
|||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
_tokenize_copy:
|
_tokenize_copy:
|
||||||
|
if ( len == 0 ) {
|
||||||
|
len = 1;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
if ( len > BASIC_TOKENIZER_MAX_LENGTH ) {
|
if ( len > BASIC_TOKENIZER_MAX_LENGTH ) {
|
||||||
basic_errno = BASIC_ERR_SYNTAX_TOKEN_LENGTH;
|
basic_errno = BASIC_ERR_SYNTAX_TOKEN_LENGTH;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memcpy((void *)&_tokenizer_value, (void *)orig, len);
|
memcpy(_tokenizer_value, (void *)orig, len);
|
||||||
lstrip((char *)&_tokenizer_value, (char *)&tmpbuff, " ");
|
|
||||||
rstrip((char *)&tmpbuff, (char *)&_tokenizer_value, " ");
|
|
||||||
|
|
||||||
_tokenizer_prev_next = (ptr + 1);
|
_tokenizer_prev_next = (ptr + 1);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *tokenizer_token(void)
|
char *tokenizer_token(void)
|
||||||
{
|
{
|
||||||
return (char *)&_tokenizer_value;
|
return _tokenizer_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
@@ -170,216 +170,88 @@ char *tokenizer_token(void)
|
|||||||
*
|
*
|
||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
|
int basic_memory_line_store(char *content, int lineno)
|
||||||
{
|
{
|
||||||
if ( expr == NULL || result == NULL ) {
|
int i = 0;
|
||||||
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
char *ptr = NULL;
|
||||||
return 0;
|
char *dest = NULL;
|
||||||
|
|
||||||
|
if ( lineno < 0 || lineno > BASIC_MAX_LINES ) {
|
||||||
|
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (expr->type) {
|
if ( lineno > basic_last_stored_lineno ) {
|
||||||
case BASIC_OPTP_NONE:
|
/*
|
||||||
if ( expr->lval_type == BASIC_LVAL_CONST_INT ) {
|
* basic_last_stored_lineno only every grows upward (even if you go back and edit a line or insert one).
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
* The memory between here (new line) and there (last line) is probably uninitialized. When we come back
|
||||||
result->value.i = expr->lval.i;
|
* through to LIST those lines later we may find garbage. Let's clean it up.
|
||||||
} else {
|
*/
|
||||||
basic_errno = BASIC_ERR_INTERNAL_UNIMPLEMENTED;
|
for ( i = basic_last_stored_lineno+1; i <= lineno ; i++ ) {
|
||||||
return 0;
|
/* We don't need to zero the entire string, just the first character, don't waste time */
|
||||||
|
basic_memory_lines[i][0] = 0x00;
|
||||||
}
|
}
|
||||||
break;
|
basic_last_stored_lineno = lineno;
|
||||||
case BASIC_OPTP_ADD:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.i = expr->lval.i + expr->rval.i;
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_SUB:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.i = expr->lval.i - expr->rval.i;
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_MUL:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.i = expr->lval.i * expr->rval.i;
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_DIV:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ( expr->rval.i == 0) {
|
|
||||||
basic_errno = BASIC_ERR_MATH_DBZ;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.i = expr->lval.i / expr->rval.i;
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_MOD:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ( expr->rval.i == 0) {
|
|
||||||
basic_errno = BASIC_ERR_MATH_DBZ;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.i = (expr->lval.i) - ((expr->lval.i / expr->rval.i)*expr->rval.i);
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_EQL:
|
|
||||||
if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
|
|
||||||
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
|
|
||||||
basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
|
|
||||||
if (expr->lval.i == expr->rval.i) {
|
|
||||||
result->value.i = BASIC_CONST_TRUE;
|
|
||||||
} else {
|
|
||||||
result->value.i = BASIC_CONST_FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BASIC_OPTP_STOR:
|
|
||||||
/* this funky pointer dereferencing is to make bcc happy, it can't
|
|
||||||
* manage indexing the array and dereferencing that */
|
|
||||||
if ( memcpy(
|
|
||||||
(((char *)&basic_memory_lines) + expr->lval.i),
|
|
||||||
(char *)expr->rval.ptr,
|
|
||||||
strlen((char *)expr->rval.ptr)
|
|
||||||
) == 0 ) {
|
|
||||||
basic_errno = BASIC_ERR_INTERNAL_MEMORY;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
result->flags = (result->flags | BASIC_VARFLAG_TSTR | BASIC_VARFLAG_INIT);
|
|
||||||
result->value.ptr = BASIC_MESSAGE_OK;
|
|
||||||
basic_last_stored_lineno = expr->lval.i;
|
|
||||||
return 0;
|
|
||||||
case BASIC_OPTP_ASN:
|
|
||||||
basic_errno = BASIC_ERR_INTERNAL_UNIMPLEMENTED;
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
basic_errno = BASIC_ERR_INTERNAL_UNDEFINED_BEHAVIOR;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
lineno -= 1;
|
||||||
|
i = (int) memcpy(basic_memory_lines[lineno], content, strlen(content));
|
||||||
|
if ( i == 0 ) {
|
||||||
|
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct basic_expr *basic_parse_expr(char *expbuf, int require_line_numbers)
|
int basic_run_line_v2(char *codeline, int repl_mode)
|
||||||
{
|
{
|
||||||
struct basic_expr *ret = &math_expressions[0];
|
char *buffptr;
|
||||||
char *token = NULL;
|
char *token;
|
||||||
char flags = 0;
|
int rc = 0;
|
||||||
if ( require_line_numbers == 1 ) {
|
int lineno = 0;
|
||||||
flags = BASIC_PARSE_FIRSTTOKEN;
|
|
||||||
|
if ( codeline == NULL ) {
|
||||||
|
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*char *subptr = 0;*/
|
/* Continue as long as there's text left in the buffer */
|
||||||
|
buffptr = lstripseek(codeline, " ");
|
||||||
_tokenizer_init();
|
while ( buffptr != NULL ) {
|
||||||
memset(ret, 0x0, sizeof(struct basic_expr));
|
buffptr = _tokenize(buffptr, " ");
|
||||||
|
|
||||||
while ( *expbuf != '\0' ) {
|
|
||||||
if ( *expbuf == ' ' ) {
|
|
||||||
expbuf += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
expbuf = _tokenize(expbuf, " ");
|
|
||||||
token = tokenizer_token();
|
token = tokenizer_token();
|
||||||
if ( isdigit(*token) == 1 ) {
|
|
||||||
if ( flags == BASIC_PARSE_FIRSTTOKEN ) {
|
/* is it a line number? If so, store it and do nothing else.*/
|
||||||
ret->type = BASIC_OPTP_STOR;
|
if ( isdigit(*token) ) {
|
||||||
ret->lval_type = BASIC_LVAL_CONST_INT;
|
memset(decimal, 0x00, 32);
|
||||||
ret->lval.i = atoi(token);
|
lineno = atoi(token);
|
||||||
#ifdef __GNUC__
|
basic_memory_line_store(buffptr, lineno);
|
||||||
printf("Stored line number %d for %s\n", ret->lval.i, token);
|
return 0;
|
||||||
#endif
|
/* Is it a command? */
|
||||||
ret->rval.ptr = expbuf;
|
|
||||||
ret->rval_type = BASIC_RVAL_PTR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( (ret->type == 0) && (flags & BASIC_PARSE_FOUND_LVAL) == BASIC_PARSE_FOUND_LVAL ) {
|
|
||||||
basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_LVALUES;
|
|
||||||
return NULL;
|
|
||||||
} else if ( ret->type == 0x0 ) {
|
|
||||||
ret->lval.i = atoi(token);
|
|
||||||
ret->lval_type = BASIC_LVAL_CONST_INT;
|
|
||||||
flags = (flags | BASIC_PARSE_FOUND_LVAL);
|
|
||||||
} else if ( ret->type != 0x0 && ((flags & BASIC_PARSE_FOUND_RVAL) == BASIC_PARSE_FOUND_RVAL)) {
|
|
||||||
basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_RVALUES;
|
|
||||||
return NULL;
|
|
||||||
} else if ( ret->type != 0x0 ) {
|
|
||||||
ret->rval.i = atoi(token);
|
|
||||||
ret->rval_type = BASIC_RVAL_CONST_INT;
|
|
||||||
}
|
|
||||||
} else if ( token != NULL && ret->type == 0x0 ) {
|
|
||||||
if ( flags == BASIC_PARSE_FIRSTTOKEN ) {
|
|
||||||
basic_errno = BASIC_ERR_SYNTAX_NO_LINE_NUMBER;
|
|
||||||
}
|
|
||||||
switch ( *token ) {
|
|
||||||
case '+':
|
|
||||||
ret->type = BASIC_OPTP_ADD;
|
|
||||||
break;
|
|
||||||
case '*':
|
|
||||||
ret->type = BASIC_OPTP_MUL;
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
ret->type = BASIC_OPTP_SUB;
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
ret->type = BASIC_OPTP_DIV;
|
|
||||||
break;
|
|
||||||
case '%':
|
|
||||||
ret->type = BASIC_OPTP_MOD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* see if a command will claim this token */
|
|
||||||
if ( strcmp(token, "REM") == 0 ) {
|
|
||||||
ret->type = BASIC_OPTP_CMD;
|
|
||||||
ret->lval_type = BASIC_LVAL_CMDPTR;
|
|
||||||
ret->lval.cmd_ptr = basic_cmd_rem;
|
|
||||||
ret->rval_type = BASIC_RVAL_NONE;
|
|
||||||
return ret;
|
|
||||||
} else if ( strcmp(token, "PRINT") == 0 ) {
|
} else if ( strcmp(token, "PRINT") == 0 ) {
|
||||||
ret->type = BASIC_OPTP_CMD;
|
basic_cmd_print(buffptr);
|
||||||
ret->lval_type = BASIC_LVAL_CMDPTR;
|
return basic_errno;
|
||||||
ret->lval.cmd_ptr = basic_cmd_print;
|
|
||||||
ret->rval_type = BASIC_RVAL_PTR;
|
|
||||||
ret->rval.ptr = expbuf;
|
|
||||||
return ret;
|
|
||||||
} else if ( strcmp(token, "LIST") == 0 ) {
|
} else if ( strcmp(token, "LIST") == 0 ) {
|
||||||
ret->type = BASIC_OPTP_CMD;
|
basic_cmd_list(NULL);
|
||||||
ret->lval_type = BASIC_LVAL_CMDPTR;
|
return basic_errno;
|
||||||
ret->lval.cmd_ptr = basic_cmd_list;
|
} else if ( strcmp(token, "GOTO") == 0 ) {
|
||||||
ret->rval_type = BASIC_RVAL_PTR;
|
basic_cmd_goto(buffptr);
|
||||||
ret->rval.ptr = expbuf;
|
return basic_errno;
|
||||||
return ret;
|
} else if ( strcmp(token, "RUN") == 0 ) {
|
||||||
|
basic_repl_mode = 1;
|
||||||
|
return basic_errno;
|
||||||
} else {
|
} else {
|
||||||
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
basic_errno = BASIC_ERR_INTERNAL_UNDEFINED_BEHAVIOR;
|
||||||
return NULL;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Report errors */
|
||||||
|
if ( basic_errno != 0 ) {
|
||||||
|
_cputsf("Parsing error: %d", basic_errno);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
/* Move to the next thing in the line and process it */
|
||||||
return NULL;
|
buffptr = lstripseek(buffptr, " ");
|
||||||
}
|
}
|
||||||
}
|
return 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
@@ -388,9 +260,8 @@ struct basic_expr *basic_parse_expr(char *expbuf, int require_line_numbers)
|
|||||||
*
|
*
|
||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
void basic_print_var(struct basic_variable *var)
|
void basic_print_var(basic_variable *var)
|
||||||
{
|
{
|
||||||
char decimal[32];
|
|
||||||
memset(&decimal, 0x00, 32);
|
memset(&decimal, 0x00, 32);
|
||||||
|
|
||||||
if ( ( (var->flags & BASIC_VARFLAG_INIT) == BASIC_VARFLAG_INIT ) &&
|
if ( ( (var->flags & BASIC_VARFLAG_INIT) == BASIC_VARFLAG_INIT ) &&
|
||||||
@@ -401,58 +272,44 @@ void basic_print_var(struct basic_variable *var)
|
|||||||
_cputs("ERROR Unable to convert decimal to integer");
|
_cputs("ERROR Unable to convert decimal to integer");
|
||||||
}
|
}
|
||||||
_cputs("\n");
|
_cputs("\n");
|
||||||
|
} else if ( ( (var->flags & BASIC_VARFLAG_INIT) == BASIC_VARFLAG_INIT ) &&
|
||||||
|
( (var->flags & BASIC_VARFLAG_TSTR) == BASIC_VARFLAG_TSTR ) ) {
|
||||||
|
if ( var->value.str == NULL ) {
|
||||||
|
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_cputs(var->value.str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void basic_repl(void)
|
void basic_repl(void)
|
||||||
{
|
{
|
||||||
char keybuff[512];
|
char keybuff[512];
|
||||||
char outbuff[128];
|
int i;
|
||||||
|
char *ptr;
|
||||||
struct basic_expr *expr;
|
char *token;
|
||||||
|
|
||||||
blankScreen();
|
blankScreen();
|
||||||
setCursorPosition(0, 0);
|
setCursorPosition(0, 0);
|
||||||
_cputs("Piquant Basic v0.1\n\n");
|
|
||||||
|
_cputs("Piquant Basic v0.1\n");
|
||||||
|
_cputs("READY\n");
|
||||||
|
|
||||||
while ( 1 ) {
|
while ( 1 ) {
|
||||||
expr = NULL;
|
basic_errno = 0;
|
||||||
_cputs("> ");
|
if ( basic_repl_mode == 1 ) {
|
||||||
|
basic_cmd_run(NULL);
|
||||||
|
basic_repl_mode = 0;
|
||||||
|
}
|
||||||
|
_cputs("\n> ");
|
||||||
|
|
||||||
/* Read */
|
/* Read */
|
||||||
|
|
||||||
memset((void *)&keybuff, 0x00, 512);
|
memset((void *)&keybuff, 0x00, 512);
|
||||||
memset((void *)&outbuff, 0x00, 128);
|
|
||||||
if ( _cgets((char *)&keybuff) != NULL ) {
|
if ( _cgets((char *)&keybuff) != NULL ) {
|
||||||
_cputs("\n");
|
_cputs("\n");
|
||||||
|
/* Eval and Print */
|
||||||
/* Evaluate
|
basic_run_line_v2((char *)&keybuff, 1);
|
||||||
* First we look for line numbers
|
|
||||||
* When we check for immediate mode operation (which catches commands like LIST)
|
|
||||||
* Then we give up
|
|
||||||
*/
|
|
||||||
expr = basic_parse_expr((char *)&keybuff, 1);
|
|
||||||
if ( basic_errno == BASIC_ERR_SYNTAX_NO_LINE_NUMBER ) {
|
|
||||||
expr = basic_parse_expr((char *)&keybuff, 0);
|
|
||||||
}
|
|
||||||
if ( expr == NULL ) {
|
|
||||||
basic_report_error();
|
|
||||||
basic_errno = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( expr->lval_type == BASIC_LVAL_CMDPTR ) {
|
|
||||||
if ( expr->lval.cmd_ptr == NULL ) {
|
|
||||||
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
|
|
||||||
basic_report_error();
|
|
||||||
basic_errno = 0;
|
|
||||||
} else {
|
|
||||||
(*expr->lval.cmd_ptr)(expr->rval.ptr);
|
|
||||||
if ( basic_errno != 0 ) {
|
|
||||||
basic_report_error();
|
|
||||||
basic_errno = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/basic.h
18
src/basic.h
@@ -2,8 +2,8 @@
|
|||||||
#define _BASIC_H_
|
#define _BASIC_H_
|
||||||
|
|
||||||
/* Per MS BASIC-80 ref page 1-2 */
|
/* Per MS BASIC-80 ref page 1-2 */
|
||||||
#define BASIC_MAX_LINES sizeof(int)
|
#define BASIC_MAX_LINES 65535
|
||||||
#define BASIC_MAX_LINE_LENGTH 255
|
#define BASIC_MAX_LINE_LENGTH 72
|
||||||
|
|
||||||
#define BASIC_OPTP_NONE 0 /* No operation (only valid when LVAL is CONST and no RVAL) */
|
#define BASIC_OPTP_NONE 0 /* No operation (only valid when LVAL is CONST and no RVAL) */
|
||||||
#define BASIC_OPTP_ADD 1 /* Add */
|
#define BASIC_OPTP_ADD 1 /* Add */
|
||||||
@@ -25,6 +25,8 @@
|
|||||||
#define BASIC_RVAL_CONST_INT 5
|
#define BASIC_RVAL_CONST_INT 5
|
||||||
#define BASIC_RVAL_PTR 6 /* Only used internally */
|
#define BASIC_RVAL_PTR 6 /* Only used internally */
|
||||||
#define BASIC_RVAL_NONE 7
|
#define BASIC_RVAL_NONE 7
|
||||||
|
#define BASIC_LVAL_CONST_STR 8
|
||||||
|
#define BASIC_RVAL_CONST_STR 9
|
||||||
|
|
||||||
#define BASIC_PARSE_FOUND_LVAL 1
|
#define BASIC_PARSE_FOUND_LVAL 1
|
||||||
#define BASIC_PARSE_FOUND_RVAL 2
|
#define BASIC_PARSE_FOUND_RVAL 2
|
||||||
@@ -44,7 +46,7 @@
|
|||||||
|
|
||||||
#define BASIC_MESSAGE_OK "OK\n"
|
#define BASIC_MESSAGE_OK "OK\n"
|
||||||
|
|
||||||
#define BASIC_TOKENIZER_TOKENS "+-/%*="
|
#define BASIC_TOKENIZER_TOKENS " =+-*/^{}%#$!'\\,;:&?<>/@_"
|
||||||
#define BASIC_TOKENIZER_MAX_LENGTH 512
|
#define BASIC_TOKENIZER_MAX_LENGTH 512
|
||||||
#define BASIC_VARNAME_MAX_LENGTH 16
|
#define BASIC_VARNAME_MAX_LENGTH 16
|
||||||
|
|
||||||
@@ -86,19 +88,23 @@ typedef struct basic_variable basic_variable;
|
|||||||
#define BASIC_CONST_FALSE 0
|
#define BASIC_CONST_FALSE 0
|
||||||
|
|
||||||
extern int basic_errno;
|
extern int basic_errno;
|
||||||
|
|
||||||
extern char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH];
|
extern char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH];
|
||||||
|
extern int basic_last_stored_lineno;
|
||||||
|
|
||||||
void basic_repl(void);
|
void basic_repl(void);
|
||||||
basic_expr *basic_parse_expr(char *, int);
|
basic_expr *basic_parse_expr(char *, int);
|
||||||
int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result);
|
int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result);
|
||||||
char *tokenizer_token(void);
|
char *tokenizer_token(void);
|
||||||
|
int basic_run_line(char *codeline, int repl_mode);
|
||||||
|
|
||||||
void basic_cmd_rem(void *data);
|
void basic_cmd_rem(void *data);
|
||||||
void basic_cmd_print(void *data);
|
void basic_cmd_print(void *data);
|
||||||
void basic_cmd_list(void *data);
|
void basic_cmd_list(void *data);
|
||||||
|
void basic_cmd_run(void *data);
|
||||||
|
void basic_cmd_goto(void *data);
|
||||||
|
|
||||||
void basic_print_var(basic_variable *var);
|
void basic_print_var(basic_variable *var);
|
||||||
void basic_report_error();
|
void basic_report_error(char *prefix);
|
||||||
|
char *basic_memory_line_address(int lineno);
|
||||||
|
int basic_memory_line_store(char *content, int lineno);
|
||||||
#endif /* _BASIC_H_ */
|
#endif /* _BASIC_H_ */
|
||||||
|
|||||||
53
src/conio.c
53
src/conio.c
@@ -6,6 +6,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char decimal[32];
|
||||||
|
|
||||||
void _putch(char c)
|
void _putch(char c)
|
||||||
{
|
{
|
||||||
#ifndef __GNUC__
|
#ifndef __GNUC__
|
||||||
@@ -32,13 +34,52 @@ void _cputs(char *ptr)
|
|||||||
if ( ptr == NULL ) {
|
if ( ptr == NULL ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while ((char)*ptr != 0x0) {
|
while (*ptr != 0x0) {
|
||||||
_putch((char)*ptr);
|
if (*ptr == '\\') {
|
||||||
if (*ptr == '\n') {
|
ptr += 1;
|
||||||
_cursor_y += 1;
|
switch ( *ptr ) {
|
||||||
_cursor_x = 0;
|
case 'n':
|
||||||
setCursorPosition(_cursor_x, _cursor_y);
|
_putch('\n');
|
||||||
|
_cursor_x = 80;
|
||||||
|
advanceCursor();
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
_putch(*ptr);
|
||||||
|
if (*ptr == '\n') {
|
||||||
|
_cursor_x = 80;
|
||||||
|
}
|
||||||
|
advanceCursor();
|
||||||
|
}
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cputsf(char *ptr, char *data)
|
||||||
|
{
|
||||||
|
if ( ptr == NULL || data == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*ptr != 0) {
|
||||||
|
if ( *ptr == '%' ) {
|
||||||
|
ptr += 1;
|
||||||
|
switch ( *ptr ) {
|
||||||
|
case 'd':
|
||||||
|
memset(decimal, 0x00, 32);
|
||||||
|
itoa((int)data, decimal);
|
||||||
|
_cputs(decimal);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
_cputs((char *)data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_putch(*ptr);
|
||||||
|
if (*ptr == '\n') {
|
||||||
|
_cursor_x = 80;
|
||||||
|
}
|
||||||
advanceCursor();
|
advanceCursor();
|
||||||
}
|
}
|
||||||
ptr += 1;
|
ptr += 1;
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
void _putch(char _c);
|
void _putch(char _c);
|
||||||
char *_cgets(char *d);
|
char *_cgets(char *d);
|
||||||
void _cputs(char *d);
|
void _cputs(char *d);
|
||||||
|
void _cputsf(char *d, char *data);
|
||||||
char _getkey(char *dest);
|
char _getkey(char *dest);
|
||||||
|
|
||||||
|
|
||||||
|
extern char decimal[32];
|
||||||
|
|
||||||
#endif /* _CONIO_H_ */
|
#endif /* _CONIO_H_ */
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ void backupCursor()
|
|||||||
void advanceCursor()
|
void advanceCursor()
|
||||||
{
|
{
|
||||||
_cursor_x += 1;
|
_cursor_x += 1;
|
||||||
if ( _cursor_x > 79 ) {
|
if ( _cursor_x > 72 ) {
|
||||||
_cursor_x = 0;
|
_cursor_x = 0;
|
||||||
_cursor_y += 1;
|
if ( _cursor_y <= 23 ) _cursor_y += 1;
|
||||||
}
|
}
|
||||||
setCursorPosition(_cursor_x, _cursor_y);
|
setCursorPosition(_cursor_x, _cursor_y);
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/string.c
22
src/string.c
@@ -107,6 +107,28 @@ _lstrip_outer_continue:
|
|||||||
return stripped;
|
return stripped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *lstripseek(char *s, char *strip)
|
||||||
|
{
|
||||||
|
int stripped = 0;
|
||||||
|
char *stripptr = strip;
|
||||||
|
if ( s == NULL || strip == NULL ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while ( *s != 0 ) {
|
||||||
|
stripped = 0;
|
||||||
|
for ( stripptr = strip; *stripptr != 0; stripptr += 1) {
|
||||||
|
if ( *s == *stripptr ) {
|
||||||
|
stripped = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( stripped == 0 ) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
s += 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int rstrip(char *s1, char *s2, char *strip)
|
int rstrip(char *s1, char *s2, char *strip)
|
||||||
{
|
{
|
||||||
int stripped = 0;
|
int stripped = 0;
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ void *memcpy(void *dest, void *src, size_t n);
|
|||||||
int strcmp(char *s1, char *s2);
|
int strcmp(char *s1, char *s2);
|
||||||
int lstrip(char *s1, char *s2, char *strip);
|
int lstrip(char *s1, char *s2, char *strip);
|
||||||
int rstrip(char *s1, char *s2, char *strip);
|
int rstrip(char *s1, char *s2, char *strip);
|
||||||
|
char *lstripseek(char *s, char *strip);
|
||||||
#endif /* _STRING_H_ */
|
#endif /* _STRING_H_ */
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ int main(void)
|
|||||||
if ( basic_errno != 0 ) return 8;
|
if ( basic_errno != 0 ) return 8;
|
||||||
if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TSTR) ) return 9;
|
if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TSTR) ) return 9;
|
||||||
if ( result.value.ptr == NULL ) return 10;
|
if ( result.value.ptr == NULL ) return 10;
|
||||||
if ( strcmp((char *)&basic_memory_lines[10], expr.rval.ptr) != 0 ) return 11;
|
if ( basic_last_stored_lineno != 9 ) return 11;
|
||||||
|
printf("last stored line number: %d\n", basic_last_stored_lineno);
|
||||||
|
printf("stored memory line 10 equals: %s\n", basic_memory_line_address(basic_last_stored_lineno));
|
||||||
|
if ( strcmp(basic_memory_line_address(basic_last_stored_lineno), expr.rval.ptr) != 0 ) return 12;
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user