forth/forth.h
#ifndef __DUCKY_FORTH_H__
#define __DUCKY_FORTH_H__
#include <config.h>
#include <arch/ducky.h>
#include <stddef.h>
/*
* Kernel version. Upper byte MAJOR, lower byte MINOR.
*/
#define FORTH_VERSION 0x0100
// Stringify helpers
#define XSTR(_e) STR(_e)
#define STR(_e) #_e
/*
* ABI helpers
*/
// I'd rather have separate calling convention for FORTH, or even better,
// update FORTH assembly code to conform to the default Ducky CC but that's
// quite a lot of work...
#define FORTHCC __attribute__((preserve_all))
/*
* Use these to declare assembly-defined structures in C code.
*/
#define ASM_PTR(_type, _name) extern _type __attribute__ ((aligned (4))) _name
#define ASM_INT(_type, _name) extern _type __attribute__ ((aligned (4))) _name
#define ASM_SHORT(_type, _name) extern _type __attribute__ ((aligned (2))) _name
#define ASM_BYTE(_type, _name) extern _type __attribute__ ((aligned (1))) _name
#define ASM_STRUCT(_type, _name) extern _type __attribute__ ((aligned (4))) _name
/**
* Internal kernel structures
*/
#ifndef __DUCKY_PURE_ASM__
typedef u32_t cell_t;
typedef struct __attribute__((packed)) {
u8_t cs_len;
char cs_str; // first character of string
} counted_string_t;
/* FORTH word header */
typedef struct word_header word_header_t;
typedef cell_t cf_t;
struct __attribute__((packed)) word_header {
word_header_t * wh_link;
u16_t wh_name_crc;
u8_t wh_flags;
counted_string_t wh_name;
};
extern int fw_search(counted_string_t *needle, word_header_t **);
extern u32_t *fw_code_field(word_header_t *word);
extern u32_t *fw_data_field(word_header_t *word);
extern u32_t *fw_value_field(word_header_t *word);
#endif // __DUCKY_PURE_ASM__
/* Offsets of word header' fields */
#define WR_LINK 0
#define WR_NAMECRC 4
#define WR_FLAGS 6
#define WR_NAMELEN 7
#define WR_NAME 8
/* Word flags */
#define F_IMMED 0x0001
#define F_HIDDEN 0x0002
/* Input stack structures */
#ifndef __DUCKY_PURE_ASM__
typedef struct input_desc input_desc_t;
typedef enum {
OK = 0,
NO_INPUT = 1,
EMPTY = 2
} input_refiller_status_t;
typedef input_refiller_status_t (*input_refiller_t)(input_desc_t *);
struct input_desc {
i32_t id_source_id;
input_refiller_t id_refiller;
char * id_buffer;
u32_t id_length;
u32_t id_index;
u32_t id_max_length;
u32_t id_blk; // BLK - non-zero if descriptor handles block
};
#define INPUT_IS_KBD() (current_input->id_source_id == 0)
#define INPUT_IS_EVAL() (current_input->id_source_id == -1)
#define INPUT_IS_BLK() (current_input->id_blk != 0)
#endif // __DUCKY_PURE_ASM__
/* Interpret state */
#define STATE_COMPILE 1
#define STATE_INTERPRET 0
/* FORTH boolean "flags" */
#define FORTH_TRUE 0xFFFFFFFF
#define FORTH_FALSE 0x00000000
/* Error codes */
#define ERR_UNKNOWN 1
#define ERR_UNDEFINED_WORD 2
#define ERR_UNHANDLED_IRQ 3
#define ERR_NO_INTERPRET_SEMANTICS 4
#define ERR_MALFORMED_HDT 5
#define ERR_UNHANDLED_ARGUMENT 6
#define ERR_INPUT_STACK_OVERFLOW 7
#define ERR_INPUT_STACK_UNDERFLOW 8
#define ERR_UNALIGNED_MEMORY_ACCESS 9
#define ERR_INTERPRET_FAIL 10
#define ERR_BIO_FAIL 11
#define ERR_WORD_TOO_LONG 12
/**
* Internal kernel API
*/
#ifndef __DUCKY_PURE_ASM__
static inline u32_t align4(u32_t u)
{
return (u + 3) & 0xFFFFFFFC;
}
#define CELL_ALIGN(_u) align4(_u)
// Low-level stuff
extern void __idle(void);
// String/memory helpers
extern int __c_strcmp(char *s1, char *s2, u32_t len1, u32_t len2);
extern void bzero(char *, u32_t);
extern void __c_memcpy(char *dst, char *src, u32_t len);
extern void memset(u8_t *dst, u32_t c, u32_t len);
extern u16_t strcrc(char *s, u8_t len);
static inline u16_t cs_crc(counted_string_t *cs) { return strcrc(&cs->cs_str, cs->cs_len); }
extern int cs_cmp(counted_string_t *s1, counted_string_t *s2);
// TTY output
extern void putc(char );
extern void puts(char *, u32_t);
extern void putcs(char *);
extern void putnl(void);
#define BR() do { putc('\r'); putc('\n'); } while(0)
// "Print <something>" helpers
extern void do_print_prompt(void);
extern void print_prompt(u32_t);
#include <stdarg.h>
extern int mini_vsnprintf(char* buffer, unsigned int buffer_len, char *fmt, va_list va);
extern int mini_snprintf(char* buffer, unsigned int buffer_len, char *fmt, ...);
extern int printf(char *fmt, ...);
extern char printf_buffer[];
#define vsnprintf mini_vsnprintf
#define snprintf mini_snprintf
// Errors and exceptions
extern void halt(int errno) __attribute__((noreturn));
extern void __ERR_die(char *msg, int errno) __attribute__((noreturn));
extern void __ERR_die_with_input(char *msg, int exit_code) __attribute__((noreturn));
#if (CONFIG_DIE_ON_UNDEF == 0)
extern void __ERR_undefined_word(void);
#else
extern void __ERR_undefined_word(void) __attribute__((noreturn));
#endif
extern void __ERR_no_interpretation_semantics(void) __attribute__((noreturn));
extern void __ERR_input_stack_overflow(void) __attribute__((noreturn));
extern void __ERR_input_stack_underflow(void) __attribute__((noreturn));
extern void __ERR_unknown(void) __attribute__((noreturn));
extern void __ERR_interpret_fail(void) __attribute__((noreturn));
extern void __ERR_bio_fail(u32_t storage, u32_t bid, u32_t status, int errno) __attribute__((noreturn));
extern void __ERR_word_too_long(void) __attribute__((noreturn));
// Input processing
extern input_desc_t *current_input;
extern void input_stack_pop(void);
extern void input_stack_push(input_desc_t *);
extern void __refill_input_buffer(void);
extern u32_t __read_line_from_kbd(char *, u32_t);
extern u8_t __read_char(void);
extern counted_string_t *__read_word(char delimiter);
extern counted_string_t *__read_word_with_refill(char delimiter);
extern counted_string_t *__read_dword(void);
extern counted_string_t *__read_dword_with_refill(void);
// Heap allocations
extern void *malloc(u32_t);
extern void free(void *);
// Enviroment queries
typedef enum {
UNKNOWN = 0,
NUMBER = 1,
DOUBLE_NUMBER = 2,
TRUE = 3,
FALSE = 4
} environment_query_status_t;
typedef struct __attribute__((packed)) {
u32_t number_lo;
u32_t number_hi;
} environment_query_result_t;
/* ----------------------------------------------------------------------
* Number parsing
*/
typedef struct {
i32_t nr_remaining;
i32_t nr_number_lo;
i32_t nr_number_hi;
} parse_number_result_t;
extern int parse_number(counted_string_t *s, parse_number_result_t * __attribute__((align_value(4))) result);
extern void print_u32(u32_t u);
extern void print_i32(i32_t i);
extern void pno_reset_buffer(void);
// Interpreter loop
typedef enum {
INTERPRET_NOP = 0,
INTERPRET_EMPTY = 1,
INTERPRET_EXECUTE_WORD = 2,
INTERPRET_EXECUTE_LIT = 3,
INTERPRET_EXECUTE_2LIT = 4
} interpret_status_t;
typedef struct __attribute__((packed)) {
interpret_status_t id_status;
union {
cf_t * id_cfa;
u32_t id_number;
u32_t id_double_number[2];
} u;
} interpret_decision_t;
typedef struct __attribute__((packed)) {
char * pr_word;
u32_t pr_length;
} parse_result_t;
/*
* Compilation helper - it's not necessary to perform full call to do_COMMA.
*/
ASM_PTR(u32_t *, var_DP);
extern void __COMPILE(u32_t u);
#define COMPILE(_u) do { __COMPILE((u32_t)(_u)); } while(0)
/*
* Compilation/interpreting state
*/
ASM_INT(u32_t, var_STATE);
#define IS_COMPILATION() (var_STATE == STATE_COMPILE)
#define IS_INTERPRET() (var_STATE == STATE_INTERPRET)
/*
* These functions implement different FORTH words. As such, they are callable
* by their assembly wrappers.
*/
extern void do_AGAIN(u32_t *dest);
extern void do_BACKSLASH(void);
extern void do_BYE(void) __attribute__((noreturn));
extern void do_PAREN(void);
extern void do_COLON(void);
extern void do_COMMA(u32_t);
extern void do_CR(void);
extern void do_DOT_PAREN(void);
extern environment_query_status_t do_ENVIRONMENT_QUERY(char *, u32_t, environment_query_result_t *);
extern void do_EVALUATE(char *, u32_t);
extern void do_HEADER_COMMA(counted_string_t *name);
extern u32_t *do_IF(void);
extern void do_INTERPRET(interpret_decision_t *);
extern int do_ISNUMBER(counted_string_t *needle, i32_t *num);
extern void do_LITERAL(u32_t u);
extern void do_LITSTRING(cf_t *);
extern void do_PARSE(char, parse_result_t *);
extern void do_SPACE(void);
extern void do_SPACES(i32_t n);
extern cf_t *do_TCFA(word_header_t *);
extern u32_t *do_TOIN(void);
extern u32_t do_UWIDTH(u32_t);
extern void do_POSTPONE(void);
extern u32_t do_REFILL(void);
extern u32_t do_SAVE_INPUT(u32_t *buffer);
extern void do_SEMICOLON(void);
extern void do_RESTORE_INPUT(u32_t n, u32_t *buffer);
extern void *do_BLK(void);
extern void *do_BLOCK(u32_t bid);
extern void *do_BUFFER(u32_t bid);
extern void do_EMPTY_BUFFERS(void);
extern void do_FLUSH(void);
extern void do_LIST(u32_t bid);
extern void do_SAVE_BUFFERS(void);
extern void do_UPDATE(void);
extern void do_BLK_LOAD(u32_t bid);
extern void do_THRU(u32_t u1, u32_t u2);
#endif // __DUCKY_PURE_ASM__
/*
* Following macros and definitions are used in the assembly
* sources.
*/
#ifdef __DUCKY_PURE_ASM__
#define COMPILE __COMPILE
// Syntax sugar, to help me define variables in assembly
#define WORD(_name, _init) \
.align CELL \
.type _name, word, _init \
.global _name
#define SHORT(_name, _init) \
.align HALFCELL \
.type _name, short, _init \
.global _name
#define BYTE(_name, _init) \
.type _name, byte, _init \
.global _name
#define ASCII(_name, _init) \
.type _name, ascii, _init \
.global _name
#define SPACE(_name, _init) \
.type _name, space, _init \
.global _name
/* Return stack manipulation */
#define PUSHRSP(_reg) \
sub RSP, CELL \
stw RSP, _reg
#define POPRSP(_reg) \
lw _reg, RSP \
add RSP, CELL
/*
* "Move to the next word" bit, at the end of every word
*
* FIP points to a cell with address of a Code Field,
* and Code Field contains address of routine.
*/
#define NEXT \
lw W, FIP \
add FIP, CELL \
lw X, W \
j X
/* Word definition macros */
#define __DEFWORD(name, len, flags, label) \
.global label \
\
.section .rodata \
\
WORD(name_ ## label, link) \
.set link, name_ ## label \
\
SHORT(__crc_ ## label, 0x7979) \
BYTE(__flags_ ## label, flags) \
BYTE(__len_ ## label, len) \
ASCII(__name_ ## label, name) \
.align CELL
#define DEFWORD(name, len, flags, label) \
__DEFWORD(name, len, flags, label) \
\
.type label, word, DOCOL
#define DEFDOESWORD(name, len, flags, label) \
__DEFWORD(name, len, flags, label) \
\
.type label, word, DODOES
#define DEFCODE(name, len, flags, label) \
__DEFWORD(name, len, flags, label) \
\
.global code_ ## label \
.type label, word, code_ ## label \
\
.text \
code_ ## label:
#define DEFVAR(name, len, flags, label, initial) \
DEFCODE(name, len, flags, label) \
\
push TOS \
la TOS, var_ ## label \
NEXT \
\
.data \
.align CELL \
.type var_ ## label, word, initial \
.global var_ ## label
#define DEFCONST(name, len, flags, label, initial) \
DEFCODE(name, len, flags, label) \
\
push TOS \
li TOS, initial \
NEXT
#define DEFCSTUB_00(name, len, flags, label) \
DEFCODE(name, len, flags, label) \
\
call do_ ## label \
NEXT
#define DEFCSTUB_10(name, len, flags, label) \
DEFCODE(name, len, flags, label) \
\
mov r0, TOS \
call do_ ## label \
pop TOS \
NEXT
#define DEFCSTUB_20(name, len, flags, label) \
DEFCODE(name, len, flags, label) \
\
mov r1, TOS \
pop r0 \
call do_ ## label \
pop TOS \
NEXT
#define DEFCSTUB_11(name, len, flags, label) \
DEFCODE(name, len, flags, label) \
\
mov r0, TOS \
call do_ ## label \
mov TOS, r0 \
NEXT
#define DEFCSTUB_01(name, len, flags, label) \
DEFCODE(name, len, flags, label) \
\
push TOS \
call do_ ## label \
mov TOS, r0 \
NEXT
#define DEFCSTUB DEFCSTUB_00
// Compare words have always the same epilog...
#define TF_FINISH(name, true_test) \
true_test __tf_finish_ ## name \
j __CMP_false \
__tf_finish_ ## name: \
j __CMP_true
#define LOAD_TRUE(reg) \
li reg, 0xFFFF \
liu reg, 0xFFFF
#define LOAD_FALSE(reg) \
li reg, 0x0000 \
liu reg, 0x0000
#define PUSH_TRUE(reg) \
LOAD_TRUE(reg) \
push reg
#define ALIGN_CELL(reg) \
add reg, 3 \
and reg, 0x7FFC
#endif // __DUCKY_PURE_ASM__
/*
* Debugging options
*/
#if 0
# define DEBUG_BLOCKS 1
# define DEBUG_printf printf
# define DEBUG_puts puts
# define DEBUG_putcs putcs
#else
# define DEBUG_BLOCKS 0
# define DEBUG_printf(...) (void)0
# define DEBUG_puts(a, b) (void)0
# define DEBUG_putcs(a) (void)0
#endif
#endif