use one buffer for both compiled code and data

master
Ondřej Hruška 3 years ago
parent 44fd2f3dbc
commit 2d30f61387
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      include/fh_config.h
  2. 20
      include/fh_mem.h
  3. 19
      include/fh_runtime.h
  4. 48
      src/fh_builtins.c
  5. 48
      src/fh_mem.c
  6. 28
      src/fh_runtime.c
  7. 4
      src/main.c

@ -12,9 +12,11 @@
#define RETURN_STACK_DEPTH 1024 #define RETURN_STACK_DEPTH 1024
#define MAX_NAME_LEN 32 #define MAX_NAME_LEN 32
#define DICT_SIZE 1024 #define DICT_SIZE 1024
#define COMPILED_BUFFER_SIZE (1024*1024)
#define HEAP_SIZE (1024*1024) #define HEAP_SIZE (1024*1024)
#define MAXLINE 65535 #define MAXLINE 65535
#define PAD_SIZE 256
#define WORDBUF_SIZE 128
#define INPUT_BUFFER_SIZE 256
#define CELL 4 #define CELL 4

@ -24,14 +24,18 @@ enum fh_error fh_heap_reserve(
); );
void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint32_t len); void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint32_t len);
enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len); enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len);
void fh_heap_copy_from_compile(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len); void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len);
enum fh_error fh_compile_reserve( static inline char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) {
struct fh_thread_s *fh, return (char *) &fh->heap[addr];
size_t len, }
uint32_t *addr
); static inline struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) {
void fh_compile_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint32_t len); return (void *) &fh->heap[addr];
enum fh_error fh_compile_put(struct fh_thread_s *fh, const void *src, uint32_t len); }
static inline struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) {
return (struct fh_word_s *) &fh->heap[addr];
}
#endif //FORTH_FH_MEM_H #endif //FORTH_FH_MEM_H

