1322 lines
29 KiB
C
1322 lines
29 KiB
C
/* tag: forth bootstrap environment
|
|
*
|
|
* Copyright (C) 2003-2006 Stefan Reinauer, Patrick Mauritz
|
|
*
|
|
* See the file "COPYING" for further information about
|
|
* the copyright and warranty status of this work.
|
|
*/
|
|
|
|
#include "sysinclude.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef __GLIBC__
|
|
#define _GNU_SOURCE
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#include "config.h"
|
|
#include "kernel/stack.h"
|
|
#include "sysinclude.h"
|
|
#include "kernel/kernel.h"
|
|
#include "dict.h"
|
|
#include "cross.h"
|
|
#include "openbios-version.h"
|
|
|
|
#define MAX_PATH_LEN 256
|
|
|
|
#define MEMORY_SIZE (1024*1024) /* 1M ram for hosted system */
|
|
#define DICTIONARY_SIZE (256*1024) /* 256k for the dictionary */
|
|
#define TRAMPOLINE_SIZE (4*sizeof(cell)) /* 4 cells for the trampoline */
|
|
|
|
/* state variables */
|
|
static ucell *latest, *state, *base;
|
|
static ucell *memory;
|
|
ucell *trampoline;
|
|
|
|
/* local variables */
|
|
static int errors = 0;
|
|
static int segfault = 0;
|
|
static int verbose = 0;
|
|
|
|
#define MAX_SRC_FILES 128
|
|
|
|
static FILE *srcfiles[MAX_SRC_FILES];
|
|
static char *srcfilenames[MAX_SRC_FILES];
|
|
static int srclines[MAX_SRC_FILES];
|
|
static unsigned int cursrc = 0;
|
|
|
|
static char *srcbasedict;
|
|
|
|
/* console variables */
|
|
static FILE *console;
|
|
|
|
#ifdef NATIVE_BITWIDTH_SMALLER_THAN_HOST_BITWIDTH
|
|
unsigned long base_address;
|
|
#endif
|
|
|
|
/* include path handling */
|
|
typedef struct include_path include;
|
|
struct include_path {
|
|
const char *path;
|
|
include *next;
|
|
};
|
|
|
|
static include includes = { ".", NULL };
|
|
static FILE *depfile;
|
|
|
|
static ucell * relocation_address=NULL;
|
|
static int relocation_length=0;
|
|
|
|
/* the word names are used to generate the prim words in the
|
|
* dictionary. This is done by the C written interpreter.
|
|
*/
|
|
static const char *wordnames[] = {
|
|
"(semis)", "", "(lit)", "", "", "", "", "(do)", "(?do)", "(loop)",
|
|
"(+loop)", "", "", "", "dup", "2dup", "?dup", "over", "2over", "pick", "drop",
|
|
"2drop", "nip", "roll", "rot", "-rot", "swap", "2swap", ">r", "r>",
|
|
"r@", "depth", "depth!", "rdepth", "rdepth!", "+", "-", "*", "u*",
|
|
"mu/mod", "abs", "negate", "max", "min", "lshift", "rshift", ">>a",
|
|
"and", "or", "xor", "invert", "d+", "d-", "m*", "um*", "@", "c@",
|
|
"w@", "l@", "!", "+!", "c!", "w!", "l!", "=", ">", "<", "u>", "u<",
|
|
"sp@", "move", "fill", "(emit)", "(key?)", "(key)", "execute",
|
|
"here", "here!", "dobranch", "do?branch", "unaligned-w@",
|
|
"unaligned-w!", "unaligned-l@", "unaligned-l!", "ioc@", "iow@",
|
|
"iol@", "ioc!", "iow!", "iol!", "i", "j", "call", "sys-debug",
|
|
"$include", "$encode-file", "(debug", "(debug-off)"
|
|
};
|
|
|
|
/*
|
|
* dictionary related functions.
|
|
*/
|
|
|
|
/*
|
|
* Compare two dictionaries constructed at different addresses. When
|
|
* the cells don't match, a need for relocation is detected and the
|
|
* corresponding bit in reloc_table bitmap is set.
|
|
*/
|
|
static void relocation_table(unsigned char * dict_one, unsigned char *dict_two, int length)
|
|
{
|
|
ucell *d1=(ucell *)dict_one, *d2=(ucell *)dict_two;
|
|
ucell *reloc_table;
|
|
int pos, bit;
|
|
int l=(length+(sizeof(cell)-1))/sizeof(ucell), i;
|
|
|
|
/* prepare relocation table */
|
|
relocation_length=(length+BITS-1)/BITS;
|
|
reloc_table = malloc(relocation_length*sizeof(cell));
|
|
memset(reloc_table,0,relocation_length*sizeof(cell));
|
|
|
|
for (i=0; i<l; i++) {
|
|
|
|
pos=i/BITS;
|
|
bit=i&~(-BITS);
|
|
|
|
if(d1[i]==d2[i]) {
|
|
reloc_table[pos] &= target_ucell(~((ucell)1ULL << bit));
|
|
|
|
// This check might bring false positives in data.
|
|
//if(d1[i] >= pointer2cell(dict_one) &&
|
|
// d1[i] <= pointer2cell(dict_one+length))
|
|
// printk("\nWARNING: inconsistent relocation (%x:%x)!\n", d1[i], d2[i]);
|
|
} else {
|
|
/* This is a pointer, it needs relocation, d2==dict */
|
|
reloc_table[pos] |= target_ucell((ucell)1ULL << bit);
|
|
d2[i] = target_ucell(target_ucell(d2[i]) - pointer2cell(d2));
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk("dict1 %lx dict2 %lx dict %lx\n",dict_one, dict_two, dict);
|
|
for (i=0; i< relocation_length ; i++)
|
|
printk("reloc %d %lx\n",i+1, reloc_table[i]);
|
|
#endif
|
|
relocation_address=reloc_table;
|
|
}
|
|
|
|
static void write_dictionary(const char *filename)
|
|
{
|
|
FILE *f;
|
|
unsigned char *write_data, *walk_data;
|
|
int write_len;
|
|
dictionary_header_t *header;
|
|
u32 checksum=0;
|
|
|
|
/*
|
|
* get memory for dictionary
|
|
*/
|
|
|
|
write_len = sizeof(dictionary_header_t)+dicthead+relocation_length*sizeof(cell);
|
|
write_data = malloc(write_len);
|
|
if(!write_data) {
|
|
printk("panic: can't allocate memory for output dictionary (%d"
|
|
" bytes\n", write_len);
|
|
exit(1);
|
|
}
|
|
memset(write_data, 0, write_len);
|
|
|
|
/*
|
|
* prepare dictionary header
|
|
*/
|
|
|
|
header = (dictionary_header_t *)write_data;
|
|
*header = (dictionary_header_t){
|
|
.signature = DICTID,
|
|
.version = 2,
|
|
.cellsize = sizeof(ucell),
|
|
#ifdef CONFIG_BIG_ENDIAN
|
|
.endianess = -1,
|
|
#else
|
|
.endianess = 0,
|
|
#endif
|
|
.checksum = 0,
|
|
.compression = 0,
|
|
.relocation = -1,
|
|
.length = target_ulong((uint32_t)dicthead),
|
|
.last = target_ucell((ucell)((unsigned long)last
|
|
- (unsigned long)dict)),
|
|
};
|
|
|
|
/*
|
|
* prepare dictionary data
|
|
*/
|
|
|
|
walk_data=write_data+sizeof(dictionary_header_t);
|
|
memcpy (walk_data, dict, dicthead);
|
|
|
|
/*
|
|
* prepare relocation data.
|
|
* relocation_address is zero when writing a dictionary core.
|
|
*/
|
|
|
|
if (relocation_address) {
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk("writing %d reloc cells \n",relocation_length);
|
|
#endif
|
|
walk_data += dicthead;
|
|
memcpy(walk_data, relocation_address,
|
|
relocation_length*sizeof(cell));
|
|
/* free relocation information */
|
|
free(relocation_address);
|
|
relocation_address=NULL;
|
|
} else {
|
|
header->relocation=0;
|
|
}
|
|
|
|
/*
|
|
* Calculate Checksum
|
|
*/
|
|
|
|
walk_data=write_data;
|
|
while (walk_data<write_data+write_len) {
|
|
checksum+=read_long(walk_data);
|
|
walk_data+=sizeof(u32);
|
|
}
|
|
checksum=(u32)-checksum;
|
|
|
|
header->checksum=target_long(checksum);
|
|
|
|
if (verbose) {
|
|
dump_header(header);
|
|
}
|
|
|
|
f = fopen(filename, "w");
|
|
if (!f) {
|
|
printk("panic: can't write to dictionary '%s'.\n", filename);
|
|
exit(1);
|
|
}
|
|
|
|
fwrite(write_data, write_len, 1, f);
|
|
|
|
free(write_data);
|
|
fclose(f);
|
|
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk("wrote dictionary to file %s.\n", filename);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Write dictionary as a list of ucell hex values to filename. Array
|
|
* header and end lines are not generated.
|
|
*
|
|
* Cells with relocations are output using the expression
|
|
* DICTIONARY_BASE + value.
|
|
*
|
|
* Define some helpful constants.
|
|
*/
|
|
static void write_dictionary_hex(const char *filename)
|
|
{
|
|
FILE *f;
|
|
ucell *walk;
|
|
|
|
f = fopen(filename, "w");
|
|
if (!f) {
|
|
printk("panic: can't write to dictionary '%s'.\n", filename);
|
|
exit(1);
|
|
}
|
|
|
|
for (walk = (ucell *)dict; walk < (ucell *)(dict + dicthead); walk++) {
|
|
int pos, bit, l;
|
|
ucell val;
|
|
|
|
l = (walk - (ucell *)dict);
|
|
pos = l / BITS;
|
|
bit = l & ~(-BITS);
|
|
|
|
val = read_ucell(walk);
|
|
if (relocation_address[pos] & target_ucell((ucell)1ULL << bit)) {
|
|
fprintf(f, "DICTIONARY_BASE + 0x%" FMT_CELL_x
|
|
",\n", val);
|
|
} else {
|
|
fprintf(f, "0x%" FMT_CELL_x",\n", val);
|
|
}
|
|
}
|
|
|
|
fprintf(f, "#define FORTH_DICTIONARY_LAST 0x%" FMT_CELL_x"\n",
|
|
(ucell)((unsigned long)last - (unsigned long)dict));
|
|
fprintf(f, "#define FORTH_DICTIONARY_END 0x%" FMT_CELL_x"\n",
|
|
(ucell)dicthead);
|
|
fclose(f);
|
|
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk("wrote dictionary to file %s.\n", filename);
|
|
#endif
|
|
}
|
|
|
|
static ucell read_dictionary(char *fil)
|
|
{
|
|
int ilen;
|
|
ucell ret;
|
|
char *mem;
|
|
FILE *f;
|
|
struct stat finfo;
|
|
|
|
if (stat(fil, &finfo))
|
|
return 0;
|
|
|
|
ilen = finfo.st_size;
|
|
|
|
if ((mem = malloc(ilen)) == NULL) {
|
|
printk("panic: not enough memory.\n");
|
|
exit(1);
|
|
}
|
|
|
|
f = fopen(fil, "r");
|
|
if (!f) {
|
|
printk("panic: can't open dictionary.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (fread(mem, ilen, 1, f) != 1) {
|
|
printk("panic: can't read dictionary.\n");
|
|
fclose(f);
|
|
exit(1);
|
|
}
|
|
fclose(f);
|
|
|
|
ret = load_dictionary(mem, ilen);
|
|
|
|
free(mem);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* C Parser related functions
|
|
*/
|
|
|
|
/*
|
|
* skipws skips all whitespaces (space, tab, newline) from the input file
|
|
*/
|
|
|
|
static void skipws(FILE * f)
|
|
{
|
|
int c;
|
|
while (!feof(f)) {
|
|
c = getc(f);
|
|
|
|
if (c == ' ' || c == '\t')
|
|
continue;
|
|
|
|
if (c == '\n') {
|
|
srclines[cursrc - 1]++;
|
|
continue;
|
|
}
|
|
|
|
ungetc(c, f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* parse gets the next word from the input stream, delimited by
|
|
* delim. If delim is 0, any word delimiter will end the stream
|
|
* word delimiters are space, tab and newline. The resulting word
|
|
* will be put zero delimited to the char array line.
|
|
*/
|
|
|
|
static int parse(FILE * f, char *line, char delim)
|
|
{
|
|
int cnt = 0, c = 0;
|
|
|
|
while (!feof(f)) {
|
|
c = getc(f);
|
|
|
|
if (delim && c == delim)
|
|
break;
|
|
|
|
if ((!delim) && (c == ' ' || c == '\t' || c == '\n'))
|
|
break;
|
|
|
|
line[cnt++] = c;
|
|
}
|
|
|
|
/* Update current line number */
|
|
if (c == '\n') {
|
|
srclines[cursrc - 1]++;
|
|
}
|
|
|
|
line[cnt] = 0;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/*
|
|
* parse_word is a small helper that skips whitespaces before a word.
|
|
* it's behaviour is similar to the forth version parse-word.
|
|
*/
|
|
|
|
static void parse_word(FILE * f, char *line)
|
|
{
|
|
skipws(f);
|
|
parse(f, line, 0);
|
|
}
|
|
|
|
|
|
static void writestring(const char *str)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < strlen(str); i++) {
|
|
dict[dicthead + i] = str[i];
|
|
}
|
|
dicthead += i + 1;
|
|
dict[dicthead - 1] = (char) strlen(str) + 128;
|
|
}
|
|
|
|
#define writebyte(value) {write_byte(dict+dicthead,value); dicthead++;}
|
|
#define writecell(value) {write_cell(dict+dicthead, value); dicthead+=sizeof(cell);}
|
|
|
|
/*
|
|
* reveal a word, ie. make it visible.
|
|
*/
|
|
|
|
static void reveal(void)
|
|
{
|
|
*last = *latest;
|
|
}
|
|
|
|
/*
|
|
* dictionary padding
|
|
*/
|
|
|
|
static void paddict(ucell align)
|
|
{
|
|
while (dicthead % align != 0)
|
|
writebyte(0);
|
|
}
|
|
|
|
/*
|
|
* generic forth word creator function.
|
|
*/
|
|
|
|
static void fcreate(const char *word, ucell cfaval)
|
|
{
|
|
if (strlen(word) == 0) {
|
|
printk("WARNING: tried to create unnamed word.\n");
|
|
return;
|
|
}
|
|
|
|
writestring(word);
|
|
/* get us at least 1 byte for flags */
|
|
writebyte(0);
|
|
paddict(sizeof(cell));
|
|
/* set flags high bit. */
|
|
dict[dicthead - 1] = 128;
|
|
/* lfa and cfa */
|
|
writecell(read_ucell(latest));
|
|
*latest = target_ucell(pointer2cell(dict) + dicthead - sizeof(cell));
|
|
writecell(cfaval);
|
|
}
|
|
|
|
|
|
static ucell *buildvariable(const char *name, cell defval)
|
|
{
|
|
fcreate(name, DOVAR); /* see dict.h for DOVAR and other CFA ids */
|
|
writecell(defval);
|
|
return (ucell *) (dict + dicthead - sizeof(cell));
|
|
}
|
|
|
|
static void buildconstant(const char *name, cell defval)
|
|
{
|
|
fcreate(name, DOCON); /* see dict.h for DOCON and other CFA ids */
|
|
writecell(defval);
|
|
}
|
|
|
|
static void builddefer(const char *name)
|
|
{
|
|
fcreate(name, DODFR); /* see dict.h for DODFR and other CFA ids */
|
|
writecell((ucell)0);
|
|
writecell((ucell)findword("(semis)"));
|
|
}
|
|
|
|
/*
|
|
* Include file handling
|
|
*/
|
|
|
|
static void add_includepath(char *path)
|
|
{
|
|
include *incl = &includes;
|
|
include *newpath;
|
|
|
|
while (incl->next)
|
|
incl = incl->next;
|
|
|
|
newpath = malloc(sizeof(include));
|
|
if (!newpath) {
|
|
printk("panic: not enough memory for include path.\n");
|
|
exit(1);
|
|
}
|
|
|
|
incl->next = newpath;
|
|
newpath->path = path;
|
|
newpath->next = NULL;
|
|
}
|
|
|
|
|
|
static FILE *fopen_include(const char *fil)
|
|
{
|
|
char fullpath[MAX_PATH_LEN];
|
|
FILE *ret;
|
|
include *incl = &includes;
|
|
|
|
while (incl) {
|
|
snprintf(fullpath, sizeof(fullpath), "%s/%s", incl->path, fil);
|
|
|
|
ret = fopen(fullpath, "r");
|
|
if (ret != NULL) {
|
|
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("Including '%s'\n", fil);
|
|
#endif
|
|
srcfilenames[cursrc] = strdup(fil);
|
|
srclines[cursrc] = 1;
|
|
srcfiles[cursrc++] = ret;
|
|
|
|
if (depfile) {
|
|
fprintf(depfile, " %s", fullpath);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
incl = incl->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Forth exception handler
|
|
*/
|
|
|
|
void exception(cell no)
|
|
{
|
|
printk("%s:%d: ", srcfilenames[cursrc - 1], srclines[cursrc - 1]);
|
|
|
|
/* See also forth/bootstrap/interpreter.fs */
|
|
switch (no) {
|
|
case -1:
|
|
case -2:
|
|
printk("Aborted.\n");
|
|
break;
|
|
case -3:
|
|
printk("Stack Overflow.\n");
|
|
break;
|
|
case -4:
|
|
printk("Stack Underflow.\n");
|
|
break;
|
|
case -5:
|
|
printk("Return Stack Overflow.\n");
|
|
break;
|
|
case -6:
|
|
printk("Return Stack Underflow.\n");
|
|
break;
|
|
case -19:
|
|
printk("undefined word.\n");
|
|
break;
|
|
case -21:
|
|
printk("out of memory.\n");
|
|
break;
|
|
case -33:
|
|
printk("undefined method.\n");
|
|
break;
|
|
case -34:
|
|
printk("no such device.\n");
|
|
break;
|
|
default:
|
|
printk("error %" FMT_CELL_d " occurred.\n", no);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* This is the C version of the forth interpreter
|
|
*/
|
|
|
|
static int interpret_source(char *fil)
|
|
{
|
|
FILE *f;
|
|
char tib[160];
|
|
cell num;
|
|
char *test;
|
|
|
|
const ucell SEMIS = (ucell)findword("(semis)");
|
|
const ucell LIT = (ucell)findword("(lit)");
|
|
const ucell DOBRANCH = (ucell)findword("dobranch");
|
|
|
|
if ((f = fopen_include(fil)) == NULL) {
|
|
printk("error while loading source file '%s'\n", fil);
|
|
errors++;
|
|
exit(1);
|
|
}
|
|
|
|
/* FIXME: We should read this file at
|
|
* once. No need to get it char by char
|
|
*/
|
|
|
|
while (!feof(f)) {
|
|
xt_t res;
|
|
parse_word(f, tib);
|
|
|
|
/* if there is actually no word, we continue right away */
|
|
if (strlen(tib) == 0) {
|
|
continue;
|
|
}
|
|
|
|
/* Checking for builtin words that are needed to
|
|
* bootstrap the forth base dictionary.
|
|
*/
|
|
|
|
if (!strcmp(tib, "(")) {
|
|
parse(f, tib, ')');
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(tib, "\\")) {
|
|
parse(f, tib, '\n');
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(tib, ":")) {
|
|
parse_word(f, tib);
|
|
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("create colon word %s\n\n", tib);
|
|
#endif
|
|
fcreate(tib, DOCOL); /* see dict.h for DOCOL and other CFA ids */
|
|
*state = (ucell) (-1);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(tib, ";")) {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("finish colon definition\n\n");
|
|
#endif
|
|
writecell((cell)SEMIS);
|
|
*state = (ucell) 0;
|
|
reveal();
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(tib, "variable")) {
|
|
parse_word(f, tib);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("defining variable %s\n\n", tib);
|
|
#endif
|
|
buildvariable(tib, 0);
|
|
reveal();
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(tib, "constant")) {
|
|
parse_word(f, tib);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("defining constant %s\n\n", tib);
|
|
#endif
|
|
buildconstant(tib, POP());
|
|
reveal();
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(tib, "value")) {
|
|
parse_word(f, tib);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("defining value %s\n\n", tib);
|
|
#endif
|
|
buildconstant(tib, POP());
|
|
reveal();
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(tib, "defer")) {
|
|
parse_word(f, tib);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("defining defer word %s\n\n", tib);
|
|
#endif
|
|
builddefer(tib);
|
|
reveal();
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(tib, "include")) {
|
|
parse_word(f, tib);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("including file %s\n\n", tib);
|
|
#endif
|
|
interpret_source(tib);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(tib, "[']")) {
|
|
xt_t xt;
|
|
parse_word(f, tib);
|
|
xt = findword(tib);
|
|
if (*state == 0) {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk
|
|
("writing address of %s to stack\n\n",
|
|
tib);
|
|
#endif
|
|
PUSH_xt(xt);
|
|
} else {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("writing lit, addr(%s) to dict\n\n",
|
|
tib);
|
|
#endif
|
|
writecell(LIT); /* lit */
|
|
writecell((cell)xt);
|
|
}
|
|
continue;
|
|
/* we have no error detection here */
|
|
}
|
|
|
|
if (!strcasecmp(tib, "s\"")) {
|
|
int cnt;
|
|
cell loco;
|
|
|
|
cnt = parse(f, tib, '"');
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("compiling string %s\n", tib);
|
|
#endif
|
|
loco = dicthead + (6 * sizeof(cell));
|
|
writecell(LIT);
|
|
writecell(pointer2cell(dict) + loco);
|
|
writecell(LIT);
|
|
writecell((ucell)cnt);
|
|
writecell(DOBRANCH);
|
|
loco = cnt + sizeof(cell) - 1;
|
|
loco &= ~(sizeof(cell) - 1);
|
|
writecell(loco);
|
|
memcpy(dict + dicthead, tib, cnt);
|
|
dicthead += cnt;
|
|
paddict(sizeof(cell));
|
|
continue;
|
|
}
|
|
|
|
/* look if tib is in dictionary. */
|
|
/* should the dictionary be searched before the builtins ? */
|
|
res = findword(tib);
|
|
if (res) {
|
|
u8 flags = read_byte((u8*)cell2pointer(res) -
|
|
sizeof(cell) - 1);
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("%s is 0x%" FMT_CELL_x "\n", tib, (ucell) res);
|
|
#endif
|
|
if (!(*state) || (flags & 3)) {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("executing %s, %" FMT_CELL_d
|
|
" (flags: %s %s)\n",
|
|
tib, res,
|
|
(flags & 1) ? "immediate" : "",
|
|
(flags & 2) ? "compile-only" : "");
|
|
#endif
|
|
PC = (ucell)res;
|
|
enterforth(res);
|
|
} else {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("writing %s to dict\n\n", tib);
|
|
#endif
|
|
writecell((cell)res);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* if not look if it's a number */
|
|
if (tib[0] == '-')
|
|
num = strtoll(tib, &test, read_ucell(base));
|
|
else
|
|
num = strtoull(tib, &test, read_ucell(base));
|
|
|
|
|
|
if (*test != 0) {
|
|
/* what is it?? */
|
|
printk("%s:%d: %s is not defined.\n\n", srcfilenames[cursrc - 1], srclines[cursrc - 1], tib);
|
|
errors++;
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
continue;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
if (*state == 0) {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("pushed %" FMT_CELL_x " to stack\n\n", num);
|
|
#endif
|
|
PUSH(num);
|
|
} else {
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("writing lit, %" FMT_CELL_x " to dict\n\n", num);
|
|
#endif
|
|
writecell(LIT); /* lit */
|
|
writecell(num);
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
cursrc--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int build_dictionary(void)
|
|
{
|
|
ucell lfa = 0;
|
|
unsigned int i;
|
|
|
|
/* we need a temporary place for latest outside the dictionary */
|
|
latest = &lfa;
|
|
|
|
/* starting a new dictionary: clear dicthead */
|
|
dicthead = 0;
|
|
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk("building dictionary, %d primitives.\nbuilt words:",
|
|
sizeof(wordnames) / sizeof(void *));
|
|
#endif
|
|
|
|
for (i = 0; i < sizeof(wordnames) / sizeof(void *); i++) {
|
|
if (strlen(wordnames[i]) != 0) {
|
|
fcreate((char *) wordnames[i], i);
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk(" %s", wordnames[i]);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef CONFIG_DEBUG_DICTIONARY
|
|
printk(".\n");
|
|
#endif
|
|
|
|
/* get last/latest and state */
|
|
state = buildvariable("state", 0);
|
|
last = buildvariable("forth-last", 0);
|
|
latest = buildvariable("latest", 0);
|
|
|
|
*latest = target_ucell(pointer2cell(latest)-2*sizeof(cell));
|
|
|
|
base=buildvariable("base", 10);
|
|
|
|
buildconstant("/c", sizeof(u8));
|
|
buildconstant("/w", sizeof(u16));
|
|
buildconstant("/l", sizeof(u32));
|
|
buildconstant("/n", sizeof(ucell));
|
|
buildconstant("/x", sizeof(u64));
|
|
|
|
reveal();
|
|
if (verbose) {
|
|
printk("Dictionary initialization finished.\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* functions used by primitives
|
|
*/
|
|
|
|
int availchar(void)
|
|
{
|
|
int tmp;
|
|
if( cursrc < 1 ) {
|
|
interruptforth |= FORTH_INTSTAT_STOP;
|
|
/* return -1 in order to exit the loop in key() */
|
|
return -1;
|
|
}
|
|
|
|
tmp = getc( srcfiles[cursrc-1] );
|
|
if (tmp != EOF) {
|
|
ungetc(tmp, srcfiles[cursrc-1]);
|
|
return -1;
|
|
}
|
|
|
|
fclose(srcfiles[--cursrc]);
|
|
|
|
return availchar();
|
|
}
|
|
|
|
int get_inputbyte( void )
|
|
{
|
|
int tmp;
|
|
|
|
if( cursrc < 1 ) {
|
|
interruptforth |= FORTH_INTSTAT_STOP;
|
|
return 0;
|
|
}
|
|
|
|
tmp = getc( srcfiles[cursrc-1] );
|
|
|
|
/* Update current line number */
|
|
if (tmp == '\n') {
|
|
srclines[cursrc - 1]++;
|
|
}
|
|
|
|
if (tmp != EOF) {
|
|
return tmp;
|
|
}
|
|
|
|
fclose(srcfiles[--cursrc]);
|
|
|
|
return get_inputbyte();
|
|
}
|
|
|
|
void put_outputbyte( int c )
|
|
{
|
|
if (console)
|
|
fputc(c, console);
|
|
}
|
|
|
|
/*
|
|
* segmentation fault handler. linux specific?
|
|
*/
|
|
|
|
static void
|
|
segv_handler(int signo __attribute__ ((unused)),
|
|
siginfo_t * si, void *context __attribute__ ((unused)))
|
|
{
|
|
static int count = 0;
|
|
ucell addr = 0xdeadbeef;
|
|
|
|
if (count) {
|
|
printk("Died while dumping forth dictionary core.\n");
|
|
goto out;
|
|
}
|
|
|
|
count++;
|
|
|
|
if (PC >= pointer2cell(dict) && PC <= pointer2cell(dict) + dicthead)
|
|
addr = read_cell(cell2pointer(PC));
|
|
|
|
printk("panic: segmentation violation at %p\n", (char *)si->si_addr);
|
|
printk("dict=%p here=%p(dict+0x%" FMT_CELL_x ") pc=0x%" FMT_CELL_x "(dict+0x%" FMT_CELL_x ")\n",
|
|
dict, dict + dicthead, dicthead, PC, PC - pointer2cell(dict));
|
|
printk("dstackcnt=%d rstackcnt=%d instruction=%" FMT_CELL_x "\n",
|
|
dstackcnt, rstackcnt, addr);
|
|
|
|
printdstack();
|
|
printrstack();
|
|
|
|
printk("Writing dictionary core file\n");
|
|
write_dictionary("forth.dict.core");
|
|
|
|
out:
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* allocate memory and prepare engine for memory management.
|
|
*/
|
|
|
|
static void init_memory(void)
|
|
{
|
|
memset(memory, 0, MEMORY_SIZE);
|
|
|
|
/* we push start and end of memory to the stack
|
|
* so that it can be used by the forth word QUIT
|
|
* to initialize the memory allocator.
|
|
* Add a cell to the start address so we don't end
|
|
* up with a start address of zero during bootstrap
|
|
*/
|
|
|
|
PUSH(pointer2cell(memory)+sizeof(cell));
|
|
PUSH(pointer2cell(memory) + MEMORY_SIZE-1);
|
|
}
|
|
|
|
|
|
void
|
|
include_file( const char *name )
|
|
{
|
|
FILE *file;
|
|
|
|
if( cursrc >= sizeof(srcfiles)/sizeof(srcfiles[0]) ) {
|
|
printk("\npanic: Maximum include depth reached!\n");
|
|
exit(1);
|
|
}
|
|
|
|
file = fopen_include( name );
|
|
if( !file ) {
|
|
printk("\npanic: Failed opening file '%s'\n", name );
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
encode_file( const char *name )
|
|
{
|
|
FILE *file = fopen_include(name);
|
|
int size;
|
|
|
|
if( !file ) {
|
|
printk("\npanic: Can't open '%s'\n", name );
|
|
exit(1);
|
|
}
|
|
fseek( file, 0, SEEK_END );
|
|
size = ftell( file );
|
|
fseek( file, 0, SEEK_SET );
|
|
|
|
if (verbose) {
|
|
printk("\nEncoding %s [%d bytes]\n", name, size );
|
|
}
|
|
fread( dict + dicthead, size, 1, file );
|
|
PUSH( pointer2cell(dict + dicthead) );
|
|
PUSH( size );
|
|
dicthead += size;
|
|
paddict(sizeof(cell));
|
|
}
|
|
|
|
|
|
static void run_dictionary(char *basedict, char *confile)
|
|
{
|
|
if(!basedict)
|
|
return;
|
|
|
|
read_dictionary(basedict);
|
|
PC = (ucell)findword("initialize");
|
|
|
|
if (!PC) {
|
|
if (verbose) {
|
|
printk("Unable to find initialize word in dictionary %s; ignoring\n", basedict);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!srcfiles[0]) {
|
|
cursrc = 1;
|
|
srcfiles[cursrc-1] = stdin;
|
|
}
|
|
|
|
dstackcnt=0;
|
|
rstackcnt=0;
|
|
|
|
init_memory();
|
|
if (verbose)
|
|
printk("Jumping to dictionary %s...\n", basedict);
|
|
|
|
/* If a console file has been specified, open it */
|
|
if (confile)
|
|
console = fopen(confile, "w");
|
|
|
|
srcbasedict = basedict;
|
|
|
|
enterforth((xt_t)PC);
|
|
|
|
/* Close the console file */
|
|
if (console)
|
|
fclose(console);
|
|
}
|
|
|
|
static void new_dictionary(const char *source)
|
|
{
|
|
build_dictionary();
|
|
|
|
interpret_source((char *)source);
|
|
|
|
if (verbose || errors > 0) {
|
|
printk("interpretion finished. %d errors occurred.\n",
|
|
errors);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* main loop
|
|
*/
|
|
|
|
#define BANNER "OpenBIOS bootstrap kernel. (C) 2003-2006 Patrick Mauritz, Stefan Reinauer\n"\
|
|
"This software comes with absolutely no warranty. "\
|
|
"All rights reserved.\n\n"
|
|
|
|
#ifdef __GLIBC__
|
|
#define USAGE "Usage: %s [options] [dictionary file|source file]\n\n" \
|
|
" -h|--help show this help\n" \
|
|
" -V|--version print version and exit\n" \
|
|
" -v|--verbose print debugging information\n" \
|
|
" -I|--include dir add dir to include path\n" \
|
|
" -d|--source-dictionary bootstrap.dict\n" \
|
|
" use this dictionary as base\n" \
|
|
" -D|--target-dictionary output.dict\n" \
|
|
" write to output.dict\n" \
|
|
" -c|--console output.log\n" \
|
|
" write kernel console output to log file\n" \
|
|
" -s|--segfault install segfault handler\n" \
|
|
" -M|--dependency-dump file\n" \
|
|
" dump dependencies in Makefile format\n\n" \
|
|
" -x|--hexdump output format is C language hex dump\n"
|
|
#else
|
|
#define USAGE "Usage: %s [options] [dictionary file|source file]\n\n" \
|
|
" -h show this help\n" \
|
|
" -V print version and exit\n" \
|
|
" -v print debugging information\n" \
|
|
" -I add dir to include path\n" \
|
|
" -d bootstrap.dict\n" \
|
|
" use this dictionary as base\n" \
|
|
" -D output.dict\n" \
|
|
" write to output.dict\n" \
|
|
" -c output.log\n" \
|
|
" write kernel console output to log file\n" \
|
|
" -s install segfault handler\n\n" \
|
|
" -M file dump dependencies in Makefile format\n\n" \
|
|
" -x output format is C language hex dump\n"
|
|
#endif
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct sigaction sa;
|
|
|
|
unsigned char *ressources=NULL; /* All memory used by us */
|
|
const char *dictname = NULL;
|
|
char *basedict = NULL;
|
|
char *consolefile = NULL;
|
|
char *depfilename = NULL;
|
|
|
|
unsigned char *bootstrapdict[2];
|
|
int c, cnt, hexdump = 0;
|
|
|
|
const char *optstring = "VvhsI:d:D:c:M:x?";
|
|
|
|
while (1) {
|
|
#ifdef __GLIBC__
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{"version", 0, NULL, 'V'},
|
|
{"verbose", 0, NULL, 'v'},
|
|
{"help", 0, NULL, 'h'},
|
|
{"segfault", 0, NULL, 's'},
|
|
{"include", 1, NULL, 'I'},
|
|
{"source-dictionary", 1, NULL, 'd'},
|
|
{"target-dictionary", 1, NULL, 'D'},
|
|
{"console", 1, NULL, 'c'},
|
|
{"dependency-dump", 1, NULL, 'M'},
|
|
{"hexdump", 0, NULL, 'x'},
|
|
};
|
|
|
|
/*
|
|
* option handling
|
|
*/
|
|
|
|
c = getopt_long(argc, argv, optstring, long_options,
|
|
&option_index);
|
|
#else
|
|
c = getopt(argc, argv, optstring);
|
|
#endif
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 'V':
|
|
printk("Version " OPENBIOS_VERSION_STR "\n");
|
|
return 0;
|
|
case 'h':
|
|
case '?':
|
|
printk("Version " OPENBIOS_VERSION_STR "\n" USAGE,
|
|
argv[0]);
|
|
return 0;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 's':
|
|
segfault = 1;
|
|
break;
|
|
case 'I':
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printk("adding '%s' to include path\n", optarg);
|
|
#endif
|
|
add_includepath(optarg);
|
|
break;
|
|
case 'd':
|
|
if (!basedict) {
|
|
basedict = optarg;
|
|
}
|
|
break;
|
|
case 'D':
|
|
if(!dictname) {
|
|
dictname = optarg;
|
|
}
|
|
break;
|
|
case 'c':
|
|
if (!consolefile) {
|
|
consolefile = optarg;
|
|
}
|
|
break;
|
|
case 'M':
|
|
if (!depfilename) {
|
|
depfilename = optarg;
|
|
}
|
|
break;
|
|
case 'x':
|
|
hexdump = 1;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!dictname) {
|
|
dictname = "bootstrap.dict";
|
|
}
|
|
if (verbose) {
|
|
printk(BANNER);
|
|
printk("Using source dictionary '%s'\n", basedict);
|
|
printk("Dumping final dictionary to '%s'\n", dictname);
|
|
printk("Dumping dependencies to '%s'\n", depfilename);
|
|
}
|
|
|
|
if (argc < optind) {
|
|
printk(USAGE, argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
if (depfilename) {
|
|
depfile = fopen(depfilename, "w");
|
|
if (!depfile) {
|
|
printk("panic: can't write to dependency file '%s'.\n",
|
|
depfilename);
|
|
exit(1);
|
|
}
|
|
fprintf(depfile, "%s:", dictname);
|
|
}
|
|
|
|
/*
|
|
* Get all required resources
|
|
*/
|
|
|
|
|
|
ressources = malloc(MEMORY_SIZE + (2 * DICTIONARY_SIZE) + TRAMPOLINE_SIZE);
|
|
if (!ressources) {
|
|
printk("panic: not enough memory on host system.\n");
|
|
return 1;
|
|
}
|
|
|
|
#ifdef NATIVE_BITWIDTH_SMALLER_THAN_HOST_BITWIDTH
|
|
base_address=(unsigned long)ressources;
|
|
#endif
|
|
|
|
memory = (ucell *)ressources;
|
|
|
|
bootstrapdict[0] = ressources + MEMORY_SIZE;
|
|
bootstrapdict[1] = ressources + MEMORY_SIZE + DICTIONARY_SIZE;
|
|
trampoline = (ucell *)(ressources + MEMORY_SIZE + DICTIONARY_SIZE + DICTIONARY_SIZE);
|
|
|
|
#ifdef CONFIG_DEBUG_INTERPRETER
|
|
printf("memory: %p\n",memory);
|
|
printf("dict1: %p\n",bootstrapdict[0]);
|
|
printf("dict2: %p\n",bootstrapdict[1]);
|
|
printf("trampoline: %p\n",trampoline);
|
|
printf("size=%d, trampoline_size=%d\n",MEMORY_SIZE + (2 *
|
|
DICTIONARY_SIZE) + TRAMPOLINE_SIZE,
|
|
TRAMPOLINE_SIZE);
|
|
#endif
|
|
|
|
if (trampoline == NULL) {
|
|
/* We're using side effects which is to some extent nasty */
|
|
printf("WARNING: no trampoline!\n");
|
|
} else {
|
|
init_trampoline(trampoline);
|
|
}
|
|
|
|
if (!segfault) {
|
|
if (verbose)
|
|
printk("Installing SIGSEGV handler...");
|
|
|
|
sa.sa_sigaction = segv_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
|
|
sigaction(SIGSEGV, &sa, NULL);
|
|
|
|
if (verbose)
|
|
printk("done.\n");
|
|
}
|
|
|
|
/*
|
|
* Now do the real work
|
|
*/
|
|
|
|
for (cnt=0; cnt<2; cnt++) {
|
|
if (verbose) {
|
|
printk("Compiling dictionary %d/%d\n", cnt+1, 2);
|
|
}
|
|
dict=bootstrapdict[cnt];
|
|
if(!basedict) {
|
|
new_dictionary(argv[optind]);
|
|
} else {
|
|
for (c=argc-1; c>=optind; c--)
|
|
include_file(argv[c]);
|
|
|
|
run_dictionary(basedict, consolefile);
|
|
}
|
|
if (depfile) {
|
|
fprintf(depfile, "\n");
|
|
fclose(depfile);
|
|
depfile = NULL;
|
|
}
|
|
if(errors)
|
|
break;
|
|
}
|
|
|
|
#ifndef CONFIG_DEBUG_INTERPRETER
|
|
if (errors)
|
|
printk("dictionary not dumped to file.\n");
|
|
else
|
|
#endif
|
|
{
|
|
relocation_table( bootstrapdict[0], bootstrapdict[1], dicthead);
|
|
if (hexdump) {
|
|
write_dictionary_hex(dictname);
|
|
} else {
|
|
write_dictionary(dictname);
|
|
}
|
|
}
|
|
|
|
free(ressources);
|
|
|
|
if (errors)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|