implement IF-THEN-ELSE
This commit is contained in:
+13
-7
@@ -28,16 +28,21 @@ enum fb_instruction_kind {
|
||||
|
||||
/* Data = numeric value to push onto the data stack */
|
||||
FH_INSTR_NUMBER,
|
||||
};
|
||||
|
||||
/** Bytecode word indices that are not in the dict, have special effect */
|
||||
enum compiler_word {
|
||||
/** End of a user defined word, pop address and jump back */
|
||||
CPLWORD_ENDWORD = DICT_SIZE + 1,
|
||||
FH_INSTR_ENDWORD,
|
||||
|
||||
/** This is the `s"` instruction, the length (u32) and string data immediately follow */
|
||||
CPLWORD_ALLOCSTR,
|
||||
FH_INSTR_ALLOCSTR,
|
||||
|
||||
/** This is the `."` instruction, same format as above. */
|
||||
CPLWORD_TYPESTR,
|
||||
FH_INSTR_TYPESTR,
|
||||
|
||||
/* Unconditional jump */
|
||||
FH_INSTR_JUMP,
|
||||
|
||||
/* Jump if zero */
|
||||
FH_INSTR_JUMPZERO,
|
||||
};
|
||||
|
||||
/** One instruction in bytecode */
|
||||
@@ -160,7 +165,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w);
|
||||
|
||||
// SFR and magic addresses are "negative"
|
||||
#define MAGICADDR_INTERACTIVE 0xFFFFFFFFULL
|
||||
#define MAGICADDR_BASE 0xFFFBA5EFULL
|
||||
#define MAGICADDR_BASE 0xFFFFBA5EULL
|
||||
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
|
||||
|
||||
/** Get a value rounded up to multiple of word size */
|
||||
#define WORDALIGNED(var) (((var) + 3) & ~3)
|
||||
|
||||
+64
-11
@@ -1,5 +1,6 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "forth.h" // for fh_init
|
||||
#include "fh_runtime.h"
|
||||
#include "fh_config.h"
|
||||
#include "fh_error.h"
|
||||
@@ -310,7 +311,7 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s
|
||||
|
||||
ENSURE_STATE(FH_STATE_COMPILE);
|
||||
|
||||
instr_init(&instr, FH_INSTR_WORD, CPLWORD_ENDWORD);
|
||||
instr_init(&instr, FH_INSTR_ENDWORD, 0);
|
||||
TRY(fh_compile_put(fh, &instr, INSTR_SIZE));
|
||||
|
||||
/* Return to interpret state */
|
||||
@@ -700,6 +701,64 @@ static enum fh_error w_bye(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
return FH_OK;
|
||||
}
|
||||
|
||||
static enum fh_error w_if(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
{
|
||||
(void) w;
|
||||
enum fh_error rv;
|
||||
struct fh_instruction_s instr;
|
||||
|
||||
ENSURE_STATE(FH_STATE_COMPILE);
|
||||
|
||||
TRY(cs_push(fh, fh->compile_top));
|
||||
instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED);
|
||||
TRY(fh_compile_put(fh, &instr, INSTR_SIZE));
|
||||
return FH_OK;
|
||||
}
|
||||
|
||||
static enum fh_error w_else(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
{
|
||||
(void) w;
|
||||
enum fh_error rv;
|
||||
struct fh_instruction_s instr;
|
||||
|
||||
ENSURE_STATE(FH_STATE_COMPILE);
|
||||
|
||||
uint32_t ifaddr = 0;
|
||||
TRY(cs_pop(fh, &ifaddr));
|
||||
struct fh_instruction_s *if_instr = (void*) &fh->compile[ifaddr];
|
||||
if (if_instr->data != MAGICADDR_UNRESOLVED) {
|
||||
LOGE("IF-ELSE control stack corruption");
|
||||
return FH_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
if_instr->data = fh->compile_top + INSTR_SIZE;
|
||||
|
||||
TRY(cs_push(fh, fh->compile_top));
|
||||
instr_init(&instr, FH_INSTR_JUMP, MAGICADDR_UNRESOLVED);
|
||||
TRY(fh_compile_put(fh, &instr, INSTR_SIZE));
|
||||
return FH_OK;
|
||||
}
|
||||
|
||||
static enum fh_error w_then(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
{
|
||||
(void) w;
|
||||
enum fh_error rv;
|
||||
struct fh_instruction_s instr;
|
||||
|
||||
ENSURE_STATE(FH_STATE_COMPILE);
|
||||
|
||||
uint32_t ifaddr = 0;
|
||||
TRY(cs_pop(fh, &ifaddr));
|
||||
struct fh_instruction_s *if_instr = (void*) &fh->compile[ifaddr];
|
||||
if (if_instr->data != MAGICADDR_UNRESOLVED) {
|
||||
LOGE("IF-ELSE control stack corruption");
|
||||
return FH_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
if_instr->data = fh->compile_top;
|
||||
return FH_OK;
|
||||
}
|
||||
|
||||
static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
{
|
||||
fh_setbase(fh, w->param);
|
||||
@@ -749,16 +808,7 @@ static enum fh_error w_reset(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||
|
||||
ENSURE_STATE(FH_STATE_INTERPRET);
|
||||
|
||||
fh->data_stack_top = 0;
|
||||
fh->return_stack_top = 0;
|
||||
fh->control_stack_top = 0;
|
||||
fh->data_stack_hwm = 0;
|
||||
fh->return_stack_hwm = 0;
|
||||
fh->control_stack_hwm = 0;
|
||||
fh->heap_top = 0;
|
||||
fh->dict_top = 0;
|
||||
|
||||
TRY(ds_push(fh, w->param));
|
||||
fh_init(fh);
|
||||
return FH_OK;
|
||||
}
|
||||
|
||||
@@ -909,6 +959,9 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
|
||||
{"abort", w_abort, 0, 0},
|
||||
{"quit", w_quit, 0, 0},
|
||||
{"exit", w_exit, 0, 0},
|
||||
{"if", w_if, 1, 0},
|
||||
{"else", w_else, 1, 0},
|
||||
{"then", w_then, 1, 0},
|
||||
/* Syntax */
|
||||
{":", w_colon, 0, 0},
|
||||
{";", w_semicolon, 1, 0},
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ enum fh_error fh_heap_reserve(
|
||||
uint32_t *addr
|
||||
)
|
||||
{
|
||||
uint32_t p = WORDALIGNED(fh->heap_top); // FIXME this shouldn't be needed
|
||||
uint32_t p = fh->heap_top;
|
||||
|
||||
if (p + len > HEAP_SIZE) {
|
||||
return FH_ERR_HEAP_FULL;
|
||||
@@ -131,7 +131,7 @@ enum fh_error fh_compile_reserve(
|
||||
uint32_t *addr
|
||||
)
|
||||
{
|
||||
uint32_t p = WORDALIGNED(fh->compile_top); // FIXME this shouldn't be needed
|
||||
uint32_t p = fh->compile_top; // FIXME this shouldn't be needed
|
||||
|
||||
if (p + len > COMPILED_BUFFER_SIZE) {
|
||||
return FH_ERR_HEAP_FULL;
|
||||
|
||||
+80
-81
@@ -72,7 +72,6 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
|
||||
enum fh_error rv;
|
||||
const struct fh_word_s *w;
|
||||
const struct fh_word_s *w2;
|
||||
uint32_t wn;
|
||||
|
||||
w = w0;
|
||||
call:
|
||||
@@ -95,6 +94,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
|
||||
fh->execptr += INSTR_SIZE;
|
||||
|
||||
uint32_t strl;
|
||||
uint32_t val;
|
||||
uint32_t addr = 0;
|
||||
switch (instr->kind) {
|
||||
case FH_INSTR_NUMBER:
|
||||
@@ -102,54 +102,60 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
|
||||
goto instr;
|
||||
|
||||
case FH_INSTR_WORD:
|
||||
wn = instr->data;
|
||||
switch (wn) {
|
||||
/* special case for strings stored in compile memory */
|
||||
case CPLWORD_ALLOCSTR:
|
||||
case CPLWORD_TYPESTR:
|
||||
strl = *((uint32_t *) &fh->compile[fh->execptr]);
|
||||
fh->execptr += 4; // advance past the length
|
||||
if (wn == CPLWORD_ALLOCSTR) {
|
||||
TRY(fh_heap_reserve(fh, strl, &addr));
|
||||
fh_heap_copy_from_compile(fh, addr, fh->execptr, strl);
|
||||
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]);
|
||||
TRY(ds_push(fh, addr));
|
||||
TRY(ds_push(fh, strl));
|
||||
} else {
|
||||
LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]);
|
||||
FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]);
|
||||
}
|
||||
fh->execptr += strl;
|
||||
goto instr;
|
||||
|
||||
case CPLWORD_ENDWORD:
|
||||
LOG("Exec: word-end (RETURN)");
|
||||
w2 = &fh->dict[instr->data];
|
||||
if (w2->builtin) {
|
||||
LOG("Exec: builtin-word %s", w2->name);
|
||||
w2->handler(fh, w2);
|
||||
if (fh->substate == FH_SUBSTATE_EXIT) {
|
||||
fh_setsubstate(fh, 0);
|
||||
LOG("Exec: early return");
|
||||
TRY(rs_pop(fh, &fh->execptr));
|
||||
if (fh->execptr == MAGICADDR_INTERACTIVE) {
|
||||
goto end;
|
||||
}
|
||||
goto instr;
|
||||
|
||||
default:
|
||||
w2 = &fh->dict[instr->data];
|
||||
if (w2->builtin) {
|
||||
LOG("Exec: builtin-word %s", w2->name);
|
||||
w2->handler(fh, w2);
|
||||
if (fh->substate == FH_SUBSTATE_EXIT) {
|
||||
fh_setsubstate(fh, 0);
|
||||
LOG("Exec: early return");
|
||||
TRY(rs_pop(fh, &fh->execptr));
|
||||
if (fh->execptr == MAGICADDR_INTERACTIVE) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
goto instr;
|
||||
} else {
|
||||
LOG("Exec: user-word %s (CALL)", w2->name);
|
||||
w = &fh->dict[instr->data];
|
||||
goto call;
|
||||
}
|
||||
}
|
||||
goto instr;
|
||||
} else {
|
||||
LOG("Exec: user-word %s (CALL)", w2->name);
|
||||
w = &fh->dict[instr->data];
|
||||
goto call;
|
||||
}
|
||||
|
||||
case FH_INSTR_JUMPZERO:
|
||||
TRY(ds_pop(fh, &val));
|
||||
if (0 == val) {
|
||||
fh->execptr = instr->data;
|
||||
}
|
||||
goto instr;
|
||||
|
||||
case FH_INSTR_JUMP:
|
||||
fh->execptr = instr->data;
|
||||
goto instr;
|
||||
|
||||
/* special case for strings stored in compile memory */
|
||||
case FH_INSTR_ALLOCSTR:
|
||||
case FH_INSTR_TYPESTR:
|
||||
strl = instr->data;
|
||||
if (instr->kind == FH_INSTR_ALLOCSTR) {
|
||||
TRY(fh_heap_reserve(fh, strl, &addr));
|
||||
fh_heap_copy_from_compile(fh, addr, fh->execptr, strl);
|
||||
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]);
|
||||
TRY(ds_push(fh, addr));
|
||||
TRY(ds_push(fh, strl));
|
||||
} else {
|
||||
LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]);
|
||||
FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]);
|
||||
}
|
||||
fh->execptr += strl;
|
||||
goto instr;
|
||||
|
||||
case FH_INSTR_ENDWORD:
|
||||
LOG("Exec: word-end (RETURN)");
|
||||
TRY(rs_pop(fh, &fh->execptr));
|
||||
if (fh->execptr == MAGICADDR_INTERACTIVE) {
|
||||
goto end;
|
||||
}
|
||||
goto instr;
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -201,18 +207,12 @@ static enum fh_error fh_handle_quoted_string(
|
||||
LOG("Compile a string");
|
||||
/* compile */
|
||||
if (fh->substate == FH_SUBSTATE_SQUOTE) {
|
||||
instr_init(&instr, FH_INSTR_WORD, CPLWORD_ALLOCSTR);
|
||||
instr_init(&instr, FH_INSTR_ALLOCSTR, len);
|
||||
} else {
|
||||
instr_init(&instr, FH_INSTR_WORD, CPLWORD_TYPESTR);
|
||||
instr_init(&instr, FH_INSTR_TYPESTR, len);
|
||||
}
|
||||
uint32_t len32 = len;
|
||||
/* string is encoded as a special compiler command, the size,
|
||||
* and then the string, all 4-byte aligned. */
|
||||
TRY(fh_compile_put(fh, &instr, INSTR_SIZE));
|
||||
|
||||
TRY(fh_compile_reserve(fh, len + 4, &addr));
|
||||
fh_compile_write(fh, addr, &len32, 4);
|
||||
fh_compile_write(fh, addr + 4, start, len);
|
||||
TRY(fh_compile_put(fh, start, len));
|
||||
}
|
||||
return FH_OK;
|
||||
}
|
||||
@@ -289,13 +289,12 @@ static enum fh_error fh_handle_word(
|
||||
|
||||
static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) {
|
||||
if (w->handler == w_user_word) {
|
||||
FHPRINT("addr 0x%08x ", w->start);
|
||||
|
||||
uint32_t execptr = w->start;
|
||||
|
||||
instr:;
|
||||
// make sure it's aligned
|
||||
execptr = WORDALIGNED(execptr);
|
||||
FHPRINT("0x%08x: ", execptr);
|
||||
const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[execptr];
|
||||
execptr += INSTR_SIZE;
|
||||
|
||||
@@ -304,39 +303,39 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) {
|
||||
const struct fh_word_s *w2;
|
||||
switch (instr->kind) {
|
||||
case FH_INSTR_NUMBER:
|
||||
FHPRINT("Value(%d) ", instr->data);
|
||||
FHPRINT("Number(%d)\n", instr->data);
|
||||
goto instr;
|
||||
|
||||
case FH_INSTR_WORD:
|
||||
wn = instr->data;
|
||||
switch (wn) {
|
||||
/* special case for strings stored in compile memory */
|
||||
case CPLWORD_ALLOCSTR:
|
||||
case CPLWORD_TYPESTR:
|
||||
strl = *((uint32_t *) &fh->compile[execptr]);
|
||||
execptr += 4; // advance past the length
|
||||
if (wn == CPLWORD_ALLOCSTR) {
|
||||
FHPRINT("AllocStr(\"%.*s\") ", strl, &fh->compile[execptr]);
|
||||
execptr += strl;
|
||||
} else {
|
||||
FHPRINT("PrintStr(\"%.*s\") ", strl, &fh->compile[execptr]);
|
||||
execptr += strl;
|
||||
}
|
||||
goto instr;
|
||||
w2 = &fh->dict[instr->data];
|
||||
FHPRINT("Call(%s, 0x%08x)\n", w2->name, instr->data);
|
||||
goto instr;
|
||||
|
||||
case CPLWORD_ENDWORD:
|
||||
FHPRINT("END");
|
||||
return;
|
||||
case FH_INSTR_JUMPZERO:
|
||||
FHPRINT("JumpIfZero(0x%08x)\n", instr->data);
|
||||
goto instr;
|
||||
|
||||
default:
|
||||
w2 = &fh->dict[instr->data];
|
||||
if (w2->name[0]) {
|
||||
FHPRINT("Call(%s) ", w2->name);
|
||||
} else {
|
||||
FHPRINT("Call(0x%08x) ", instr->data);
|
||||
}
|
||||
goto instr;
|
||||
case FH_INSTR_JUMP:
|
||||
FHPRINT("Jump(0x%08x)\n", instr->data);
|
||||
goto instr;
|
||||
|
||||
/* special case for strings stored in compile memory */
|
||||
case FH_INSTR_ALLOCSTR:
|
||||
case FH_INSTR_TYPESTR:
|
||||
strl = instr->data;
|
||||
if (instr->kind == FH_INSTR_ALLOCSTR) {
|
||||
FHPRINT("AllocStr(\"%.*s\")\n", strl, &fh->compile[execptr]);
|
||||
execptr += strl;
|
||||
} else {
|
||||
FHPRINT("PrintStr(\"%.*s\")\n", strl, &fh->compile[execptr]);
|
||||
execptr += strl;
|
||||
}
|
||||
goto instr;
|
||||
|
||||
case FH_INSTR_ENDWORD:
|
||||
FHPRINT("END\n");
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user