| git.druid.rocks | index | druid520 | forthc | src/ | forthc.c |
src/forthc.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define BASE 0x100000L
#define STACKBASE 0x400000L
#define EHSIZE 64L
#define PHSIZE 56L
#define NOTESIZE 24L
#define SCRATCH_SIZE 8L
#define LOOP_MAX 32L
#define LOOPAREA_SIZE (LOOP_MAX * 16L)
#define DSTACK_SIZE (8192L * 8L)
#define TOK_LEN 128
#define TOK_MAX 65536
#define DICT_MAX 8192
#define CTRL_MAX 1024
#define RAX 0
#define RCX 1
#define RDX 2
#define RBX 3
#define RSP 4
#define RBP 5
#define RSI 6
#define RDI 7
#define R8 8
#define R9 9
#define R15 15
#define CC_E 4
#define CC_NE 5
#define CC_S 8
#define CC_NS 9
#define CC_L 12
#define CC_GE 13
#define CC_LE 14
#define CC_G 15
#define G1_ADD 0
#define G1_SUB 5
#define F7_NOT 2
#define F7_NEG 3
#define F7_DIV 6
#define F7_IDIV 7
#define OP_ADD 1
#define OP_SUB 2
#define OP_MUL 3
#define OP_DIV 4
#define OP_MOD 5
#define OP_DUP 6
#define OP_DROP 7
#define OP_SWAP 8
#define OP_OVER 9
#define OP_ROT 10
#define OP_EQ 11
#define OP_NE 12
#define OP_LT 13
#define OP_GT 14
#define OP_LE 15
#define OP_GE 16
#define OP_ZEQ 17
#define OP_ZLT 18
#define OP_AND 19
#define OP_OR 20
#define OP_XOR 21
#define OP_INVERT 22
#define OP_NEG 23
#define OP_INC 24
#define OP_DEC 25
#define OP_FETCH 26
#define OP_STORE 27
#define OP_PLUSSTORE 28
#define OP_EMIT 29
#define OP_DOT 30
#define OP_CR 31
#define OP_SPACE 32
#define OP_TRUE 33
#define OP_FALSE 34
#define OP_BYE 35
#define OP_NIP 36
#define CK_IF 1
#define CK_ELSE 2
#define CK_BEGIN 3
#define CK_WHILE 4
#define CK_DO 5
#define CK_COLON 6
#define DK_WORD 0
#define DK_VAR 1
#define DK_CONST 2
#define OS_LINUX 0
#define OS_NETBSD 1
typedef struct
{
unsigned char* data;
long len;
long cap;
} buf;
typedef struct
{
char name[32];
int kind;
long val;
} dent;
typedef struct
{
int kind;
long a;
long b;
} cent;
typedef struct
{
const char* name;
int op;
} primword;
static primword PRIMS[] =
{
{"+", OP_ADD}, {"-", OP_SUB}, {"*", OP_MUL}, {"/", OP_DIV}, {"mod", OP_MOD},
{"dup", OP_DUP}, {"drop", OP_DROP}, {"swap", OP_SWAP}, {"over", OP_OVER}, {"rot", OP_ROT},
{"=", OP_EQ}, {"<>", OP_NE}, {"<", OP_LT}, {">", OP_GT}, {"<=", OP_LE}, {">=", OP_GE},
{"0=", OP_ZEQ}, {"0<", OP_ZLT},
{"and", OP_AND}, {"or", OP_OR}, {"xor", OP_XOR}, {"invert", OP_INVERT}, {"negate", OP_NEG},
{"1+", OP_INC}, {"1-", OP_DEC},
{"@", OP_FETCH}, {"!", OP_STORE}, {"+!", OP_PLUSSTORE},
{"emit", OP_EMIT}, {".", OP_DOT}, {"cr", OP_CR}, {"space", OP_SPACE},
{"true", OP_TRUE}, {"false", OP_FALSE}, {"bye", OP_BYE},
{"nip", OP_NIP},
{NULL, 0}
};
static dent dict[DICT_MAX];
static long ndict = 0;
static cent cstack[CTRL_MAX];
static long ctop = 0;
static char toks[TOK_MAX][TOK_LEN];
static long toklines[TOK_MAX];
static int tokisstr[TOK_MAX];
static long ntok = 0;
static long scratch_addr = 0;
static long looparea_addr = 0;
static long vars_addr = 0;
static long dstack_base = 0;
static long dstack_top = 0;
static long codebase = 0;
static long printnum_addr = 0;
static long varcount = 0;
static long loopdepth = 0;
static int compiling = 0;
static long curwordaddr = 0;
static long targetos = OS_LINUX;
static long sys_write = 1;
static long sys_exit = 60;
static void fail(const char* msg);
static void failat(long line, const char* msg, const char* tok);
static char* readall(FILE* f, long* outlen);
static void tokenize(const char* src, long srclen);
static int isnum(const char* s, long* out);
static long dictfind(const char* name);
static void dictadd(const char* name, int kind, long val);
static void cpush(int kind, long a, long b);
static cent cpop(void);
static void bufinit(buf* c);
static void bufneed(buf* c, long more);
static void bufbyte(buf* c, long v);
static void bufu16(buf* c, long v);
static void bufu32(buf* c, long v);
static void bufu64(buf* c, long v);
static void bufpatch32(buf* c, long pos, long v);
static void bufzero(buf* c, long n);
static void bufappend(buf* c, unsigned char* p, long n);
static long here(buf* c);
static void rex(buf* c, int w, int reg, int rm);
static void modrm(buf* c, int mod, int reg, int rm);
static void memop(buf* c, int reg, int base, long disp);
static void i_movrm(buf* c, int dst, int base, long disp);
static void i_movmr(buf* c, int base, long disp, int src);
static void i_movmr8(buf* c, int base, long disp, int src);
static void i_movmi8(buf* c, int base, long disp, int val);
static void i_movrr(buf* c, int dst, int src);
static void i_movi32z(buf* c, int dst, unsigned long val);
static void i_movi64(buf* c, int dst, long val);
static void i_lea(buf* c, int dst, int base, long disp);
static void i_alurr(buf* c, int opcode, int reg, int rm);
static void i_imulrr(buf* c, int dst, int src);
static void i_testrr(buf* c, int reg, int rm);
static void i_g1i8(buf* c, int ext, int rm, int imm8);
static void i_f7(buf* c, int ext, int rm);
static void i_cqo(buf* c);
static void i_ret(buf* c);
static void i_syscall(buf* c);
static long i_jccph(buf* c, int cc);
static long i_jmpph(buf* c);
static void i_patchhere(buf* c, long pos);
static void i_jccto(buf* c, int cc, long target);
static void i_jmpto(buf* c, long target);
static void i_callto(buf* c, long target);
static void gen_lit(buf* c, long v);
static void gen_dup(buf* c);
static void gen_drop(buf* c);
static void gen_swap(buf* c);
static void gen_over(buf* c);
static void gen_rot(buf* c);
static void gen_add(buf* c);
static void gen_sub(buf* c);
static void gen_mul(buf* c);
static void gen_divmod(buf* c, int ismod);
static void gen_cmp(buf* c, int skipcc);
static void gen_unarytest(buf* c, int skipcc);
static void gen_bitwise(buf* c, int opcode);
static void gen_unaryf7(buf* c, int ext);
static void gen_incdec(buf* c, int ext);
static void gen_fetch(buf* c);
static void gen_store(buf* c);
static void gen_plusstore(buf* c);
static void gen_emitcharimm(buf* c, int ch);
static void gen_emit(buf* c);
static void gen_dot(buf* c);
static void gen_dotquote(buf* c, const char* text, long len);
static void gen_bye(buf* c);
static void gen_printnum(buf* c);
static long loopidxaddr(long depth);
static long looplimaddr(long depth);
static void do_if(buf* c);
static void do_else(buf* c);
static void do_then(buf* c);
static void do_begin(buf* c);
static void do_until(buf* c);
static void do_while(buf* c);
static void do_repeat(buf* c);
static void do_do(buf* c);
static void do_i(buf* c);
static void do_loop(buf* c);
static void do_colon(buf* c, const char* name);
static void do_semi(buf* c);
static void do_recurse(buf* c);
static void do_variable(const char* name);
static void do_constant(const char* name, long val);
static long opfind(const char* name);
static void genop(buf* c, long idx);
static void compile(buf* c);
static long osphnum(long os);
static long osnotesize(long os);
static void emitelf(buf* o, long entry, long totalsize, long os);
static void emitnote(buf* o);
int main(int argc, char** argv);
static void
fail(const char* msg)
{
fprintf(stderr, "err: %s\n", msg);
exit(1);
}
static void
failat(long line, const char* msg, const char* tok)
{
fprintf(stderr, "err: line %ld: %s '%s'.\n", line, msg, tok);
exit(1);
}
static char*
readall(FILE* f, long* outlen)
{
long cap;
long len;
char* b;
long n;
cap = 65536;
len = 0;
b = (char*)malloc((size_t)cap);
if(b == NULL)
{
fail("out of memory.");
}
for(;;)
{
if(len + 4096 > cap)
{
cap = cap * 2;
b = (char*)realloc(b, (size_t)cap);
if(b == NULL)
{
fail("out of memory.");
}
}
n = (long)fread(b + len, 1, 4096, f);
len = len + n;
if(n < 4096)
{
break;
}
}
*outlen = len;
return b;
}
static void
tokenize(const char* src, long srclen)
{
long i;
long line;
long j;
i = 0;
line = 1;
while(i < srclen)
{
if(src[i] == '\n')
{
line = line + 1;
i = i + 1;
continue;
}
if(src[i] == ' ' || src[i] == '\t' || src[i] == '\r')
{
i = i + 1;
continue;
}
if(src[i] == '\\')
{
while(i < srclen && src[i] != '\n')
{
i = i + 1;
}
continue;
}
if(src[i] == '(')
{
while(i < srclen && src[i] != ')')
{
if(src[i] == '\n')
{
line = line + 1;
}
i = i + 1;
}
if(i < srclen)
{
i = i + 1;
}
continue;
}
if(src[i] == '.' && i + 1 < srclen && src[i + 1] == '"')
{
if(ntok == TOK_MAX)
{
fail("too many tokens.");
}
i = i + 2;
if(i < srclen && src[i] == ' ')
{
i = i + 1;
}
toklines[ntok] = line;
tokisstr[ntok] = 1;
j = 0;
while(i < srclen && src[i] != '"')
{
if(src[i] == '\n')
{
line = line + 1;
}
if(j == TOK_LEN - 1)
{
fail("string literal too long.");
}
toks[ntok][j] = src[i];
j = j + 1;
i = i + 1;
}
if(i >= srclen)
{
fail("unterminated string literal.");
}
i = i + 1;
toks[ntok][j] = '\0';
ntok = ntok + 1;
continue;
}
if(ntok == TOK_MAX)
{
fail("too many tokens.");
}
toklines[ntok] = line;
j = 0;
while(i < srclen && src[i] != ' ' && src[i] != '\t' && src[i] != '\r' && src[i] != '\n')
{
if(j == TOK_LEN - 1)
{
fail("token too long.");
}
toks[ntok][j] = src[i];
j = j + 1;
i = i + 1;
}
toks[ntok][j] = '\0';
ntok = ntok + 1;
}
}
static int
isnum(const char* s, long* out)
{
long i;
long neg;
long val;
long got;
i = 0;
neg = 0;
val = 0;
got = 0;
if(s[0] == '-')
{
neg = 1;
i = 1;
}
while(s[i] != '\0')
{
if(s[i] < '0' || s[i] > '9')
{
return 0;
}
val = (val * 10) + (s[i] - '0');
got = 1;
i = i + 1;
}
if(got == 0)
{
return 0;
}
if(neg != 0)
{
*out = -val;
}
else
{
*out = val;
}
return 1;
}
static long
dictfind(const char* name)
{
long i;
for(i = ndict - 1; i >= 0; i = i - 1)
{
if(strcmp(dict[i].name, name) == 0)
{
return i;
}
}
return -1;
}
static void
dictadd(const char* name, int kind, long val)
{
if(ndict == DICT_MAX)
{
fail("too many words.");
}
if(strlen(name) >= 32)
{
fail("word name too long.");
}
strcpy(dict[ndict].name, name);
dict[ndict].kind = kind;
dict[ndict].val = val;
ndict = ndict + 1;
}
static void
cpush(int kind, long a, long b)
{
if(ctop == CTRL_MAX)
{
fail("control stack overflow.");
}
cstack[ctop].kind = kind;
cstack[ctop].a = a;
cstack[ctop].b = b;
ctop = ctop + 1;
}
static cent
cpop(void)
{
if(ctop == 0)
{
fail("unbalanced control structure.");
}
ctop = ctop - 1;
return cstack[ctop];
}
static void
bufinit(buf* c)
{
c->cap = 4096;
c->len = 0;
c->data = (unsigned char*)malloc((size_t)c->cap);
if(c->data == NULL)
{
fail("out of memory.");
}
}
static void
bufneed(buf* c, long more)
{
long newcap;
unsigned char* p;
if(c->len + more <= c->cap)
{
return;
}
newcap = (c->cap * 2) + more + 256;
p = (unsigned char*)malloc((size_t)newcap);
if(p == NULL)
{
fail("out of memory.");
}
memcpy(p, c->data, (size_t)c->len);
free(c->data);
c->data = p;
c->cap = newcap;
}
static void
bufbyte(buf* c, long v)
{
bufneed(c, 1);
c->data[c->len] = (unsigned char)(v & 0xFF);
c->len = c->len + 1;
}
static void
bufu16(buf* c, long v)
{
bufbyte(c, v);
bufbyte(c, v >> 8);
}
static void
bufu32(buf* c, long v)
{
bufbyte(c, v);
bufbyte(c, v >> 8);
bufbyte(c, v >> 16);
bufbyte(c, v >> 24);
}
static void
bufu64(buf* c, long v)
{
bufbyte(c, v);
bufbyte(c, v >> 8);
bufbyte(c, v >> 16);
bufbyte(c, v >> 24);
bufbyte(c, v >> 32);
bufbyte(c, v >> 40);
bufbyte(c, v >> 48);
bufbyte(c, v >> 56);
}
static void
bufpatch32(buf* c, long pos, long v)
{
c->data[pos] = (unsigned char)(v & 0xFF);
c->data[pos + 1] = (unsigned char)((v >> 8) & 0xFF);
c->data[pos + 2] = (unsigned char)((v >> 16) & 0xFF);
c->data[pos + 3] = (unsigned char)((v >> 24) & 0xFF);
}
static void
bufzero(buf* c, long n)
{
long i;
for(i = 0; i < n; i = i + 1)
{
bufbyte(c, 0);
}
}
static void
bufappend(buf* c, unsigned char* p, long n)
{
long i;
for(i = 0; i < n; i = i + 1)
{
bufbyte(c, p[i]);
}
}
static long
here(buf* c)
{
return codebase + c->len;
}
static void
rex(buf* c, int w, int reg, int rm)
{
int r;
int b;
r = 0;
if(reg >= 8)
{
r = 1;
}
b = 0;
if(rm >= 8)
{
b = 1;
}
if(w != 0 || r != 0 || b != 0)
{
bufbyte(c, 0x40 | (w << 3) | (r << 2) | b);
}
}
static void
modrm(buf* c, int mod, int reg, int rm)
{
bufbyte(c, (mod << 6) | ((reg & 7) << 3) | (rm & 7));
}
static void
memop(buf* c, int reg, int base, long disp)
{
int bl;
bl = base & 7;
if(disp == 0 && bl != 5)
{
modrm(c, 0, reg, base);
if(bl == 4)
{
bufbyte(c, 0x24);
}
}
else if(disp >= -128 && disp <= 127)
{
modrm(c, 1, reg, base);
if(bl == 4)
{
bufbyte(c, 0x24);
}
bufbyte(c, disp);
}
else
{
modrm(c, 2, reg, base);
if(bl == 4)
{
bufbyte(c, 0x24);
}
bufu32(c, disp);
}
}
static void
i_movrm(buf* c, int dst, int base, long disp)
{
rex(c, 1, dst, base);
bufbyte(c, 0x8B);
memop(c, dst, base, disp);
}
static void
i_movmr(buf* c, int base, long disp, int src)
{
rex(c, 1, src, base);
bufbyte(c, 0x89);
memop(c, src, base, disp);
}
static void
i_movmr8(buf* c, int base, long disp, int src)
{
rex(c, 0, src, base);
bufbyte(c, 0x88);
memop(c, src, base, disp);
}
static void
i_movmi8(buf* c, int base, long disp, int val)
{
rex(c, 0, 0, base);
bufbyte(c, 0xC6);
memop(c, 0, base, disp);
bufbyte(c, val);
}
static void
i_movrr(buf* c, int dst, int src)
{
rex(c, 1, src, dst);
bufbyte(c, 0x89);
modrm(c, 3, src, dst);
}
static void
i_movi32z(buf* c, int dst, unsigned long val)
{
rex(c, 0, 0, dst);
bufbyte(c, 0xB8 | (dst & 7));
bufu32(c, (long)val);
}
static void
i_movi64(buf* c, int dst, long val)
{
rex(c, 1, 0, dst);
bufbyte(c, 0xB8 | (dst & 7));
bufu64(c, val);
}
static void
i_lea(buf* c, int dst, int base, long disp)
{
rex(c, 1, dst, base);
bufbyte(c, 0x8D);
memop(c, dst, base, disp);
}
static void
i_alurr(buf* c, int opcode, int reg, int rm)
{
rex(c, 1, reg, rm);
bufbyte(c, opcode);
modrm(c, 3, reg, rm);
}
static void
i_imulrr(buf* c, int dst, int src)
{
rex(c, 1, dst, src);
bufbyte(c, 0x0F);
bufbyte(c, 0xAF);
modrm(c, 3, dst, src);
}
static void
i_testrr(buf* c, int reg, int rm)
{
rex(c, 1, reg, rm);
bufbyte(c, 0x85);
modrm(c, 3, reg, rm);
}
static void
i_g1i8(buf* c, int ext, int rm, int imm8)
{
rex(c, 1, 0, rm);
bufbyte(c, 0x83);
modrm(c, 3, ext, rm);
bufbyte(c, imm8);
}
static void
i_f7(buf* c, int ext, int rm)
{
rex(c, 1, 0, rm);
bufbyte(c, 0xF7);
modrm(c, 3, ext, rm);
}
static void
i_cqo(buf* c)
{
bufbyte(c, 0x48);
bufbyte(c, 0x99);
}
static void
i_ret(buf* c)
{
bufbyte(c, 0xC3);
}
static void
i_syscall(buf* c)
{
bufbyte(c, 0x0F);
bufbyte(c, 0x05);
}
static long
i_jccph(buf* c, int cc)
{
long pos;
bufbyte(c, 0x0F);
bufbyte(c, 0x80 | cc);
pos = c->len;
bufu32(c, 0);
return pos;
}
static long
i_jmpph(buf* c)
{
long pos;
bufbyte(c, 0xE9);
pos = c->len;
bufu32(c, 0);
return pos;
}
static void
i_patchhere(buf* c, long pos)
{
bufpatch32(c, pos, c->len - (pos + 4));
}
static void
i_jccto(buf* c, int cc, long target)
{
long pos;
bufbyte(c, 0x0F);
bufbyte(c, 0x80 | cc);
pos = c->len;
bufu32(c, 0);
bufpatch32(c, pos, target - (codebase + pos + 4));
}
static void
i_jmpto(buf* c, long target)
{
long pos;
bufbyte(c, 0xE9);
pos = c->len;
bufu32(c, 0);
bufpatch32(c, pos, target - (codebase + pos + 4));
}
static void
i_callto(buf* c, long target)
{
long pos;
bufbyte(c, 0xE8);
pos = c->len;
bufu32(c, 0);
bufpatch32(c, pos, target - (codebase + pos + 4));
}
static void
gen_lit(buf* c, long v)
{
i_movi64(c, RAX, v);
i_g1i8(c, G1_SUB, R15, 8);
i_movmr(c, R15, 0, RAX);
}
static void
gen_dup(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_SUB, R15, 8);
i_movmr(c, R15, 0, RAX);
}
static void
gen_drop(buf* c)
{
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_swap(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_movmr(c, R15, 0, RBX);
i_movmr(c, R15, 8, RAX);
}
static void
gen_over(buf* c)
{
i_movrm(c, RAX, R15, 8);
i_g1i8(c, G1_SUB, R15, 8);
i_movmr(c, R15, 0, RAX);
}
static void
gen_rot(buf* c)
{
i_movrm(c, RAX, R15, 16);
i_movrm(c, RBX, R15, 8);
i_movrm(c, RCX, R15, 0);
i_movmr(c, R15, 16, RBX);
i_movmr(c, R15, 8, RCX);
i_movmr(c, R15, 0, RAX);
}
static void
gen_add(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_alurr(c, 0x01, RAX, RBX);
i_movmr(c, R15, 8, RBX);
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_sub(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_alurr(c, 0x29, RAX, RBX);
i_movmr(c, R15, 8, RBX);
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_mul(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_imulrr(c, RBX, RAX);
i_movmr(c, R15, 8, RBX);
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_divmod(buf* c, int ismod)
{
i_movrm(c, RAX, R15, 8);
i_movrm(c, RBX, R15, 0);
i_cqo(c);
i_f7(c, F7_IDIV, RBX);
if(ismod != 0)
{
i_movmr(c, R15, 8, RDX);
}
else
{
i_movmr(c, R15, 8, RAX);
}
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_cmp(buf* c, int skipcc)
{
long p;
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_alurr(c, 0x39, RAX, RBX);
i_movi32z(c, RAX, 0);
p = i_jccph(c, skipcc);
i_movi32z(c, RAX, 1);
i_patchhere(c, p);
i_movmr(c, R15, 8, RAX);
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_unarytest(buf* c, int skipcc)
{
long p;
i_movrm(c, RAX, R15, 0);
i_testrr(c, RAX, RAX);
i_movi32z(c, RCX, 0);
p = i_jccph(c, skipcc);
i_movi32z(c, RCX, 1);
i_patchhere(c, p);
i_movmr(c, R15, 0, RCX);
}
static void
gen_bitwise(buf* c, int opcode)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_alurr(c, opcode, RAX, RBX);
i_movmr(c, R15, 8, RBX);
i_g1i8(c, G1_ADD, R15, 8);
}
static void
gen_unaryf7(buf* c, int ext)
{
i_movrm(c, RAX, R15, 0);
i_f7(c, ext, RAX);
i_movmr(c, R15, 0, RAX);
}
static void
gen_incdec(buf* c, int ext)
{
i_movrm(c, RAX, R15, 0);
i_g1i8(c, ext, RAX, 1);
i_movmr(c, R15, 0, RAX);
}
static void
gen_fetch(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RAX, RAX, 0);
i_movmr(c, R15, 0, RAX);
}
static void
gen_store(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_movmr(c, RAX, 0, RBX);
i_g1i8(c, G1_ADD, R15, 16);
}
static void
gen_plusstore(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_movrm(c, RCX, RAX, 0);
i_alurr(c, 0x01, RBX, RCX);
i_movmr(c, RAX, 0, RCX);
i_g1i8(c, G1_ADD, R15, 16);
}
static void
gen_emitcharimm(buf* c, int ch)
{
i_movi32z(c, RCX, (unsigned long)scratch_addr);
i_movmi8(c, RCX, 0, ch);
i_movi32z(c, RAX, (unsigned long)sys_write);
i_movi32z(c, RDI, 1);
i_movi32z(c, RSI, (unsigned long)scratch_addr);
i_movi32z(c, RDX, 1);
i_syscall(c);
}
static void
gen_emit(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_ADD, R15, 8);
i_movi32z(c, RCX, (unsigned long)scratch_addr);
i_movmr8(c, RCX, 0, RAX);
i_movi32z(c, RAX, (unsigned long)sys_write);
i_movi32z(c, RDI, 1);
i_movi32z(c, RSI, (unsigned long)scratch_addr);
i_movi32z(c, RDX, 1);
i_syscall(c);
}
static void
gen_dot(buf* c)
{
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_ADD, R15, 8);
i_callto(c, printnum_addr);
}
static void
gen_dotquote(buf* c, const char* text, long len)
{
long p;
long straddr;
p = i_jmpph(c);
straddr = here(c);
bufappend(c, (unsigned char*)text, len);
i_patchhere(c, p);
i_movi32z(c, RAX, (unsigned long)sys_write);
i_movi32z(c, RDI, 1);
i_movi32z(c, RSI, (unsigned long)straddr);
i_movi32z(c, RDX, (unsigned long)len);
i_syscall(c);
}
static void
gen_bye(buf* c)
{
i_movi32z(c, RAX, (unsigned long)sys_exit);
i_movi32z(c, RDI, 0);
i_syscall(c);
}
static void
gen_printnum(buf* c)
{
long jsign;
long jnz;
long jafter0;
long jlex;
long jneg;
long looptarget;
i_g1i8(c, G1_SUB, RSP, 32);
i_movi32z(c, R9, 0);
i_testrr(c, RAX, RAX);
jsign = i_jccph(c, CC_NS);
i_movi32z(c, R9, 1);
i_f7(c, F7_NEG, RAX);
i_patchhere(c, jsign);
i_lea(c, RDI, RSP, 31);
i_movi32z(c, RCX, 0);
i_movi32z(c, RBX, 10);
i_testrr(c, RAX, RAX);
jnz = i_jccph(c, CC_NE);
i_g1i8(c, G1_SUB, RDI, 1);
i_movmi8(c, RDI, 0, '0');
i_g1i8(c, G1_ADD, RCX, 1);
jafter0 = i_jmpph(c);
i_patchhere(c, jnz);
looptarget = here(c);
i_testrr(c, RAX, RAX);
jlex = i_jccph(c, CC_E);
i_movi32z(c, RDX, 0);
i_f7(c, F7_DIV, RBX);
i_g1i8(c, G1_ADD, RDX, '0');
i_g1i8(c, G1_SUB, RDI, 1);
i_movmr8(c, RDI, 0, RDX);
i_g1i8(c, G1_ADD, RCX, 1);
i_jmpto(c, looptarget);
i_patchhere(c, jlex);
i_patchhere(c, jafter0);
i_testrr(c, R9, R9);
jneg = i_jccph(c, CC_E);
i_g1i8(c, G1_SUB, RDI, 1);
i_movmi8(c, RDI, 0, '-');
i_g1i8(c, G1_ADD, RCX, 1);
i_patchhere(c, jneg);
i_movmi8(c, RSP, 31, ' ');
i_g1i8(c, G1_ADD, RCX, 1);
i_movrr(c, RSI, RDI);
i_movrr(c, RDX, RCX);
i_movi32z(c, RDI, 1);
i_movi32z(c, RAX, (unsigned long)sys_write);
i_syscall(c);
i_g1i8(c, G1_ADD, RSP, 32);
i_ret(c);
}
static long
loopidxaddr(long depth)
{
return looparea_addr + (depth * 16);
}
static long
looplimaddr(long depth)
{
return looparea_addr + (depth * 16) + 8;
}
static void
do_if(buf* c)
{
long p;
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_ADD, R15, 8);
i_testrr(c, RAX, RAX);
p = i_jccph(c, CC_E);
cpush(CK_IF, p, 0);
}
static void
do_else(buf* c)
{
cent e;
long p;
e = cpop();
if(e.kind != CK_IF)
{
fail("else without matching if.");
}
p = i_jmpph(c);
i_patchhere(c, e.a);
cpush(CK_ELSE, p, 0);
}
static void
do_then(buf* c)
{
cent e;
e = cpop();
if(e.kind != CK_IF && e.kind != CK_ELSE)
{
fail("then without matching if.");
}
i_patchhere(c, e.a);
}
static void
do_begin(buf* c)
{
cpush(CK_BEGIN, here(c), 0);
}
static void
do_until(buf* c)
{
cent e;
e = cpop();
if(e.kind != CK_BEGIN)
{
fail("until without matching begin.");
}
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_ADD, R15, 8);
i_testrr(c, RAX, RAX);
i_jccto(c, CC_E, e.a);
}
static void
do_while(buf* c)
{
cent e;
long p;
e = cpop();
if(e.kind != CK_BEGIN)
{
fail("while without matching begin.");
}
i_movrm(c, RAX, R15, 0);
i_g1i8(c, G1_ADD, R15, 8);
i_testrr(c, RAX, RAX);
p = i_jccph(c, CC_E);
cpush(CK_WHILE, e.a, p);
}
static void
do_repeat(buf* c)
{
cent e;
e = cpop();
if(e.kind != CK_WHILE)
{
fail("repeat without matching while.");
}
i_jmpto(c, e.a);
i_patchhere(c, e.b);
}
static void
do_do(buf* c)
{
long idxaddr;
long limaddr;
if(loopdepth == LOOP_MAX)
{
fail("do loops nested too deeply.");
}
idxaddr = loopidxaddr(loopdepth);
limaddr = looplimaddr(loopdepth);
i_movrm(c, RAX, R15, 0);
i_movrm(c, RBX, R15, 8);
i_g1i8(c, G1_ADD, R15, 16);
i_movi32z(c, RCX, (unsigned long)idxaddr);
i_movmr(c, RCX, 0, RAX);
i_movi32z(c, RCX, (unsigned long)limaddr);
i_movmr(c, RCX, 0, RBX);
cpush(CK_DO, here(c), loopdepth);
loopdepth = loopdepth + 1;
}
static void
do_i(buf* c)
{
long k;
long idxaddr;
int found;
found = 0;
idxaddr = 0;
for(k = ctop - 1; k >= 0; k = k - 1)
{
if(cstack[k].kind == CK_DO)
{
idxaddr = loopidxaddr(cstack[k].b);
found = 1;
break;
}
}
if(found == 0)
{
fail("i used outside do loop.");
}
i_movi32z(c, RCX, (unsigned long)idxaddr);
i_movrm(c, RAX, RCX, 0);
i_g1i8(c, G1_SUB, R15, 8);
i_movmr(c, R15, 0, RAX);
}
static void
do_loop(buf* c)
{
cent e;
long idxaddr;
long limaddr;
e = cpop();
if(e.kind != CK_DO)
{
fail("loop without matching do.");
}
idxaddr = loopidxaddr(e.b);
limaddr = looplimaddr(e.b);
i_movi32z(c, RCX, (unsigned long)idxaddr);
i_movrm(c, RAX, RCX, 0);
i_g1i8(c, G1_ADD, RAX, 1);
i_movi32z(c, RCX, (unsigned long)idxaddr);
i_movmr(c, RCX, 0, RAX);
i_movi32z(c, RCX, (unsigned long)limaddr);
i_movrm(c, RBX, RCX, 0);
i_alurr(c, 0x39, RBX, RAX);
i_jccto(c, CC_L, e.a);
loopdepth = loopdepth - 1;
}
static void
do_colon(buf* c, const char* name)
{
long p;
long bodyaddr;
if(compiling != 0)
{
fail("nested colon definition.");
}
p = i_jmpph(c);
bodyaddr = here(c);
dictadd(name, DK_WORD, bodyaddr);
cpush(CK_COLON, p, bodyaddr);
compiling = 1;
curwordaddr = bodyaddr;
}
static void
do_semi(buf* c)
{
cent e;
e = cpop();
if(e.kind != CK_COLON)
{
fail("semicolon without matching colon.");
}
i_ret(c);
i_patchhere(c, e.a);
compiling = 0;
}
static void
do_recurse(buf* c)
{
if(compiling == 0)
{
fail("recurse outside colon definition.");
}
i_callto(c, curwordaddr);
}
static void
do_variable(const char* name)
{
long addr;
addr = vars_addr + (varcount * 8);
varcount = varcount + 1;
dictadd(name, DK_VAR, addr);
}
static void
do_constant(const char* name, long val)
{
dictadd(name, DK_CONST, val);
}
static long
opfind(const char* name)
{
long i;
i = 0;
while(PRIMS[i].name != NULL)
{
if(strcmp(name, PRIMS[i].name) == 0)
{
return i;
}
i = i + 1;
}
return -1;
}
static void
genop(buf* c, long idx)
{
switch(PRIMS[idx].op)
{
case OP_ADD:
gen_add(c);
break;
case OP_SUB:
gen_sub(c);
break;
case OP_MUL:
gen_mul(c);
break;
case OP_DIV:
gen_divmod(c, 0);
break;
case OP_MOD:
gen_divmod(c, 1);
break;
case OP_DUP:
gen_dup(c);
break;
case OP_DROP:
gen_drop(c);
break;
case OP_SWAP:
gen_swap(c);
break;
case OP_OVER:
gen_over(c);
break;
case OP_ROT:
gen_rot(c);
break;
case OP_EQ:
gen_cmp(c, CC_NE);
break;
case OP_NE:
gen_cmp(c, CC_E);
break;
case OP_LT:
gen_cmp(c, CC_GE);
break;
case OP_GT:
gen_cmp(c, CC_LE);
break;
case OP_LE:
gen_cmp(c, CC_G);
break;
case OP_GE:
gen_cmp(c, CC_L);
break;
case OP_ZEQ:
gen_unarytest(c, CC_NE);
break;
case OP_ZLT:
gen_unarytest(c, CC_NS);
break;
case OP_AND:
gen_bitwise(c, 0x21);
break;
case OP_OR:
gen_bitwise(c, 0x09);
break;
case OP_XOR:
gen_bitwise(c, 0x31);
break;
case OP_INVERT:
gen_unaryf7(c, F7_NOT);
break;
case OP_NEG:
gen_unaryf7(c, F7_NEG);
break;
case OP_INC:
gen_incdec(c, G1_ADD);
break;
case OP_DEC:
gen_incdec(c, G1_SUB);
break;
case OP_FETCH:
gen_fetch(c);
break;
case OP_STORE:
gen_store(c);
break;
case OP_PLUSSTORE:
gen_plusstore(c);
break;
case OP_EMIT:
gen_emit(c);
break;
case OP_DOT:
gen_dot(c);
break;
case OP_CR:
gen_emitcharimm(c, 10);
break;
case OP_SPACE:
gen_emitcharimm(c, ' ');
break;
case OP_TRUE:
gen_lit(c, 1);
break;
case OP_FALSE:
gen_lit(c, 0);
break;
case OP_BYE:
gen_bye(c);
break;
case OP_NIP:
gen_swap(c);
gen_drop(c);
break;
default:
fail("bad op.");
break;
}
}
static void
compile(buf* c)
{
long pos;
char* t;
long litval;
long oi;
long di;
pos = 0;
while(pos < ntok)
{
t = toks[pos];
if(tokisstr[pos] != 0)
{
gen_dotquote(c, t, (long)strlen(t));
pos = pos + 1;
continue;
}
if(isnum(t, &litval) != 0)
{
if(pos + 1 < ntok && strcmp(toks[pos + 1], "constant") == 0)
{
if(pos + 2 >= ntok)
{
fail("constant needs a name.");
}
do_constant(toks[pos + 2], litval);
pos = pos + 3;
continue;
}
gen_lit(c, litval);
pos = pos + 1;
continue;
}
if(strcmp(t, ":") == 0)
{
if(pos + 1 >= ntok)
{
fail("colon needs a name.");
}
do_colon(c, toks[pos + 1]);
pos = pos + 2;
continue;
}
if(strcmp(t, ";") == 0)
{
do_semi(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "if") == 0)
{
do_if(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "else") == 0)
{
do_else(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "then") == 0)
{
do_then(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "begin") == 0)
{
do_begin(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "until") == 0)
{
do_until(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "while") == 0)
{
do_while(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "repeat") == 0)
{
do_repeat(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "do") == 0)
{
do_do(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "loop") == 0)
{
do_loop(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "i") == 0)
{
do_i(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "recurse") == 0)
{
do_recurse(c);
pos = pos + 1;
continue;
}
if(strcmp(t, "variable") == 0)
{
if(pos + 1 >= ntok)
{
fail("variable needs a name.");
}
do_variable(toks[pos + 1]);
pos = pos + 2;
continue;
}
oi = opfind(t);
if(oi >= 0)
{
genop(c, oi);
pos = pos + 1;
continue;
}
di = dictfind(t);
if(di >= 0)
{
if(dict[di].kind == DK_WORD)
{
i_callto(c, dict[di].val);
}
else
{
gen_lit(c, dict[di].val);
}
pos = pos + 1;
continue;
}
failat(toklines[pos], "undefined word", t);
}
if(compiling != 0)
{
fail("unterminated colon definition.");
}
if(ctop != 0)
{
fail("unterminated control structure.");
}
}
static long
osphnum(long os)
{
if(os == OS_NETBSD)
{
return 3;
}
return 2;
}
static long
osnotesize(long os)
{
if(os == OS_NETBSD)
{
return NOTESIZE;
}
return 0;
}
static void
emitelf(buf* o, long entry, long totalsize, long os)
{
long i;
long phnum;
long notesize;
long noteoff;
phnum = osphnum(os);
notesize = osnotesize(os);
noteoff = EHSIZE + (PHSIZE * phnum);
bufbyte(o, 0x7F);
bufbyte(o, 'E');
bufbyte(o, 'L');
bufbyte(o, 'F');
bufbyte(o, 2);
bufbyte(o, 1);
bufbyte(o, 1);
bufbyte(o, 0);
for(i = 0; i < 8; i = i + 1)
{
bufbyte(o, 0);
}
bufu16(o, 2);
bufu16(o, 0x3E);
bufu32(o, 1);
bufu64(o, entry);
bufu64(o, EHSIZE);
bufu64(o, 0);
bufu32(o, 0);
bufu16(o, EHSIZE);
bufu16(o, PHSIZE);
bufu16(o, phnum);
bufu16(o, 0);
bufu16(o, 0);
bufu16(o, 0);
if(notesize > 0)
{
bufu32(o, 4);
bufu32(o, 4);
bufu64(o, noteoff);
bufu64(o, BASE + noteoff);
bufu64(o, BASE + noteoff);
bufu64(o, notesize);
bufu64(o, notesize);
bufu64(o, 4);
}
bufu32(o, 1);
bufu32(o, 7);
bufu64(o, 0);
bufu64(o, BASE);
bufu64(o, BASE);
bufu64(o, totalsize);
bufu64(o, totalsize);
bufu64(o, 0x1000);
/* data stack lives in its own segment, far from BASE and backed by no
file bytes (filesz 0, pure anonymous zero pages), non-executable.
over/underflowing it walks off the end of mapped memory and faults
right there instead of corrupting code or vars in the main segment. */
bufu32(o, 1);
bufu32(o, 6);
bufu64(o, 0);
bufu64(o, STACKBASE);
bufu64(o, STACKBASE);
bufu64(o, 0);
bufu64(o, DSTACK_SIZE);
bufu64(o, 0x1000);
}
static void
emitnote(buf* o)
{
bufu32(o, 7);
bufu32(o, 4);
bufu32(o, 1);
bufbyte(o, 'N');
bufbyte(o, 'e');
bufbyte(o, 't');
bufbyte(o, 'B');
bufbyte(o, 'S');
bufbyte(o, 'D');
bufbyte(o, 0);
bufbyte(o, 0);
bufu32(o, 1000000000);
}
int
main(int argc, char** argv)
{
const char* infile;
const char* outfile;
const char* target;
const char* pos[2];
long npos;
long ai;
FILE* in;
FILE* out;
char* src;
long srclen;
long numvars;
long i;
long n;
buf code;
buf out2;
long totalsize;
long datasize;
long phnum;
long notesize;
long hdrsz;
target = NULL;
npos = 0;
ai = 1;
while(ai < argc)
{
if(strcmp(argv[ai], "-b") == 0)
{
if(ai + 1 >= argc)
{
fprintf(stderr, "err: -b needs an argument.\n");
return 1;
}
target = argv[ai + 1];
ai = ai + 2;
continue;
}
if(npos == 2)
{
fprintf(stderr, "err: bad usage.\n");
fprintf(stderr, "usage: %s -b linux|netbsd [in.fs|-] [out]\n", argv[0]);
return 1;
}
pos[npos] = argv[ai];
npos = npos + 1;
ai = ai + 1;
}
infile = "a.fs";
outfile = "a.out";
if(npos >= 1)
{
infile = pos[0];
}
if(npos >= 2)
{
outfile = pos[1];
}
#ifdef FIXED_TARGET
if(target != NULL && strcmp(target, FIXED_TARGET) != 0)
{
fprintf(stderr, "err: this build only supports -b %s.\n", FIXED_TARGET);
return 1;
}
target = FIXED_TARGET;
#else
if(target == NULL)
{
fprintf(stderr, "err: -b is required.\n");
fprintf(stderr, "usage: %s -b linux|netbsd [in.fs|-] [out]\n", argv[0]);
return 1;
}
#endif
if(strcmp(target, "linux") == 0)
{
targetos = OS_LINUX;
}
else if(strcmp(target, "netbsd") == 0)
{
targetos = OS_NETBSD;
}
else
{
fprintf(stderr, "err: unknown target '%s' (use linux or netbsd).\n", target);
return 1;
}
if(targetos == OS_NETBSD)
{
sys_write = 4;
sys_exit = 1;
}
else
{
sys_write = 1;
sys_exit = 60;
}
if(strcmp(infile, "-") == 0)
{
in = stdin;
}
else
{
in = fopen(infile, "rb");
if(in == NULL)
{
fprintf(stderr, "err: cant open %s.\n", infile);
return 1;
}
}
src = readall(in, &srclen);
if(strcmp(infile, "-") != 0)
{
fclose(in);
}
tokenize(src, srclen);
free(src);
numvars = 0;
for(i = 0; i < ntok; i = i + 1)
{
if(strcmp(toks[i], "variable") == 0)
{
numvars = numvars + 1;
}
}
phnum = osphnum(targetos);
notesize = osnotesize(targetos);
hdrsz = EHSIZE + (PHSIZE * phnum);
scratch_addr = BASE + hdrsz + notesize;
looparea_addr = scratch_addr + SCRATCH_SIZE;
vars_addr = looparea_addr + LOOPAREA_SIZE;
codebase = vars_addr + (numvars * 8);
datasize = codebase - scratch_addr;
dstack_base = STACKBASE;
dstack_top = dstack_base + DSTACK_SIZE;
bufinit(&code);
printnum_addr = here(&code);
gen_printnum(&code);
n = here(&code);
i_movi32z(&code, R15, (unsigned long)dstack_top);
compile(&code);
gen_bye(&code);
totalsize = (codebase - BASE) + code.len;
if(BASE + totalsize > STACKBASE)
{
fail("program too large: grew past the reserved gap before the stack segment.");
}
bufinit(&out2);
emitelf(&out2, n, totalsize, targetos);
if(notesize > 0)
{
emitnote(&out2);
}
bufzero(&out2, datasize);
bufappend(&out2, code.data, code.len);
if(strcmp(outfile, "-") == 0)
{
fail("cant write elf to stdout.");
}
out = fopen(outfile, "wb");
if(out == NULL)
{
fprintf(stderr, "err: cant create %s.\n", outfile);
return 1;
}
fwrite(out2.data, 1, (size_t)out2.len, out);
fclose(out);
chmod(outfile, 0755);
printf("ok: wrote %s with %ld bytes.\n", outfile, out2.len);
return 0;
}