Compare commits

...

10 Commits

Author SHA1 Message Date
40a2de796d Updated README 2024-05-12 21:42:09 -04:00
d5d96676b1 Added a GOTO command 2024-05-12 21:39:59 -04:00
10eb159ef8 WIP Removed some debug stuff 2024-05-12 21:33:55 -04:00
683b47f245 WIP : Eff it, I left the magic memcpy in. Printing mostly works but it's still broken. Calls to _cputs and _cputsf may mysteriously fail due to some kind of memory corruption I haven't tracked down yet. This commit leaves in a bunch of commented testing code, the next commit will rip them out. 2024-05-12 21:28:27 -04:00
f2cab38cf2 WIP. Somewhere in between the init of basic_cmd_run and basic_cmd_list calls to _cputs stop working with constant character string pointers and only work with pointers off the heap. 2024-05-12 08:54:11 -04:00
0826d1946b WIP Immediate mode, printing, and listing is working, but something wierd is going on with _cputs. See the comment in basic_cmd_list. I'm tempted to throw the C away and move it all to ASM so I can have more control & awareness of what's going on. 2024-05-07 18:56:07 -04:00
06d110e28b WIP : About to rip out the old parser and solver, can't get things working the way I expect and the old code is in the way 2024-05-07 18:06:01 -04:00
bd9399e060 WIP scrolling seems to work correctly now if all you ever do is call advanceCursor() 2024-05-07 17:01:19 -04:00
5b4e0e7a0f WIP minor improvements to conio/screen for scrolling, still doesn't work reliably once we print more than 25 lines 2024-05-07 16:17:36 -04:00
559ff15dc0 WIP. Storing code in line numbers and listing is working. Added a RUN command, and refactored the line runner logic out of basic_repl into basic_run_line. The repl still works but the RUN function is busted. The parser logic is increasingly getting in the way, and the solver is not as useful as I originally thought. The original design of the datastructures and logic is getting in my way and adding confusion. I may need to try something simpler. 2024-05-07 08:08:40 -04:00
11 changed files with 255 additions and 316 deletions

View File

@@ -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 | \

View File

@@ -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!
![Image of Piquant v0.1](media/screenshot.png) ![Image of Piquant v0.1](media/screenshot.png)
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

View File

@@ -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;
return;
}
memset((void *)&result, 0x00, sizeof(struct basic_variable)); void basic_cmd_run(void *data)
basic_solve_expr(expr, &result); {
if ( basic_errno != 0 ) { int rc = 0;
return; char *lineptr;
} else { for ( basic_run_ip = 0; basic_run_ip <= basic_last_stored_lineno ; basic_run_ip++ ) {
basic_print_var(&result); 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;
}
}
} }
} }
@@ -131,37 +135,33 @@ 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;
} }
} }
ptr += 1; ptr += 1;
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 || lineno -= 1;
( expr->rval_type != BASIC_RVAL_CONST_INT ) ) { i = (int) memcpy(basic_memory_lines[lineno], content, strlen(content));
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; if ( i == 0 ) {
return 0; basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
} return 1;
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;
} }
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; } else if ( strcmp(token, "PRINT") == 0 ) {
ret->rval_type = BASIC_RVAL_PTR; basic_cmd_print(buffptr);
break; return basic_errno;
} } else if ( strcmp(token, "LIST") == 0 ) {
if ( (ret->type == 0) && (flags & BASIC_PARSE_FOUND_LVAL) == BASIC_PARSE_FOUND_LVAL ) { basic_cmd_list(NULL);
basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_LVALUES; return basic_errno;
return NULL; } else if ( strcmp(token, "GOTO") == 0 ) {
} else if ( ret->type == 0x0 ) { basic_cmd_goto(buffptr);
ret->lval.i = atoi(token); return basic_errno;
ret->lval_type = BASIC_LVAL_CONST_INT; } else if ( strcmp(token, "RUN") == 0 ) {
flags = (flags | BASIC_PARSE_FOUND_LVAL); basic_repl_mode = 1;
} else if ( ret->type != 0x0 && ((flags & BASIC_PARSE_FOUND_RVAL) == BASIC_PARSE_FOUND_RVAL)) { return basic_errno;
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 ) {
ret->type = BASIC_OPTP_CMD;
ret->lval_type = BASIC_LVAL_CMDPTR;
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 ) {
ret->type = BASIC_OPTP_CMD;
ret->lval_type = BASIC_LVAL_CMDPTR;
ret->lval.cmd_ptr = basic_cmd_list;
ret->rval_type = BASIC_RVAL_PTR;
ret->rval.ptr = expbuf;
return ret;
} else {
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
return NULL;
break;
}
}
} else { } else {
basic_errno = BASIC_ERR_SYNTAX_GENERAL; basic_errno = BASIC_ERR_INTERNAL_UNDEFINED_BEHAVIOR;
return NULL;
} }
/* Report errors */
if ( basic_errno != 0 ) {
_cputsf("Parsing error: %d", basic_errno);
return 1;
}
/* Move to the next thing in the line and process it */
buffptr = lstripseek(buffptr, " ");
} }
return ret; return 0;
} }
/*************************************************************** /***************************************************************
@@ -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;
}
}
}
} }
} }
} }

View File

@@ -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_ */

View File

@@ -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__
@@ -27,18 +29,57 @@ void _putch(char c)
#endif #endif
} }
void _cputs(char *ptr) 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;

View File

@@ -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_ */

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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_ */

View File

@@ -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;