diff --git a/Makefile b/Makefile index ab9e689..5e71951 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,28 @@ -CXXSTD := c++14 -CXXFLAGS := -std=$(CXXSTD) -Wall -Werror -O0 -g +PLATFORM ?= default +CSTD := c99 +CFLAGS ?= -std=$(CSTD) -Wall -Werror -O0 -g -DPLATFORM_$(PLATFORM) LDFLAGS := -static -OBJS := linux/io.o \ - io.o \ - system.o \ - parser.o \ +OBJS := stack.o \ + eval.o \ word.o \ - dict.o \ - kforth.o -TARGET := kforth + kf.o +TARGET := kf-$(PLATFORM) all: $(TARGET) $(TARGET): $(OBJS) - $(CXX) $(LDFLAGS) -o $@ $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) -clean: - rm -f $(OBJS) $(TARGET) +clean-objs: + rm -f $(OBJS) + +clean: clean-objs + rm -f kf-pc kf-default install: $(TARGET) cp $(TARGET) ~/bin chmod 0755 ~/bin/$(TARGET) + +cross: + make PLATFORM=default clean-objs all + make PLATFORM=pc clean-objs all diff --git a/default/defs.h b/default/defs.h index 5ccb461..aecced5 100644 --- a/default/defs.h +++ b/default/defs.h @@ -1,14 +1,12 @@ -#ifndef __KF_PLATFORM_DEFAULT_H__ -#define __KF_PLATFORM_DEFAULT_H__ +#ifndef __KF_DEFAULT_DEFS_H__ +#define __KF_DEFAULT_DEFS_H__ -#include - -typedef int KF_INT; +typedef int KF_INT; typedef uintptr_t KF_ADDR; -constexpr static size_t STACK_SIZE = 48 / sizeof(KF_INT); -constexpr static size_t DICT_SIZE = 4096; -constexpr static size_t ARENA_SIZE = 256; +static const size_t DSTACK_SIZE = 12; +static const size_t RSTACK_SIZE = 12; +static const size_t DICT_SIZE = 4096; +#endif /* __KF_DEFAULT_DEFS_H__ */ -#endif // __KF_PLATFORM_DEFAULT_H__ \ No newline at end of file diff --git a/defs.h b/defs.h index d4847b8..9902df1 100644 --- a/defs.h +++ b/defs.h @@ -1,12 +1,15 @@ #ifndef __KF_DEFS_H__ #define __KF_DEFS_H__ +#include +#include +#include -#if PLATFORM == PC +#ifdef PLATFORM_pc #include "pc/defs.h" #else #include "default/defs.h" #endif -#endif __KF_DEFS_H__ \ No newline at end of file +#endif /* __KF_DEFS_H__ */ diff --git a/doc/part-0x08.rst b/doc/part-0x08.rst index bd437dc..7f938c8 100644 --- a/doc/part-0x08.rst +++ b/doc/part-0x08.rst @@ -16,3 +16,17 @@ Some design choices that didn't really work out: + my linked list approach to the dictionary + my class-based approach to words +I get the distinct feeling that I could (maybe should) be doing this in C99, so +I think I'll switch to that. + +The new design +^^^^^^^^^^^^^^ + +I'll need to provide a few initial pieces: + +1. eval.c +2. stack.c +3. the platform parts + +I'll skip the parser at first and hand hack some things, then try to +port over my I/O layer from before. diff --git a/eval.c b/eval.c new file mode 100644 index 0000000..79acd90 --- /dev/null +++ b/eval.c @@ -0,0 +1,21 @@ +#include "defs.h" +#include "eval.h" + +#include + +void +cwexec(uintptr_t entry) +{ + uintptr_t target = 0; + uintptr_t codeword = 0; + + memcpy(&codeword, (void *)entry, sizeof(uintptr_t)); + memcpy(&target, (void *)(entry + sizeof(uintptr_t)), sizeof(uintptr_t)); + ((void(*)(uintptr_t))codeword)(target); +} + +void +nexec(uintptr_t target) +{ + ((void(*)(void))target)(); +} diff --git a/eval.h b/eval.h new file mode 100644 index 0000000..ab8e885 --- /dev/null +++ b/eval.h @@ -0,0 +1,28 @@ +#ifndef __KF_EVAL_H__ +#define __KF_EVAL_H__ + +#include "defs.h" + + +/* + * cwexec is the codeword executor. It assumes that the uintptr_t + * passed into it points to the correct executor (e.g. nexec), + * which is called with the next address. + */ +void cwexec(uintptr_t); + + +/* + * nexec is the native executor. + * + * It should take a uintptr_t containing the address of a code block + * and will execute the function starting there. The function should + * half the signature void(*target)(void) - a function returning + * nothing and taking no arguments. + */ +void nexec(uintptr_t); + +static const uintptr_t nexec_p = (uintptr_t)&nexec; + + +#endif /* __KF_EVAL_H__ */ diff --git a/kf.c b/kf.c new file mode 100644 index 0000000..4e5e18f --- /dev/null +++ b/kf.c @@ -0,0 +1,28 @@ +#include "defs.h" +#include "eval.h" +#include "stack.h" +#include "word.h" + +#include +#include + +void +hello(void) +{ + printf("hello, world\n"); +} + +int +main(void) +{ + dstack_push(2); + dstack_push(3); + + uint8_t arena[128] = {0}; + uintptr_t arena_p = (uintptr_t)arena; + store_native(arena, hello); + + cwexec(arena_p); + + printf("finished\n"); +} diff --git a/pc/defs.h b/pc/defs.h index 050aded..a88cfc2 100644 --- a/pc/defs.h +++ b/pc/defs.h @@ -1,14 +1,11 @@ -#ifndef __KF_PLATFORM_PC_H__ -#define __KF_PLATFORM_PC_H__ +#ifndef __KF_PC_DEFS_H__ +#define __KF_PC_DEFS_H__ -#include - -typedef int32_t KF_INT; +typedef int32_t KF_INT; typedef uintptr_t KF_ADDR; -constexpr static size_t STACK_SIZE = 65535; -constexpr static size_t DICT_SIZE = 65535; -constexpr static size_t ARENA_SIZE = 65535; +static const size_t DSTACK_SIZE = 65535; +static const size_t RSTACK_SIZE = 65535; +static const size_t DICT_SIZE = 65535; - -#endif // __KF_PLATFORM_PC_H__ \ No newline at end of file +#endif /* __KF_PC_DEFS_H__ */ diff --git a/stack.c b/stack.c new file mode 100644 index 0000000..a9a7bee --- /dev/null +++ b/stack.c @@ -0,0 +1,98 @@ +#include "defs.h" +#include "stack.h" + +static KF_INT dstack[DSTACK_SIZE] = {0}; +static size_t dstack_len = 0; + +bool +dstack_pop(KF_INT *a) +{ + if (dstack_len == 0) { + return false; + } + + *a = dstack[--dstack_len]; + return true; +} + +bool +dstack_push(KF_INT a) +{ + if (dstack_len == DSTACK_SIZE) { + return false; + } + + dstack[dstack_len++] = a; + return true; +} + +bool +dstack_get(size_t i, KF_INT *a) +{ + if (i >= dstack_len) { + return false; + } + + *a = dstack[dstack_len - i - 1]; + return true; +} + +size_t +dstack_size() +{ + return dstack_len; +} + +void +dstack_clear() +{ + dstack_len = 0; +} + +static KF_ADDR rstack[RSTACK_SIZE] = {0}; +static size_t rstack_len = 0; + +bool +rstack_pop(KF_ADDR *a) +{ + if (rstack_len == 0) { + return false; + } + + *a = rstack[--rstack_len]; + return true; +} + +bool +rstack_push(KF_ADDR a) +{ + if (rstack_len == DSTACK_SIZE) { + return false; + } + + rstack[rstack_len++] = a; + return true; +} + +bool +rstack_get(size_t i, KF_ADDR *a) +{ + if (i >= rstack_len) { + return false; + } + + *a = rstack[rstack_len - i - 1]; + return true; +} + +size_t +rstack_size() +{ + return rstack_len; +} + +void +rstack_clear() +{ + rstack_len = 0; +} diff --git a/stack.h b/stack.h index a41a1ce..56a7dbb 100644 --- a/stack.h +++ b/stack.h @@ -1,9 +1,18 @@ #ifndef __KF_STACK_H__ #define __KF_STACK_H__ +/* data stack interaction */ +bool dstack_pop(KF_INT *); +bool dstack_push(KF_INT); +bool dstack_get(size_t, KF_INT *); +size_t dstack_size(void); +void dstack_clear(void); +/* return stack interaction */ +bool rstack_pop(KF_ADDR *); +bool rstack_push(KF_ADDR); +bool rstack_get(size_t, KF_ADDR *); +size_t rstack_size(void); +void rstack_clear(void); -static - - -#endif // __KF_STACK_H__ \ No newline at end of file +#endif /* __KF_STACK_H__ */ diff --git a/word.c b/word.c new file mode 100644 index 0000000..fa67e5f --- /dev/null +++ b/word.c @@ -0,0 +1,82 @@ +#include "defs.h" +#include "eval.h" +#include "word.h" + +#include + +static uint8_t dict[DICT_SIZE] = {0}; +static size_t last = 0; + +void +append_word(const char *name, const uint8_t len, void(*target)(void)) +{ + store_native(dict+last, name, len, target); +} + +bool +execute(const char *name, const uint8_t len) +{ + size_t offset = 0; + size_t body = 0; + while (true) { + if (!match_word(dict+offset, name, len)) { + if ((offset = word_link(dict+offset)) == 0) { + return false; + } + continue; + } + + body = word_body(dict+offset); + cwexec(dict + body + offset); + return true; + } +} + +void +store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(void)) +{ + uintptr_t target_p = (uintptr_t)target; + size_t link = 2 + len + (2 * sizeof(uintptr_t)); + + /* write the header */ + entry[0] = len; + entry[1] = 0; // flags aren't used yet + memcpy(entry+2, name, len); + memcpy(entry+2+len, &link, sizeof(link)); + + /* write the native executor codeword and the function pointer */ + memcpy(entry, (uint8_t *)(&nexec_p), sizeof(uintptr_t)); + memcpy(entry + sizeof(uintptr_t), (uint8_t *)(&target_p), sizeof(uintptr_t)); +} + +bool +match_word(uint8_t *entry, const char *name, const uint8_t len) +{ + if (entry[0] != len) { + return false; + } + + if (memcmp(entry+2, name, len) != 0) { + return false; + } + + return true; +} + +size_t +word_link(uint8_t *entry) +{ + size_t link; + + if (entry[0] == 0) { + return 0; + } + memcpy(&link, entry+2+entry[0], sizeof(link)); + return link; +} + +size_t +word_body(uint8_t *entry) +{ + return 2 + entry[0] + sizeof(size_t); +} diff --git a/word.h b/word.h new file mode 100644 index 0000000..fbc5c80 --- /dev/null +++ b/word.h @@ -0,0 +1,42 @@ +#ifndef __KF_WORD_H__ +#define __KF_WORD_H__ + +/* + * Every word in the dictionary starts with a header: + * uint8_t length; + * uint8_t flags; + * char *name; + * uintptr_t next; + * + * The body looks like the following: + * uintptr_t codeword; + * uintptr_t body[]; + * + * The codeword is the interpreter for the body. This is defined in + * eval.c. Note that a native (or builtin function) has only a single + * body element. + * + * The body of a native word points to a function that's compiled in already. + */ + + +/* + * store_native writes a new dictionary entry for a native-compiled + * function. + */ +void store_native(uint8_t *, const char *, const uint8_t, void(*)(void)); + +/* + * match_word returns true if the current dictionary entry matches the + * token being searched for. + */ +bool match_word(uint8_t *, const char *, const uint8_t); + +/* + * word_link returns the offset to the next word. + */ +size_t word_link(uint8_t *); + +size_t word_body(uint8_t *); + +#endif /* __KF_WORD_H__ */