| /* 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; |
| } |