@ -108,8 +108,7 @@ struct fh_word_s {
word_exec_t handler; word_exec_t handler;
/** Indicates that this is a built-in instruction and not a word call */ /** Indicates that this is a built-in instruction and not a word call */
bool builtin; bool builtin;
/** Indicates that this instruction should always be treated as interpreted, /** Indicates that this instruction should always be treated as interpreted */
* in practice this is only used for `;` */
bool immediate; bool immediate;
/** Start address in case of user words, or param for builtins */ /** Start address in case of user words, or param for builtins */
union { union {
@ -140,13 +139,10 @@ struct fh_thread_s {
size_t return_stack_top; size_t return_stack_top;
size_t return_stack_hwm; size_t return_stack_hwm;
/** Data heap */ /** Data buffer used for everything */
uint8_t heap[HEAP_SIZE]; uint8_t heap[HEAP_SIZE];
size_t heap_top; // aka "HERE" size_t here;
/** Compile buffer, used for both word data and literals */
uint8_t compile[COMPILED_BUFFER_SIZE];
size_t compile_top;
/** Pointer into the compile buffer for execution */ /** Pointer into the compile buffer for execution */
uint32_t execptr; uint32_t execptr;
@ -154,6 +150,15 @@ struct fh_thread_s {
struct fh_word_s dict[DICT_SIZE]; struct fh_word_s dict[DICT_SIZE];
uint32_t dict_top; uint32_t dict_top;
/** Pad buffer */
uint8_t pad[PAD_SIZE];
/** WORD and pictured output buffer */
uint8_t wordbuf[WORDBUF_SIZE];
/** Input buffer */
uint8_t inbuf[INPUT_BUFFER_SIZE];
uint32_t inbuf_ptr;
/** Forth state */ /** Forth state */
enum fh_state state; enum fh_state state;

@ -368,7 +368,7 @@ static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w)
} }
struct fh_word_s *new_word = &fh->dict[fh->dict_top]; struct fh_word_s *new_word = &fh->dict[fh->dict_top];
new_word->index = fh->dict_top; new_word->index = fh->dict_top;
new_word->start = fh->compile_top; new_word->start = fh->here;
new_word->handler = w_user_word; new_word->handler = w_user_word;
return FH_OK; return FH_OK;
@ -409,7 +409,7 @@ static enum fh_error w_literal(struct fh_thread_s *fh, const struct fh_word_s *w
uint32_t val; uint32_t val;
TRY(ds_pop(fh, &val)); TRY(ds_pop(fh, &val));
instr_init(&instr, FH_INSTR_NUMBER, val); instr_init(&instr, FH_INSTR_NUMBER, val);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -423,7 +423,7 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s
ENSURE_STATE(FH_STATE_COMPILE); ENSURE_STATE(FH_STATE_COMPILE);
instr_init(&instr, FH_INSTR_ENDWORD, 0); instr_init(&instr, FH_INSTR_ENDWORD, 0);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_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);
@ -472,7 +472,7 @@ static enum fh_error w_recurse(struct fh_thread_s *fh, const struct fh_word_s *w
ENSURE_STATE(FH_STATE_COMPILE); ENSURE_STATE(FH_STATE_COMPILE);
instr_init(&instr, FH_INSTR_WORD, fh->dict_top); instr_init(&instr, FH_INSTR_WORD, fh->dict_top);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -724,7 +724,7 @@ static enum fh_error w_type(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(ds_pop(fh, &count)); TRY(ds_pop(fh, &count));
TRY(ds_pop(fh, &addr)); TRY(ds_pop(fh, &addr));
FHPRINT("%.*s", count, &fh->heap[addr]); FHPRINT("%.*s", count, fh_str_at(fh, addr));
return FH_OK; return FH_OK;
} }
@ -850,9 +850,9 @@ static enum fh_error w_if(struct fh_thread_s *fh, const struct fh_word_s *w)
ENSURE_STATE(FH_STATE_COMPILE); ENSURE_STATE(FH_STATE_COMPILE);
TRY(cs_push(fh, fh->compile_top)); TRY(cs_push(fh, fh->here));
instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED); instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -866,17 +866,17 @@ static enum fh_error w_else(struct fh_thread_s *fh, const struct fh_word_s *w)
uint32_t ifaddr = 0; uint32_t ifaddr = 0;
TRY(cs_pop(fh, &ifaddr)); TRY(cs_pop(fh, &ifaddr));
struct fh_instruction_s *if_instr = (void *) &fh->compile[ifaddr]; struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr);
if (if_instr->data != MAGICADDR_UNRESOLVED) { if (if_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("IF-ELSE control stack corruption"); LOGE("IF-ELSE control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
if_instr->data = fh->compile_top + INSTR_SIZE; if_instr->data = fh->here + INSTR_SIZE;
TRY(cs_push(fh, fh->compile_top)); TRY(cs_push(fh, fh->here));
instr_init(&instr, FH_INSTR_JUMP, MAGICADDR_UNRESOLVED); instr_init(&instr, FH_INSTR_JUMP, MAGICADDR_UNRESOLVED);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -889,13 +889,13 @@ static enum fh_error w_then(struct fh_thread_s *fh, const struct fh_word_s *w)
uint32_t ifaddr = 0; uint32_t ifaddr = 0;
TRY(cs_pop(fh, &ifaddr)); TRY(cs_pop(fh, &ifaddr));
struct fh_instruction_s *if_instr = (void *) &fh->compile[ifaddr]; struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr);
if (if_instr->data != MAGICADDR_UNRESOLVED) { if (if_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("IF-ELSE control stack corruption"); LOGE("IF-ELSE control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
if_instr->data = fh->compile_top; if_instr->data = fh->here;
return FH_OK; return FH_OK;
} }
@ -911,7 +911,7 @@ static enum fh_error w_until(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(cs_pop(fh, &destaddr)); TRY(cs_pop(fh, &destaddr));
instr_init(&instr, FH_INSTR_JUMPZERO, destaddr); instr_init(&instr, FH_INSTR_JUMPZERO, destaddr);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -920,7 +920,7 @@ static enum fh_error w_begin(struct fh_thread_s *fh, const struct fh_word_s *w)
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
ENSURE_STATE(FH_STATE_COMPILE); ENSURE_STATE(FH_STATE_COMPILE);
TRY(cs_push(fh, fh->compile_top)); /* dest */ TRY(cs_push(fh, fh->here)); /* dest */
return FH_OK; return FH_OK;
} }
@ -935,11 +935,11 @@ static enum fh_error w_while(struct fh_thread_s *fh, const struct fh_word_s *w)
uint32_t destaddr = 0; uint32_t destaddr = 0;
TRY(cs_pop(fh, &destaddr)); TRY(cs_pop(fh, &destaddr));
TRY(cs_push(fh, fh->compile_top)); // orig TRY(cs_push(fh, fh->here)); // orig
TRY(cs_push(fh, destaddr)); // dest TRY(cs_push(fh, destaddr)); // dest
instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED); instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -956,15 +956,15 @@ static enum fh_error w_repeat(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(cs_pop(fh, &destaddr)); TRY(cs_pop(fh, &destaddr));
TRY(cs_pop(fh, &origaddr)); TRY(cs_pop(fh, &origaddr));
struct fh_instruction_s *branch_instr = (void *) &fh->compile[origaddr]; struct fh_instruction_s *branch_instr = fh_instr_at(fh, origaddr);
if (branch_instr->data != MAGICADDR_UNRESOLVED) { if (branch_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("REPEAT control stack corruption"); LOGE("REPEAT control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
branch_instr->data = fh->compile_top + INSTR_SIZE; branch_instr->data = fh->here + INSTR_SIZE;
instr_init(&instr, FH_INSTR_JUMP, destaddr); instr_init(&instr, FH_INSTR_JUMP, destaddr);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -981,7 +981,7 @@ static enum fh_error w_again(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(cs_pop(fh, &destaddr)); TRY(cs_pop(fh, &destaddr));
instr_init(&instr, FH_INSTR_JUMP, destaddr); instr_init(&instr, FH_INSTR_JUMP, destaddr);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }
@ -1031,7 +1031,7 @@ static enum fh_error w_unused(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
TRY(ds_push(fh, HEAP_SIZE - fh->heap_top)); TRY(ds_push(fh, HEAP_SIZE - fh->here));
return FH_OK; return FH_OK;
} }
@ -1126,7 +1126,7 @@ static enum fh_error w_comma(struct fh_thread_s *fh, const struct fh_word_s *w)
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
if (fh->heap_top & 3) { if (fh->here & 3) {
LOGE("HERE not aligned before 'comma'"); LOGE("HERE not aligned before 'comma'");
return FH_ERR_ILLEGAL_STORE; return FH_ERR_ILLEGAL_STORE;
} }
@ -1141,7 +1141,7 @@ static enum fh_error w_align(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
fh->heap_top = WORDALIGNED(fh->heap_top); fh->here = WORDALIGNED(fh->here);
return FH_OK; return FH_OK;
} }

@ -18,7 +18,7 @@ enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst)
break; break;
case MAGICADDR_HERE: case MAGICADDR_HERE:
*dst = fh->heap_top; *dst = fh->here;
break; break;
default: default:
@ -95,7 +95,7 @@ enum fh_error fh_heap_reserve(
uint32_t *addr uint32_t *addr
) )
{ {
uint32_t p = fh->heap_top; uint32_t p = fh->here;
if (p + len > HEAP_SIZE) { if (p + len > HEAP_SIZE) {
return FH_ERR_HEAP_FULL; return FH_ERR_HEAP_FULL;
@ -105,7 +105,7 @@ enum fh_error fh_heap_reserve(
*addr = p; *addr = p;
} }
fh->heap_top = WORDALIGNED(p + len); fh->here = WORDALIGNED(p + len);
return FH_OK; return FH_OK;
} }
@ -127,45 +127,7 @@ enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len)
} }
/** Copy bytes from compile area to heap. The region must have been previously allocated! */ /** 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) void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len)
{ {
memcpy(&fh->heap[addr], &fh->compile[srcaddr], len); memcpy(&fh->heap[addr], &fh->heap[srcaddr], len);
}
/** Reserve space in the compile memory area */
enum fh_error fh_compile_reserve(
struct fh_thread_s *fh,
size_t len,
uint32_t *addr
)
{
uint32_t p = fh->compile_top; // FIXME this shouldn't be needed
if (p + len > COMPILED_BUFFER_SIZE) {
return FH_ERR_HEAP_FULL;
}
if (addr) {
*addr = p;
}
fh->compile_top = WORDALIGNED(p + len);
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;
} }

