|
|
@ -43,9 +43,9 @@ _Static_assert(WORDALIGNED(1023) == 1024, "word align"); |
|
|
|
_Static_assert(WORDALIGNED(1024) == 1024, "word align"); |
|
|
|
_Static_assert(WORDALIGNED(1024) == 1024, "word align"); |
|
|
|
|
|
|
|
|
|
|
|
/* logging */ |
|
|
|
/* logging */ |
|
|
|
#define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\r\n", ##__VA_ARGS__); } } while (0) |
|
|
|
#define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\n", ##__VA_ARGS__); } } while (0) |
|
|
|
#define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\r\n", ##__VA_ARGS__) |
|
|
|
#define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\n", ##__VA_ARGS__) |
|
|
|
#define LOGE(format, ...) fprintf(stderr, "\x1b[31;1m" format "\x1b[m\r\n", ##__VA_ARGS__) |
|
|
|
#define LOGE(format, ...) fprintf(stderr, "\x1b[31;1m" format "\x1b[m\n", ##__VA_ARGS__) |
|
|
|
/* Forth standard output. XXX should be stdout, but then colors get mangled if logging is used */ |
|
|
|
/* Forth standard output. XXX should be stdout, but then colors get mangled if logging is used */ |
|
|
|
#define FHPRINT(format, ...) fprintf(stderr, "\x1b[33;1m" format "\x1b[m", ##__VA_ARGS__) |
|
|
|
#define FHPRINT(format, ...) fprintf(stderr, "\x1b[33;1m" format "\x1b[m", ##__VA_ARGS__) |
|
|
|
#define FHPRINT_SVC(format, ...) fprintf(stderr, "" format "", ##__VA_ARGS__) |
|
|
|
#define FHPRINT_SVC(format, ...) fprintf(stderr, "" format "", ##__VA_ARGS__) |
|
|
@ -136,6 +136,8 @@ struct fh_instruction_s { |
|
|
|
uint32_t data; |
|
|
|
uint32_t data; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define INSTR_SIZE (sizeof(struct fh_instruction_s)) |
|
|
|
|
|
|
|
|
|
|
|
/** Bytecode word indices that are not in the dict, have special effect */ |
|
|
|
/** Bytecode word indices that are not in the dict, have special effect */ |
|
|
|
enum compiler_word { |
|
|
|
enum compiler_word { |
|
|
|
/** End of a user defined word, pop address and jump back */ |
|
|
|
/** End of a user defined word, pop address and jump back */ |
|
|
@ -194,14 +196,17 @@ struct fh_thread_s { |
|
|
|
/** Control stack */ |
|
|
|
/** Control stack */ |
|
|
|
uint32_t control_stack[CONTROL_STACK_DEPTH]; |
|
|
|
uint32_t control_stack[CONTROL_STACK_DEPTH]; |
|
|
|
size_t control_stack_top; |
|
|
|
size_t control_stack_top; |
|
|
|
|
|
|
|
size_t control_stack_hwm; |
|
|
|
|
|
|
|
|
|
|
|
/** Data stack */ |
|
|
|
/** Data stack */ |
|
|
|
uint32_t data_stack[DATA_STACK_DEPTH]; |
|
|
|
uint32_t data_stack[DATA_STACK_DEPTH]; |
|
|
|
size_t data_stack_top; |
|
|
|
size_t data_stack_top; |
|
|
|
|
|
|
|
size_t data_stack_hwm; |
|
|
|
|
|
|
|
|
|
|
|
/** Return stack */ |
|
|
|
/** Return stack */ |
|
|
|
uint32_t return_stack[RETURN_STACK_DEPTH]; |
|
|
|
uint32_t return_stack[RETURN_STACK_DEPTH]; |
|
|
|
size_t return_stack_top; |
|
|
|
size_t return_stack_top; |
|
|
|
|
|
|
|
size_t return_stack_hwm; |
|
|
|
|
|
|
|
|
|
|
|
/** Data heap */ |
|
|
|
/** Data heap */ |
|
|
|
uint8_t heap[HEAP_SIZE]; |
|
|
|
uint8_t heap[HEAP_SIZE]; |
|
|
@ -279,6 +284,13 @@ static inline enum fh_error cs_pop(struct fh_thread_s *fh, uint32_t *out) |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define UPDATE_HWM(hwm, top) \ |
|
|
|
|
|
|
|
do { \
|
|
|
|
|
|
|
|
if((hwm) < (top)) { \
|
|
|
|
|
|
|
|
(hwm) = (top); \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
} while(0) |
|
|
|
|
|
|
|
|
|
|
|
/** Push to data stack */ |
|
|
|
/** Push to data stack */ |
|
|
|
static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in) |
|
|
|
static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -287,6 +299,7 @@ static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in) |
|
|
|
return FH_ERR_DS_OVERFLOW; |
|
|
|
return FH_ERR_DS_OVERFLOW; |
|
|
|
} |
|
|
|
} |
|
|
|
fh->data_stack[fh->data_stack_top++] = in; |
|
|
|
fh->data_stack[fh->data_stack_top++] = in; |
|
|
|
|
|
|
|
UPDATE_HWM(fh->data_stack_hwm, fh->data_stack_top); |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -298,6 +311,7 @@ static inline enum fh_error rs_push(struct fh_thread_s *fh, uint32_t in) |
|
|
|
return FH_ERR_RS_OVERFLOW; |
|
|
|
return FH_ERR_RS_OVERFLOW; |
|
|
|
} |
|
|
|
} |
|
|
|
fh->return_stack[fh->return_stack_top++] = in; |
|
|
|
fh->return_stack[fh->return_stack_top++] = in; |
|
|
|
|
|
|
|
UPDATE_HWM(fh->return_stack_hwm, fh->return_stack_top); |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -309,6 +323,7 @@ static inline enum fh_error cs_push(struct fh_thread_s *fh, uint32_t in) |
|
|
|
return FH_ERR_CS_OVERFLOW; |
|
|
|
return FH_ERR_CS_OVERFLOW; |
|
|
|
} |
|
|
|
} |
|
|
|
fh->control_stack[fh->control_stack_top++] = in; |
|
|
|
fh->control_stack[fh->control_stack_top++] = in; |
|
|
|
|
|
|
|
UPDATE_HWM(fh->control_stack_hwm, fh->control_stack_top); |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -357,6 +372,28 @@ enum fh_error fh_heap_reserve( |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Write bytes to heap at a given location. The region must have been previously allocated! */ |
|
|
|
|
|
|
|
void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint32_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
memcpy(&fh->heap[addr], src, len); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Allocate heap region and write bytes to it */ |
|
|
|
|
|
|
|
enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
enum fh_error rv; |
|
|
|
|
|
|
|
uint32_t addr; |
|
|
|
|
|
|
|
TRY(fh_heap_reserve(fh, len, &addr)); |
|
|
|
|
|
|
|
fh_heap_write(fh, addr, src, len); |
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Copy bytes from compile area to heap. The region must have been previously allocated! */ |
|
|
|
|
|
|
|
void fh_heap_copy_from_compile(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
memcpy(&fh->heap[addr], &fh->compile[srcaddr], len); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Reserve space in the compile memory area */ |
|
|
|
/** Reserve space in the compile memory area */ |
|
|
|
enum fh_error fh_compile_reserve( |
|
|
|
enum fh_error fh_compile_reserve( |
|
|
|
struct fh_thread_s *fh, |
|
|
|
struct fh_thread_s *fh, |
|
|
@ -377,6 +414,22 @@ enum fh_error fh_compile_reserve( |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Write bytes to compile area at a given location. The region must have been previously allocated! */ |
|
|
|
|
|
|
|
void fh_compile_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint32_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
memcpy(&fh->compile[addr], src, len); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Allocate compile region and write bytes to it */ |
|
|
|
|
|
|
|
enum fh_error fh_compile_put(struct fh_thread_s *fh, const void *src, uint32_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
enum fh_error rv; |
|
|
|
|
|
|
|
uint32_t addr; |
|
|
|
|
|
|
|
TRY(fh_compile_reserve(fh, len, &addr)); |
|
|
|
|
|
|
|
fh_compile_write(fh, addr, src, len); |
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
enum fh_error w_add(struct fh_thread_s *fh) |
|
|
|
enum fh_error w_add(struct fh_thread_s *fh) |
|
|
|
{ |
|
|
|
{ |
|
|
|
enum fh_error rv; |
|
|
|
enum fh_error rv; |
|
|
@ -442,17 +495,18 @@ enum fh_error w_user_word(struct fh_thread_s *fh) |
|
|
|
case CPLWORD_ALLOCSTR: |
|
|
|
case CPLWORD_ALLOCSTR: |
|
|
|
case CPLWORD_TYPESTR: |
|
|
|
case CPLWORD_TYPESTR: |
|
|
|
strl = *((uint32_t *) &fh->compile[fh->execptr]); |
|
|
|
strl = *((uint32_t *) &fh->compile[fh->execptr]); |
|
|
|
fh->execptr += 4; |
|
|
|
LOG("strl %d", strl); |
|
|
|
|
|
|
|
fh->execptr += 4; // advance past the length
|
|
|
|
if (wn == CPLWORD_ALLOCSTR) { |
|
|
|
if (wn == CPLWORD_ALLOCSTR) { |
|
|
|
TRY(fh_heap_reserve(fh, strl, &addr)); |
|
|
|
TRY(fh_heap_reserve(fh, strl, &addr)); |
|
|
|
memcpy(&fh->heap[addr], &fh->compile[fh->execptr], strl); |
|
|
|
fh_heap_copy_from_compile(fh, addr, fh->execptr, strl); |
|
|
|
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); |
|
|
|
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); |
|
|
|
TRY(ds_push(fh, addr)); |
|
|
|
TRY(ds_push(fh, addr)); |
|
|
|
TRY(ds_push(fh, strl)); |
|
|
|
TRY(ds_push(fh, strl)); |
|
|
|
fh->execptr += strl; |
|
|
|
fh->execptr += strl; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]); |
|
|
|
FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]); |
|
|
|
LOG("Exec: type-str \"%.*s\"", strl, &fh->heap[addr]); |
|
|
|
LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]); |
|
|
|
} |
|
|
|
} |
|
|
|
goto instr; |
|
|
|
goto instr; |
|
|
|
|
|
|
|
|
|
|
@ -508,10 +562,9 @@ enum fh_error w_semicolon(struct fh_thread_s *fh) |
|
|
|
return FH_ERR_INVALID_STATE; |
|
|
|
return FH_ERR_INVALID_STATE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr)); |
|
|
|
|
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.data = CPLWORD_ENDWORD; |
|
|
|
instr.data = CPLWORD_ENDWORD; |
|
|
|
memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s)); |
|
|
|
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); |
|
|
|
|
|
|
|
|
|
|
|
/* Return to interpret state */ |
|
|
|
/* Return to interpret state */ |
|
|
|
fh_setstate(fh, FH_STATE_INTERPRET, 0); |
|
|
|
fh_setstate(fh, FH_STATE_INTERPRET, 0); |
|
|
@ -543,7 +596,7 @@ enum fh_error w_type(struct fh_thread_s *fh) |
|
|
|
enum fh_error w_cr(struct fh_thread_s *fh) |
|
|
|
enum fh_error w_cr(struct fh_thread_s *fh) |
|
|
|
{ |
|
|
|
{ |
|
|
|
(void) fh; |
|
|
|
(void) fh; |
|
|
|
FHPRINT("\r\n"); |
|
|
|
FHPRINT("\n"); |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -595,8 +648,8 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const struct name_and_handler builtins[] = { |
|
|
|
const struct name_and_handler builtins[] = { |
|
|
|
{"s\"", w_s_quote}, |
|
|
|
{"s\"", w_s_quote, 1}, |
|
|
|
{".\"", w_dot_quote}, |
|
|
|
{".\"", w_dot_quote, 1}, |
|
|
|
/* Compiler control words */ |
|
|
|
/* Compiler control words */ |
|
|
|
{"bye", w_bye}, |
|
|
|
{"bye", w_bye}, |
|
|
|
/* Basic arithmetics */ |
|
|
|
/* Basic arithmetics */ |
|
|
@ -658,14 +711,12 @@ static enum fh_error fh_handle_quoted_string( |
|
|
|
{ |
|
|
|
{ |
|
|
|
enum fh_error rv; |
|
|
|
enum fh_error rv; |
|
|
|
uint32_t addr = 0; |
|
|
|
uint32_t addr = 0; |
|
|
|
uint32_t addr2 = 0; |
|
|
|
|
|
|
|
struct fh_instruction_s instr; |
|
|
|
struct fh_instruction_s instr; |
|
|
|
|
|
|
|
|
|
|
|
if (fh->state == FH_STATE_INTERPRET) { |
|
|
|
if (fh->state == FH_STATE_INTERPRET) { |
|
|
|
switch (fh->substate) { |
|
|
|
switch (fh->substate) { |
|
|
|
case FH_SUBSTATE_SQUOTE: |
|
|
|
case FH_SUBSTATE_SQUOTE: |
|
|
|
TRY(fh_heap_reserve(fh, len, &addr)); |
|
|
|
TRY(fh_heap_put(fh, start, len)); |
|
|
|
memcpy(&fh->heap[addr], start, len); |
|
|
|
|
|
|
|
TRY(ds_push(fh, addr)); |
|
|
|
TRY(ds_push(fh, addr)); |
|
|
|
TRY(ds_push(fh, len)); |
|
|
|
TRY(ds_push(fh, len)); |
|
|
|
break; |
|
|
|
break; |
|
|
@ -677,9 +728,8 @@ static enum fh_error fh_handle_quoted_string( |
|
|
|
LOGE("Bad substate in interpret mode: %s", substatenames[fh->substate]); |
|
|
|
LOGE("Bad substate in interpret mode: %s", substatenames[fh->substate]); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
LOG("Compile a string"); |
|
|
|
/* compile */ |
|
|
|
/* compile */ |
|
|
|
TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr)); |
|
|
|
|
|
|
|
TRY(fh_compile_reserve(fh, len + 4, &addr2)); |
|
|
|
|
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.data = fh->substate == FH_SUBSTATE_SQUOTE ? |
|
|
|
instr.data = fh->substate == FH_SUBSTATE_SQUOTE ? |
|
|
|
CPLWORD_ALLOCSTR : |
|
|
|
CPLWORD_ALLOCSTR : |
|
|
@ -687,9 +737,11 @@ static enum fh_error fh_handle_quoted_string( |
|
|
|
uint32_t len32 = len; |
|
|
|
uint32_t len32 = len; |
|
|
|
/* string is encoded as a special compiler command, the size,
|
|
|
|
/* string is encoded as a special compiler command, the size,
|
|
|
|
* and then the string, all 4-byte aligned. */ |
|
|
|
* and then the string, all 4-byte aligned. */ |
|
|
|
memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s)); |
|
|
|
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); |
|
|
|
memcpy(&fh->compile[addr2], &len32, 4); |
|
|
|
|
|
|
|
memcpy(&fh->compile[addr2 + 4], &start, len); |
|
|
|
TRY(fh_compile_reserve(fh, len + 4, &addr)); |
|
|
|
|
|
|
|
fh_compile_write(fh, addr, &len32, 4); |
|
|
|
|
|
|
|
fh_compile_write(fh, addr + 4, start, len); |
|
|
|
} |
|
|
|
} |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
@ -716,10 +768,9 @@ static enum fh_error fh_handle_word( |
|
|
|
// word found!
|
|
|
|
// word found!
|
|
|
|
if (fh->state == FH_STATE_COMPILE && !w->immediate) { |
|
|
|
if (fh->state == FH_STATE_COMPILE && !w->immediate) { |
|
|
|
LOG("Compile word call: %s", w->name); |
|
|
|
LOG("Compile word call: %s", w->name); |
|
|
|
TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr)); |
|
|
|
|
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.kind = FH_INSTR_WORD; |
|
|
|
instr.data = cnt; |
|
|
|
instr.data = cnt; |
|
|
|
memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s)); |
|
|
|
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
/* interpret */ |
|
|
|
/* interpret */ |
|
|
|
LOG("Interpret word: %s", w->name); |
|
|
|
LOG("Interpret word: %s", w->name); |
|
|
@ -743,10 +794,9 @@ static enum fh_error fh_handle_word( |
|
|
|
|
|
|
|
|
|
|
|
if (fh->state == FH_STATE_COMPILE) { |
|
|
|
if (fh->state == FH_STATE_COMPILE) { |
|
|
|
LOG("Compile number: %ld", v); |
|
|
|
LOG("Compile number: %ld", v); |
|
|
|
TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr)); |
|
|
|
|
|
|
|
instr.kind = FH_INSTR_NUMBER; |
|
|
|
instr.kind = FH_INSTR_NUMBER; |
|
|
|
instr.data = (uint32_t) v; |
|
|
|
instr.data = (uint32_t) v; |
|
|
|
memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s)); |
|
|
|
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
/* interpret */ |
|
|
|
/* interpret */ |
|
|
|
LOG("Interpret number: %ld", v); |
|
|
|
LOG("Interpret number: %ld", v); |
|
|
@ -923,7 +973,7 @@ int main(int argc, char *argv[]) |
|
|
|
|
|
|
|
|
|
|
|
rv = fh_process_line(&fh, linebuf); |
|
|
|
rv = fh_process_line(&fh, linebuf); |
|
|
|
if (rv == FH_OK) { |
|
|
|
if (rv == FH_OK) { |
|
|
|
FHPRINT_SVC(" ok\r\n"); |
|
|
|
FHPRINT_SVC(" ok\n"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
LOGE("ERROR %s on line %d", fherr_name(rv), linecnt); |
|
|
|
LOGE("ERROR %s on line %d", fherr_name(rv), linecnt); |
|
|
|
if (!fh_globals.interactive) { |
|
|
|
if (!fh_globals.interactive) { |
|
|
@ -934,6 +984,15 @@ int main(int argc, char *argv[]) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
FHPRINT_SVC("Bye.\r\n"); |
|
|
|
// Show resource usage
|
|
|
|
|
|
|
|
LOG("\nResources used: DS %dW, RS %dW, CS %dW, heap %dB, program %dB, dict %dx\n", |
|
|
|
|
|
|
|
(int) fh.data_stack_hwm, |
|
|
|
|
|
|
|
(int) fh.return_stack_hwm, |
|
|
|
|
|
|
|
(int) fh.control_stack_hwm, |
|
|
|
|
|
|
|
(int) fh.heap_top, |
|
|
|
|
|
|
|
(int) fh.compile_top, |
|
|
|
|
|
|
|
(int) fh.dict_top); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FHPRINT_SVC("Bye.\n"); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|