@ -93,7 +93,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
} }
// make sure it's aligned // make sure it's aligned
fh->execptr = WORDALIGNED(fh->execptr); fh->execptr = WORDALIGNED(fh->execptr);
const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[fh->execptr]; const struct fh_instruction_s *instr = fh_instr_at(fh, fh->execptr);
fh->execptr += INSTR_SIZE; fh->execptr += INSTR_SIZE;
uint32_t strl; uint32_t strl;
@ -115,7 +115,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
} else { } else {
LOG("Add postponed word: %s", w2->name); LOG("Add postponed word: %s", w2->name);
instr_init(&instr2, FH_INSTR_WORD, instr->data); instr_init(&instr2, FH_INSTR_WORD, instr->data);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
} }
} else { } else {
LOGE("Postpone in interpret mode!"); LOGE("Postpone in interpret mode!");
@ -169,13 +169,13 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
strl = instr->data; strl = instr->data;
if (instr->kind == FH_INSTR_ALLOCSTR) { if (instr->kind == FH_INSTR_ALLOCSTR) {
TRY(fh_heap_reserve(fh, strl, &addr)); TRY(fh_heap_reserve(fh, strl, &addr));
fh_heap_copy_from_compile(fh, addr, fh->execptr, strl); LOG("Exec: alloc-str \"%.*s\"", strl, fh_str_at(fh, fh->execptr));
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); fh_heap_copy(fh, addr, fh->execptr, strl);
TRY(ds_push(fh, addr)); TRY(ds_push(fh, addr));
TRY(ds_push(fh, strl)); TRY(ds_push(fh, strl));
} else { } else {
LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]); LOG("Exec: type-str \"%.*s\"", strl, fh_str_at(fh, fh->execptr));
FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]); FHPRINT("%.*s", (int) strl, fh_str_at(fh, fh->execptr));
} }
fh->execptr += strl; fh->execptr += strl;
goto instr; goto instr;
@ -242,8 +242,8 @@ static enum fh_error fh_handle_quoted_string(
} else { } else {
instr_init(&instr, FH_INSTR_TYPESTR, len); instr_init(&instr, FH_INSTR_TYPESTR, len);
} }
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
TRY(fh_compile_put(fh, start, len)); TRY(fh_heap_put(fh, start, len));
} }
return FH_OK; return FH_OK;
} }
@ -255,7 +255,7 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w)
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);
instr_init(&instr, FH_INSTR_WORD, w->index); instr_init(&instr, FH_INSTR_WORD, w->index);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
} else { } else {
/* interpret or immediate in compiled code */ /* interpret or immediate in compiled code */
LOG("Run word: %s", w->name); LOG("Run word: %s", w->name);
@ -324,7 +324,7 @@ enum fh_error fh_handle_ascii_word(
if (fh->state == FH_STATE_COMPILE) { if (fh->state == FH_STATE_COMPILE) {
LOG("Compile number: %ld", v); LOG("Compile number: %ld", v);
instr_init(&instr, FH_INSTR_NUMBER, (uint32_t) v); instr_init(&instr, FH_INSTR_NUMBER, (uint32_t) v);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
} else { } else {
/* interpret */ /* interpret */
LOG("Interpret number: %ld", v); LOG("Interpret number: %ld", v);
@ -343,7 +343,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
// make sure it's aligned // make sure it's aligned
execptr = WORDALIGNED(execptr); execptr = WORDALIGNED(execptr);
FHPRINT("0x%08x: ", execptr); FHPRINT("0x%08x: ", execptr);
const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[execptr]; const struct fh_instruction_s *instr = fh_instr_at(fh, execptr);
execptr += INSTR_SIZE; execptr += INSTR_SIZE;
uint32_t strl; uint32_t strl;
@ -385,10 +385,10 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
case FH_INSTR_TYPESTR: case FH_INSTR_TYPESTR:
strl = instr->data; strl = instr->data;
if (instr->kind == FH_INSTR_ALLOCSTR) { if (instr->kind == FH_INSTR_ALLOCSTR) {
FHPRINT("AllocStr(\"%.*s\")\n", strl, &fh->compile[execptr]); FHPRINT("AllocStr(\"%.*s\")\n", strl, fh_str_at(fh, execptr));
execptr += strl; execptr += strl;
} else { } else {
FHPRINT("PrintStr(\"%.*s\")\n", strl, &fh->compile[execptr]); FHPRINT("PrintStr(\"%.*s\")\n", strl, fh_str_at(fh, execptr));
execptr += strl; execptr += strl;
} }
goto instr; goto instr;
@ -434,7 +434,7 @@ static enum fh_error fh_postpone_word(
struct fh_instruction_s instr; struct fh_instruction_s instr;
LOG("Postpone %s", w->name); LOG("Postpone %s", w->name);
instr_init(&instr, FH_INSTR_POSTPONED_WORD, w->index); instr_init(&instr, FH_INSTR_POSTPONED_WORD, w->index);
TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
} }

@ -84,9 +84,9 @@ int main(int argc, char *argv[])
} }
// Show resource usage // Show resource usage
LOG("\nResources used: DS %dW, RS %dW, CS %dW, heap %dB, program %dB, dict %dx\n", LOG("\nResources used: DS %dW, RS %dW, CS %dW, memory %dB\n",
(int) fh.data_stack_hwm, (int) fh.return_stack_hwm, (int) fh.control_stack_hwm, (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); (int) fh.here);
FHPRINT_SVC("Bye.\n"); FHPRINT_SVC("Bye.\n");
return 0; return 0;

Loading…
Cancel
Save