OpenCores
URL https://opencores.org/ocsvn/forth-cpu/forth-cpu/trunk

Subversion Repositories forth-cpu

[/] [forth-cpu/] [trunk/] [h2.c] - Diff between revs 3 and 5

Show entire file | Details | Blame | View Log

Rev 3 Rev 5
Line 1... Line 1...
/** @file      h2.c
/** @file      h2.c
 *  @brief     Simulate the H2 CPU and surrounding system
 *  @brief     Simulate the H2 CPU and surrounding system
 *  @copyright Richard James Howe (2017)
 *  @copyright Richard James Howe (2017)
 *  @license   MIT
 *  @license   MIT
 *
 *
 * This file contains the toolchain for the H2, it is an assembler/compiler,
 * This file contains the simulator and debugger for the H2,
 * a simulator, a disassembler and a debugger. The H2 is written in VHDL and
 * The H2 is written in VHDL and
 * is based on the J1 processor (see http://excamera.com/sphinx/fpga-j1.html).
 * is based on the J1 processor (see http://excamera.com/sphinx/fpga-j1.html).
 *
 *
 * The processor has been tested on an FPGA and is working.
 * The processor has been tested on an FPGA and is working.
 * The project can be found at: https://github.com/howerj/forth-cpu
 * The project can be found at: https://github.com/howerj/forth-cpu */
 *
 
 * @todo Generate VCD or GHW files directly so that GTKWave can view
 
 * the resulting trace. See
 
 * <http://www.ic.unicamp.br/~ducatte/mc542/Docs/gtkwave.pdf>
 
 * @todo The compiler section could be replace by an embedded Forth interpreter
 
 * The one available at <https://github.com/howerj/embed> would work well, the
 
 * metacompiler could be retargeted for the H2 processor instead of its own
 
 * 16-bit virtual machine. This should cut down on program size and increase
 
 * functionality.
 
 * @todo Allow state to be saved/loaded by serializing the objects within
 
 * here. This would allow the simulation state to be saved and resumed.
 
 */
 
 
 
/* ========================== Preamble: Types, Macros, Globals ============= */
/* ========================== Preamble: Types, Macros, Globals ============= */
 
 
#include "h2.h"
#include "h2.h"
#include <assert.h>
#include <assert.h>
Line 106... Line 94...
        ALU_OP_ENABLE_INTERRUPTS,  /**< Enable interrupts    */
        ALU_OP_ENABLE_INTERRUPTS,  /**< Enable interrupts    */
 
 
        ALU_OP_INTERRUPTS_ENABLED, /**< Are interrupts on?   */
        ALU_OP_INTERRUPTS_ENABLED, /**< Are interrupts on?   */
        ALU_OP_RDEPTH,             /**< R Stack Depth        */
        ALU_OP_RDEPTH,             /**< R Stack Depth        */
        ALU_OP_T_EQUAL_0,          /**< T == 0               */
        ALU_OP_T_EQUAL_0,          /**< T == 0               */
        ALU_OP_CPU_ID              /**< CPU Identifier       */
        ALU_OP_CPU_ID,             /**< CPU Identifier       */
 
 
 
        ALU_OP_LITERAL,            /**< undocumented; set T to instruction & $7fff */
} alu_code_e;
} alu_code_e;
 
 
#define DELTA_0  (0)
#define DELTA_0  (0)
#define DELTA_1  (1)
#define DELTA_1  (1)
#define DELTA_N2 (2)
#define DELTA_N2 (2)
Line 129... Line 119...
 * their T_TO_R bit set in their instruction description tables, this
 * their T_TO_R bit set in their instruction description tables, this
 * appears to be incorrect */
 * appears to be incorrect */
#define X_MACRO_INSTRUCTIONS \
#define X_MACRO_INSTRUCTIONS \
        X(DUP,    "dup",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T)        | T_TO_N  | MK_DSTACK(DELTA_1)))\
        X(DUP,    "dup",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T)        | T_TO_N  | MK_DSTACK(DELTA_1)))\
        X(OVER,   "over",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)        | T_TO_N  | MK_DSTACK(DELTA_1)))\
        X(OVER,   "over",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)        | T_TO_N  | MK_DSTACK(DELTA_1)))\
 
        X(OVERADD,"over+",  false, (OP_ALU_OP | MK_CODE(ALU_OP_T_PLUS_N)))\
 
        X(OVERAND,"over-and",  false, (OP_ALU_OP | MK_CODE(ALU_OP_T_AND_N)))\
        X(INVERT, "invert", true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_INVERT)))\
        X(INVERT, "invert", true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_INVERT)))\
        X(ADD,    "+",      true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_PLUS_N)               | MK_DSTACK(DELTA_N1)))\
        X(ADD,    "+",      true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_PLUS_N)               | MK_DSTACK(DELTA_N1)))\
        X(SWAP,   "swap",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)        | T_TO_N))\
        X(SWAP,   "swap",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)        | T_TO_N))\
        X(NIP,    "nip",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T)                      | MK_DSTACK(DELTA_N1)))\
        X(NIP,    "nip",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T)                      | MK_DSTACK(DELTA_N1)))\
        X(DROP,   "drop",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)                      | MK_DSTACK(DELTA_N1)))\
        X(DROP,   "drop",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_N)                      | MK_DSTACK(DELTA_N1)))\
Line 150... Line 142...
        X(AND,    "and",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_AND_N)                | MK_DSTACK(DELTA_N1)))\
        X(AND,    "and",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_AND_N)                | MK_DSTACK(DELTA_N1)))\
        X(XOR,    "xor",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_XOR_N)                | MK_DSTACK(DELTA_N1)))\
        X(XOR,    "xor",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_XOR_N)                | MK_DSTACK(DELTA_N1)))\
        X(OR,     "or",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_OR_N)                 | MK_DSTACK(DELTA_N1)))\
        X(OR,     "or",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_OR_N)                 | MK_DSTACK(DELTA_N1)))\
        X(DEPTH,  "sp@",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_DEPTH)   | T_TO_N       | MK_DSTACK(DELTA_1)))\
        X(DEPTH,  "sp@",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_DEPTH)   | T_TO_N       | MK_DSTACK(DELTA_1)))\
        X(T_N1,   "1-",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_DECREMENT)))\
        X(T_N1,   "1-",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_DECREMENT)))\
        X(IEN,    "ien",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_ENABLE_INTERRUPTS)     /* | MK_DSTACK(DELTA_N1) */))\
        X(IEN,    "ien!",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_ENABLE_INTERRUPTS)      | MK_DSTACK(DELTA_N1)))\
        X(ISIEN,  "ien?",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_INTERRUPTS_ENABLED) | T_TO_N  | MK_DSTACK(DELTA_1)))\
        X(ISIEN,  "ien?",   true,  (OP_ALU_OP | MK_CODE(ALU_OP_INTERRUPTS_ENABLED) | T_TO_N  | MK_DSTACK(DELTA_1)))\
        X(RDEPTH, "rp@",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_RDEPTH)  | T_TO_N       | MK_DSTACK(DELTA_1)))\
        X(RDEPTH, "rp@",    true,  (OP_ALU_OP | MK_CODE(ALU_OP_RDEPTH)  | T_TO_N       | MK_DSTACK(DELTA_1)))\
        X(TE0,    "0=",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_EQUAL_0)))\
        X(TE0,    "0=",     true,  (OP_ALU_OP | MK_CODE(ALU_OP_T_EQUAL_0)))\
        X(NOP,    "nop",    false, (OP_ALU_OP | MK_CODE(ALU_OP_T)))\
        X(NOP,    "nop",    false, (OP_ALU_OP | MK_CODE(ALU_OP_T)))\
        X(CPU_ID, "cpu-id", true,  (OP_ALU_OP | MK_CODE(ALU_OP_CPU_ID))                | MK_DSTACK(DELTA_1))\
        X(CPU_ID, "cpu-id", true,  (OP_ALU_OP | MK_CODE(ALU_OP_CPU_ID))                | MK_DSTACK(DELTA_1))\
        X(RUP,    "rup",    false, (OP_ALU_OP | MK_CODE(ALU_OP_T))                     | MK_RSTACK(DELTA_1))\
        X(RUP,    "rup",    false, (OP_ALU_OP | MK_CODE(ALU_OP_T))                     | MK_RSTACK(DELTA_1))\
 
        X(DUPTOR, "dup>r",  false, (OP_ALU_OP | MK_CODE(ALU_OP_T)) | T_TO_R            | MK_RSTACK(DELTA_1))\
        X(RDROP,  "rdrop",  true,  (OP_ALU_OP | MK_CODE(ALU_OP_T) | MK_RSTACK(DELTA_N1)))
        X(RDROP,  "rdrop",  true,  (OP_ALU_OP | MK_CODE(ALU_OP_T) | MK_RSTACK(DELTA_N1)))
 
 
 
 
typedef enum {
typedef enum {
#define X(NAME, STRING, DEFINE, INSTRUCTION) CODE_ ## NAME = INSTRUCTION,
#define X(NAME, STRING, DEFINE, INSTRUCTION) CODE_ ## NAME = INSTRUCTION,
        X_MACRO_INSTRUCTIONS
        X_MACRO_INSTRUCTIONS
#undef X
#undef X
} forth_word_codes_e;
} forth_word_codes_e;
 
 
static const char *log_levels[] =
static const char *log_levels[] = {
{
 
#define X(ENUM, NAME) [ENUM] = NAME,
#define X(ENUM, NAME) [ENUM] = NAME,
        X_MACRO_LOGGING
        X_MACRO_LOGGING
#undef X
#undef X
};
};
 
 
Line 185... Line 177...
 
 
/* ========================== Preamble: Types, Macros, Globals ============= */
/* ========================== Preamble: Types, Macros, Globals ============= */
 
 
/* ========================== Utilities ==================================== */
/* ========================== Utilities ==================================== */
 
 
int logger(log_level_e level, const char *func,
int logger(log_level_e level, const char *func, const unsigned line, const char *fmt, ...) {
                const unsigned line, const char *fmt, ...)
 
{
 
        int r = 0;
        int r = 0;
        va_list ap;
 
        assert(func);
        assert(func);
        assert(fmt);
        assert(fmt);
        assert(level <= LOG_ALL_MESSAGES);
        assert(level <= LOG_ALL_MESSAGES);
        if(level <= log_level) {
        if(level <= log_level) {
 
                va_list ap;
                fprintf(stderr, "[%s %u] %s: ", func, line, log_levels[level]);
                fprintf(stderr, "[%s %u] %s: ", func, line, log_levels[level]);
                va_start(ap, fmt);
                va_start(ap, fmt);
                r = vfprintf(stderr, fmt, ap);
                r = vfprintf(stderr, fmt, ap);
                va_end(ap);
                va_end(ap);
                fputc('\n', stderr);
                fputc('\n', stderr);
Line 206... Line 196...
        if(level == LOG_FATAL)
        if(level == LOG_FATAL)
                exit(EXIT_FAILURE);
                exit(EXIT_FAILURE);
        return r;
        return r;
}
}
 
 
static const char *reason(void)
static const char *reason(void) {
{
 
        static const char *unknown = "unknown reason";
        static const char *unknown = "unknown reason";
        const char *r;
 
        if(errno == 0)
        if(errno == 0)
                return unknown;
                return unknown;
        r = strerror(errno);
        const char *r = strerror(errno);
        if(!r)
        if(!r)
                return unknown;
                return unknown;
        return r;
        return r;
}
}
 
 
void *allocate_or_die(size_t length)
void *allocate_or_die(const size_t length) {
{
 
        void *r;
 
        errno = 0;
        errno = 0;
        r = calloc(1, length);
        void *r = calloc(1, length);
        if(!r)
        if(!r)
                fatal("allocation of size %zu failed: %s",
                fatal("allocation of size %u failed: %s", (unsigned)length, reason());
                                length, reason());
 
        return r;
        return r;
}
}
 
 
FILE *fopen_or_die(const char *file, const char *mode)
FILE *fopen_or_die(const char *file, const char *mode) {
{
 
        FILE *f = NULL;
 
        assert(file);
        assert(file);
        assert(mode);
        assert(mode);
        errno = 0;
        errno = 0;
        f = fopen(file, mode);
        FILE *f = fopen(file, mode);
        if(!f)
        if(!f)
                fatal("failed to open file '%s' (mode %s): %s",
                fatal("failed to open file '%s' (mode %s): %s", file, mode, reason());
                                file, mode, reason());
 
        return f;
        return f;
}
}
 
 
static int indent(FILE *output, char c, unsigned i)
static int string_to_long(const int base, long *n, const char *s) {
{
 
        assert(output);
 
        while(i--)
 
                if(fputc(c, output) != c)
 
                        return -1;
 
        return 0;
 
}
 
 
 
static int string_to_long(int base, long *n, const char *s)
 
{
 
        char *end = NULL;
        char *end = NULL;
        assert(base >= 0);
        assert(base >= 0);
        assert(base != 1);
        assert(base != 1);
        assert(base <= 36);
        assert(base <= 36);
        assert(n);
        assert(n);
Line 264... Line 236...
        errno = 0;
        errno = 0;
        *n = strtol(s, &end, base);
        *n = strtol(s, &end, base);
        return errno || *s == '\0' || *end != '\0';
        return errno || *s == '\0' || *end != '\0';
}
}
 
 
static int string_to_cell(int base, uint16_t *n, const char *s)
static int string_to_cell(int base, uint16_t *n, const char *s) {
{
 
        long n1 = 0;
        long n1 = 0;
        int r = string_to_long(base, &n1, s);
        const int r = string_to_long(base, &n1, s);
        *n = n1;
        *n = n1;
        return r;
        return r;
}
}
 
 
static char *duplicate(const char *str)
static char *duplicate(const char *str) {
{
 
        char *r;
 
        assert(str);
        assert(str);
 
        const size_t length = strlen(str);
 
        assert((length + 1) > length);
        errno = 0;
        errno = 0;
        r = malloc(strlen(str) + 1);
        char *r = malloc(length + 1);
        if(!r)
        if(!r)
                fatal("duplicate of '%s' failed: %s", str, reason());
                fatal("duplicate of '%s' failed: %s", str, reason());
        strcpy(r, str);
        strcpy(r, str);
        return r;
        return r;
}
}
 
 
static void ethrow(error_t *e)
static void ethrow(error_t *e) {
{
 
        if(e && e->jmp_buf_valid) {
        if(e && e->jmp_buf_valid) {
                e->jmp_buf_valid = 0;
                e->jmp_buf_valid = 0;
                e->error = 1;
                e->error = 1;
                longjmp(e->j, 1);
                longjmp(e->j, 1);
        }
        }
        exit(EXIT_FAILURE);
        exit(EXIT_FAILURE);
}
}
 
 
h2_t *h2_new(uint16_t start_address)
h2_t *h2_new(const uint16_t start_address) {
{
 
        h2_t *h = allocate_or_die(sizeof(h2_t));
        h2_t *h = allocate_or_die(sizeof(h2_t));
        h->pc = start_address;
        h->pc = start_address;
        for(uint16_t i = 0; i < start_address; i++)
        for(uint16_t i = 0; i < start_address; i++)
                h->core[i] = OP_BRANCH | start_address;
                h->core[i] = OP_BRANCH | start_address;
        return h;
        return h;
}
}
 
 
void h2_free(h2_t *h)
void h2_free(h2_t * const h) {
{
 
        if(!h)
        if(!h)
                return;
                return;
        free(h->bp.points);
        free(h->bp.points);
        memset(h, 0, sizeof(*h));
        memset(h, 0, sizeof(*h));
        free(h);
        free(h);
}
}
 
 
int binary_memory_load(FILE *input, uint16_t *p, size_t length)
int binary_memory_load(FILE *input, uint16_t *p, const size_t length) {
{
 
        assert(input);
        assert(input);
        assert(p);
        assert(p);
        for(size_t i = 0; i < length; i++) {
        for(size_t i = 0; i < length; i++) {
                errno = 0;
                errno = 0;
                const int r1 = fgetc(input);
                const int r1 = fgetc(input);
Line 329... Line 296...
                p[i] = (((unsigned)r1 & 0xffu)) | (((unsigned)r2 & 0xffu) << 8u);
                p[i] = (((unsigned)r1 & 0xffu)) | (((unsigned)r2 & 0xffu) << 8u);
        }
        }
        return 0;
        return 0;
}
}
 
 
int binary_memory_save(FILE *output, uint16_t *p, size_t length)
int binary_memory_save(FILE *output, const uint16_t * const p, const size_t length) {
{
 
        assert(output);
        assert(output);
        assert(p);
        assert(p);
        for(size_t i = 0; i < length; i++) {
        for(size_t i = 0; i < length; i++) {
                errno = 0;
                errno = 0;
                const int r1 = fputc((p[i])&0xff,output);
                const int r1 = fputc((p[i])&0xff,output);
Line 345... Line 311...
                }
                }
        }
        }
        return 0;
        return 0;
}
}
 
 
int nvram_load_and_transfer(h2_io_t *io, const char *name, bool transfer_to_sram)
int nvram_load_and_transfer(h2_io_t *io, const char *name, const bool transfer_to_sram) {
{
 
        assert(io);
        assert(io);
        assert(name);
        assert(name);
        FILE *input = NULL;
        FILE *input = NULL;
        int r = 0;
        int r = 0;
        errno = 0;
        errno = 0;
Line 364... Line 329...
                r = -1;
                r = -1;
        }
        }
        return r;
        return r;
}
}
 
 
int nvram_save(h2_io_t *io, const char *name)
int nvram_save(h2_io_t *io, const char *name) {
{
 
        FILE *output = NULL;
        FILE *output = NULL;
        int r = 0;
        int r = 0;
        assert(io);
        assert(io);
        assert(name);
        assert(name);
        errno = 0;
        errno = 0;
Line 381... Line 345...
                r = -1;
                r = -1;
        }
        }
        return r;
        return r;
}
}
 
 
int memory_load(FILE *input, uint16_t *p, size_t length)
int memory_load(FILE *input, uint16_t *p, const size_t length) {
{
 
        assert(input);
        assert(input);
        assert(p);
        assert(p);
        char line[80] = {0}; /*more than enough!*/
        char line[80] = {0}; /*more than enough!*/
        size_t i = 0;
        size_t i = 0;
 
 
        for(;fgets(line, sizeof(line), input); i++) {
        for(;fgets(line, sizeof(line), input); i++) {
                int r;
                int r;
                if(i >= length) {
                if(i >= length) {
                        error("file contains too many lines: %zu", i);
                        error("file contains too many lines: %u", (unsigned)i);
                        return -1;
                        return -1;
                }
                }
                r = string_to_cell(16, &p[i], line);
                r = string_to_cell(16, &p[i], line);
                if(!r) {
                if(!r) {
                        error("invalid line - expected hex string: %s", line);
                        error("invalid line - expected hex string: %s", line);
                        return -1;
                        return -1;
                }
                }
                debug("%zu %u", i, (unsigned)p[i]);
                debug("%u %u", (unsigned)i, (unsigned)p[i]);
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
int memory_save(FILE *output, uint16_t *p, size_t length)
int memory_save(FILE *output, const uint16_t * const p, const size_t length) {
{
 
        assert(output);
        assert(output);
        assert(p);
        assert(p);
        for(size_t i = 0; i < length; i++)
        for(size_t i = 0; i < length; i++)
                if(fprintf(output, "%04"PRIx16"\n", p[i]) < 0) {
                if(fprintf(output, "%04"PRIx16"\n", p[i]) < 0) {
                        error("failed to write line: %"PRId16, i);
                        error("failed to write line: %lu", (unsigned long)i);
                        return -1;
                        return -1;
                }
                }
        return 0;
        return 0;
}
}
 
 
int h2_load(h2_t *h, FILE *hexfile)
int h2_load(h2_t *h, FILE *hexfile) {
{
 
        assert(h);
        assert(h);
        assert(hexfile);
        assert(hexfile);
        return memory_load(hexfile, h->core, MAX_CORE);
        return memory_load(hexfile, h->core, MAX_CORE);
}
}
 
 
int h2_save(h2_t *h, FILE *output, bool full)
int h2_save(const h2_t * const h, FILE *output, const bool full) {
{
 
        assert(h);
        assert(h);
        assert(output);
        assert(output);
        return memory_save(output, h->core, full ? MAX_CORE : h->pc);
        return memory_save(output, h->core, full ? MAX_CORE : h->pc);
}
}
 
 
/* From: https://stackoverflow.com/questions/215557/how-do-i-implement-a-circular-list-ring-buffer-in-c */
/* From: https://stackoverflow.com/questions/215557/how-do-i-implement-a-circular-list-ring-buffer-in-c */
 
 
fifo_t *fifo_new(size_t size)
fifo_t *fifo_new(const size_t size) {
{
 
        assert(size >= 2); /* It does not make sense to have a FIFO less than this size */
        assert(size >= 2); /* It does not make sense to have a FIFO less than this size */
        fifo_data_t *buffer = allocate_or_die(size * sizeof(buffer[0]));
        fifo_data_t *buffer = allocate_or_die(size * sizeof(buffer[0]));
        fifo_t *fifo = allocate_or_die(sizeof(fifo_t));
        fifo_t *fifo = allocate_or_die(sizeof(fifo_t));
 
 
        fifo->buffer = buffer;
        fifo->buffer = buffer;
Line 447... Line 406...
        fifo->size   = size;
        fifo->size   = size;
 
 
        return fifo;
        return fifo;
}
}
 
 
void fifo_free(fifo_t *fifo)
void fifo_free(fifo_t *fifo) {
{
 
        if(!fifo)
        if(!fifo)
                return;
                return;
        free(fifo->buffer);
        free(fifo->buffer);
        free(fifo);
        free(fifo);
}
}
 
 
bool fifo_is_full(fifo_t * fifo)
bool fifo_is_full(const fifo_t * const fifo) {
{
 
        assert(fifo);
        assert(fifo);
        return (fifo->head == (fifo->size - 1) && fifo->tail == 0)
        return (fifo->head == (fifo->size - 1) && fifo->tail == 0)
            || (fifo->head == (fifo->tail - 1));
            || (fifo->head == (fifo->tail - 1));
}
}
 
 
bool fifo_is_empty(fifo_t * fifo)
bool fifo_is_empty(const fifo_t * const fifo) {
{
 
        assert(fifo);
        assert(fifo);
        return fifo->head == fifo->tail;
        return fifo->head == fifo->tail;
}
}
 
 
size_t fifo_count(fifo_t * fifo)
size_t fifo_count(const fifo_t * const fifo) {
{
 
        assert(fifo);
        assert(fifo);
        if (fifo_is_empty(fifo))
        if (fifo_is_empty(fifo))
                return 0;
                return 0;
        else if (fifo_is_full(fifo))
        else if (fifo_is_full(fifo))
                return fifo->size;
                return fifo->size;
Line 481... Line 436...
                return fifo->head + (fifo->size - fifo->tail);
                return fifo->head + (fifo->size - fifo->tail);
        else
        else
                return fifo->head - fifo->tail;
                return fifo->head - fifo->tail;
}
}
 
 
size_t fifo_push(fifo_t * fifo, fifo_data_t data)
size_t fifo_push(fifo_t * fifo, fifo_data_t data) {
{
 
        assert(fifo);
        assert(fifo);
 
 
        if (fifo_is_full(fifo))
        if (fifo_is_full(fifo))
                return 0;
                return 0;
 
 
        fifo->buffer[fifo->head] = data;
        fifo->buffer[fifo->head] = data;
 
 
Line 497... Line 450...
                fifo->head = 0;
                fifo->head = 0;
 
 
        return 1;
        return 1;
}
}
 
 
size_t fifo_pop(fifo_t * fifo, fifo_data_t * data)
size_t fifo_pop(fifo_t * fifo, fifo_data_t * data) {
{
 
        assert(fifo);
        assert(fifo);
        assert(data);
        assert(data);
 
 
        if (fifo_is_empty(fifo))
        if (fifo_is_empty(fifo))
                return 0;
                return 0;
Line 517... Line 469...
}
}
 
 
#ifdef __unix__
#ifdef __unix__
#include <unistd.h>
#include <unistd.h>
#include <termios.h>
#include <termios.h>
static int getch(void)
static int getch(void) {
{
 
        struct termios oldattr, newattr;
        struct termios oldattr, newattr;
        int ch;
 
        tcgetattr(STDIN_FILENO, &oldattr);
        tcgetattr(STDIN_FILENO, &oldattr);
        newattr = oldattr;
        newattr = oldattr;
        newattr.c_iflag &= ~(ICRNL);
        newattr.c_iflag &= ~(ICRNL);
        newattr.c_lflag &= ~(ICANON | ECHO);
        newattr.c_lflag &= ~(ICANON | ECHO);
 
 
        tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
        tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
        ch = getchar();
        const int ch = getchar();
 
 
        tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
        tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
 
 
        return ch;
        return ch;
}
}
 
 
static int putch(int c)
static int putch(int c) {
{
 
        int res = putchar(c);
        int res = putchar(c);
        fflush(stdout);
        fflush(stdout);
        return res;
        return res;
}
}
#else
#else
Line 547... Line 496...
 
 
extern int getch(void);
extern int getch(void);
extern int putch(int c);
extern int putch(int c);
 
 
#else
#else
static int getch(void)
static int getch(void) {
{
 
        return getchar();
        return getchar();
}
}
 
 
static int putch(int c)
static int putch(const int c) {
{
 
        return putchar(c);
        return putchar(c);
}
}
#endif
#endif
#endif /** __unix__ **/
#endif /** __unix__ **/
 
 
static int wrap_getch(bool *debug_on)
static int wrap_getch(bool *debug_on) {
{
        const int ch = getch();
        int ch = getch();
 
        assert(debug_on);
        assert(debug_on);
        if(ch == EOF) {
        if(ch == EOF) {
                note("End Of Input - exiting", ESCAPE);
                note("End Of Input - exiting");
                exit(EXIT_SUCCESS);
                exit(EXIT_SUCCESS);
        }
        }
        if(ch == ESCAPE && debug_on)
        if(ch == ESCAPE && debug_on)
                *debug_on = true;
                *debug_on = true;
 
 
Line 577... Line 523...
 
 
/* ========================== Utilities ==================================== */
/* ========================== Utilities ==================================== */
 
 
/* ========================== Symbol Table ================================= */
/* ========================== Symbol Table ================================= */
 
 
static const char *symbol_names[] =
static const char *symbol_names[] = {
{
 
        [SYMBOL_TYPE_LABEL]       = "label",
        [SYMBOL_TYPE_LABEL]       = "label",
        [SYMBOL_TYPE_CALL]        = "call",
        [SYMBOL_TYPE_CALL]        = "call",
        [SYMBOL_TYPE_CONSTANT]    = "constant",
        [SYMBOL_TYPE_CONSTANT]    = "constant",
        [SYMBOL_TYPE_VARIABLE]    = "variable",
        [SYMBOL_TYPE_VARIABLE]    = "variable",
        NULL
        NULL
};
};
 
 
static symbol_t *symbol_new(symbol_type_e type, const char *id, uint16_t value)
static symbol_t *symbol_new(const symbol_type_e type, const char *id, const uint16_t value) {
{
 
        symbol_t *s = allocate_or_die(sizeof(*s));
        symbol_t *s = allocate_or_die(sizeof(*s));
        assert(id);
        assert(id);
        s->id = duplicate(id);
        s->id = duplicate(id);
        s->value = value;
        s->value = value;
        s->type = type;
        s->type = type;
        return s;
        return s;
}
}
 
 
static void symbol_free(symbol_t *s)
static void symbol_free(symbol_t *s) {
{
 
        if(!s)
        if(!s)
                return;
                return;
        free(s->id);
        free(s->id);
        memset(s, 0, sizeof(*s));
        memset(s, 0, sizeof(*s));
        free(s);
        free(s);
}
}
 
 
static symbol_table_t *symbol_table_new(void)
static symbol_table_t *symbol_table_new(void) {
{
 
        symbol_table_t *t = allocate_or_die(sizeof(*t));
        symbol_table_t *t = allocate_or_die(sizeof(*t));
        return t;
        return t;
}
}
 
 
static void symbol_table_free(symbol_table_t *t)
static void symbol_table_free(symbol_table_t *t) {
{
 
        if(!t)
        if(!t)
                return;
                return;
        for(size_t i = 0; i < t->length; i++)
        for(size_t i = 0; i < t->length; i++)
                symbol_free(t->symbols[i]);
                symbol_free(t->symbols[i]);
        free(t->symbols);
        free(t->symbols);
        memset(t, 0, sizeof(*t));
        memset(t, 0, sizeof(*t));
        free(t);
        free(t);
}
}
 
 
static symbol_t *symbol_table_lookup(symbol_table_t *t, const char *id)
static symbol_t *symbol_table_lookup(const symbol_table_t * const t, const char *id) {
{
 
        for(size_t i = 0; i < t->length; i++)
        for(size_t i = 0; i < t->length; i++)
                if(!strcmp(t->symbols[i]->id, id))
                if(!strcmp(t->symbols[i]->id, id))
                        return t->symbols[i];
                        return t->symbols[i];
        return NULL;
        return NULL;
}
}
 
 
/** @note There can be multiple symbols with the same value of the same type */
/** @note There can be multiple symbols with the same value of the same type */
static symbol_t *symbol_table_reverse_lookup(symbol_table_t *t, symbol_type_e type, uint16_t value)
static const symbol_t *symbol_table_reverse_lookup(const symbol_table_t * const t, const symbol_type_e type, const uint16_t value) {
{
 
        for(size_t i = 0; i < t->length; i++)
        for(size_t i = 0; i < t->length; i++)
                if(t->symbols[i]->type == type && t->symbols[i]->value == value)
                if(t->symbols[i]->type == type && t->symbols[i]->value == value)
                        return t->symbols[i];
                        return t->symbols[i];
        return NULL;
        return NULL;
}
}
 
 
static int symbol_table_add(symbol_table_t *t, symbol_type_e type, const char *id, uint16_t value, error_t *e, bool hidden)
static int symbol_table_add(symbol_table_t *t, symbol_type_e type, const char *id, uint16_t value, error_t *e, bool hidden, bool used) {
{
 
        symbol_t *s = symbol_new(type, id, value);
        symbol_t *s = symbol_new(type, id, value);
        symbol_t **xs = NULL;
        symbol_t **xs = NULL;
        assert(t);
        assert(t);
 
 
        if(symbol_table_lookup(t, id)) {
        if(symbol_table_lookup(t, id)) {
 
                symbol_free(s);
                error("redefinition of symbol: %s", id);
                error("redefinition of symbol: %s", id);
                if(e)
                if(e)
                        ethrow(e);
                        ethrow(e);
                else
                else
                        return -1;
                        return -1;
        }
        }
        s->hidden = hidden;
        s->hidden = hidden;
 
        s->used   = used;
        t->length++;
        t->length++;
        errno = 0;
        errno = 0;
        xs = realloc(t->symbols, sizeof(*t->symbols) * t->length);
        xs = realloc(t->symbols, sizeof(*t->symbols) * t->length);
        if(!xs)
        if(!xs)
                fatal("reallocate of size %zu failed: %s", t->length, reason());
                fatal("reallocate of size %u failed: %s", (unsigned)t->length, reason());
        t->symbols = xs;
        t->symbols = xs;
        t->symbols[t->length - 1] = s;
        t->symbols[t->length - 1] = s;
        return 0;
        return 0;
}
}
 
 
static int symbol_table_print(symbol_table_t *t, FILE *output)
static int symbol_table_print(symbol_table_t *t, FILE *output) {
{
 
 
 
        assert(t);
        assert(t);
 
        assert(output);
        for(size_t i = 0; i < t->length; i++) {
        for(size_t i = 0; i < t->length; i++) {
                symbol_t *s = t->symbols[i];
                symbol_t *s = t->symbols[i];
                char *visibility = s->hidden ? "hidden" : "visible";
                char *visibility = s->hidden ? "hidden" : "visible";
                if(fprintf(output, "%s %s %"PRId16" %s\n", symbol_names[s->type], s->id, s->value, visibility) < 0)
                char *used = s->used ? "used" : "unused";
 
                if (fprintf(output, "%s %s %"PRId16" %s %s\n", symbol_names[s->type], s->id, s->value, visibility, used) < 0)
                        return -1;
                        return -1;
        }
        }
        return 0;
        return 0;
}
}
 
 
symbol_table_t *symbol_table_load(FILE *input)
symbol_table_t *symbol_table_load(FILE *input) {
{
 
        symbol_table_t *t = symbol_table_new();
 
        assert(input);
        assert(input);
        char symbol[80];
        symbol_table_t *t = symbol_table_new();
        char id[256];
        char symbol[80] = { 0 };
        char visibility[80];
        char id[256] = { 0 };
        uint16_t value;
        char visibility[80] = { 0 };
 
        uint16_t value = false;
 
 
        while(!feof(input)) {
        while(!feof(input)) {
                int r = 0;
                int r = 0;
                memset(symbol,     0, sizeof(symbol));
                memset(symbol,     0, sizeof(symbol));
                memset(id,         0, sizeof(id));
                memset(id,         0, sizeof(id));
                memset(visibility, 0, sizeof(visibility));
                memset(visibility, 0, sizeof(visibility));
                value = 0;
                value = 0;
                r = fscanf(input, "%79s%255s%"SCNd16"%79s", symbol, id, &value, visibility);
                r = fscanf(input, "%79s%255s%"SCNu16"%79s", symbol, id, &value, visibility);
                if(r != 4 && r > 0) {
                if(r != 4 && r > 0) {
                        error("invalid symbol table: %d", r);
                        error("invalid symbol table: %d", r);
                        goto fail;
                        goto fail;
                }
                }
                if(r == 4) {
                if(r == 4) {
Line 710... Line 648...
                        }
                        }
 
 
                        for(i = 0; symbol_names[i] && strcmp(symbol_names[i], symbol); i++)
                        for(i = 0; symbol_names[i] && strcmp(symbol_names[i], symbol); i++)
                                /*do nothing*/;
                                /*do nothing*/;
                        if(symbol_names[i]) {
                        if(symbol_names[i]) {
                                if(symbol_table_add(t, i, id, value, NULL, hidden) < 0)
                                if (symbol_table_add(t, i, id, value, NULL, hidden, false) < 0)
                                        goto fail;
                                        goto fail;
                        } else {
                        } else {
                                error("invalid symbol: %s", symbol);
                                error("invalid symbol: %s", symbol);
                                goto fail;
                                goto fail;
                        }
                        }
Line 729... Line 667...
}
}
/* ========================== Symbol Table ================================= */
/* ========================== Symbol Table ================================= */
 
 
/* ========================== Disassembler ================================= */
/* ========================== Disassembler ================================= */
 
 
static const char *instruction_to_string(uint16_t i)
static const char *instruction_to_string(const uint16_t i) {
{
 
        switch(i) {
        switch(i) {
#define X(NAME, STRING, DEFINE, INSTRUCTION) case CODE_ ## NAME : return STRING ;
#define X(NAME, STRING, DEFINE, INSTRUCTION) case CODE_ ## NAME : return STRING ;
        X_MACRO_INSTRUCTIONS
        X_MACRO_INSTRUCTIONS
#undef X
#undef X
        default:          break;
        default:          break;
        }
        }
        return NULL;
        return NULL;
}
}
 
 
static const char *alu_op_to_string(uint16_t instruction)
static const char *alu_op_to_string(const uint16_t instruction) {
{
 
        switch(ALU_OP(instruction)) {
        switch(ALU_OP(instruction)) {
        case ALU_OP_T:                  return "T";
        case ALU_OP_T:                  return "T";
        case ALU_OP_N:                  return "N";
        case ALU_OP_N:                  return "N";
        case ALU_OP_T_PLUS_N:           return "T+N";
        case ALU_OP_T_PLUS_N:           return "T+N";
        case ALU_OP_T_AND_N:            return "T&N";
        case ALU_OP_T_AND_N:            return "T&N";
Line 764... Line 700...
        case ALU_OP_ENABLE_INTERRUPTS:  return "ien";
        case ALU_OP_ENABLE_INTERRUPTS:  return "ien";
        case ALU_OP_INTERRUPTS_ENABLED: return "ien?";
        case ALU_OP_INTERRUPTS_ENABLED: return "ien?";
        case ALU_OP_RDEPTH:             return "rdepth";
        case ALU_OP_RDEPTH:             return "rdepth";
        case ALU_OP_T_EQUAL_0:          return "0=";
        case ALU_OP_T_EQUAL_0:          return "0=";
        case ALU_OP_CPU_ID:             return "cpu-id";
        case ALU_OP_CPU_ID:             return "cpu-id";
 
        case ALU_OP_LITERAL:            return "literal";
        default:                        return "unknown";
        default:                        return "unknown";
        }
        }
}
}
 
 
static char *disassembler_alu(uint16_t instruction)
static char *disassembler_alu(const uint16_t instruction) {
{
 
        char buf[256] = {0};
        char buf[256] = {0};
        const char *r = instruction_to_string(OP_ALU_OP | instruction);
        const char *r = instruction_to_string(OP_ALU_OP | instruction);
        if(r)
        if(r)
                return duplicate(r);
                return duplicate(r);
        sprintf(buf, "%04x:%s:%s:%s:%s:%s:%u:%u",
        sprintf(buf, "%04x:%s:%s:%s:%s:%s:%u:%u",
                        (unsigned)instruction,
                        (unsigned)instruction,
                        alu_op_to_string(instruction),
                        alu_op_to_string(instruction),
                        instruction & T_TO_N ? "T->N" : "",
                        (instruction & T_TO_N) ? "T->N" : "",
                        instruction & T_TO_R ? "T->R" : "",
                        (instruction & T_TO_R) ? "T->R" : "",
                        instruction & N_TO_ADDR_T ? "N->[T]" : "",
                        (instruction & N_TO_ADDR_T) ? "N->[T]" : "",
                        instruction & R_TO_PC ? "R->PC" : "",
                        (instruction & R_TO_PC) ? "R->PC" : "",
                        (unsigned)(instruction & 0x000C),
                        (unsigned)(instruction & 0x000C),
                        (unsigned)(instruction & 0x0003));
                        (unsigned)(instruction & 0x0003));
        return duplicate(buf);
        return duplicate(buf);
}
}
 
 
static const char *disassemble_jump(symbol_table_t *symbols, symbol_type_e type, uint16_t address)
static const char *disassemble_jump(const symbol_table_t * const symbols, const symbol_type_e type, const uint16_t address) {
{
 
        static const char *r = "";
 
        symbol_t *found = NULL;
 
        if(!symbols)
        if(!symbols)
                return r;
                return "";
        if((found = symbol_table_reverse_lookup(symbols, type, address)))
        const symbol_t * const found = symbol_table_reverse_lookup(symbols, type, address);
                return found->id;
        return found ? found->id : "";
        return r;
 
}
}
 
 
 
 
#define CSI "\033["
#define CSI "\033["
#define ANSI_RESET   (CSI "0m")
#define ANSI_RESET   (CSI "0m")
#define ANSI_BLACK   (CSI "30m")
#define ANSI_BLACK   (CSI "30m")
#define ANSI_RED     (CSI "31m")
#define ANSI_RED     (CSI "31m")
#define ANSI_GREEN   (CSI "32m")
#define ANSI_GREEN   (CSI "32m")
Line 826... Line 757...
        DC_BRANCH,
        DC_BRANCH,
        DC_ERROR,  /* Invalid instruction */
        DC_ERROR,  /* Invalid instruction */
        DC_RESET,  /* Reset color */
        DC_RESET,  /* Reset color */
} decompilation_color_e;
} decompilation_color_e;
 
 
static int disassemble_instruction(uint16_t instruction, FILE *output, symbol_table_t *symbols, disassemble_color_method_e dcm)
static int disassemble_instruction(const uint16_t instruction, FILE *output, const symbol_table_t * const symbols, const disassemble_color_method_e dcm) {
{
 
        int r = 0;
        int r = 0;
        unsigned short literal, address;
 
        char *s = NULL;
        char *s = NULL;
        assert(output);
        assert(output);
        assert(dcm < DCM_MAX_DCM);
        assert(dcm < DCM_MAX_DCM);
 
 
        static const char *colors[3][7] = { /* for colorizing decompilation stream with in-band signalling */
        static const char *colors[3][7] = { /* for colorizing decompilation stream with in-band signalling */
Line 842... Line 771...
                [DCM_X11]  = { "?HotPink?",  "?SkyBlue?",  "?GreenYellow?",  "?Khaki?",  "?MediumTurquoise?",  "?FireBrick?",  "" },         /* X11/GTKWave */
                [DCM_X11]  = { "?HotPink?",  "?SkyBlue?",  "?GreenYellow?",  "?Khaki?",  "?MediumTurquoise?",  "?FireBrick?",  "" },         /* X11/GTKWave */
                [DCM_ANSI] = { ANSI_MAGENTA, ANSI_BLUE,    ANSI_GREEN, ANSI_YELLOW, ANSI_CYAN, ANSI_RED, ANSI_RESET }, /* ANSI Escape Sequences */
                [DCM_ANSI] = { ANSI_MAGENTA, ANSI_BLUE,    ANSI_GREEN, ANSI_YELLOW, ANSI_CYAN, ANSI_RED, ANSI_RESET }, /* ANSI Escape Sequences */
        };
        };
 
 
        const char **color = colors[dcm];
        const char **color = colors[dcm];
 
        const unsigned short literal = instruction & 0x7FFF;
        literal = instruction & 0x7FFF;
        const unsigned short address = instruction & 0x1FFF;
        address = instruction & 0x1FFF;
 
 
 
        if (IS_LITERAL(instruction))
        if (IS_LITERAL(instruction))
                r = fprintf(output, "%s%hx%s", color[DC_LITERAL], literal, color[DC_RESET]);
                r = fprintf(output, "%s%hx%s", color[DC_LITERAL], literal, color[DC_RESET]);
        else if (IS_ALU_OP(instruction))
        else if (IS_ALU_OP(instruction))
                r = fprintf(output, "%s%s%s", color[DC_ALU], s = disassembler_alu(instruction), color[DC_RESET]);
                r = fprintf(output, "%s%s%s", color[DC_ALU], s = disassembler_alu(instruction), color[DC_RESET]);
Line 862... Line 790...
                r = fprintf(output, "%s?(%hx)%s", color[DC_ERROR], instruction, color[DC_RESET]);
                r = fprintf(output, "%s?(%hx)%s", color[DC_ERROR], instruction, color[DC_RESET]);
        free(s);
        free(s);
        return r < 0 ? -1 : 0;
        return r < 0 ? -1 : 0;
}
}
 
 
int h2_disassemble(disassemble_color_method_e dcm, FILE *input, FILE *output, symbol_table_t *symbols)
int h2_disassemble(const disassemble_color_method_e dcm, FILE *input, FILE *output, const symbol_table_t * const symbols) {
{
 
        assert(input);
        assert(input);
        assert(output);
        assert(output);
        assert(dcm < DCM_MAX_DCM);
        assert(dcm < DCM_MAX_DCM);
        char line[80] = {0};
 
        while(!feof(input)) {
        while(!feof(input)) {
                memset(line, 0, sizeof(line));
                char line[80] = { 0 };
                fscanf(input, "%79s", line);
                if (fscanf(input, "%79s", line) != 1)
 
                        return -1;
                if(line[0]) {
                if(line[0]) {
                        uint16_t instruction;
                        uint16_t instruction = 0;
                        if(string_to_cell(16, &instruction, line)) {
                        if(string_to_cell(16, &instruction, line)) {
                                error("invalid input to disassembler: %s", line);
                                error("invalid input to disassembler: %s", line);
                                return -1;
                                return -1;
                        }
                        }
                        if(disassemble_instruction(instruction, output, symbols, dcm) < 0) {
                        if(disassemble_instruction(instruction, output, symbols, dcm) < 0) {
Line 895... Line 822...
 
 
/* ========================== Disassembler ================================= */
/* ========================== Disassembler ================================= */
 
 
/* ========================== Simulation And Debugger ====================== */
/* ========================== Simulation And Debugger ====================== */
 
 
/* @note At the moment I/O is not timing accurate, the UART behaves as if reads
/* @note At the moment I/O is not cycle accurate, the UART behaves as if reads
 * and writes happened instantly, along with the PS/2 keyboard. Also the UART
 * and writes happen instantly, along with the PS/2 keyboard. Also the UART
 * has a FIFO which is not simulated. It should be easy enough to delay for
 * has a FIFO which is not simulated. It should be easy enough to delay for
 * the roughly the right number of cycles, but not to get exact cycle
 * the roughly the right number of cycles, but not to get exact cycle
 * accurate timing */
 * accurate timing. */
 
 
static char to_char(uint8_t c)
static char to_char(const uint8_t c) {
{
 
        return isprint(c) ? c : '.';
        return isprint(c) ? c : '.';
}
}
 
 
static void memory_print(FILE *out, uint16_t start, uint16_t *p, uint16_t length, bool chars)
static void memory_print(FILE *out, const uint16_t start, const uint16_t * const p, const uint16_t length, const bool chars) {
{
 
        const uint16_t line_length = 16;
        const uint16_t line_length = 16;
        assert(out);
        assert(out);
        assert(p);
        assert(p);
        for(uint16_t i = 0; i < length; i += line_length) {
        for(uint16_t i = 0; i < length; i += line_length) {
                fprintf(out, "%04"PRIx16 ": ", i + start);
                fprintf(out, "%04"PRIx16 ": ", i + start);
Line 925... Line 850...
                putc('\n', out);
                putc('\n', out);
        }
        }
        putc('\n', out);
        putc('\n', out);
}
}
 
 
static bool break_point_find(break_point_t *bp, uint16_t find_me)
static bool break_point_find(const break_point_t * const bp, const uint16_t find_me) {
{
 
        assert(bp);
        assert(bp);
        for(size_t i = 0; i < bp->length; i++)
        for(size_t i = 0; i < bp->length; i++)
                if(bp->points[i] == find_me)
                if(bp->points[i] == find_me)
                        return true;
                        return true;
        return false;
        return false;
}
}
 
 
static void break_point_add(break_point_t *bp, uint16_t point)
static void break_point_add(break_point_t *bp, const uint16_t point) {
{
 
        assert(bp);
        assert(bp);
        size_t a;
 
        uint16_t *r;
 
        if(break_point_find(bp, point))
        if(break_point_find(bp, point))
                return;
                return;
 
        const size_t a = (bp->length + 1) * sizeof(bp->points[0]);
        a = (bp->length + 1) * sizeof(bp->points[0]);
        uint16_t *r = realloc(bp->points, a);
        r = realloc(bp->points, a);
 
        if(!r || a < bp->length)
        if(!r || a < bp->length)
                fatal("realloc of size %zu failed", a);
                fatal("realloc of size %u failed", (unsigned)a);
        r[bp->length] = point;
        r[bp->length] = point;
        bp->length++;
        bp->length++;
        bp->points = r;
        bp->points = r;
}
}
 
 
static int break_point_print(FILE *out, break_point_t *bp)
static int break_point_print(FILE *out, const break_point_t * const bp) {
{
        assert(out);
 
        assert(bp);
        for(size_t i = 0; i < bp->length; i++)
        for(size_t i = 0; i < bp->length; i++)
                if(fprintf(out, "\t0x%04"PRIx16 "\n", bp->points[i]) < 0)
                if(fprintf(out, "\t0x%04"PRIx16 "\n", bp->points[i]) < 0)
                        return -1;
                        return -1;
        return 0;
        return 0;
}
}
 
 
#define LED_7_SEGMENT_DISPLAY_CHARSET_HEX  "0123456789AbCdEF"
#define LED_7_SEGMENT_DISPLAY_CHARSET_HEX  "0123456789AbCdEF"
#define LED_7_SEGMENT_DISPLAY_CHARSET_BCD  "0123456789 .-   "
#define LED_7_SEGMENT_DISPLAY_CHARSET_BCD  "0123456789 .-   "
 
 
static char l7seg(uint8_t c)
static char l7seg(const uint8_t c) {
{
 
        static const char *v = LED_7_SEGMENT_DISPLAY_CHARSET_HEX;
        static const char *v = LED_7_SEGMENT_DISPLAY_CHARSET_HEX;
        return v[c & 0xf];
        return v[c & 0xf];
}
}
 
 
void soc_print(FILE *out, h2_soc_state_t *soc)
void soc_print(FILE *out, const h2_soc_state_t * const soc) {
{
 
        assert(out);
        assert(out);
        assert(soc);
        assert(soc);
        unsigned char led0 = l7seg(soc->led_7_segments >> 12);
        const unsigned char led0 = l7seg(soc->led_7_segments >> 12);
        unsigned char led1 = l7seg(soc->led_7_segments >>  8);
        const unsigned char led1 = l7seg(soc->led_7_segments >>  8);
        unsigned char led2 = l7seg(soc->led_7_segments >>  4);
        const unsigned char led2 = l7seg(soc->led_7_segments >>  4);
        unsigned char led3 = l7seg(soc->led_7_segments);
        const unsigned char led3 = l7seg(soc->led_7_segments);
 
 
        fprintf(out, "LEDS:             %02"PRIx8"\n",  soc->leds);
        fprintf(out, "LEDS:             %02"PRIx8"\n",  soc->leds);
        /*fprintf(out, "VGA Cursor:       %04"PRIx16"\n", soc->vga_cursor);
        /*fprintf(out, "VGA Cursor:       %04"PRIx16"\n", soc->vga_cursor);
        fprintf(out, "VGA Control:      %04"PRIx16"\n", soc->vga_control);*/
        fprintf(out, "VGA Control:      %04"PRIx16"\n", soc->vga_control);*/
        fprintf(out, "Timer Control:    %04"PRIx16"\n", soc->timer_control);
        fprintf(out, "Timer Control:    %04"PRIx16"\n", soc->timer_control);
Line 990... Line 909...
        fprintf(out, "Switches:         %04"PRIx16"\n", soc->switches);
        fprintf(out, "Switches:         %04"PRIx16"\n", soc->switches);
        fprintf(out, "Waiting:          %s\n",          soc->wait ? "true" : "false");
        fprintf(out, "Waiting:          %s\n",          soc->wait ? "true" : "false");
        fprintf(out, "Flash Control:    %04"PRIx16"\n", soc->mem_control);
        fprintf(out, "Flash Control:    %04"PRIx16"\n", soc->mem_control);
        fprintf(out, "Flash Address Lo: %04"PRIx16"\n", soc->mem_addr_low);
        fprintf(out, "Flash Address Lo: %04"PRIx16"\n", soc->mem_addr_low);
        fprintf(out, "Flash Data Out:   %04"PRIx16"\n", soc->mem_dout);
        fprintf(out, "Flash Data Out:   %04"PRIx16"\n", soc->mem_dout);
 
        fprintf(out, "UART TX Baud:     %04"PRIx16"\n", soc->uart_tx_baud);
 
        fprintf(out, "UART RX Baud:     %04"PRIx16"\n", soc->uart_rx_baud);
 
        fprintf(out, "UART Control:     %04"PRIx16"\n", soc->uart_control);
}
}
 
 
static void terminal_default_command_sequence(vt100_t *t)
static void terminal_default_command_sequence(vt100_t * const t) {
{
 
        assert(t);
        assert(t);
        t->n1 = 1;
        t->n1 = 1;
        t->n2 = 1;
        t->n2 = 1;
        t->command_index = 0;
        t->command_index = 0;
}
}
 
 
static void terminal_at_xy(vt100_t *t, unsigned x, unsigned y, bool limit_not_wrap)
static void terminal_at_xy(vt100_t * const t, unsigned x, unsigned y, const bool limit_not_wrap) {
{
 
        assert(t);
        assert(t);
        if(limit_not_wrap) {
        if(limit_not_wrap) {
                x = MAX(x, 0);
                x = MAX(x, 0);
                y = MAX(y, 0);
                y = MAX(y, 0);
                x = MIN(x, t->width - 1);
                x = MIN(x, t->width - 1);
Line 1015... Line 935...
                y %= t->height;
                y %= t->height;
        }
        }
        t->cursor = (y * t->width) + x;
        t->cursor = (y * t->width) + x;
}
}
 
 
static int terminal_x_current(vt100_t *t)
static int terminal_x_current(const vt100_t * const t) {
{
 
        assert(t);
        assert(t);
        return t->cursor % t->width;
        return t->cursor % t->width;
}
}
 
 
static int terminal_y_current(vt100_t *t)
static int terminal_y_current(const vt100_t * const t) {
{
 
        assert(t);
        assert(t);
        return t->cursor / t->width;
        return t->cursor / t->width;
}
}
 
 
static void terminal_at_xy_relative(vt100_t *t, int x, int y, bool limit_not_wrap)
static void terminal_at_xy_relative(vt100_t *t, const int x, const int y, const bool limit_not_wrap) {
{
 
        assert(t);
        assert(t);
        int x_current = terminal_x_current(t);
        const int x_current = terminal_x_current(t);
        int y_current = terminal_y_current(t);
        const int y_current = terminal_y_current(t);
        terminal_at_xy(t, x_current + x, y_current + y, limit_not_wrap);
        terminal_at_xy(t, x_current + x, y_current + y, limit_not_wrap);
}
}
 
 
static void terminal_parse_attribute(vt100_attribute_t *a, unsigned v)
static void terminal_parse_attribute(vt100_attribute_t * const a, const unsigned v) {
{
        assert(a);
        switch(v) {
        switch(v) {
        case 0:
        case 0:
                memset(a, 0, sizeof(*a));
                memset(a, 0, sizeof(*a));
                a->foreground_color = WHITE;
                a->foreground_color = WHITE;
                a->background_color = BLACK;
                a->background_color = BLACK;
                return;
                return;
        case 1: a->bold          = true; return;
        case 1: a->bold          = true; return;
 
        case 22: a->bold          = false; return;
        case 4: a->under_score   = true; return;
        case 4: a->under_score   = true; return;
        case 5: a->blink         = true; return;
        case 5: a->blink         = true; return;
 
        case 25: a->blink         = false; return;
        case 7: a->reverse_video = true; return;
        case 7: a->reverse_video = true; return;
 
        case 39: a->reverse_video = false; return;
        case 8: a->conceal       = true; return;
        case 8: a->conceal       = true; return;
 
        case 28: a->conceal       = false; return;
        default:
        default:
                if(v >= 30 && v <= 37)
                if(v >= 30 && v <= 37)
                        a->foreground_color = v - 30;
                        a->foreground_color = v - 30;
                if(v >= 40 && v <= 47)
                if(v >= 40 && v <= 47)
                        a->background_color = v - 40;
                        a->background_color = v - 40;
Line 1061... Line 982...
static const vt100_attribute_t vt100_default_attribute = {
static const vt100_attribute_t vt100_default_attribute = {
        .foreground_color = WHITE,
        .foreground_color = WHITE,
        .background_color = BLACK,
        .background_color = BLACK,
};
};
 
 
static void terminal_attribute_block_set(vt100_t *t, size_t size, const vt100_attribute_t const *a)
static void terminal_attribute_block_set(vt100_t *t, const size_t size, const vt100_attribute_t * const a) {
{
 
        assert(t);
        assert(t);
        assert(a);
        assert(a);
        for(size_t i = 0; i < size; i++)
        for(size_t i = 0; i < size; i++)
                memcpy(&t->attributes[i], a, sizeof(*a));
                memcpy(&t->attributes[i], a, sizeof(*a));
}
}
 
 
static int terminal_escape_sequences(vt100_t *t, uint8_t c)
static int terminal_escape_sequences(vt100_t * const t, const uint8_t c) {
{
 
        assert(t);
        assert(t);
        assert(t->state != TERMINAL_NORMAL_MODE);
        assert(t->state != TERMINAL_NORMAL_MODE);
        switch(t->state) {
        switch(t->state) {
        case TERMINAL_CSI:
        case TERMINAL_CSI: /* process CSI and some non-CSI Escape Only commands */
                if(c == '[')
                switch (c) {
                        t->state = TERMINAL_COMMAND;
                case '[': t->state = TERMINAL_COMMAND; break;
                else
                case 'c': goto eraser; /*reset display*/ break;
                        goto fail;
                case '7': t->cursor_saved = t->cursor; t->attribute_saved = t->attribute; break;
 
                case '8': t->cursor = t->cursor_saved; t->attribute = t->attribute_saved; break;
 
                default: goto fail;
 
                }
 
 
                break;
                break;
        case TERMINAL_COMMAND:
        case TERMINAL_COMMAND:
                switch(c) {
                switch(c) {
                case 's':
                case 's':
                        t->cursor_saved = t->cursor;
                        t->cursor_saved = t->cursor;
Line 1135... Line 1058...
                                goto success;
                                goto success;
                        goto fail;
                        goto fail;
                case 'n': /* Device Status Report */
                case 'n': /* Device Status Report */
                        /** @note This should transmit to the H2 system the
                        /** @note This should transmit to the H2 system the
                         * following "ESC[n;mR", where n is the row and m is the column,
                         * following "ESC[n;mR", where n is the row and m is the column,
                         * we're not going to do this, although fifo_push() on
                         * we're not going to do this as the hardware does not, although
                         * uart_rx_fifo could be called to do this */
                         * 'fifo_push()' on 'uart_rx_fifo' could be called to do this */
                        if(t->n1 == 6)
                        if(t->n1 == 6)
                                goto success;
                                goto success;
                        goto fail;
                        goto fail;
 
eraser: /* HAHA: This is clearly the best way of doing things. */
 
                        t->command_index = 1;
 
                        t->n1 = 3; /* fall-through */
                case 'J': /* reset */
                case 'J': /* reset */
 
 
                        switch(t->n1) {
                        switch(t->n1) {
                        case 3:
                        case 3: /* fall-through */
                        case 2: t->cursor = 0; /* with cursor */
                        case 2: t->cursor = 0; /* with cursor */ /* fall-through */
                        case 1:
                        case 1:
                                if(t->command_index) {
                                if(t->command_index) {
                                        memset(t->m, ' ', t->size);
                                        memset(t->m, ' ', t->size);
                                        terminal_attribute_block_set(t, t->size, &vt100_default_attribute);
                                        terminal_attribute_block_set(t, t->size, &vt100_default_attribute);
                                        goto success;
                                        goto success;
                                } /* fall through if number not supplied */
                                } /* fall through if number not supplied */ /* fall-through */
                        case 0:
                        case 0:
                                memset(t->m, ' ', t->cursor);
                                memset(t->m, ' ', t->cursor);
                                terminal_attribute_block_set(t, t->cursor, &vt100_default_attribute);
                                terminal_attribute_block_set(t, t->cursor, &vt100_default_attribute);
                                goto success;
                                goto success;
                        }
                        }
Line 1218... Line 1145...
fail:
fail:
        t->state = TERMINAL_NORMAL_MODE;
        t->state = TERMINAL_NORMAL_MODE;
        return -1;
        return -1;
}
}
 
 
void vt100_update(vt100_t *t, uint8_t c)
void vt100_update(vt100_t *t, const uint8_t c) {
{
 
        assert(t);
        assert(t);
        assert(t->size <= VT100_MAX_SIZE);
        assert(t->size <= VT100_MAX_SIZE);
        assert((t->width * t->height) <= VT100_MAX_SIZE);
        assert((t->width * t->height) <= VT100_MAX_SIZE);
 
 
        if(t->state != TERMINAL_NORMAL_MODE) {
        if(t->state != TERMINAL_NORMAL_MODE) {
Line 1254... Line 1180...
                        t->m[t->cursor] = c;
                        t->m[t->cursor] = c;
                        memcpy(&t->attributes[t->cursor], &t->attribute, sizeof(t->attribute));
                        memcpy(&t->attributes[t->cursor], &t->attribute, sizeof(t->attribute));
                        t->cursor++;
                        t->cursor++;
                }
                }
                if(t->cursor >= t->size) {
                if(t->cursor >= t->size) {
                        terminal_attribute_block_set(t, t->size, &vt100_default_attribute);
                        const vt100_attribute_t *a = &vt100_default_attribute;
                        memset(t->m, ' ', t->size);
                        t->cursor -= t->width;
 
                        memmove(t->m, t->m + t->width, t->size - t->width);
 
                        memset((t->m + t->size) - t->width, ' ', t->width);
 
                        for (size_t i = 0; i < (t->size - t->width); i++)
 
                                t->attributes[i] = t->attributes[i + t->width];
 
                        for (size_t i = t->size - t->width; i < t->size; i++)
 
                                memcpy(&t->attributes[i], a, sizeof(*a));
                }
                }
                t->cursor %= t->size;
                t->cursor %= t->size;
        }
        }
}
}
 
 
Line 1360... Line 1292...
0x0001, 0x4000, 0x0000, 0x1000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0x0001, 0x4000, 0x0000, 0x1000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
};
};
 
 
uint16_t PC28F128P33BF60_CFI_Query_Read(uint32_t addr)
uint16_t PC28F128P33BF60_CFI_Query_Read(uint32_t addr) {
{
 
        addr &= 0x3ff;
        addr &= 0x3ff;
        if(addr > 0x1ff) {
        if(addr > 0x1ff) {
                addr &= 0x7;
                addr &= 0x7;
                static const uint16_t r[] = {
                static const uint16_t r[] = {
                        0x0089, 0x881E, 0x0000, 0x0000,
                        0x0089, 0x881E, 0x0000, 0x0000,
Line 1374... Line 1305...
                return r[addr];
                return r[addr];
        }
        }
        return PC28F128P33BF60_CFI_Query_Table[addr];
        return PC28F128P33BF60_CFI_Query_Table[addr];
}
}
 
 
static uint16_t h2_io_flash_read(flash_t *f, uint32_t addr, bool oe, bool we, bool rst)
static uint16_t h2_io_flash_read(const flash_t * const f, const uint32_t addr, const bool oe, const bool we, const bool rst) {
{
 
        if(rst)
        if(rst)
                return 0;
                return 0;
 
 
        if(oe && we) {
        if(oe && we) {
                warning("OE and WE set at the same time");
                warning("OE and WE set at the same time");
Line 1407... Line 1337...
        }
        }
 
 
        return 0;
        return 0;
}
}
 
 
static unsigned addr_to_block(uint32_t addr)
static unsigned addr_to_block(uint32_t addr) {
{
        const uint32_t lower_64k_blocks_highest_address = 127ul * 64ul * 1024ul; /* 0x7F000 */
        uint32_t lower_64k_blocks_highest_address = 127u * 64u * 1024u; /* 0x7F000 */
 
        /*assert(addr < 0x7ffffff);*/
        /*assert(addr < 0x7ffffff);*/
        if(addr < lower_64k_blocks_highest_address)
        if(addr < lower_64k_blocks_highest_address)
                return addr / (64u * 1024u);
                return addr / (64ul * 1024ul);
        addr -= lower_64k_blocks_highest_address;
        addr -= lower_64k_blocks_highest_address;
        addr /= (16u * 1024u);
        addr /= (16ul * 1024ul);
        return addr + 127u;
        return addr + 127ul;
}
}
 
 
static unsigned block_size(unsigned block)
static unsigned block_size(const unsigned block) {
{
        if (block >= 127ul)
        if(block >= 127u)
                return 16ul * 1024ul;
                return 16u * 1024u;
        return 64ul * 1024ul;
        return 64u * 1024u;
 
}
}
 
 
static bool block_locked(flash_t *f, unsigned block)
static bool block_locked(const flash_t * const f, const unsigned block) {
{
 
        assert(f);
        assert(f);
        assert(block < FLASH_BLOCK_MAX);
        assert(block < FLASH_BLOCK_MAX);
 
        /* The locks block would probably be best be represented as a bit
 
         * vector, the functions to manipulate a bit vector can quite easily be
 
         * turned into a useful header only library */
        return !!(f->locks[block]);
        return !!(f->locks[block]);
}
}
 
 
static bool address_protected(flash_t *f, uint32_t addr)
static bool address_protected(const flash_t *const f, const uint32_t addr) {
{
 
        assert(f);
        assert(f);
        return block_locked(f, addr_to_block(addr));
        return block_locked(f, addr_to_block(addr));
}
}
 
 
/**@todo implement the full standard for the Common Flash Memory Interface, and
/* We could implement the full standard for the Common Flash Memory
 * make the timing based on a simulated calculated time instead multiples of
 * Interface, and make the timing based on a simulated calculated time
 * 10us see:
 * instead multiples of 10us see:
 * <https://en.wikipedia.org/wiki/Common_Flash_Memory_Interface> with the
 * <https://en.wikipedia.org/wiki/Common_Flash_Memory_Interface> with the
 * devices PC28F128P33BF60 and NP8P128A13T1760E. The lock status of a register
 * devices PC28F128P33BF60 and NP8P128A13T1760E. The lock status of a register
 * should be read as well as checking f->arg1_address == f->arg2_address for
 * should be read as well as checking f->arg1_address == f->arg2_address for
 * commands which require this.*/
 * commands which require this.
static void h2_io_flash_update(flash_t *f, uint32_t addr, uint16_t data, bool oe, bool we, bool rst, bool cs)
 *
{
 * We *could* do that, however this simulator is 'good enough' for our
 
 * purposes. */
 
static void h2_io_flash_update(flash_t * const f, const uint32_t addr, const uint16_t data, const bool oe, const bool we, const bool rst, const bool cs) {
        assert(f);
        assert(f);
        if(oe && we)
        if(oe && we)
                warning("OE and WE set at the same time");
                warning("OE and WE set at the same time");
 
 
        if(rst) {
        if(rst) {
Line 1570... Line 1501...
                f->cycle = 0;
                f->cycle = 0;
                break;
                break;
        case FLASH_BLOCK_ERASING:
        case FLASH_BLOCK_ERASING:
                f->status &= ~FLASH_STATUS_DEVICE_READY;
                f->status &= ~FLASH_STATUS_DEVICE_READY;
                if(f->cycle++ > FLASH_ERASE_CYCLES) {
                if(f->cycle++ > FLASH_ERASE_CYCLES) {
                        unsigned block = f->arg1_address;
                        const unsigned block = f->arg1_address;
                        unsigned size  = block_size(block);
                        const unsigned size  = block_size(block);
                        if(block >= FLASH_BLOCK_MAX) {
                        if(block >= FLASH_BLOCK_MAX) {
                                warning("block operation out of range: %u", block);
                                warning("block operation out of range: %u", block);
                                f->status |= FLASH_STATUS_ERASE_BLANK;
                                f->status |= FLASH_STATUS_ERASE_BLANK;
                        } else {
                        } else {
                                memset(f->nvram+block*size, 0xff, sizeof(f->nvram[0])*size);
                                memset(f->nvram+block*size, 0xff, sizeof(f->nvram[0])*size);
Line 1599... Line 1530...
                f->data = data;
                f->data = data;
        f->we = we;
        f->we = we;
        f->cs = cs;
        f->cs = cs;
}
}
 
 
uint16_t h2_io_memory_read_operation(h2_soc_state_t *soc)
uint16_t h2_io_memory_read_operation(const h2_soc_state_t * const soc) {
{
 
        assert(soc);
        assert(soc);
        uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;
        const uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;
        bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;
        const bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;
        bool flash_cs  = soc->mem_control & FLASH_CHIP_SELECT;
        const bool flash_cs  = soc->mem_control & FLASH_CHIP_SELECT;
        bool sram_cs   = soc->mem_control & SRAM_CHIP_SELECT;
        const bool sram_cs   = soc->mem_control & SRAM_CHIP_SELECT;
        bool oe        = soc->mem_control & FLASH_MEMORY_OE;
        const bool oe        = soc->mem_control & FLASH_MEMORY_OE;
        bool we        = soc->mem_control & FLASH_MEMORY_WE;
        const bool we        = soc->mem_control & FLASH_MEMORY_WE;
 
 
        if(oe && we)
        if(oe && we)
                return 0;
                return 0;
 
 
        if(flash_cs && sram_cs)
        if(flash_cs && sram_cs)
Line 1623... Line 1553...
        if(sram_cs && oe && !we)
        if(sram_cs && oe && !we)
                return soc->vram[flash_addr >> 1];
                return soc->vram[flash_addr >> 1];
        return 0;
        return 0;
}
}
 
 
static uint16_t h2_io_get_default(h2_soc_state_t *soc, uint16_t addr, bool *debug_on)
static uint16_t h2_io_get_default(h2_soc_state_t * const soc, const uint16_t addr, bool *debug_on) {
{
 
        assert(soc);
        assert(soc);
        debug("IO read addr: %"PRIx16, addr);
        debug("IO read addr: %"PRIx16, addr);
        (void)debug_on;
        (void)debug_on;
        switch(addr) {
        switch(addr) {
        case iUart:         return UART_TX_FIFO_EMPTY | soc->uart_getchar_register;
        case iUart:         return UART_TX_FIFO_EMPTY | soc->uart_getchar_register;
Line 1640... Line 1569...
                warning("invalid read from %04"PRIx16, addr);
                warning("invalid read from %04"PRIx16, addr);
        }
        }
        return 0;
        return 0;
}
}
 
 
static void h2_io_set_default(h2_soc_state_t *soc, uint16_t addr, uint16_t value, bool *debug_on)
static void h2_io_set_default(h2_soc_state_t *soc, const uint16_t addr, const uint16_t value, bool *debug_on) {
{
 
        assert(soc);
        assert(soc);
        debug("IO write addr/value: %"PRIx16"/%"PRIx16, addr, value);
        debug("IO write addr/value: %"PRIx16"/%"PRIx16, addr, value);
 
 
        switch(addr) {
        switch(addr) {
        case oUart:
        case oUart:
Line 1665... Line 1593...
        case o7SegLED:    soc->led_7_segments = value; break;
        case o7SegLED:    soc->led_7_segments = value; break;
        case oIrcMask:    soc->irc_mask       = value; break;
        case oIrcMask:    soc->irc_mask       = value; break;
        case oMemControl:
        case oMemControl:
        {
        {
                soc->mem_control    = value;
                soc->mem_control    = value;
 
                const bool sram_cs = soc->mem_control & SRAM_CHIP_SELECT;
                bool sram_cs   = soc->mem_control & SRAM_CHIP_SELECT;
                const bool oe      = soc->mem_control & FLASH_MEMORY_OE;
                bool oe        = soc->mem_control & FLASH_MEMORY_OE;
                const bool we      = soc->mem_control & FLASH_MEMORY_WE;
                bool we        = soc->mem_control & FLASH_MEMORY_WE;
 
 
 
                if(sram_cs && !oe && we)
                if(sram_cs && !oe && we)
                        soc->vram[(((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low) >> 1] = soc->mem_dout;
                        soc->vram[(((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low) >> 1] = soc->mem_dout;
                break;
                break;
        }
        }
        case oMemAddrLow: soc->mem_addr_low   = value; break;
        case oMemAddrLow: soc->mem_addr_low   = value; break;
        case oMemDout:    soc->mem_dout       = value; break;
        case oMemDout:    soc->mem_dout       = value; break;
 
        case oUartTxBaud:  soc->uart_tx_baud = value; break;
 
        case oUartRxBaud:  soc->uart_rx_baud = value; break;
 
        case oUartControl: soc->uart_control = value; break;
        default:
        default:
                warning("invalid write to %04"PRIx16 ":%04"PRIx16, addr, value);
                warning("invalid write to %04"PRIx16 ":%04"PRIx16, addr, value);
        }
        }
}
}
 
 
static void h2_io_update_default(h2_soc_state_t *soc)
static void h2_io_update_default(h2_soc_state_t * const soc) {
{
 
        assert(soc);
        assert(soc);
 
 
        if(soc->timer_control & TIMER_ENABLE) {
        if(soc->timer_control & TIMER_ENABLE) {
                if(soc->timer_control & TIMER_RESET) {
                if(soc->timer_control & TIMER_RESET) {
                        soc->timer = 0;
                        soc->timer = 0;
Line 1701... Line 1630...
                                soc->timer = 0;
                                soc->timer = 0;
                        }
                        }
                }
                }
        }
        }
 
 
        { /* DPAD interrupt on change state */
        /* DPAD interrupt on change state */
                uint16_t prev = soc->switches_previous;
        const uint16_t prev = soc->switches_previous;
                uint16_t cur  = soc->switches;
        const uint16_t cur  = soc->switches;
                if((prev & 0xff00) != (cur & 0xff00)) {
                if((prev & 0xff00) != (cur & 0xff00)) {
                        soc->interrupt           = soc->irc_mask & (1 << isrDPadButton);
                soc->interrupt           = soc->irc_mask & (1u << isrDPadButton);
                        soc->interrupt_selector |= soc->irc_mask & (1 << isrDPadButton);
                soc->interrupt_selector |= soc->irc_mask & (1u << isrDPadButton);
                }
                }
                soc->switches_previous = soc->switches;
                soc->switches_previous = soc->switches;
        }
 
 
 
        {
        const uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;
                uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;
        const bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;
                bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;
        const bool flash_cs  = soc->mem_control & FLASH_CHIP_SELECT;
                bool flash_cs  = soc->mem_control & FLASH_CHIP_SELECT;
        const bool oe        = soc->mem_control & FLASH_MEMORY_OE;
                bool oe        = soc->mem_control & FLASH_MEMORY_OE;
        const bool we        = soc->mem_control & FLASH_MEMORY_WE;
                bool we        = soc->mem_control & FLASH_MEMORY_WE;
 
                h2_io_flash_update(&soc->flash, flash_addr >> 1, soc->mem_dout, oe, we, flash_rst, flash_cs);
                h2_io_flash_update(&soc->flash, flash_addr >> 1, soc->mem_dout, oe, we, flash_rst, flash_cs);
        }
        }
}
 
 
 
h2_soc_state_t *h2_soc_state_new(void)
h2_soc_state_t *h2_soc_state_new(void) {
{
 
        h2_soc_state_t *r = allocate_or_die(sizeof(h2_soc_state_t));
        h2_soc_state_t *r = allocate_or_die(sizeof(h2_soc_state_t));
        vt100_t *v = &r->vt100;
        vt100_t *v = &r->vt100;
        memset(r->flash.nvram, 0xff, sizeof(r->flash.nvram[0])*FLASH_BLOCK_MAX);
        memset(r->flash.nvram, 0xff, sizeof(r->flash.nvram[0])*FLASH_BLOCK_MAX);
        memset(r->flash.locks, FLASH_LOCKED, FLASH_BLOCK_MAX);
        memset(r->flash.locks, FLASH_LOCKED, FLASH_BLOCK_MAX);
 
 
Line 1743... Line 1668...
        for(size_t i = 0; i < v->size; i++)
        for(size_t i = 0; i < v->size; i++)
                v->attributes[i] = v->attribute;
                v->attributes[i] = v->attribute;
        return r;
        return r;
}
}
 
 
void h2_soc_state_free(h2_soc_state_t *soc)
void h2_soc_state_free(h2_soc_state_t *soc) {
{
 
        if(!soc)
        if(!soc)
                return;
                return;
        memset(soc, 0, sizeof(*soc));
        memset(soc, 0, sizeof(*soc));
        free(soc);
        free(soc);
}
}
 
 
h2_io_t *h2_io_new(void)
h2_io_t *h2_io_new(void) {
{
 
        h2_io_t *io =  allocate_or_die(sizeof(*io));
        h2_io_t *io =  allocate_or_die(sizeof(*io));
        io->in      = h2_io_get_default;
        io->in      = h2_io_get_default;
        io->out     = h2_io_set_default;
        io->out     = h2_io_set_default;
        io->update  = h2_io_update_default;
        io->update  = h2_io_update_default;
        io->soc     = h2_soc_state_new();
        io->soc     = h2_soc_state_new();
        return io;
        return io;
}
}
 
 
void h2_io_free(h2_io_t *io)
void h2_io_free(h2_io_t *io) {
{
 
        if(!io)
        if(!io)
                return;
                return;
        h2_soc_state_free(io->soc);
        h2_soc_state_free(io->soc);
        memset(io, 0, sizeof(*io));
        memset(io, 0, sizeof(*io));
        free(io);
        free(io);
}
}
 
 
static void dpush(h2_t *h, uint16_t v)
static void dpush(h2_t * const h, const uint16_t v) {
{
 
        assert(h);
        assert(h);
        h->sp++;
        h->sp++;
        h->dstk[h->sp % STK_SIZE] = h->tos;
        h->dstk[h->sp % STK_SIZE] = h->tos;
        h->tos = v;
        h->tos = v;
        if(h->sp >= STK_SIZE)
        if(h->sp >= STK_SIZE)
                warning("data stack overflow");
                warning("data stack overflow");
        h->sp %= STK_SIZE;
        h->sp %= STK_SIZE;
}
}
 
 
static uint16_t dpop(h2_t *h)
static uint16_t dpop(h2_t * const h) {
{
 
        uint16_t r;
 
        assert(h);
        assert(h);
        r = h->tos;
        const uint16_t r = h->tos;
        h->tos = h->dstk[h->sp % STK_SIZE];
        h->tos = h->dstk[h->sp % STK_SIZE];
        h->sp--;
        h->sp--;
        if(h->sp >= STK_SIZE)
        if(h->sp >= STK_SIZE)
                warning("data stack underflow");
                warning("data stack underflow");
        h->sp %= STK_SIZE;
        h->sp %= STK_SIZE;
        return r;
        return r;
}
}
 
 
static void rpush(h2_t *h, uint16_t r)
static void rpush(h2_t *h, const uint16_t r) {
{
 
        assert(h);
        assert(h);
        h->rp++;
        h->rp++;
        h->rstk[(h->rp) % STK_SIZE] = r;
        h->rstk[(h->rp) % STK_SIZE] = r;
        if(h->rp >= STK_SIZE)
        if(h->rp >= STK_SIZE)
                warning("return stack overflow");
                warning("return stack overflow");
        h->rp %= STK_SIZE;
        h->rp %= STK_SIZE;
}
}
 
 
static uint16_t stack_delta(uint16_t d)
static uint16_t stack_delta(const uint16_t d) {
{
 
        static const uint16_t i[4] = { 0x0000, 0x0001, 0xFFFE, 0xFFFF };
        static const uint16_t i[4] = { 0x0000, 0x0001, 0xFFFE, 0xFFFF };
        assert((d & 0xFFFC) == 0);
        assert((d & 0xFFFC) == 0);
        return i[d];
        return i[d];
}
}
 
 
static int trace(FILE *output, uint16_t instruction, symbol_table_t *symbols, const char *fmt, ...)
static inline void reverse(char * const r, const size_t length) {
{
        const size_t last = length - 1;
        int r = 0;
        for (size_t i = 0; i < length/2ul; i++) {
        va_list ap;
                const size_t t = r[i];
        assert(output);
                r[i] = r[last - i];
        if(!output)
                r[last - i] = t;
                return r;
        }
        assert(fmt);
}
        va_start(ap, fmt);
 
        r = vfprintf(output, fmt, ap);
static inline void unsigned_to_csv(char b[64], unsigned u, const char delimiter) {
        va_end(ap);
        unsigned i = 0;
        if(r < 0)
        do {
                return r;
                const unsigned base = 10; /* bases 2-10 allowed */
        if(fputc('\t', output) != '\t')
                const unsigned q = u % base;
                return -1;
                const unsigned r = u / base;
        r = disassemble_instruction(instruction, output, symbols, DCM_NONE);
                b[i++] = q + '0';
        if(r < 0)
                u = r;
                return r;
        } while (u);
        if(fputc('\n', output) != '\n')
        b[i] = delimiter;
 
        b[i+1] = '\0';
 
        reverse(b, i);
 
}
 
 
 
static inline void csv_value(FILE *o, const unsigned u) {
 
        char b[64] = { 0 };
 
        unsigned_to_csv(b, u, ',');
 
        fputs(b, o);
 
}
 
 
 
/* This is a fairly fast trace/CSV generation routine which avoids the use of fprintf,
 
 * speeding up this routine would greatly improve the speed of the interpreter when
 
 * tracing is on. The symbol table lookup can be disabled by passing in NULL, this
 
 * also greatly speeds things up. It can be used in a roundabout way to generate
 
 * a file viewable by GTKWave, its output can be fed into 'csv2vcd' after some
 
 * minimal processing with AWK, turning it into a VCD file, which is viewable in
 
 * the wave form viewer, see:
 
 * <http://www.ic.unicamp.br/~ducatte/mc542/Docs/gtkwave.pdf> and
 
 * <https://github.com/carlos-jenkins/csv2vcd> for more details.  */
 
static int h2_log_csv(FILE *o, const h2_t * const h, const symbol_table_t * const symbols, const bool header) {
 
        if (!o)
 
                return 0;
 
        assert(h);
 
        if (header) {
 
                fputs("\"pc[15:0]\",", o);
 
                fputs("\"tos[15:0]\",", o);
 
                fputs("\"rp[7:0]\",", o);
 
                fputs("\"sp[7:0]\",", o);
 
                fputs("\"ie\",", o);
 
                fputs("\"instruction[15:0]\",", o);
 
                if (symbols)
 
                        fputs("\"disassembled\",", o);
 
                fputs("\"Time\"", o);
 
                if (fputc('\n', o) != '\n')
                return -1;
                return -1;
        if(fflush(output) == EOF)
                return 0;
 
        }
 
 
 
        csv_value(o, h->pc);
 
        csv_value(o, h->tos);
 
        csv_value(o, h->rp);
 
        csv_value(o, h->sp);
 
        csv_value(o, h->ie);
 
        csv_value(o, h->core[h->pc]);
 
        if (symbols) {
 
                fputc('"', o);
 
                disassemble_instruction(h->core[h->pc], o, symbols, DCM_NONE);
 
                fputs("\",", o);
 
        }
 
        csv_value(o, h->time*10);
 
 
 
        if (fputc('\n', o) != '\n')
                return -1;
                return -1;
        return r;
        return 0;
}
}
 
 
typedef struct {
typedef struct {
        FILE *input;
        FILE *input;
        FILE *output;
        FILE *output;
Line 1845... Line 1812...
        bool trace_on;
        bool trace_on;
} debug_state_t;
} debug_state_t;
 
 
static const char *debug_prompt = "debug> ";
static const char *debug_prompt = "debug> ";
 
 
static int number(char *s, uint16_t *o, size_t length);
static uint16_t map_char_to_number(int c) {
 
        if (c >= '0' && c <= '9')
 
                return c - '0';
 
        c = tolower(c);
 
        if (c >= 'a' && c <= 'z')
 
                return c + 10 - 'a';
 
        fatal("invalid numeric character: %c", c);
 
        return 0;
 
}
 
 
static void h2_print(FILE *out, h2_t *h)
static bool numeric(const int c, const int base) {
{
        assert(base == 10 || base == 16);
 
        if (base == 10)
 
                return isdigit(c);
 
        return isxdigit(c);
 
}
 
 
 
static int number(const char * const s, uint16_t * const o, const size_t length) {
 
        size_t i = 0, start = 0;
 
        uint32_t out = 0;
 
        int base = 10;
 
        bool negate = false;
 
        assert(o);
 
        if (s[i] == '\0')
 
                return 0;
 
 
 
        if (s[i] == '-') {
 
                if (s[i+1] == '\0')
 
                        return 0;
 
                negate = true;
 
                start = ++i;
 
        }
 
 
 
        if (s[i] == '$') {
 
                base = 16;
 
                if (s[i+1] == '\0')
 
                        return 0;
 
                start = i + 1;
 
        }
 
 
 
        for (i = start; i < length; i++)
 
                if (!numeric(s[i], base))
 
                        return 0;
 
 
 
        for (i = start; i < length; i++)
 
                out = out * base + map_char_to_number(s[i]);
 
 
 
        *o = negate ? out * (uint16_t)-1 : out;
 
        return 1;
 
}
 
static void h2_print(FILE *out, const h2_t *const h) {
 
        assert(h);
        fputs("Return Stack:\n", out);
        fputs("Return Stack:\n", out);
        memory_print(out, 0, h->rstk, STK_SIZE, false);
        memory_print(out, 0, h->rstk, STK_SIZE, false);
        fputs("Variable Stack:\n", out);
        fputs("Variable Stack:\n", out);
        fprintf(out, "tos:  %04"PRIx16"\n", h->tos);
        fprintf(out, "tos:  %04"PRIx16"\n", h->tos);
        memory_print(out, 1, h->dstk, STK_SIZE, false);
        memory_print(out, 1, h->dstk, STK_SIZE, false);
Line 1868... Line 1883...
        DBG_CMD_NUMBER,
        DBG_CMD_NUMBER,
        DBG_CMD_STRING,
        DBG_CMD_STRING,
        DBG_CMD_EITHER,
        DBG_CMD_EITHER,
} debug_command_type_e;
} debug_command_type_e;
 
 
typedef struct
typedef struct {
{
 
        int cmd;
        int cmd;
        int argc;
        int argc;
        debug_command_type_e arg1;
        debug_command_type_e arg1;
        debug_command_type_e arg2;
        debug_command_type_e arg2;
        char *description;
        char *description;
} debug_command_t;
} debug_command_t;
 
 
static const debug_command_t debug_commands[] = {
static const debug_command_t debug_commands[] = {
        { .cmd = 'a', .argc = 1, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NO_ARG, .description = "assemble               " },
 
        { .cmd = 'b', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "set break point        " },
        { .cmd = 'b', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "set break point        " },
        { .cmd = 'c', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "continue               " },
        { .cmd = 'c', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "continue               " },
        { .cmd = 'd', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "dump                   " },
        { .cmd = 'd', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "dump                   " },
        { .cmd = 'f', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "save to file           " },
        { .cmd = 'f', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "save to file           " },
        { .cmd = 'g', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "goto address           " },
        { .cmd = 'g', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "goto address           " },
Line 1905... Line 1918...
        { .cmd = '!', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "set value              " },
        { .cmd = '!', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "set value              " },
        { .cmd = '.', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "print H2 CPU state     " },
        { .cmd = '.', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "print H2 CPU state     " },
        { .cmd = -1,  .argc = 0, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = NULL },
        { .cmd = -1,  .argc = 0, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = NULL },
};
};
 
 
static void debug_command_print_help(FILE *out, const debug_command_t *dc)
static void debug_command_print_help(FILE *out, const debug_command_t *dc) {
{
 
        assert(out);
        assert(out);
        assert(dc);
        assert(dc);
 
 
        static const char *debug_help = "\
        static const char *debug_help = "\
Debugger Help: \n\n\
Debugger Help: \n\n\
Line 1927... Line 1939...
        fputs(debug_help, out);
        fputs(debug_help, out);
        for(unsigned i = 0; dc[i].cmd != -1; i++)
        for(unsigned i = 0; dc[i].cmd != -1; i++)
                fprintf(out, " %c %s\t%d\t%s %s\n", dc[i].cmd, dc[i].description, dc[i].argc, arg_type[dc[i].arg1], arg_type[dc[i].arg2]);
                fprintf(out, " %c %s\t%d\t%s %s\n", dc[i].cmd, dc[i].description, dc[i].argc, arg_type[dc[i].arg1], arg_type[dc[i].arg2]);
}
}
 
 
static int debug_command_check(FILE *out, const debug_command_t *dc, int cmd, int argc, bool is_numeric1, bool is_numeric2)
static int debug_command_check(FILE *out, const debug_command_t * const dc, const int cmd, const int argc, const bool is_numeric1, const bool is_numeric2) {
{
 
        assert(out);
        assert(out);
        assert(dc);
        assert(dc);
        for(unsigned i = 0; dc[i].cmd != -1 ; i++) {
        for(unsigned i = 0; dc[i].cmd != -1 ; i++) {
                if(dc[i].cmd == cmd) {
                if(dc[i].cmd == cmd) {
                        if(dc[i].argc != argc) {
                        if(dc[i].argc != argc) {
Line 1961... Line 1972...
        }
        }
        fprintf(out, "unrecognized command '%c'\n", cmd);
        fprintf(out, "unrecognized command '%c'\n", cmd);
        return -1;
        return -1;
}
}
 
 
static int debug_resolve_symbol(FILE *out, char *symbol, symbol_table_t *symbols, uint16_t *value)
static int debug_resolve_symbol(FILE *out, const char *symbol, const symbol_table_t * const symbols, uint16_t * const value) {
{
 
        assert(out);
        assert(out);
        assert(symbol);
        assert(symbol);
        assert(symbols);
        assert(symbols);
        assert(value);
        assert(value);
        symbol_t *sym;
 
        *value = 0;
        *value = 0;
        if(!(sym = symbol_table_lookup(symbols, symbol))) {
        const symbol_t * const sym = symbol_table_lookup(symbols, symbol);
 
        if (!sym) {
                fprintf(out, "symbol '%s' not found\n", symbol);
                fprintf(out, "symbol '%s' not found\n", symbol);
                return -1;
                return -1;
        }
        }
        if(sym->type != SYMBOL_TYPE_LABEL && sym->type != SYMBOL_TYPE_CALL) {
        if(sym->type != SYMBOL_TYPE_LABEL && sym->type != SYMBOL_TYPE_CALL) {
                fprintf(out, "symbol is not call or label\n");
                fprintf(out, "symbol is not call or label\n");
Line 1981... Line 1991...
        }
        }
        *value = sym->value;
        *value = sym->value;
        return 0;
        return 0;
}
}
 
 
static int h2_debugger(debug_state_t *ds, h2_t *h, h2_io_t *io, symbol_table_t *symbols, uint16_t point)
static int h2_debugger(debug_state_t *ds, h2_t *h, h2_io_t *io, symbol_table_t *symbols, const uint16_t point) {
{
 
        bool breaks = false;
 
        assert(h);
        assert(h);
        assert(ds);
        assert(ds);
 
 
        breaks = break_point_find(&h->bp, point);
        const bool breaks = break_point_find(&h->bp, point);
        if(breaks)
        if(breaks)
                fprintf(ds->output, "\n === BREAK(0x%04"PRIx16") ===\n", h->pc);
                fprintf(ds->output, "\n === BREAK(0x%04"PRIx16") ===\n", h->pc);
 
 
        if(ds->step || breaks) {
        if(ds->step || breaks) {
                char line[256];
                char line[256];
Line 2012... Line 2020...
                if(!fgets(line, sizeof(line), ds->input)) {
                if(!fgets(line, sizeof(line), ds->input)) {
                        fputs("End Of Input - exiting\n", ds->output);
                        fputs("End Of Input - exiting\n", ds->output);
                        return -1;
                        return -1;
                }
                }
 
 
                argc = sscanf(line, "%256s %256s %256s", op, arg1, arg2);
                argc = sscanf(line, "%255s %255s %255s", op, arg1, arg2);
                if(argc < 1)
                if(argc < 1)
                        goto again;
                        goto again;
 
 
                is_numeric1 = number(arg1, &num1, strlen(arg1));
                is_numeric1 = number(arg1, &num1, strlen(arg1));
                is_numeric2 = number(arg2, &num2, strlen(arg2));
                is_numeric2 = number(arg2, &num2, strlen(arg2));
Line 2033... Line 2041...
                case ' ':
                case ' ':
                case '\t':
                case '\t':
                case '\r':
                case '\r':
                case '\n':
                case '\n':
                        break;
                        break;
                case 'a':
 
                        fprintf(ds->output, "command '%c' not implemented yet!\n", op[0]);
 
                        break;
 
                case 'f':
                case 'f':
                {
                {
                        FILE *o = fopen(arg1, "wb");
                        FILE *o = fopen(arg1, "wb");
                        if(!o) {
                        if(!o) {
                                fprintf(ds->output, "could not open file '%s 'for writing: %s", arg1, strerror(errno));
                                fprintf(ds->output, "could not open file '%s 'for writing: %s", arg1, strerror(errno));
Line 2187... Line 2192...
                goto again;
                goto again;
        }
        }
        return 0;
        return 0;
}
}
 
 
static uint16_t interrupt_decode(uint8_t *vector)
static uint16_t interrupt_decode(uint8_t *vector) {
{
 
        for(unsigned i = 0; i < NUMBER_OF_INTERRUPTS; i++)
        for(unsigned i = 0; i < NUMBER_OF_INTERRUPTS; i++)
                if(*vector & (1 << i)) {
                if (*vector & (1u << i)) {
                        *vector ^= 1 << i;
                        *vector ^= 1u << i;
                        return i;
                        return i;
                }
                }
        return 0;
        return 0;
}
}
 
 
int h2_run(h2_t *h, h2_io_t *io, FILE *output, unsigned steps, symbol_table_t *symbols, bool run_debugger)
int h2_run(h2_t *h, h2_io_t *io, FILE *output, const unsigned steps, symbol_table_t *symbols, bool run_debugger, FILE *trace) {
{
 
        bool turn_debug_on = false;
        bool turn_debug_on = false;
        assert(h);
        assert(h);
        debug_state_t ds = { .input = stdin, .output = stderr, .step = run_debugger, .trace_on = false /*run_debugger*/ };
        debug_state_t ds = { .input = stdin, .output = stderr, .step = run_debugger, .trace_on = false /*run_debugger*/ };
 
 
 
        if (trace)
 
                h2_log_csv(trace, h, NULL, true);
 
 
        if(run_debugger)
        if(run_debugger)
                fputs("Debugger running, type 'h' for a list of command\n", ds.output);
                fputs("Debugger running, type 'h' for a list of command\n", ds.output);
 
 
        for(unsigned i = 0; i < steps || steps == 0 || run_debugger; i++) {
        for(unsigned i = 0; i < steps || steps == 0 || run_debugger; i++) {
                uint16_t instruction,
                uint16_t instruction,
                         literal,
                         literal,
                         address,
                         address,
                         pc_plus_one;
                         pc_plus_one;
 
                if (log_level >= LOG_DEBUG || ds.trace_on)
 
                       h2_log_csv(output, h, symbols, false);
 
                if (trace)
 
                       h2_log_csv(trace, h, NULL, false);
 
 
                if(run_debugger)
                if(run_debugger)
                        if(h2_debugger(&ds, h, io, symbols, h->pc))
                        if(h2_debugger(&ds, h, io, symbols, h->pc))
                                return 0;
                                return 0;
 
 
                if(io)
                h->time++;
                        io->update(io->soc);
 
 
 
                if(io && io->soc->wait) /* wait only applies to the H2 core not the rest of the SoC */
                if (io) {
                        continue;
                        io->update(io->soc);
 
                        if (io->soc->wait)
 
                                continue; /* wait only applies to the H2 core not the rest of the SoC */
 
                }
 
 
                if(h->pc >= MAX_CORE) {
                if(h->pc >= MAX_CORE) {
                        error("invalid program counter: %04x > %04x", (unsigned)h->pc, MAX_CORE);
                        error("invalid program counter: %04x > %04x", (unsigned)h->pc, MAX_CORE);
                        return -1;
                        return -1;
                }
                }
Line 2240... Line 2252...
                        continue;
                        continue;
                }
                }
 
 
                pc_plus_one = (h->pc + 1) % MAX_CORE;
                pc_plus_one = (h->pc + 1) % MAX_CORE;
 
 
                if(log_level >= LOG_DEBUG || ds.trace_on)
                /* NB. This is not quite what the hardware is doing, but it should be equivalent */
                        trace(output, instruction, symbols,
 
                                "%04u: pc(%04x) inst(%04x) sp(%x) rp(%x) tos(%04x) r(%04x)",
 
                                i,
 
                                (unsigned)h->pc,
 
                                (unsigned)instruction,
 
                                (unsigned)h->sp,
 
                                (unsigned)h->rp,
 
                                (unsigned)h->tos,
 
                                (unsigned)h->rstk[h->rp % STK_SIZE]);
 
 
 
                /* decode / execute */
                /* decode / execute */
                if(IS_LITERAL(instruction)) {
                if (IS_LITERAL(instruction)) { /* The hardware actually uses ALU_OP_LITERAL */
                        dpush(h, literal);
                        dpush(h, literal);
                        h->pc = pc_plus_one;
                        h->pc = pc_plus_one;
                } else if (IS_ALU_OP(instruction)) {
                } else if (IS_ALU_OP(instruction)) {
                        uint16_t rd  = stack_delta(RSTACK(instruction));
                        const uint16_t rd  = stack_delta(RSTACK(instruction));
                        uint16_t dd  = stack_delta(DSTACK(instruction));
                        const uint16_t dd  = stack_delta(DSTACK(instruction));
                        uint16_t nos = h->dstk[h->sp % STK_SIZE];
                        const uint16_t nos = h->dstk[h->sp % STK_SIZE];
                        uint16_t tos = h->tos;
 
                        uint16_t npc = pc_plus_one;
                        uint16_t npc = pc_plus_one;
 
                        uint16_t tos = h->tos;
 
 
                        if(instruction & R_TO_PC)
                        if(instruction & R_TO_PC)
                                npc = h->rstk[h->rp % STK_SIZE] >> 1;
                                npc = h->rstk[h->rp % STK_SIZE] >> 1;
 
 
                        switch(ALU_OP(instruction)) {
                        switch(ALU_OP(instruction)) {
Line 2299... Line 2301...
                                }
                                }
                                break;
                                break;
                        case ALU_OP_N_LSHIFT_T: tos = nos << tos;           break;
                        case ALU_OP_N_LSHIFT_T: tos = nos << tos;           break;
                        case ALU_OP_DEPTH:      tos = h->sp;                break;
                        case ALU_OP_DEPTH:      tos = h->sp;                break;
                        case ALU_OP_N_ULESS_T:  tos = -(nos < tos);         break;
                        case ALU_OP_N_ULESS_T:  tos = -(nos < tos);         break;
                        case ALU_OP_ENABLE_INTERRUPTS: h->ie = tos & 1; /*tos = nos;*/ break;
                        case ALU_OP_ENABLE_INTERRUPTS: h->ie = tos & 1; tos = nos; break;
                        case ALU_OP_INTERRUPTS_ENABLED: tos = -h->ie;       break;
                        case ALU_OP_INTERRUPTS_ENABLED: tos = ((1 & h->ie) << 0); break;
                        case ALU_OP_RDEPTH:     tos = h->rp;                break;
                        case ALU_OP_RDEPTH:     tos = h->rp;                break;
                        case ALU_OP_T_EQUAL_0:  tos = -(tos == 0);          break;
                        case ALU_OP_T_EQUAL_0:  tos = -(tos == 0);          break;
                        case ALU_OP_CPU_ID:     tos = H2_CPU_ID_SIMULATION; break;
                        case ALU_OP_CPU_ID:     tos = H2_CPU_ID_SIMULATION; break;
 
                        case ALU_OP_LITERAL:    tos = instruction & 0x7fffu; break; // This makes more sense in the hardware
                        default:
                        default:
                                warning("unknown ALU operation: %u", (unsigned)ALU_OP(instruction));
                                warning("unknown ALU operation: %u", (unsigned)ALU_OP(instruction));
                        }
                        }
 
 
                        h->sp += dd;
                        h->sp += dd;
Line 2367... Line 2370...
        return 0;
        return 0;
}
}
 
 
/* ========================== Simulation And Debugger ====================== */
/* ========================== Simulation And Debugger ====================== */
 
 
/* ========================== Assembler ==================================== */
/* ========================== Main ========================================= */
/* This section is the most complex, it implements a lexer, parser and code
 
 * compiler for a simple pseudo Forth like language, whilst it looks like
 
 * Forth it is not Forth. */
 
 
 
#define MAX_ID_LENGTH (256u)
 
 
 
/**@warning The ordering of the following enumerations matters a lot */
#ifndef NO_MAIN
typedef enum {
typedef enum {
        LEX_LITERAL,
        DEFAULT_COMMAND,
        LEX_IDENTIFIER,
        DISASSEMBLE_COMMAND,
        LEX_LABEL,
        RUN_COMMAND,
        LEX_STRING,
} command_e;
 
 
        LEX_CONSTANT, /* start of named tokens */
 
        LEX_CALL,
 
        LEX_BRANCH,
 
        LEX_0BRANCH,
 
        LEX_BEGIN,
 
        LEX_WHILE,
 
        LEX_REPEAT,
 
        LEX_AGAIN,
 
        LEX_UNTIL,
 
        LEX_FOR,
 
        LEX_AFT,
 
        LEX_NEXT,
 
        LEX_IF,
 
        LEX_ELSE,
 
        LEX_THEN,
 
        LEX_DEFINE,
 
        LEX_ENDDEFINE,
 
        LEX_CHAR,
 
        LEX_VARIABLE,
 
        LEX_LOCATION,
 
        LEX_IMMEDIATE,
 
        LEX_HIDDEN,
 
        LEX_INLINE,
 
        LEX_QUOTE,
 
 
 
        LEX_PWD,
 
        LEX_SET,
 
        LEX_PC,
 
        LEX_BREAK,
 
        LEX_MODE,
 
        LEX_ALLOCATE,
 
        LEX_BUILT_IN,
 
 
 
        /* start of instructions */
 
#define X(NAME, STRING, DEFINE, INSTRUCTION) LEX_ ## NAME,
 
        X_MACRO_INSTRUCTIONS
 
#undef X
 
        /* end of named tokens and instructions */
 
 
 
        LEX_ERROR, /* error token: this needs to be after the named tokens */
 
 
 
        LEX_EOI = EOF
 
} token_e;
 
 
 
static const char *keywords[] =
 
{
 
        [LEX_LITERAL]    = "literal",
 
        [LEX_IDENTIFIER] = "identifier",
 
        [LEX_LABEL]      = "label",
 
        [LEX_STRING]     = "string",
 
        [LEX_CONSTANT]   =  "constant",
 
        [LEX_CALL]       =  "call",
 
        [LEX_BRANCH]     =  "branch",
 
        [LEX_0BRANCH]    =  "0branch",
 
        [LEX_BEGIN]      =  "begin",
 
        [LEX_WHILE]      =  "while",
 
        [LEX_REPEAT]     =  "repeat",
 
        [LEX_AGAIN]      =  "again",
 
        [LEX_UNTIL]      =  "until",
 
        [LEX_FOR]        =  "for",
 
        [LEX_AFT]        =  "aft",
 
        [LEX_NEXT]       =  "next",
 
        [LEX_IF]         =  "if",
 
        [LEX_ELSE]       =  "else",
 
        [LEX_THEN]       =  "then",
 
        [LEX_DEFINE]     =  ":",
 
        [LEX_ENDDEFINE]  =  ";",
 
        [LEX_CHAR]       =  "[char]",
 
        [LEX_VARIABLE]   =  "variable",
 
        [LEX_LOCATION]   =  "location",
 
        [LEX_IMMEDIATE]  =  "immediate",
 
        [LEX_HIDDEN]     =  "hidden",
 
        [LEX_INLINE]     =  "inline",
 
        [LEX_QUOTE]      =  "'",
 
        [LEX_PWD]        =  ".pwd",
 
        [LEX_SET]        =  ".set",
 
        [LEX_PC]         =  ".pc",
 
        [LEX_BREAK]      =  ".break",
 
        [LEX_MODE]       =  ".mode",
 
        [LEX_ALLOCATE]   =  ".allocate",
 
        [LEX_BUILT_IN]   =  ".built-in",
 
 
 
        /* start of instructions */
 
#define X(NAME, STRING, DEFINE, INSTRUCTION) [ LEX_ ## NAME ] = STRING,
 
        X_MACRO_INSTRUCTIONS
 
#undef X
 
        /* end of named tokens and instructions */
 
 
 
        [LEX_ERROR]      =  NULL,
 
        NULL
 
};
 
 
 
typedef struct {
 
        union {
 
                char *id;
 
                uint16_t number;
 
        } p;
 
        unsigned location;
 
        unsigned line;
 
        token_e type;
 
} token_t;
 
 
 
typedef struct {
typedef struct {
        error_t error;
        command_e cmd;
        FILE *input;
        long steps;
        unsigned line;
        bool full_disassembly;
        int c;
        bool debug_mode;
        char id[MAX_ID_LENGTH];
        bool hacks;
        token_t *token;
        disassemble_color_method_e dcm;
        token_t *accepted;
        const char *nvram;
        bool in_definition;
} command_args_t;
} lexer_t;
 
 
 
/********* LEXER *********/
 
 
 
/**@note it would be possible to add a very small amount of state to the
 
 * lexer, so when keywords like 'hex' and 'decimal' are encountered, the
 
 * base is changed. */
 
 
 
static token_t *token_new(token_e type, unsigned line)
 
{
 
        token_t *r = allocate_or_die(sizeof(*r));
 
        r->type = type;
 
        r->line = line;
 
        return r;
 
}
 
 
 
void token_free(token_t *t)
 
{
 
        if(!t)
 
                return;
 
        if(t->type == LEX_IDENTIFIER || t->type == LEX_STRING || t->type == LEX_LABEL)
 
                free(t->p.id);
 
        memset(t, 0, sizeof(*t));
 
        free(t);
 
}
 
 
 
static int next_char(lexer_t *l)
 
{
 
        assert(l);
 
        return fgetc(l->input);
 
}
 
 
 
static int unget_char(lexer_t *l, int c)
 
{
 
        assert(l);
 
        return ungetc(c, l->input);
 
}
 
 
 
static lexer_t* lexer_new(FILE *input)
 
{
 
        lexer_t *l = allocate_or_die(sizeof(lexer_t));
 
        l->input = input;
 
        return l;
 
}
 
 
 
static void lexer_free(lexer_t *l)
 
{
 
        assert(l);
 
        token_free(l->token);
 
        memset(l, 0, sizeof(*l));
 
        free(l);
 
}
 
 
 
static int token_print(token_t *t, FILE *output, unsigned depth)
 
{
 
        token_e type;
 
        int r = 0;
 
        if(!t)
 
                return 0;
 
        indent(output, ' ', depth);
 
        type = t->type;
 
        if(type == LEX_LITERAL) {
 
                r = fprintf(output, "number: %"PRId16, t->p.number);
 
        } else if(type == LEX_LABEL) {
 
                r = fprintf(output, "label: %s", t->p.id);
 
        } else if(type == LEX_IDENTIFIER) {
 
                r = fprintf(output, "id: %s", t->p.id);
 
        } else if(type == LEX_ERROR) {
 
                r = fputs("error", output);
 
        } else if(type == LEX_EOI) {
 
                r = fputs("EOI", output);
 
        } else {
 
                r = fprintf(output, "keyword: %s", keywords[type]);
 
        }
 
        return r < 0 ? -1 : 0;
 
}
 
 
 
static int _syntax_error(lexer_t *l,
 
                const char *func, unsigned line, const char *fmt, ...)
 
{
 
        va_list ap;
 
        assert(l);
 
        assert(func);
 
        assert(fmt);
 
        fprintf(stderr, "%s:%u\n", func, line);
 
        fprintf(stderr, "  syntax error on line %u of input\n", l->line);
 
        va_start(ap, fmt);
 
        vfprintf(stderr, fmt, ap);
 
        va_end(ap);
 
        fputc('\n', stderr);
 
        token_print(l->token, stderr, 2);
 
        fputc('\n', stderr);
 
        ethrow(&l->error);
 
        return 0;
 
}
 
 
 
#define syntax_error(LEXER, ...) _syntax_error(LEXER, __func__, __LINE__, ## __VA_ARGS__)
 
 
 
static uint16_t map_char_to_number(int c)
 
{
 
        if(c >= '0' && c <= '9')
 
                return c - '0';
 
        c = tolower(c);
 
        if(c >= 'a' && c <= 'z')
 
                return c + 10 - 'a';
 
        fatal("invalid numeric character: %c", c);
 
        return 0;
 
}
 
 
 
static bool numeric(int c, int base)
 
{
 
        assert(base == 10 || base == 16);
 
        if(base == 10)
 
                return isdigit(c);
 
        return isxdigit(c);
 
}
 
 
 
static int number(char *s, uint16_t *o, size_t length)
 
{
 
        size_t i = 0, start = 0;
 
        uint32_t out = 0;
 
        int base = 10;
 
        bool negate = false;
 
        assert(o);
 
        if(s[i] == '\0')
 
                return 0;
 
 
 
        if(s[i] == '-') {
 
                if(s[i+1] == '\0')
 
                        return 0;
 
                negate = true;
 
                start = ++i;
 
        }
 
 
 
        if(s[i] == '$') {
 
                base = 16;
 
                if(s[i+1] == '\0')
 
                        return 0;
 
                start = i + 1;
 
        }
 
 
 
        for(i = start; i < length; i++)
 
                if(!numeric(s[i], base))
 
                        return 0;
 
 
 
        for(i = start; i < length; i++)
 
                out = out * base + map_char_to_number(s[i]);
 
 
 
        *o = negate ? out * -1 : out;
 
        return 1;
 
}
 
 
 
static void lexer(lexer_t *l)
 
{
 
        size_t i;
 
        int ch;
 
        token_e sym;
 
        uint16_t lit = 0;
 
        assert(l);
 
        ch = next_char(l);
 
        l->token = token_new(LEX_ERROR, l->line);
 
 
 
again:
 
        switch(ch) {
 
        case '\n':
 
                l->line++;
 
        case ' ':
 
        case '\t':
 
        case '\r':
 
        case '\v':
 
                ch = next_char(l);
 
                goto again;
 
        case EOF:
 
                l->token->type = LEX_EOI;
 
                return;
 
        case '\\':
 
                for(; '\n' != (ch = next_char(l));)
 
                        if(ch == EOF)
 
                                syntax_error(l, "'\\' commented terminated by EOF");
 
                ch = next_char(l);
 
                l->line++;
 
                goto again;
 
        case '(':
 
                ch = next_char(l);
 
                if(!isspace(ch)) {
 
                        unget_char(l, ch);
 
                        ch = '(';
 
                        goto graph;
 
                }
 
                for(; ')' != (ch = next_char(l));)
 
                        if(ch == EOF)
 
                                syntax_error(l, "'(' comment terminated by EOF");
 
                        else if(ch == '\n')
 
                                l->line++;
 
                ch = next_char(l);
 
                goto again;
 
        case '"':
 
                for(i = 0; '"' != (ch = next_char(l));) {
 
                        if(ch == EOF)
 
                                syntax_error(l, "string terminated by EOF");
 
                        if(i >= MAX_ID_LENGTH - 1)
 
                                syntax_error(l, "identifier too large: %s", l->id);
 
                        l->id[i++] = ch;
 
                }
 
                l->id[i] = '\0';
 
                l->token->type = LEX_STRING;
 
                l->token->p.id = duplicate(l->id);
 
                ch = next_char(l);
 
                break;
 
        default:
 
                i = 0;
 
        graph:
 
                if(isgraph(ch)) {
 
                        while(isgraph(ch)) {
 
                                if(i >= MAX_ID_LENGTH - 1)
 
                                        syntax_error(l, "identifier too large: %s", l->id);
 
                                l->id[i++] = ch;
 
                                ch = next_char(l);
 
                        }
 
                        l->id[i] = '\0';
 
                } else {
 
                        syntax_error(l, "invalid character: %c", ch);
 
                }
 
 
 
                if(number(l->id, &lit, i)) {
 
                        l->token->type = LEX_LITERAL;
 
                        l->token->p.number = lit;
 
                        break;
 
                }
 
 
 
                for(sym = LEX_CONSTANT; sym != LEX_ERROR && keywords[sym] && strcmp(keywords[sym], l->id); sym++)
 
                        /*do nothing*/;
 
                if(!keywords[sym]) {
 
                        if(i > 1 && l->id[i - 1] == ':') {
 
                                l->id[strlen(l->id) - 1] = '\0';
 
                                l->token->type = LEX_LABEL;
 
                        } else { /* IDENTIFIER */
 
                                l->token->type = LEX_IDENTIFIER;
 
                        }
 
                        l->token->p.id = duplicate(l->id);
 
                } else {
 
                        l->token->type = sym;
 
 
 
                        if(sym == LEX_DEFINE) {
 
                                if(l->in_definition)
 
                                        syntax_error(l, "Nested definitions are not allowed");
 
                                l->in_definition = true;
 
                        }
 
                        if(sym == LEX_ENDDEFINE) {
 
                                if(!(l->in_definition))
 
                                        syntax_error(l, "Use of ';' not terminating word definition");
 
                                l->in_definition = false;
 
                        }
 
                }
 
                break;
 
        }
 
        unget_char(l, ch);
 
}
 
 
 
/********* PARSER *********/
 
 
 
#define X_MACRO_PARSE\
 
        X(SYM_PROGRAM,             "program")\
 
        X(SYM_STATEMENTS,          "statements")\
 
        X(SYM_LABEL,               "label")\
 
        X(SYM_BRANCH,              "branch")\
 
        X(SYM_0BRANCH,             "0branch")\
 
        X(SYM_CALL,                "call")\
 
        X(SYM_CONSTANT,            "constant")\
 
        X(SYM_VARIABLE,            "variable")\
 
        X(SYM_LOCATION,            "location")\
 
        X(SYM_LITERAL,             "literal")\
 
        X(SYM_STRING,              "string")\
 
        X(SYM_INSTRUCTION,         "instruction")\
 
        X(SYM_BEGIN_UNTIL,         "begin...until")\
 
        X(SYM_BEGIN_AGAIN,         "begin...again")\
 
        X(SYM_BEGIN_WHILE_REPEAT,  "begin...while...repeat")\
 
        X(SYM_FOR_NEXT,            "for...next")\
 
        X(SYM_FOR_AFT_THEN_NEXT,   "for...aft...then...next")\
 
        X(SYM_IF1,                 "if1")\
 
        X(SYM_DEFINITION,          "definition")\
 
        X(SYM_CHAR,                "[char]")\
 
        X(SYM_QUOTE,               "'")\
 
        X(SYM_PWD,                 "pwd")\
 
        X(SYM_SET,                 "set")\
 
        X(SYM_PC,                  "pc")\
 
        X(SYM_BREAK,               "break")\
 
        X(SYM_BUILT_IN,            "built-in")\
 
        X(SYM_MODE,                "mode")\
 
        X(SYM_ALLOCATE,            "allocate")\
 
        X(SYM_CALL_DEFINITION,     "call-definition")
 
 
 
typedef enum {
 
#define X(ENUM, NAME) ENUM,
 
        X_MACRO_PARSE
 
#undef X
 
} parse_e;
 
 
 
static const char *names[] = {
 
#define X(ENUM, NAME) [ENUM] = NAME,
 
        X_MACRO_PARSE
 
#undef X
 
        NULL
 
};
 
 
 
typedef struct node_t  {
 
        parse_e type;
 
        size_t length;
 
        uint16_t bits; /*general use bits*/
 
        token_t *token, *value;
 
        struct node_t *o[];
 
} node_t;
 
 
 
static node_t *node_new(parse_e type, size_t size)
 
{
 
        node_t *r = allocate_or_die(sizeof(*r) + sizeof(r->o[0]) * size);
 
        if(log_level >= LOG_DEBUG)
 
                fprintf(stderr, "node> %s\n", names[type]);
 
        r->length = size;
 
        r->type = type;
 
        return r;
 
}
 
 
 
static node_t *node_grow(node_t *n)
 
{
 
        node_t *r = NULL;
 
        assert(n);
 
        errno = 0;
 
        r = realloc(n, sizeof(*n) + (sizeof(n->o[0]) * (n->length + 1)));
 
        if(!r)
 
                fatal("reallocate of size %zu failed: %s", n->length + 1, reason());
 
        r->o[r->length++] = 0;
 
        return r;
 
}
 
 
 
static void node_free(node_t *n)
 
{
 
        if(!n)
 
                return;
 
        for(unsigned i = 0; i < n->length; i++)
 
                node_free(n->o[i]);
 
        token_free(n->token);
 
        token_free(n->value);
 
        free(n);
 
}
 
 
 
static int accept_token(lexer_t *l, token_e sym)
 
{
 
        assert(l);
 
        if(sym == l->token->type) {
 
                token_free(l->accepted); /* free token owned by lexer */
 
                l->accepted = l->token;
 
                if(sym != LEX_EOI)
 
                        lexer(l);
 
                return 1;
 
        }
 
        return 0;
 
}
 
 
 
static int accept_range(lexer_t *l, token_e low, token_e high)
 
{
 
        assert(l);
 
        assert(low <= high);
 
        for(token_e i = low; i <= high; i++)
 
                if(accept_token(l, i))
 
                        return 1;
 
        return 0;
 
}
 
 
 
static void use(lexer_t *l, node_t *n)
 
{ /* move ownership of token from lexer to parse tree */
 
        assert(l);
 
        assert(n);
 
        if(n->token)
 
                n->value = l->accepted;
 
        else
 
                n->token = l->accepted;
 
        l->accepted = NULL;
 
}
 
 
 
static int token_enum_print(token_e sym, FILE *output)
 
{
 
        assert(output);
 
        assert(sym < LEX_ERROR);
 
        const char *s = keywords[sym];
 
        return fprintf(output, "%s(%u)", s ? s : "???", sym);
 
}
 
 
 
static void node_print(FILE *output, node_t *n, bool shallow, unsigned depth)
 
{
 
        if(!n)
 
                return;
 
        assert(output);
 
        indent(output, ' ', depth);
 
        fprintf(output, "node(%d): %s\n", n->type, names[n->type]);
 
        token_print(n->token, output, depth);
 
        if(n->token)
 
                fputc('\n', output);
 
        if(shallow)
 
                return;
 
        for(size_t i = 0; i < n->length; i++)
 
                node_print(output, n->o[i], shallow, depth+1);
 
}
 
 
 
static int _expect(lexer_t *l, token_e token, const char *file, const char *func, unsigned line)
 
{
 
        assert(l);
 
        assert(file);
 
        assert(func);
 
        if(accept_token(l, token))
 
                return 1;
 
        fprintf(stderr, "%s:%s:%u\n", file, func, line);
 
        fprintf(stderr, "  Syntax error: unexpected token\n  Got:          ");
 
        token_print(l->token, stderr, 0);
 
        fputs("  Expected:     ", stderr);
 
        token_enum_print(token, stderr);
 
        fprintf(stderr, "\n  On line: %u\n", l->line);
 
        ethrow(&l->error);
 
        return 0;
 
}
 
 
 
#define expect(L, TOKEN) _expect((L), (TOKEN), __FILE__, __func__, __LINE__)
 
 
 
/* for rules in the BNF tree defined entirely by their token */
 
static node_t *defined_by_token(lexer_t *l, parse_e type)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(type, 0);
 
        use(l, r);
 
        return r;
 
}
 
 
 
typedef enum {
 
        DEFINE_HIDDEN    = 1 << 0,
 
        DEFINE_IMMEDIATE = 1 << 1,
 
        DEFINE_INLINE    = 1 << 2,
 
} define_type_e;
 
 
 
/** @note LEX_LOCATION handled by modifying return node in statement() */
 
static node_t *variable_or_constant(lexer_t *l, bool variable)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(variable ? SYM_VARIABLE : SYM_CONSTANT, 1);
 
        expect(l, LEX_IDENTIFIER);
 
        use(l, r);
 
        if(accept_token(l, LEX_LITERAL)) {
 
                r->o[0] = defined_by_token(l, SYM_LITERAL);
 
        } else {
 
                expect(l, LEX_STRING);
 
                r->o[0] = defined_by_token(l, SYM_STRING);
 
        }
 
        if(accept_token(l, LEX_HIDDEN)) {
 
                if(r->bits & DEFINE_HIDDEN)
 
                        syntax_error(l, "hidden bit already set on latest word definition");
 
                r->bits |= DEFINE_HIDDEN;
 
        }
 
        return r;
 
}
 
 
 
static node_t *jump(lexer_t *l, parse_e type)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(type, 0);
 
        (void)(accept_token(l, LEX_LITERAL) || accept_token(l, LEX_STRING) || expect(l, LEX_IDENTIFIER));
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *statements(lexer_t *l);
 
 
 
static node_t *for_next(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_FOR_NEXT, 1);
 
        r->o[0] = statements(l);
 
        if(accept_token(l, LEX_AFT)) {
 
                r->type = SYM_FOR_AFT_THEN_NEXT;
 
                r = node_grow(r);
 
                r->o[1] = statements(l);
 
                r = node_grow(r);
 
                expect(l, LEX_THEN);
 
                r->o[2] = statements(l);
 
        }
 
        expect(l, LEX_NEXT);
 
        return r;
 
}
 
 
 
static node_t *begin(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_BEGIN_UNTIL, 1);
 
        r->o[0] = statements(l);
 
        if(accept_token(l, LEX_AGAIN)) {
 
                r->type = SYM_BEGIN_AGAIN;
 
        } else if(accept_token(l, LEX_WHILE)) {
 
                r->type = SYM_BEGIN_WHILE_REPEAT;
 
                r = node_grow(r);
 
                r->o[1] = statements(l);
 
                expect(l, LEX_REPEAT);
 
        } else {
 
                expect(l, LEX_UNTIL);
 
        }
 
        return r;
 
}
 
 
 
static node_t *if1(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_IF1, 2);
 
        r->o[0] = statements(l);
 
        if(accept_token(l, LEX_ELSE))
 
                r->o[1] = statements(l);
 
        expect(l, LEX_THEN);
 
        return r;
 
}
 
 
 
static node_t *define(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_DEFINITION, 1);
 
        if(accept_token(l, LEX_IDENTIFIER))
 
                ;
 
        else
 
                expect(l, LEX_STRING);
 
        use(l, r);
 
        r->o[0] = statements(l);
 
        expect(l, LEX_ENDDEFINE);
 
again:
 
        if(accept_token(l, LEX_IMMEDIATE)) {
 
                if(r->bits & DEFINE_IMMEDIATE)
 
                        syntax_error(l, "immediate bit already set on latest word definition");
 
                r->bits |= DEFINE_IMMEDIATE;
 
                goto again;
 
        }
 
        if(accept_token(l, LEX_HIDDEN)) {
 
                if(r->bits & DEFINE_HIDDEN)
 
                        syntax_error(l, "hidden bit already set on latest word definition");
 
                r->bits |= DEFINE_HIDDEN;
 
                goto again;
 
        }
 
        if(accept_token(l, LEX_INLINE)) {
 
                if(r->bits & DEFINE_INLINE)
 
                        syntax_error(l, "inline bit already set on latest word definition");
 
                r->bits |= DEFINE_INLINE;
 
                goto again;
 
        }
 
        return r;
 
}
 
 
 
static node_t *char_compile(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_CHAR, 0);
 
        expect(l, LEX_IDENTIFIER);
 
        use(l, r);
 
        if(strlen(r->token->p.id) > 1)
 
                syntax_error(l, "expected single character, got identifier: %s", r->token->p.id);
 
        return r;
 
}
 
 
 
static node_t *mode(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_MODE, 0);
 
        expect(l, LEX_LITERAL);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *pc(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_PC, 0);
 
        if(!accept_token(l, LEX_LITERAL))
 
                expect(l, LEX_IDENTIFIER);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *pwd(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_PWD, 0);
 
        if(!accept_token(l, LEX_LITERAL))
 
                expect(l, LEX_IDENTIFIER);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *set(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_SET, 0);
 
        if(!accept_token(l, LEX_IDENTIFIER) && !accept_token(l, LEX_STRING))
 
                expect(l, LEX_LITERAL);
 
        use(l, r);
 
        if(!accept_token(l, LEX_IDENTIFIER) && !accept_token(l, LEX_STRING))
 
                expect(l, LEX_LITERAL);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *allocate(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_ALLOCATE, 0);
 
        if(!accept_token(l, LEX_IDENTIFIER))
 
                expect(l, LEX_LITERAL);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *quote(lexer_t *l)
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_QUOTE, 0);
 
        if(!accept_token(l, LEX_IDENTIFIER))
 
                expect(l, LEX_STRING);
 
        use(l, r);
 
        return r;
 
}
 
 
 
static node_t *statements(lexer_t *l)
 
{
 
        node_t *r;
 
        size_t i = 0;
 
        assert(l);
 
        r = node_new(SYM_STATEMENTS, 2);
 
again:
 
        r = node_grow(r);
 
        if(accept_token(l, LEX_CALL)) {
 
                r->o[i++] = jump(l, SYM_CALL);
 
                goto again;
 
        } else if(accept_token(l, LEX_BRANCH)) {
 
                r->o[i++] = jump(l, SYM_BRANCH);
 
                goto again;
 
        } else if(accept_token(l, LEX_0BRANCH)) {
 
                r->o[i++] = jump(l, SYM_0BRANCH);
 
                goto again;
 
        } else if(accept_token(l, LEX_LITERAL)) {
 
                r->o[i++] = defined_by_token(l, SYM_LITERAL);
 
                goto again;
 
        } else if(accept_token(l, LEX_LABEL)) {
 
                r->o[i++] = defined_by_token(l, SYM_LABEL);
 
                goto again;
 
        } else if(accept_token(l, LEX_CONSTANT)) {
 
                r->o[i++] = variable_or_constant(l, false);
 
                goto again;
 
        } else if(accept_token(l, LEX_VARIABLE)) {
 
                r->o[i++] = variable_or_constant(l, true);
 
                goto again;
 
        } else if(accept_token(l, LEX_LOCATION)) {
 
                r->o[i]   = variable_or_constant(l, true);
 
                r->o[i++]->type = SYM_LOCATION;
 
                goto again;
 
        } else if(accept_token(l, LEX_IF)) {
 
                r->o[i++] = if1(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_DEFINE)) {
 
                r->o[i++] = define(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_CHAR)) {
 
                r->o[i++] = char_compile(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_BEGIN)) {
 
                r->o[i++] = begin(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_FOR)) {
 
                r->o[i++] = for_next(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_QUOTE)) {
 
                r->o[i++] = quote(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_IDENTIFIER)) {
 
                r->o[i++] = defined_by_token(l, SYM_CALL_DEFINITION);
 
                goto again;
 
        } else if(accept_token(l, LEX_PWD)) {
 
                r->o[i++] = pwd(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_SET)) {
 
                r->o[i++] = set(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_PC)) {
 
                r->o[i++] = pc(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_BREAK)) {
 
                r->o[i++] = defined_by_token(l, SYM_BREAK);
 
                goto again;
 
        } else if(accept_token(l, LEX_MODE)) {
 
                r->o[i++] = mode(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_ALLOCATE)) {
 
                r->o[i++] = allocate(l);
 
                goto again;
 
        } else if(accept_token(l, LEX_BUILT_IN)) {
 
                r->o[i++] = defined_by_token(l, SYM_BUILT_IN);
 
                goto again;
 
        /**@warning This is a token range from the first instruction to the
 
         * last instruction */
 
        } else if(accept_range(l, LEX_DUP, LEX_RDROP)) {
 
                r->o[i++] = defined_by_token(l, SYM_INSTRUCTION);
 
                goto again;
 
        }
 
        return r;
 
}
 
 
 
static node_t *program(lexer_t *l) /* block ( "." | EOF ) */
 
{
 
        node_t *r;
 
        assert(l);
 
        r = node_new(SYM_PROGRAM, 1);
 
        lexer(l);
 
        r->o[0] = statements(l);
 
        expect(l, LEX_EOI);
 
        return r;
 
}
 
 
 
static node_t *parse(FILE *input)
 
{
 
        lexer_t *l;
 
        assert(input);
 
        l = lexer_new(input);
 
        l->error.jmp_buf_valid = 1;
 
        if(setjmp(l->error.j)) {
 
                lexer_free(l);
 
                return NULL;
 
        }
 
        node_t *n = program(l);
 
        lexer_free(l);
 
        return n;
 
}
 
 
 
/********* CODE ***********/
 
 
 
typedef enum {
 
        MODE_NORMAL              = 0 << 0,
 
        MODE_COMPILE_WORD_HEADER = 1 << 0,
 
        MODE_OPTIMIZATION_ON     = 1 << 1,
 
} assembler_mode_e;
 
 
 
typedef struct {
 
        bool in_definition;
 
        bool start_defined;
 
        bool built_in_words_defined;
 
        uint16_t start;
 
        uint16_t mode;
 
        uint16_t pwd; /* previous word register */
 
        uint16_t fence; /* mark a boundary before which optimization cannot take place */
 
        symbol_t *do_r_minus_one;
 
        symbol_t *do_next;
 
        symbol_t *do_var;
 
        symbol_t *do_const;
 
} assembler_t;
 
 
 
static void update_fence(assembler_t *a, uint16_t pc)
 
{
 
        assert(a);
 
        a->fence = MAX(a->fence, pc);
 
}
 
 
 
static void generate(h2_t *h, assembler_t *a, uint16_t instruction)
 
{
 
        assert(h);
 
        assert(a);
 
        debug("%"PRIx16":\t%"PRIx16, h->pc, instruction);
 
 
 
        if(IS_CALL(instruction) || IS_LITERAL(instruction) || IS_0BRANCH(instruction) || IS_BRANCH(instruction))
 
                update_fence(a, h->pc);
 
 
 
        /** @note This implements two ad-hoc optimizations, both related to
 
         * CODE_EXIT, they should be replaced by a generic peep hole optimizer */
 
        if(a->mode & MODE_OPTIMIZATION_ON && h->pc) {
 
                uint16_t previous = h->core[h->pc - 1];
 
                if(((h->pc - 1) > a->fence) && IS_ALU_OP(previous) && (instruction == CODE_EXIT)) {
 
                        /* merge the CODE_EXIT instruction with the previous instruction if it is possible to do so */
 
                        if(!(previous & R_TO_PC) && !(previous & MK_RSTACK(DELTA_N1))) {
 
                                debug("optimization EXIT MERGE pc(%04"PRIx16 ") [%04"PRIx16 " -> %04"PRIx16"]", h->pc, previous, previous|instruction);
 
                                previous |= instruction;
 
                                h->core[h->pc - 1] = previous;
 
                                update_fence(a, h->pc - 1);
 
                                return;
 
                        }
 
                } else if(h->pc > a->fence && IS_CALL(previous) && (instruction == CODE_EXIT)) {
 
                        /* do not emit CODE_EXIT if last instruction in a word
 
                         * definition is a call, instead replace that call with
 
                         * a jump */
 
                        debug("optimization TAIL CALL pc(%04"PRIx16 ") [%04"PRIx16 " -> %04"PRIx16"]", h->pc, previous, OP_BRANCH | (previous & 0x1FFF));
 
                        h->core[h->pc - 1] = (OP_BRANCH | (previous & 0x1FFF));
 
                        update_fence(a, h->pc - 1);
 
                        return;
 
                }
 
        }
 
 
 
        h->core[h->pc++] = instruction;
 
}
 
 
 
static uint16_t here(h2_t *h, assembler_t *a)
 
{
 
        assert(h);
 
        assert(h->pc < MAX_CORE);
 
        update_fence(a, h->pc);
 
        return h->pc;
 
}
 
 
 
static uint16_t hole(h2_t *h, assembler_t *a)
 
{
 
        assert(h);
 
        assert(h->pc < MAX_CORE);
 
        here(h, a);
 
        return h->pc++;
 
}
 
 
 
static void fix(h2_t *h, uint16_t hole, uint16_t patch)
 
{
 
        assert(h);
 
        assert(hole < MAX_CORE);
 
        h->core[hole] = patch;
 
}
 
 
 
#define assembly_error(ERROR, FMT, ...) do{ error(FMT, ##__VA_ARGS__); ethrow(e); }while(0)
 
 
 
static void generate_jump(h2_t *h, assembler_t *a, symbol_table_t *t, token_t *tok, parse_e type, error_t *e)
 
{
 
        uint16_t or = 0;
 
        uint16_t addr = 0;
 
        symbol_t *s;
 
        assert(h);
 
        assert(t);
 
        assert(a);
 
 
 
        if(tok->type == LEX_IDENTIFIER || tok->type == LEX_STRING) {
 
                s = symbol_table_lookup(t, tok->p.id);
 
                if(!s)
 
                        assembly_error(e, "undefined symbol: %s", tok->p.id);
 
                addr = s->value;
 
 
 
                if(s->type == SYMBOL_TYPE_CALL && type != SYM_CALL)
 
                        assembly_error(e, "cannot branch/0branch to call: %s", tok->p.id);
 
 
 
        } else if (tok->type == LEX_LITERAL) {
 
                addr = tok->p.number;
 
        } else {
 
                fatal("invalid jump target token type");
 
        }
 
 
 
        if(addr > MAX_CORE)
 
                assembly_error(e, "invalid jump address: %"PRId16, addr);
 
 
 
        switch(type) {
 
        case SYM_BRANCH:  or = OP_BRANCH ; break;
 
        case SYM_0BRANCH: or = OP_0BRANCH; break;
 
        case SYM_CALL:    or = OP_CALL;    break;
 
        default:
 
                fatal("invalid call type: %u", type);
 
        }
 
        generate(h, a, or | addr);
 
}
 
 
 
static void generate_literal(h2_t *h, assembler_t *a, uint16_t number)
 
{
 
        if(number & OP_LITERAL) {
 
                number = ~number;
 
                generate(h, a, OP_LITERAL | number);
 
                generate(h, a, CODE_INVERT);
 
        } else {
 
                generate(h, a, OP_LITERAL | number);
 
        }
 
}
 
 
 
static uint16_t lexer_to_alu_op(token_e t)
 
{
 
        assert(t >= LEX_DUP && t <= LEX_RDROP);
 
        switch(t) {
 
#define X(NAME, STRING, DEFINE, INSTRUCTION) case LEX_ ## NAME : return CODE_ ## NAME ;
 
        X_MACRO_INSTRUCTIONS
 
#undef X
 
        default: fatal("invalid ALU operation: %u", t);
 
        }
 
        return 0;
 
}
 
 
 
static uint16_t literal_or_symbol_lookup(token_t *token, symbol_table_t *t, error_t *e)
 
{
 
        symbol_t *s = NULL;
 
        assert(token);
 
        assert(t);
 
        if(token->type == LEX_LITERAL)
 
                return token->p.number;
 
 
 
        assert(token->type == LEX_IDENTIFIER);
 
 
 
        if(!(s = symbol_table_lookup(t, token->p.id)))
 
                assembly_error(e, "symbol not found: %s", token->p.id);
 
        return s->value;
 
}
 
 
 
static uint16_t pack_16(const char lb, const char hb)
 
{
 
        return (((uint16_t)hb) << 8) | (uint16_t)lb;
 
}
 
 
 
static uint16_t pack_string(h2_t *h, assembler_t *a, const char *s, error_t *e)
 
{
 
        assert(h);
 
        assert(s);
 
        size_t l = strlen(s);
 
        size_t i = 0;
 
        uint16_t r = h->pc;
 
        if(l > 255)
 
                assembly_error(e, "string \"%s\" is too large (%zu > 255)", s, l);
 
        h->core[hole(h, a)] = pack_16(l, s[0]);
 
        for(i = 1; i < l; i += 2)
 
                h->core[hole(h, a)] = pack_16(s[i], s[i+1]);
 
        if(i < l)
 
                h->core[hole(h, a)] = pack_16(s[i], 0);
 
        here(h, a);
 
        return r;
 
}
 
 
 
static uint16_t symbol_special(h2_t *h, assembler_t *a, const char *id, error_t *e)
 
{
 
        static const char *special[] = {
 
                "$pc",
 
                "$pwd",
 
                NULL
 
        };
 
 
 
        enum special_e {
 
                SPECIAL_VARIABLE_PC,
 
                SPECIAL_VARIABLE_PWD
 
        };
 
 
 
        size_t i;
 
        assert(h);
 
        assert(id);
 
        assert(a);
 
 
 
        for(i = 0; special[i]; i++)
 
                if(!strcmp(id, special[i]))
 
                        break;
 
        if(!special[i])
 
                assembly_error(e, "'%s' is not a symbol", id);
 
 
 
        switch(i) {
 
        case SPECIAL_VARIABLE_PC:   return h->pc << 1;
 
        case SPECIAL_VARIABLE_PWD:  return a->pwd; /**@note already as a character address */
 
        default: fatal("reached the unreachable: %zu", i);
 
        }
 
 
 
        return 0;
 
}
 
 
 
typedef struct {
 
        char *name;
 
        size_t len;
 
        bool inline_bit;
 
        bool hidden;
 
        bool compile;
 
        uint16_t code[32];
 
} built_in_words_t;
 
 
 
static built_in_words_t built_in_words[] = {
 
#define X(NAME, STRING, DEFINE, INSTRUCTION) \
 
        {\
 
                .name = STRING,\
 
                .compile = DEFINE,\
 
                .len = 1,\
 
                .inline_bit = true,\
 
                .hidden = false,\
 
                .code = { INSTRUCTION }\
 
        },
 
        X_MACRO_INSTRUCTIONS
 
#undef X
 
        /**@note We might want to compile these words, even if we are not
 
         * compiling the other in-line-able, so the compiler can use them for
 
         * variable declaration and for...next loops */
 
        { .name = "doVar",   .compile = true, .inline_bit = false, .hidden = true, .len = 1, .code = {CODE_FROMR} },
 
        { .name = "doConst", .compile = true, .inline_bit = false, .hidden = true, .len = 2, .code = {CODE_FROMR, CODE_LOAD} },
 
        { .name = "r1-",     .compile = true, .inline_bit = false, .hidden = true, .len = 5, .code = {CODE_FROMR, CODE_FROMR, CODE_T_N1, CODE_TOR, CODE_TOR} },
 
        { .name = NULL,      .compile = true, .inline_bit = false, .hidden = true, .len = 0, .code = {0} }
 
};
 
 
 
static void generate_loop_decrement(h2_t *h, assembler_t *a, symbol_table_t *t)
 
{
 
        a->do_r_minus_one = a->do_r_minus_one ? a->do_r_minus_one : symbol_table_lookup(t, "r1-");
 
        if(a->do_r_minus_one && a->mode & MODE_OPTIMIZATION_ON) {
 
                generate(h, a, OP_CALL | a->do_r_minus_one->value);
 
        } else {
 
                generate(h, a, CODE_FROMR);
 
                generate(h, a, CODE_T_N1);
 
                generate(h, a, CODE_TOR);
 
        }
 
}
 
 
 
static void assemble(h2_t *h, assembler_t *a, node_t *n, symbol_table_t *t, error_t *e)
 
{
 
        uint16_t hole1, hole2;
 
        assert(h);
 
        assert(t);
 
        assert(e);
 
 
 
        if(!n)
 
                return;
 
 
 
        if(h->pc > MAX_CORE)
 
                assembly_error(e, "PC/Dictionary overflow: %"PRId16, h->pc);
 
 
 
        switch(n->type) {
 
        case SYM_PROGRAM:
 
                assemble(h, a, n->o[0], t, e);
 
                break;
 
        case SYM_STATEMENTS:
 
                for(size_t i = 0; i < n->length; i++)
 
                        assemble(h, a, n->o[i], t, e);
 
                break;
 
        case SYM_LABEL:
 
                symbol_table_add(t, SYMBOL_TYPE_LABEL, n->token->p.id, here(h, a), e, false);
 
                break;
 
        case SYM_BRANCH:
 
        case SYM_0BRANCH:
 
        case SYM_CALL:
 
                generate_jump(h, a, t, n->token, n->type, e);
 
                break;
 
        case SYM_CONSTANT:
 
                if(a->mode & MODE_COMPILE_WORD_HEADER && a->built_in_words_defined && (!(n->bits & DEFINE_HIDDEN))) {
 
                        a->do_const = a->do_const ? a->do_const : symbol_table_lookup(t, "doConst");
 
                        assert(a->do_const);
 
                        hole1 = hole(h, a);
 
                        fix(h, hole1, a->pwd);
 
                        a->pwd = hole1 << 1;
 
                        pack_string(h, a, n->token->p.id, e);
 
                        generate(h, a, OP_CALL | a->do_const->value);
 
                        hole1 = hole(h, a);
 
                        fix(h, hole1, n->o[0]->token->p.number);
 
                }
 
                symbol_table_add(t, SYMBOL_TYPE_CONSTANT, n->token->p.id, n->o[0]->token->p.number, e, false);
 
                break;
 
        case SYM_VARIABLE:
 
                if(a->mode & MODE_COMPILE_WORD_HEADER && a->built_in_words_defined && (!(n->bits & DEFINE_HIDDEN))) {
 
                        a->do_var = a->do_var ? a->do_var : symbol_table_lookup(t, "doVar");
 
                        assert(a->do_var);
 
                        hole1 = hole(h, a);
 
                        fix(h, hole1, a->pwd);
 
                        a->pwd = hole1 << 1;
 
                        pack_string(h, a, n->token->p.id, e);
 
                        generate(h, a, OP_CALL | a->do_var->value);
 
                } else if (!(n->bits & DEFINE_HIDDEN)) {
 
                        assembly_error(e, "variable used but doVar not defined, use location");
 
                }
 
                /* fall through */
 
        case SYM_LOCATION:
 
                here(h, a);
 
 
 
                if(n->o[0]->token->type == LEX_LITERAL) {
 
                        hole1 = hole(h, a);
 
                        fix(h, hole1, n->o[0]->token->p.number);
 
                } else {
 
                        assert(n->o[0]->token->type == LEX_STRING);
 
                        hole1 = pack_string(h, a, n->o[0]->token->p.id, e);
 
                }
 
 
 
                /**@note The lowest bit of the address for memory loads is
 
                 * discarded. */
 
                symbol_table_add(t, SYMBOL_TYPE_VARIABLE, n->token->p.id, hole1 << 1, e, n->type == SYM_LOCATION ? true : false);
 
                break;
 
        case SYM_QUOTE:
 
        {
 
                symbol_t *s = symbol_table_lookup(t, n->token->p.id);
 
                if(!s || (s->type != SYMBOL_TYPE_CALL && s->type != SYMBOL_TYPE_LABEL))
 
                        assembly_error(e, "not a defined procedure: %s", n->token->p.id);
 
                generate_literal(h, a, s->value << 1);
 
                break;
 
        }
 
        case SYM_LITERAL:
 
                generate_literal(h, a, n->token->p.number);
 
                break;
 
        case SYM_INSTRUCTION:
 
                generate(h, a, lexer_to_alu_op(n->token->type));
 
                break;
 
        case SYM_BEGIN_AGAIN: /* fall through */
 
        case SYM_BEGIN_UNTIL:
 
                hole1 = here(h, a);
 
                assemble(h, a, n->o[0], t, e);
 
                generate(h, a, (n->type == SYM_BEGIN_AGAIN ? OP_BRANCH : OP_0BRANCH) | hole1);
 
                break;
 
 
 
        case SYM_FOR_NEXT:
 
        {
 
                symbol_t *s = a->do_next ? a->do_next : symbol_table_lookup(t, "doNext");
 
                if(s && a->mode & MODE_OPTIMIZATION_ON) {
 
                        generate(h, a, CODE_TOR);
 
                        hole1 = here(h, a);
 
                        assemble(h, a, n->o[0], t, e);
 
                        generate(h, a, OP_CALL | s->value);
 
                        generate(h, a, hole1 << 1);
 
                } else {
 
                        generate(h, a, CODE_TOR);
 
                        hole1 = here(h, a);
 
                        assemble(h, a, n->o[0], t, e);
 
                        generate(h, a, CODE_RAT);
 
                        hole2 = hole(h, a);
 
                        generate_loop_decrement(h, a, t);
 
                        generate(h, a, OP_BRANCH | hole1);
 
                        fix(h, hole2, OP_0BRANCH | here(h, a));
 
                        generate(h, a, CODE_RDROP);
 
                }
 
                break;
 
        }
 
        case SYM_FOR_AFT_THEN_NEXT:
 
        {
 
                symbol_t *s = a->do_next ? a->do_next : symbol_table_lookup(t, "doNext");
 
                if(s && a->mode & MODE_OPTIMIZATION_ON) {
 
                        generate(h, a, CODE_TOR);
 
                        assemble(h, a, n->o[0], t, e);
 
                        hole1 = hole(h, a);
 
                        hole2 = here(h, a);
 
                        assemble(h, a, n->o[1], t, e);
 
                        fix(h, hole1, OP_BRANCH | here(h, a));
 
                        assemble(h, a, n->o[2], t, e);
 
                        generate(h, a, OP_CALL | s->value);
 
                        generate(h, a, hole2 << 1);
 
                } else {
 
                        generate(h, a, CODE_TOR);
 
                        assemble(h, a, n->o[0], t, e);
 
                        hole1 = hole(h, a);
 
                        generate(h, a, CODE_RAT);
 
                        generate_loop_decrement(h, a, t);
 
                        hole2 = hole(h, a);
 
                        assemble(h, a, n->o[1], t, e);
 
                        fix(h, hole1, OP_BRANCH | (here(h, a)));
 
                        assemble(h, a, n->o[2], t, e);
 
                        generate(h, a, OP_BRANCH | (hole1 + 1));
 
                        fix(h, hole2, OP_0BRANCH | (here(h, a)));
 
                        generate(h, a, CODE_RDROP);
 
                }
 
                break;
 
        }
 
        case SYM_BEGIN_WHILE_REPEAT:
 
                hole1 = here(h, a);
 
                assemble(h, a, n->o[0], t, e);
 
                hole2 = hole(h, a);
 
                assemble(h, a, n->o[1], t, e);
 
                generate(h, a, OP_BRANCH  | hole1);
 
                fix(h, hole2, OP_0BRANCH | here(h, a));
 
                break;
 
        case SYM_IF1:
 
                hole1 = hole(h, a);
 
                assemble(h, a, n->o[0], t, e);
 
                if(n->o[1]) { /* if ... else .. then */
 
                        hole2 = hole(h, a);
 
                        fix(h, hole1, OP_0BRANCH | (hole2 + 1));
 
                        assemble(h, a, n->o[1], t, e);
 
                        fix(h, hole2, OP_BRANCH  | here(h, a));
 
                } else { /* if ... then */
 
                        fix(h, hole1, OP_0BRANCH | here(h, a));
 
                }
 
                break;
 
        case SYM_CALL_DEFINITION:
 
        {
 
                symbol_t *s = symbol_table_lookup(t, n->token->p.id);
 
                if(!s)
 
                        assembly_error(e, "not a constant or a defined procedure: %s", n->token->p.id);
 
                if(s->type == SYMBOL_TYPE_CALL) {
 
                        generate(h, a, OP_CALL | s->value);
 
                } else if(s->type == SYMBOL_TYPE_CONSTANT || s->type == SYMBOL_TYPE_VARIABLE) {
 
                        generate_literal(h, a, s->value);
 
                } else {
 
                        error("can only call or push literal: %s", s->id);
 
                        ethrow(e);
 
                }
 
                break;
 
        }
 
        case SYM_DEFINITION:
 
                if(n->bits && !(a->mode & MODE_COMPILE_WORD_HEADER))
 
                        assembly_error(e, "cannot modify word bits (immediate/hidden/inline) if not in compile mode");
 
                if(a->mode & MODE_COMPILE_WORD_HEADER && !(n->bits & DEFINE_HIDDEN)) {
 
                        hole1 = hole(h, a);
 
                        n->bits &= (DEFINE_IMMEDIATE | DEFINE_INLINE);
 
                        fix(h, hole1, a->pwd | (n->bits << 13)); /* shift in word bits into PWD field */
 
                        a->pwd = hole1 << 1;
 
                        pack_string(h, a, n->token->p.id, e);
 
                }
 
                symbol_table_add(t, SYMBOL_TYPE_CALL, n->token->p.id, here(h, a), e, n->bits & DEFINE_HIDDEN);
 
                if(a->in_definition)
 
                        assembly_error(e, "nested word definition is not allowed");
 
                a->in_definition = true;
 
                assemble(h, a, n->o[0], t, e);
 
                generate(h, a, CODE_EXIT);
 
                a->in_definition = false;
 
                break;
 
        case SYM_CHAR: /* [char] A  */
 
                generate(h, a, OP_LITERAL | n->token->p.id[0]);
 
                break;
 
        case SYM_SET:
 
        {
 
                uint16_t location, value;
 
                symbol_t *l = NULL;
 
                location = literal_or_symbol_lookup(n->token, t, e);
 
 
 
                if(n->value->type == LEX_LITERAL) {
 
                        value = n->value->p.number;
 
                } else {
 
                        l = symbol_table_lookup(t, n->value->p.id);
 
                        if(l) {
 
                                value = l->value;
 
                                if(l->type == SYMBOL_TYPE_CALL) // || l->type == SYMBOL_TYPE_LABEL)
 
                                        value <<= 1;
 
                        } else {
 
                                value = symbol_special(h, a, n->value->p.id, e);
 
                        }
 
                }
 
                fix(h, location >> 1, value);
 
                break;
 
        }
 
        case SYM_PWD:
 
                a->pwd = literal_or_symbol_lookup(n->token, t, e);
 
                break;
 
        case SYM_PC:
 
                h->pc = literal_or_symbol_lookup(n->token, t, e);
 
                update_fence(a, h->pc);
 
                break;
 
        case SYM_MODE:
 
                a->mode = n->token->p.number;
 
                break;
 
        case SYM_ALLOCATE:
 
                h->pc += literal_or_symbol_lookup(n->token, t, e) >> 1;
 
                update_fence(a, h->pc);
 
                break;
 
        case SYM_BREAK:
 
                break_point_add(&h->bp, h->pc);
 
                update_fence(a, h->pc);
 
                break;
 
        case SYM_BUILT_IN:
 
                if(!(a->mode & MODE_COMPILE_WORD_HEADER))
 
                        break;
 
 
 
                if(a->built_in_words_defined)
 
                        assembly_error(e, "built in words already defined");
 
                a->built_in_words_defined = true;
 
 
 
                for(unsigned i = 0; built_in_words[i].name; i++) {
 
                        if(!(built_in_words[i].compile))
 
                                continue;
 
 
 
                        if(!built_in_words[i].hidden) {
 
                                uint16_t pwd = a->pwd;
 
                                hole1 = hole(h, a);
 
                                if(built_in_words[i].inline_bit)
 
                                        pwd |= (DEFINE_INLINE << 13);
 
                                fix(h, hole1, pwd);
 
                                a->pwd = hole1 << 1;
 
                                pack_string(h, a, built_in_words[i].name, e);
 
                        }
 
                        symbol_table_add(t, SYMBOL_TYPE_CALL, built_in_words[i].name, here(h, a), e, built_in_words[i].hidden);
 
                        for(size_t j = 0; j < built_in_words[i].len; j++)
 
                                generate(h, a, built_in_words[i].code[j]);
 
                        generate(h, a, CODE_EXIT);
 
                }
 
                break;
 
        default:
 
                fatal("Invalid or unknown type: %u", n->type);
 
        }
 
}
 
 
 
static bool assembler(h2_t *h, assembler_t *a, node_t *n, symbol_table_t *t, error_t *e)
 
{
 
        assert(h && a && n && t && e);
 
        if(setjmp(e->j))
 
                return false;
 
        assemble(h, a, n, t, e);
 
        return true;
 
}
 
 
 
static h2_t *code(node_t *n, symbol_table_t *symbols)
 
{
 
        error_t e;
 
        h2_t *h;
 
        symbol_table_t *t = NULL;
 
        assembler_t a;
 
        assert(n);
 
        memset(&a, 0, sizeof a);
 
 
 
        t = symbols ? symbols : symbol_table_new();
 
        h = h2_new(START_ADDR);
 
        a.fence = h->pc;
 
 
 
        e.jmp_buf_valid = 1;
 
        if(!assembler(h, &a, n, t, &e)) {
 
                h2_free(h);
 
                if(!symbols)
 
                        symbol_table_free(t);
 
                return NULL;
 
        }
 
 
 
        if(log_level >= LOG_DEBUG)
 
                symbol_table_print(t, stderr);
 
        if(!symbols)
 
                symbol_table_free(t);
 
        return h;
 
}
 
 
 
int h2_assemble_file(FILE *input, FILE *output, symbol_table_t *symbols)
 
{
 
        int r = 0;
 
        node_t *n;
 
        assert(input);
 
        assert(output);
 
 
 
        n = parse(input);
 
 
 
        if(log_level >= LOG_DEBUG)
 
                node_print(stderr, n, false, 0);
 
        if(n) {
 
                h2_t *h = code(n, symbols);
 
                if(h)
 
                        r = h2_save(h, output, false);
 
                else
 
                        r = -1;
 
                h2_free(h);
 
        } else {
 
                r = -1;
 
        }
 
        node_free(n);
 
        return r;
 
}
 
 
 
h2_t *h2_assemble_core(FILE *input, symbol_table_t *symbols)
 
{
 
        assert(input);
 
        h2_t *h = NULL;
 
        node_t *n = parse(input);
 
        if(log_level >= LOG_DEBUG)
 
                node_print(stderr, n, false, 0);
 
        if(n)
 
                h = code(n, symbols);
 
        node_free(n);
 
        return h;
 
}
 
 
 
/* ========================== Assembler ==================================== */
 
 
 
/* ========================== Assembler ==================================== */
 
 
 
/* ========================== Embed Forth Virtual Machine ================== */
 
/* This Forth virtual machine is based on the H2 processor, but it is meant
 
 * to run on a hosted system. It is a small, limited virtual machine, but
 
 * capable of running a cross-compiler (metacompiler) and saving the resulting
 
 * binary. This will eventually replace the assembler/compiler. This requires
 
 * another file which contains the metacompiler, and an image for the virtual
 
 * machine.
 
 *
 
 * The 'embed' project can be found at <https://github.com/howerj/embed> */
 
 
 
#define CORE (65536u)  /* core size in bytes */
 
#define SP0  (8704u)   /* Variable Stack Start: 8192 (end of program area) + 512 (block size) */
 
#define RP0  (32767u)  /* Return Stack Start: end of CORE in words */
 
 
 
#ifdef TRON
 
#define TRACE(PC,I,SP,RP) \
 
        fprintf(stderr, "%04x %04x %04x %04x\n", (unsigned)(PC), (unsigned)(I), (unsigned)(SP), (unsigned)(RP));
 
#else
 
#define TRACE(PC, I, SP, RP)
 
#endif
 
 
 
typedef uint16_t uw_t;
 
typedef int16_t  sw_t;
 
typedef uint32_t ud_t;
 
 
 
typedef struct { uw_t pc, t, rp, sp, core[CORE/sizeof(uw_t)]; } forth_t;
 
 
 
static const uw_t embed_image[] = { /* MAGIC! */
 
0x094d, 0x034d, 0x4689, 0x4854, 0x0a0d, 0x0a1a, 0x1570, 0x00fe, 0x0001, 0x1984, 0x0001, 0x628d, 0x601c, 0x628d, 0x631c, 0x1570,
 
0x10d2, 0x1566, 0x149e, 0x0024, 0x0000, 0x6403, 0x7075, 0x609d, 0x0028, 0x6f04, 0x6576, 0x0072, 0x619d, 0x0030, 0x6906, 0x766e,
 
0x7265, 0x0074, 0x6a1c, 0x003a, 0x7503, 0x2b6d, 0x651c, 0x0046, 0x2b01, 0x653f, 0x004e, 0x7503, 0x2a6d, 0x661c, 0x0054, 0x2a01,
 
0x663f, 0x005c, 0x7304, 0x6177, 0x0070, 0x619c, 0x0062, 0x6e03, 0x7069, 0x601f, 0x006c, 0x6404, 0x6f72, 0x0070, 0x611f, 0x0074,
 
0x4001, 0x631c, 0x007e, 0x2101, 0x641f, 0x0084, 0x7206, 0x6873, 0x6669, 0x0074, 0x701f, 0x008a, 0x6c06, 0x6873, 0x6669, 0x0074,
 
0x711f, 0x0096, 0x3d01, 0x6d1f, 0x00a2, 0x7502, 0x003c, 0x6e1f, 0x00a8, 0x3c01, 0x6f1f, 0x00b0, 0x6103, 0x646e, 0x671f, 0x00b6,
 
0x7803, 0x726f, 0x691f, 0x00be, 0x6f02, 0x0072, 0x681f, 0x00c6, 0x3102, 0x002d, 0x6b1c, 0x00ce, 0x3002, 0x003d, 0x6c1c, 0x00d6,
 
0x2805, 0x7962, 0x2965, 0x7b1c, 0x00de, 0x7203, 0x3f78, 0x789d, 0x00e8, 0x7403, 0x2178, 0x773f, 0x00f0, 0x2806, 0x6173, 0x6576,
 
0x0029, 0x761f, 0x00f8, 0x7505, 0x6d2f, 0x646f, 0x799c, 0x0104, 0x2f04, 0x6f6d, 0x0064, 0x7a9c, 0x010e, 0x2f01, 0x7a1f, 0x0118,
 
0x6d03, 0x646f, 0x7a3f, 0x811e, 0x6504, 0x6978, 0x0074, 0x601c, 0x8126, 0x3e02, 0x0072, 0x6147, 0x8130, 0x7202, 0x003e, 0x628d,
 
0x8138, 0x7202, 0x0040, 0x6281, 0x8140, 0x7205, 0x7264, 0x706f, 0x600c, 0x0148, 0x6304, 0x6c65, 0x006c, 0x400d, 0x0002, 0x0152,
 
0x3e03, 0x6e69, 0x400b, 0x0000, 0x015e, 0x7305, 0x6174, 0x6574, 0x400b, 0x0000, 0x0168, 0x6803, 0x646c, 0x400b, 0x0000, 0x0174,
 
0x6204, 0x7361, 0x0065, 0x400b, 0x0010, 0x017e, 0x7304, 0x6170, 0x006e, 0x400b, 0x0000, 0x018a, 0x2305, 0x6f76, 0x7363, 0x400d,
 
0x0008, 0x0196, 0x6205, 0x622f, 0x6675, 0x400d, 0x0400, 0x01a2, 0x6203, 0x6b6c, 0x400b, 0x0000, 0x01ae, 0x7003, 0x6461, 0x400d,
 
0x4280, 0x01b8, 0x3c09, 0x696c, 0x6574, 0x6172, 0x3e6c, 0x400b, 0x0c2a, 0x01c2, 0x3c06, 0x6f62, 0x746f, 0x003e, 0x400b, 0x12e2,
 
0x01d2, 0x3c04, 0x6b6f, 0x003e, 0x400b, 0x0000, 0x8000, 0x6a1c, 0xffff, 0x6a1c, 0x6103, 0x6103, 0x8000, 0x601c, 0x8172, 0x631c,
 
0x8001, 0x671f, 0x8166, 0x641f, 0x8166, 0x631c, 0x01e0, 0x3205, 0x7264, 0x706f, 0x6103, 0x611f, 0x020c, 0x3102, 0x002b, 0x8001,
 
0x653f, 0x0218, 0x6e06, 0x6765, 0x7461, 0x0065, 0x6a00, 0x010f, 0x0222, 0x2d01, 0x4116, 0x653f, 0x6181, 0x011a, 0x6181, 0x653f,
 
0x0230, 0x6107, 0x696c, 0x6e67, 0x6465, 0x6081, 0x4100, 0x653f, 0x0240, 0x6203, 0x6579, 0x8000, 0x7b1c, 0x8002, 0x011a, 0x0250,
 
0x6305, 0x6c65, 0x2b6c, 0x8002, 0x653f, 0x025e, 0x6305, 0x6c65, 0x736c, 0x8001, 0x711f, 0x026a, 0x6305, 0x6168, 0x7372, 0x8001,
 
0x701f, 0x0276, 0x3f04, 0x7564, 0x0070, 0x6081, 0x2148, 0x609d, 0x601c, 0x0282, 0x3e01, 0x6180, 0x6f1f, 0x0292, 0x7502, 0x003e,
 
0x6180, 0x6e1f, 0x6e03, 0x6a1c, 0x029a, 0x3c02, 0x003e, 0x6d03, 0x6a1c, 0x02a8, 0x3003, 0x3e3c, 0x6c00, 0x6a1c, 0x02b2, 0x3002,
 
0x003e, 0x8000, 0x014b, 0x02bc, 0x3002, 0x003c, 0x8000, 0x6f1f, 0x02c6, 0x3204, 0x7564, 0x0070, 0x6181, 0x619d, 0x02d0, 0x7404,
 
0x6375, 0x006b, 0x6180, 0x619d, 0x02dc, 0x2b02, 0x0021, 0x4172, 0x6300, 0x6523, 0x6180, 0x641f, 0x02e8, 0x3103, 0x212b, 0x8001,
 
0x6180, 0x0177, 0x02f8, 0x3103, 0x212d, 0x40f6, 0x6180, 0x0177, 0x0304, 0x3202, 0x0021, 0x4172, 0x6403, 0x4133, 0x641f, 0x0310,
 
0x3202, 0x0040, 0x6081, 0x4133, 0x6300, 0x6180, 0x631c, 0x031e, 0x670b, 0x7465, 0x632d, 0x7275, 0x6572, 0x746e, 0x8026, 0x631c,
 
0x032e, 0x730b, 0x7465, 0x632d, 0x7275, 0x6572, 0x746e, 0x8026, 0x641f, 0x0340, 0x6202, 0x006c, 0x8020, 0x601c, 0x0352, 0x7706,
 
0x7469, 0x6968, 0x006e, 0x411c, 0x6147, 0x411a, 0x628d, 0x6e1f, 0x035c, 0x6103, 0x7362, 0x6081, 0x4166, 0x21bf, 0x0116, 0x601c,
 
0x0370, 0x7403, 0x6269, 0xc122, 0x4133, 0x631c, 0x0380, 0x7306, 0x756f, 0x6372, 0x0065, 0xc122, 0x0192, 0x038c, 0x7309, 0x756f,
 
0x6372, 0x2d65, 0x6469, 0xc006, 0x631c, 0x039a, 0x6403, 0x3d30, 0x6c00, 0x6180, 0x6c00, 0x671f, 0x03aa, 0x6407, 0x656e, 0x6167,
 
0x6574, 0x6a00, 0x6147, 0x6a00, 0x8001, 0x6500, 0x628d, 0x653f, 0x03b8, 0x6507, 0x6578, 0x7563, 0x6574, 0x6147, 0x601c, 0x6300,
 
0x4145, 0x21f3, 0x6147, 0x601c, 0x03d0, 0x6302, 0x0040, 0x6381, 0x6180, 0x4100, 0x21fd, 0x8008, 0x701f, 0x80ff, 0x671f, 0x03e8,
 
0x6302, 0x0021, 0x6180, 0x80ff, 0x6703, 0x6081, 0x8008, 0x7103, 0x6803, 0x6180, 0x6180, 0x6181, 0x6081, 0x6300, 0x6180, 0x4100,
 
0x6c00, 0x80ff, 0x6903, 0x6147, 0x6181, 0x6903, 0x628d, 0x6703, 0x6903, 0x6180, 0x641f, 0x40fe, 0x6c1c, 0x03fe, 0x6804, 0x7265,
 
0x0065, 0x801e, 0x631c, 0x043a, 0x6105, 0x696c, 0x6e67, 0x4221, 0x4125, 0x801e, 0x641f, 0x0446, 0x6105, 0x6c6c, 0x746f, 0x801e,
 
0x0177, 0x0456, 0x7203, 0x746f, 0x6147, 0x6180, 0x628d, 0x619c, 0x0462, 0x2d04, 0x6f72, 0x0074, 0x6180, 0x6147, 0x6180, 0x628d,
 
0x601c, 0x6240, 0x6180, 0x6147, 0x6147, 0x601c, 0x628d, 0x628d, 0x6180, 0x6240, 0x601c, 0x4246, 0x4145, 0x2253, 0x6b00, 0x6147,
 
0x6300, 0x6147, 0x601c, 0x4133, 0x6147, 0x601c, 0x0470, 0x6d03, 0x6e69, 0x416c, 0x6f03, 0x225d, 0x611f, 0x601f, 0x04ac, 0x6d03,
 
0x7861, 0x416c, 0x414b, 0x025b, 0x04bc, 0x6b03, 0x7965, 0xc010, 0x41ef, 0x6081, 0x40f6, 0x6d03, 0x226f, 0x412b, 0x00fc, 0x601c,
 
0x04c8, 0x2f07, 0x7473, 0x6972, 0x676e, 0x6181, 0x4259, 0x4234, 0x411e, 0x423c, 0x011a, 0x8001, 0x0275, 0x04e0, 0x6305, 0x756f,
 
0x746e, 0x6081, 0x410f, 0x6180, 0x01f7, 0x6181, 0x01f7, 0x6181, 0x8008, 0x7003, 0x6903, 0x6081, 0x8004, 0x7003, 0x6903, 0x6081,
 
0x8005, 0x7103, 0x6903, 0x6081, 0x800c, 0x7103, 0x6903, 0x6180, 0x8008, 0x7103, 0x691f, 0x04fa, 0x6303, 0x6372, 0x8000, 0x6a00,
 
0x6147, 0x6081, 0x22ab, 0x4285, 0x628d, 0x6180, 0x4287, 0x6147, 0x8001, 0x4275, 0x02a1, 0x410a, 0x628d, 0x601c, 0x6300, 0xbfff,
 
0x671f, 0x419e, 0x02ae, 0x0536, 0x6504, 0x696d, 0x0074, 0xc012, 0x01ef, 0x0566, 0x6302, 0x0072, 0x800d, 0x42b7, 0x800a, 0x02b7,
 
0x803a, 0x02b7, 0x0572, 0x7305, 0x6170, 0x6563, 0x8020, 0x02b7, 0x8020, 0x6180, 0x8000, 0x4261, 0x6147, 0x02d0, 0x6081, 0x42b7,
 
0x424b, 0x059c, 0x611f, 0x0584, 0x6405, 0x7065, 0x6874, 0x7281, 0xc400, 0x411a, 0x013f, 0x05a6, 0x7004, 0x6369, 0x006b, 0x4139,
 
0x7281, 0x6180, 0x411a, 0x631c, 0x807f, 0x6703, 0x6081, 0x807f, 0x8020, 0x41b3, 0x22ed, 0x6103, 0x805f, 0x601c, 0x05b6, 0x7404,
 
0x7079, 0x0065, 0x8000, 0x6147, 0x6081, 0x22ff, 0x6180, 0x4281, 0x6281, 0x22fb, 0x42e4, 0x42b7, 0x6180, 0x6b00, 0x02f4, 0x600c,
 
0x010a, 0x4281, 0x02f2, 0x40f6, 0x02f3, 0x05dc, 0x6305, 0x6f6d, 0x6576, 0x6147, 0x0313, 0x6147, 0x6081, 0x41f7, 0x6281, 0x4202,
 
0x410f, 0x628d, 0x410f, 0x424b, 0x0616, 0x010a, 0x060a, 0x6604, 0x6c69, 0x006c, 0x6180, 0x6147, 0x6180, 0x0321, 0x416c, 0x4202,
 
0x410f, 0x424b, 0x063c, 0x010a, 0x6147, 0x0327, 0x6103, 0x424b, 0x064c, 0x601c, 0x062c, 0x6305, 0x7461, 0x6863, 0x7281, 0x6147,
 
0xc00a, 0x6300, 0x6147, 0x7381, 0xc00a, 0x6403, 0x41ed, 0x628d, 0xc00a, 0x6403, 0x628d, 0x00fb, 0x0654, 0x7405, 0x7268, 0x776f,
 
0x4145, 0x234c, 0xc00a, 0x6300, 0x7503, 0x628d, 0xc00a, 0x6403, 0x6240, 0x7400, 0x6103, 0x628d, 0x601c, 0x4116, 0x0340, 0x8001,
 
0x42d7, 0x6b00, 0x4150, 0x2356, 0x8004, 0x034d, 0x601c, 0x8002, 0x0350, 0x0678, 0x7506, 0x2f6d, 0x6f6d, 0x0064, 0x4145, 0x6c00,
 
0x2363, 0x800a, 0x034d, 0x416c, 0x6e03, 0x2386, 0x4116, 0x800f, 0x6147, 0x6147, 0x6081, 0x6500, 0x6147, 0x6147, 0x6081, 0x6500,
 
0x628d, 0x6523, 0x6081, 0x628d, 0x6281, 0x6180, 0x6147, 0x6500, 0x628d, 0x6803, 0x2380, 0x6147, 0x6103, 0x410f, 0x628d, 0x0381,
 
0x6103, 0x628d, 0x424b, 0x06d2, 0x6103, 0x619c, 0x6103, 0x410a, 0x40f6, 0x609d, 0x06b2, 0x6407, 0x6365, 0x6d69, 0x6c61, 0x800a,
 
0x8188, 0x641f, 0x0714, 0x6803, 0x7865, 0x8010, 0x8188, 0x641f, 0x8188, 0x6300, 0x6081, 0x8002, 0x411a, 0x8022, 0x4150, 0x23a3,
 
0x4395, 0x8028, 0x034d, 0x601c, 0x0724, 0x6804, 0x6c6f, 0x0064, 0x817c, 0x6300, 0x6b00, 0x6081, 0x817c, 0x6403, 0x4202, 0x817c,
 
0x6300, 0xc280, 0x8100, 0x6523, 0x4150, 0x23b8, 0x8011, 0x034d, 0x601c, 0x6081, 0x6147, 0x435e, 0x628d, 0x6180, 0x6147, 0x435e,
 
0x628d, 0x0234, 0x8009, 0x6181, 0x6f03, 0x8007, 0x6703, 0x6523, 0x8030, 0x653f, 0x0748, 0x2302, 0x003e, 0x410a, 0x817c, 0x6300,
 
0xc280, 0x6181, 0x011a, 0x0794, 0x2301, 0x4357, 0x8000, 0x8188, 0x6300, 0x43b9, 0x43c2, 0x03a8, 0x07a6, 0x2302, 0x0073, 0x43d5,
 
0x416c, 0x41d8, 0x23df, 0x601c, 0x07b8, 0x3c02, 0x0023, 0xc280, 0x817c, 0x641f, 0x07c8, 0x7304, 0x6769, 0x006e, 0x4166, 0x23f2,
 
0x802d, 0x03a8, 0x601c, 0x6044, 0x41bb, 0x8000, 0x43e7, 0x43df, 0x628d, 0x43ee, 0x03cd, 0x8000, 0x43e7, 0x43df, 0x03cd, 0x07d4,
 
0x7503, 0x722e, 0x6147, 0x43fb, 0x628d, 0x411c, 0x42c8, 0x02f2, 0x8005, 0x0402, 0x07fe, 0x7502, 0x002e, 0x43fb, 0x42c6, 0x02f2,
 
0x0814, 0x2e01, 0x4398, 0x800a, 0x6903, 0x2417, 0x040d, 0x43f3, 0x42c6, 0x02f2, 0xc000, 0x4221, 0x011a, 0x441a, 0x040d, 0x0820,
 
0x7005, 0x6361, 0x246b, 0x4125, 0x6044, 0x6181, 0x6081, 0x8002, 0x4116, 0x6703, 0x411a, 0x411e, 0x8000, 0x417a, 0x416c, 0x4202,
 
0x410f, 0x6180, 0x4309, 0x628d, 0x601c, 0x083e, 0x3d07, 0x7473, 0x6972, 0x676e, 0x6147, 0x6180, 0x628d, 0x6181, 0x6903, 0x2442,
 
0x6103, 0x00fa, 0x6147, 0x044d, 0x4281, 0x6147, 0x6180, 0x4281, 0x628d, 0x6903, 0x244d, 0x600c, 0x00fa, 0x424b, 0x0888, 0x410a,
 
0x00f6, 0x6181, 0x4202, 0x010f, 0x086a, 0x6106, 0x6363, 0x7065, 0x0074, 0x411e, 0x6181, 0x6981, 0x2468, 0x4267, 0x6081, 0x800a,
 
0x6903, 0x2464, 0x4451, 0x0467, 0x6103, 0x6003, 0x6081, 0x045b, 0x6103, 0x011c, 0x08a8, 0x6506, 0x7078, 0x6365, 0x0074, 0xc014,
 
0x41ef, 0x8194, 0x6403, 0x611f, 0x08d4, 0x7105, 0x6575, 0x7972, 0x41c3, 0x8050, 0xc014, 0x41ef, 0xc122, 0x6403, 0x40fb, 0x0102,
 
0x08e8, 0x6e03, 0x6166, 0x42af, 0x0133, 0x0900, 0x6303, 0x6166, 0x4483, 0x6081, 0x41f7, 0x6523, 0x4133, 0x8001, 0x6a00, 0x671f,
 
0x4483, 0x0301, 0x6300, 0xc000, 0x6703, 0x6c00, 0x6c1c, 0x6300, 0x40f8, 0x6703, 0x0495, 0x8126, 0x8152, 0x01b3, 0x6180, 0x6147,
 
0x6081, 0x6081, 0x24b6, 0x6081, 0x4483, 0x4281, 0x6281, 0x4281, 0x443a, 0x24b2, 0x6081, 0x4492, 0x24af, 0x8001, 0x04b0, 0x40f6,
 
0x600c, 0x601c, 0x6003, 0x6081, 0x42ae, 0x04a1, 0x600c, 0x00fa, 0x6147, 0xc110, 0x6381, 0x24cb, 0x6381, 0x6300, 0x6281, 0x6180,
 
0x449e, 0x4145, 0x24c9, 0x6147, 0x4234, 0x6103, 0x628d, 0x600c, 0x601c, 0x4133, 0x04ba, 0x40fb, 0x628d, 0x00fc, 0x090a, 0x730f,
 
0x6165, 0x6372, 0x2d68, 0x6f77, 0x6472, 0x696c, 0x7473, 0x449e, 0x4234, 0x611f, 0x099c, 0x6604, 0x6e69, 0x0064, 0x44b8, 0x4234,
 
0x611f, 0x8030, 0x803a, 0x01b3, 0x8061, 0x807b, 0x01b3, 0x8041, 0x805b, 0x01b3, 0x6081, 0x44e7, 0x24ef, 0x8020, 0x691f, 0x601c,
 
0x44ea, 0x6081, 0x44e4, 0x24f6, 0x8057, 0x011a, 0x6081, 0x44e1, 0x24fb, 0x8030, 0x011a, 0x6103, 0x00f6, 0x44ea, 0x44f0, 0x8188,
 
0x6300, 0x6e1f, 0x416c, 0x4241, 0x6103, 0x41f7, 0x6081, 0x44fd, 0x2511, 0x6180, 0x8188, 0x6300, 0x6623, 0x6180, 0x44f0, 0x6523,
 
0x0514, 0x6103, 0x4246, 0x601c, 0x4246, 0x427b, 0x6081, 0x6c00, 0x2502, 0x601c, 0x4285, 0x802d, 0x6d03, 0x2520, 0x427b, 0x00f6,
 
0x00fc, 0x4285, 0x8024, 0x6d03, 0x2527, 0x427b, 0x0395, 0x4285, 0x8023, 0x6d03, 0x252d, 0x427b, 0x038f, 0x601c, 0x09b4, 0x3e07,
 
0x756e, 0x626d, 0x7265, 0x4398, 0x6147, 0x451a, 0x6147, 0x4521, 0x4502, 0x628d, 0x253e, 0x4234, 0x4116, 0x423c, 0x628d, 0x8188,
 
0x641f, 0x8000, 0x423c, 0x4533, 0x6003, 0x6c1c, 0x6147, 0x0551, 0x8020, 0x6181, 0x6281, 0x6523, 0x41f7, 0x6f03, 0x2551, 0x628d,
 
0x010f, 0x424b, 0x0a90, 0x00fc, 0x6147, 0x6081, 0x2564, 0x4285, 0x6281, 0x411a, 0x6281, 0x8020, 0x6d03, 0xc000, 0x41ef, 0x2562,
 
0x600c, 0x601c, 0x427b, 0x0555, 0x600c, 0x601c, 0x2568, 0x0161, 0x015c, 0x4566, 0x6a1c, 0x8acc, 0xc000, 0x6403, 0x0554, 0x8ad2,
 
0xc000, 0x6403, 0x0554, 0x6147, 0x6181, 0x628d, 0x6180, 0x4241, 0x6281, 0x456b, 0x416c, 0x628d, 0x456f, 0x6180, 0x628d, 0x411a,
 
0x6147, 0x411a, 0x628d, 0x010f, 0x0a5c, 0x7005, 0x7261, 0x6573, 0x6147, 0x41c3, 0x4104, 0x6523, 0xc122, 0x6300, 0x4104, 0x411a,
 
0x628d, 0x4573, 0x8166, 0x4177, 0x4546, 0x8000, 0x0261, 0x4b08, 0x2901, 0x601c, 0x4b2e, 0x2801, 0x8029, 0x4588, 0x010a, 0x0b34,
 
0x2e02, 0x0028, 0x8029, 0x4588, 0x02f2, 0x4b3e, 0x5c01, 0xc122, 0x6300, 0x0102, 0x6081, 0x801f, 0x4150, 0x25b0, 0x8013, 0x034d,
 
0x601c, 0x0b4a, 0x7704, 0x726f, 0x0064, 0x434f, 0x4588, 0x45aa, 0x4221, 0x0423, 0x0b62, 0x7405, 0x6b6f, 0x6e65, 0x8020, 0x05b5,
 
0x0b74, 0x6304, 0x6168, 0x0072, 0x45be, 0x4281, 0x6103, 0x01f7, 0x6081, 0xbf00, 0x4150, 0x25ce, 0x8008, 0x034d, 0x601c, 0x0b80,
 
0x2c01, 0x4221, 0x6081, 0x4133, 0x45c8, 0x4228, 0x641f, 0x0b9e, 0x6302, 0x002c, 0x4221, 0x45c8, 0x4202, 0x801e, 0x017f, 0x40f8,
 
0x6803, 0x05d1, 0xcbae, 0x6c07, 0x7469, 0x7265, 0x6c61, 0x6081, 0x40f8, 0x6703, 0x25ef, 0x6a00, 0x45df, 0xea00, 0x05d1, 0x05df,
 
0x413f, 0xc000, 0x681f, 0x0bc4, 0x6308, 0x6d6f, 0x6970, 0x656c, 0x002c, 0x45f0, 0x05d1, 0x6081, 0x449b, 0x2601, 0x4488, 0x6300,
 
0x05d1, 0x4488, 0x05f9, 0x41cb, 0x42f2, 0x800d, 0x034d, 0x6081, 0x4497, 0x260e, 0x41cb, 0x42f2, 0x800e, 0x034d, 0x601c, 0x0be6,
 
0x2809, 0x696c, 0x6574, 0x6172, 0x296c, 0x40fe, 0x2618, 0x05e7, 0x601c, 0x0c1e, 0x6909, 0x746e, 0x7265, 0x7270, 0x7465, 0x44de,
 
0x4145, 0x262d, 0x40fe, 0x2629, 0x4161, 0x2628, 0x4488, 0x01ed, 0x05fb, 0x6103, 0x4607, 0x4488, 0x01ed, 0x6081, 0x4281, 0x4541,
 
0x2634, 0x6003, 0x81d0, 0x01ef, 0x0603, 0x8c32, 0x6307, 0x6d6f, 0x6970, 0x656c, 0x628d, 0x6381, 0x45d1, 0x4133, 0x6147, 0x601c,
 
0x0c6a, 0x6909, 0x6d6d, 0x6465, 0x6169, 0x6574, 0xc000, 0x42b1, 0x4172, 0x6300, 0x6903, 0x017a, 0x0c80, 0x7306, 0x756d, 0x6764,
 
0x0065, 0x42b1, 0x4483, 0x8080, 0x6180, 0x0648, 0x628d, 0x6281, 0x628d, 0x4281, 0x6523, 0x4125, 0x6147, 0x6180, 0x6147, 0x601c,
 
0x4656, 0x601c, 0x4656, 0x0301, 0x8022, 0x45b5, 0x4281, 0x6523, 0x0228, 0xcc98, 0x2402, 0x0022, 0x463a, 0x4660, 0x0664, 0xccd2,
 
0x2e02, 0x0022, 0x463a, 0x4662, 0x0664, 0x0cde, 0x6105, 0x6f62, 0x7472, 0x40f6, 0x7b1c, 0x6180, 0x2681, 0x4301, 0x42bc, 0x4679,
 
0x0682, 0x6103, 0x601c, 0x4656, 0x067b, 0xccea, 0x6106, 0x6f62, 0x7472, 0x0022, 0x463a, 0x4683, 0x0664, 0xc126, 0xc122, 0x4133,
 
0x6403, 0x8000, 0x4102, 0x8000, 0xc006, 0x641f, 0x0d0a, 0x5d01, 0x40f6, 0x8172, 0x641f, 0x4d2c, 0x5b01, 0x8000, 0x8172, 0x641f,
 
0x4145, 0x26aa, 0x4412, 0x803f, 0x42b7, 0x42bc, 0xc400, 0x7400, 0x468d, 0x069d, 0x601c, 0x421b, 0x26b1, 0x4662, 0x2003, 0x6b6f,
 
0x02bc, 0x601c, 0x7281, 0xc400, 0x6e03, 0x26b8, 0x8004, 0x034d, 0x601c, 0x45be, 0x6081, 0x41f7, 0x26c0, 0x461f, 0x46b2, 0x06b9,
 
0x6103, 0x81ea, 0x01ef, 0x0d36, 0x7104, 0x6975, 0x0074, 0x468d, 0x469d, 0x4478, 0x8d72, 0x432e, 0x46a0, 0x06c9, 0x601c, 0x41cb,
 
0x4104, 0xc006, 0x6300, 0x81ea, 0x631c, 0x81ea, 0x6403, 0xc006, 0x6403, 0x4102, 0xc122, 0x018b, 0x0d86, 0x6508, 0x6176, 0x756c,
 
0x7461, 0x0065, 0x46cf, 0x4241, 0x4241, 0x6147, 0x8000, 0x40f6, 0x8000, 0x46d5, 0x8d72, 0x432e, 0x628d, 0x4246, 0x4246, 0x46d5,
 
0x0340, 0x468d, 0x80ee, 0xc010, 0x6403, 0x80f6, 0xc012, 0x6403, 0x8d56, 0x88b2, 0xc014, 0x6403, 0x81ea, 0x641f, 0xabad, 0x4157,
 
0x2703, 0x8016, 0x034d, 0x601c, 0x6081, 0x42b1, 0x6300, 0x449e, 0x2717, 0x42c6, 0x410a, 0xc002, 0x6300, 0x4483, 0x4301, 0x4662,
 
0x200a, 0x6572, 0x6564, 0x6966, 0x656e, 0x0064, 0x02bc, 0x601c, 0x4281, 0x6c00, 0x271d, 0x800a, 0x034d, 0x6b1c, 0x45be, 0x44de,
 
0x6c00, 0x2723, 0x0603, 0x601c, 0x471e, 0x0488, 0x4db8, 0x2701, 0x4724, 0x40fe, 0x272c, 0x05e7, 0x601c, 0xce4c, 0x5b09, 0x6f63,
 
0x706d, 0x6c69, 0x5d65, 0x4724, 0x05f9, 0xce5a, 0x5b06, 0x6863, 0x7261, 0x005d, 0x45c4, 0x05e7, 0xce6a, 0x3b01, 0x46fe, 0xe01c,
 
0x45d1, 0x469d, 0x4145, 0x2746, 0x419e, 0x641f, 0x601c, 0x0e78, 0x3a01, 0x4227, 0x4221, 0x6081, 0xc002, 0x6403, 0x42b1, 0x45d1,
 
0x45be, 0x4718, 0x4704, 0x4281, 0x6523, 0x4228, 0xabad, 0x0698, 0xce8e, 0x6205, 0x6765, 0x6e69, 0x0221, 0xceb0, 0x7505, 0x746e,
 
0x6c69, 0x413f, 0xa000, 0x6803, 0x05d1, 0xceba, 0x6105, 0x6167, 0x6e69, 0x413f, 0x05d1, 0x4221, 0x00fc, 0x476b, 0x0769, 0xceca,
 
0x6902, 0x0066, 0x476b, 0x0761, 0xcede, 0x7404, 0x6568, 0x006e, 0x4221, 0x413f, 0x6181, 0x6300, 0x6803, 0x017a, 0xcee8, 0x6504,
 
0x736c, 0x0065, 0x476d, 0x6180, 0x0778, 0xcefc, 0x7705, 0x6968, 0x656c, 0x0772, 0xcf0a, 0x7206, 0x7065, 0x6165, 0x0074, 0x6180,
 
0x4769, 0x0778, 0xc002, 0x6300, 0x0488, 0xcf14, 0x7207, 0x6365, 0x7275, 0x6573, 0x4792, 0x05f9, 0xcf2a, 0x7404, 0x6961, 0x006c,
 
0x4792, 0x0769, 0x0f38, 0x6306, 0x6572, 0x7461, 0x0065, 0x4749, 0x6103, 0x463a, 0x400b, 0x419e, 0x6403, 0x069d, 0x0f44, 0x3e05,
 
0x6f62, 0x7964, 0x0133, 0x628d, 0x413f, 0x4221, 0x413f, 0x4792, 0x6081, 0x4133, 0x45df, 0x6403, 0x05d1, 0xcf5c, 0x6405, 0x656f,
 
0x3e73, 0x463a, 0x47b3, 0x601c, 0x0f7a, 0x7608, 0x7261, 0x6169, 0x6c62, 0x0065, 0x47a7, 0x8000, 0x05d1, 0x0f88, 0x6308, 0x6e6f,
 
0x7473, 0x6e61, 0x0074, 0x47a7, 0x801a, 0x45f0, 0x4221, 0x412d, 0x6403, 0x05d1, 0x0f9a, 0x3a07, 0x6f6e, 0x616e, 0x656d, 0x476b,
 
0xabad, 0x0698, 0xcfb4, 0x6603, 0x726f, 0xe147, 0x45d1, 0x0221, 0xcfc4, 0x6e04, 0x7865, 0x0074, 0x463a, 0x424b, 0x05d1, 0xcfd0,
 
0x6103, 0x7466, 0x6103, 0x476d, 0x475c, 0x619c, 0x0fde, 0x6804, 0x6469, 0x0065, 0x471e, 0x0652, 0x8000, 0x6147, 0x6381, 0x6281,
 
0x4157, 0x2804, 0x4133, 0x07fe, 0x600c, 0x601c, 0x0fec, 0x6709, 0x7465, 0x6f2d, 0x6472, 0x7265, 0xc110, 0x47fc, 0x6081, 0x412d,
 
0x6180, 0xc110, 0x411a, 0x413f, 0x6044, 0x6b00, 0x6081, 0x4166, 0x281b, 0x8032, 0x034d, 0x6147, 0x0820, 0x6381, 0x6180, 0x412d,
 
0x424b, 0x103a, 0x6300, 0x628d, 0x601c, 0x0000, 0x660e, 0x726f, 0x6874, 0x772d, 0x726f, 0x6c64, 0x7369, 0x0074, 0x8024, 0x601c,
 
0x104a, 0x7309, 0x7465, 0x6f2d, 0x6472, 0x7265, 0x6081, 0x40f6, 0x6d03, 0x283e, 0x6103, 0x8020, 0x8001, 0x0836, 0x6081, 0x8008,
 
0x414b, 0x2844, 0x8031, 0x034d, 0xc110, 0x6180, 0x6147, 0x084b, 0x4172, 0x6403, 0x4133, 0x424b, 0x1090, 0x8000, 0x017a, 0x1060,
 
0x6605, 0x726f, 0x6874, 0x8020, 0x482e, 0x8002, 0x0836, 0x4483, 0x41f7, 0x8080, 0x6703, 0x6c1c, 0x42c6, 0x6081, 0x2867, 0x6081,
 
0x4857, 0x2865, 0x6081, 0x4490, 0x42c6, 0x42ae, 0x085d, 0x6103, 0x02bc, 0x109e, 0x7705, 0x726f, 0x7364, 0x480c, 0x4145, 0x2879,
 
0x6180, 0x6081, 0x42bc, 0x440d, 0x42c0, 0x6300, 0x485c, 0x6b00, 0x086e, 0x601c, 0x100c, 0x6f04, 0x6c6e, 0x0079, 0x40f6, 0x0836,
 
0x10f4, 0x640b, 0x6665, 0x6e69, 0x7469, 0x6f69, 0x736e, 0xc110, 0x6300, 0x01a7, 0x6081, 0x2898, 0x6b00, 0x6180, 0x6147, 0x488a,
 
0x6181, 0x6281, 0x6903, 0x2897, 0x410f, 0x628d, 0x023c, 0x600c, 0x601c, 0x1100, 0x2d06, 0x726f, 0x6564, 0x0072, 0x480c, 0x488a,
 
0x6003, 0x0836, 0x1132, 0x2b06, 0x726f, 0x6564, 0x0072, 0x6044, 0x489e, 0x480c, 0x628d, 0x6180, 0x410f, 0x0836, 0x1144, 0x6506,
 
0x6964, 0x6f74, 0x0072, 0x438f, 0x8022, 0x08a7, 0x115c, 0x7506, 0x6470, 0x7461, 0x0065, 0x40f6, 0xc00c, 0x641f, 0x81b6, 0x631c,
 
0x48be, 0x653f, 0x116c, 0x7304, 0x7661, 0x0065, 0x8000, 0x4221, 0x7603, 0x0340, 0x1184, 0x6605, 0x756c, 0x6873, 0xc00c, 0x6300,
 
0x28d5, 0x8000, 0x40f6, 0x7603, 0x0340, 0x601c, 0x1194, 0x6205, 0x6f6c, 0x6b63, 0x434f, 0x6081, 0x803f, 0x4150, 0x28e1, 0x8023,
 
0x034d, 0x6081, 0x81b6, 0x6403, 0x800a, 0x711f, 0x8006, 0x711f, 0x8006, 0x701f, 0x6180, 0x48da, 0x6180, 0x48e6, 0x6523, 0x8040,
 
0x601c, 0x48ea, 0x06e2, 0x11ac, 0x6c04, 0x616f, 0x0064, 0x8000, 0x8010, 0x6b00, 0x6147, 0x416c, 0x4241, 0x48f1, 0x4246, 0x410f,
 
0x424b, 0x11f6, 0x010a, 0x807c, 0x02b7, 0x8003, 0x42c8, 0x8040, 0x802d, 0x42c9, 0x02bc, 0x6081, 0x8002, 0x0402, 0x8020, 0x031a,
 
0x48da, 0x611f, 0x11e6, 0x6c04, 0x7369, 0x0074, 0x6081, 0x4910, 0x42bc, 0x4905, 0x8000, 0x6081, 0x8010, 0x6f03, 0x2928, 0x416c,
 
0x490b, 0x4903, 0x48ea, 0x4303, 0x4903, 0x42bc, 0x410f, 0x091b, 0x4905, 0x010a, 0x8014, 0x6300, 0x4100, 0x6c1c, 0x8001, 0x8014,
 
0x0648, 0x492a, 0x2934, 0x00fc, 0x800c, 0x6300, 0x4221, 0x6903, 0x293b, 0x8002, 0x601c, 0x800e, 0x6300, 0x8000, 0x800e, 0x6403,
 
0x8000, 0x4221, 0x429e, 0x6903, 0x2947, 0x8003, 0x601c, 0x492e, 0x00fc, 0x1224, 0x6304, 0x6c6f, 0x0064, 0x4931, 0x4145, 0x2952,
 
0x4116, 0x7b1c, 0x8010, 0x48da, 0x8400, 0x8000, 0x431a, 0x8012, 0x4910, 0x46f1, 0x4853, 0xc400, 0x7400, 0x81de, 0x41ef, 0x012b,
 
0x4395, 0x42bc, 0x4662, 0x6508, 0x4f46, 0x5452, 0x2048, 0x0056, 0x9984, 0x8000, 0x4402, 0x42bc, 0x4221, 0x4412, 0x441d, 0x42bc,
 
0x069d, 0x4960, 0x06c7, 0x4172, 0x4488, 0x4157, 0x2978, 0x00fb, 0x0483, 0x42af, 0x4139, 0x6147, 0x6081, 0x298e, 0x42af, 0x6081,
 
0x6281, 0x6180, 0x6381, 0x42af, 0x6180, 0x41b3, 0x298b, 0x42ae, 0x628d, 0x6180, 0x0973, 0x42af, 0x6300, 0x097c, 0x600c, 0x601c,
 
0x6147, 0x480c, 0x6081, 0x29a1, 0x6180, 0x6281, 0x4979, 0x4145, 0x299f, 0x6147, 0x6b00, 0x4324, 0x628d, 0x600c, 0x601c, 0x6b00,
 
0x0992, 0x600c, 0x601c, 0x4990, 0x4145, 0x6c00, 0x29aa, 0x4660, 0x3f03, 0x3f3f, 0x0301, 0x6147, 0x6181, 0x6703, 0x628d, 0x4172,
 
0x6d03, 0x29b4, 0x6003, 0x00f6, 0x00fb, 0x40f8, 0x40f8, 0x49ab, 0x29bd, 0x4662, 0x4c03, 0x5449, 0x601c, 0xe000, 0xe000, 0x49ab,
 
0x29c5, 0x4662, 0x4103, 0x554c, 0x601c, 0xe000, 0xc000, 0x49ab, 0x29cd, 0x4662, 0x4303, 0x4c41, 0x601c, 0xe000, 0xa000, 0x49ab,
 
0x29d5, 0x4662, 0x4203, 0x5a52, 0x601c, 0x40fb, 0x4662, 0x4203, 0x4e52, 0x601c, 0x1292, 0x6409, 0x6365, 0x6d6f, 0x6970, 0x656c,
 
0x6081, 0x49b5, 0xc000, 0x6d03, 0x29e7, 0x42c6, 0x09a3, 0x611f, 0x6147, 0x6081, 0x6281, 0x6e03, 0x29f9, 0x6081, 0x4408, 0x42c0,
 
0x42c6, 0x6381, 0x6081, 0x4408, 0x42c6, 0x49e0, 0x42bc, 0x4133, 0x09e9, 0x600c, 0x611f, 0x13b4, 0x7303, 0x6565, 0x45be, 0x44b8,
 
0x6c00, 0x2a03, 0x0603, 0x6180, 0x6d81, 0x2a08, 0x6103, 0x4221, 0x6147, 0x42bc, 0x42c0, 0x42c6, 0x6081, 0x4490, 0x42c6, 0x6081,
 
0x42bc, 0x4488, 0x628d, 0x49e8, 0x42c6, 0x803b, 0x42b7, 0x6081, 0x4497, 0x2a22, 0x4662, 0x200d, 0x6f63, 0x706d, 0x6c69, 0x2d65,
 
0x6e6f, 0x796c, 0x6081, 0x449b, 0x2a2a, 0x4662, 0x2007, 0x6e69, 0x696c, 0x656e, 0x4492, 0x2a33, 0x4662, 0x200a, 0x6d69, 0x656d,
 
0x6964, 0x7461, 0x0065, 0x02bc, 0x13f6, 0x2e02, 0x0073, 0x42bc, 0x42d7, 0x6147, 0x0a3e, 0x6281, 0x42df, 0x4412, 0x424b, 0x1476,
 
0x4662, 0x2004, 0x733c, 0x0070, 0x601c, 0x413f, 0x6147, 0x0a4c, 0x6381, 0x42c6, 0x4408, 0x4133, 0x424b, 0x1490, 0x601c, 0x1468,
 
0x6404, 0x6d75, 0x0070, 0x8010, 0x6523, 0x8004, 0x7003, 0x6147, 0x0a65, 0x42bc, 0x8010, 0x416c, 0x6181, 0x4408, 0x42c0, 0x42c6,
 
0x4a45, 0x423c, 0x8002, 0x42c8, 0x4303, 0x424b, 0x14b2, 0x611f, 0x48be, 0x08da, 0x6081, 0x8400, 0x48e8, 0x4152, 0x2a71, 0x8018,
 
0x034d, 0x601c, 0x4a6a, 0x48e6, 0x4a68, 0x653f, 0x0000, 0x6201, 0x0910, 0x14ec, 0x6c01, 0x48be, 0x0916, 0x14f2, 0x6e01, 0x8001,
 
0x48c0, 0x4a78, 0x0a7b, 0x14fa, 0x7001, 0x40f6, 0x48c0, 0x4a78, 0x0a7b, 0x1506, 0x6401, 0x4a72, 0x8040, 0x090e, 0x1512, 0x7801,
 
0x4a68, 0x8400, 0x090e, 0x151c, 0x7301, 0x48bb, 0x08ce, 0x1526, 0x7101, 0x8022, 0x089e, 0x152e, 0x6501, 0x4a99, 0x48be, 0x48f7,
 
0x08b3, 0x1536, 0x6902, 0x0061, 0x48e6, 0x6523, 0x4a68, 0x6523, 0x41cb, 0x6103, 0x4104, 0x6523, 0x6180, 0x41cb, 0x6003, 0x4104,
 
0x411a, 0x4309, 0x05a7, 0x1542, 0x6901, 0x8000, 0x6180, 0x0aa4,
 
};
 
 
 
forth_t *embed_new(void)
 
{
 
        forth_t *h = allocate_or_die(sizeof(*h));
 
        memcpy(h->core, embed_image, sizeof(embed_image));
 
        h->pc = 0;
 
        h->t  = 0;
 
        h->rp = RP0;
 
        h->sp = SP0;
 
        return h;
 
}
 
 
 
void embed_free(forth_t *h)
 
{
 
        assert(h);
 
        free(h);
 
}
 
 
 
static int embed_save(forth_t *h, const char *name, size_t start, size_t length)
 
{
 
        assert(h);
 
        if(!name)
 
                return -1;
 
        FILE *output = fopen_or_die(name, "wb");
 
        const int r  = binary_memory_save(output, h->core+start, length);
 
        fclose(output);
 
        return r;
 
}
 
 
 
int embed_forth(forth_t *h, FILE *in, FILE *out, const char *block)
 
{
 
        static const uw_t delta[] = { 0, 1, -2, -1};
 
        assert(h && in && out);
 
        uw_t pc = h->pc, t = h->t, rp = h->rp, sp = h->sp, *m = h->core;
 
        ud_t d;
 
        for(;;) {
 
                const uw_t instruction = m[pc];
 
                TRACE(pc, instruction, sp, rp);
 
                assert(!(sp & 0x8000) && !(rp & 0x8000));
 
 
 
                if(0x8000 & instruction) { /* literal */
 
                        m[++sp] = t;
 
                        t       = instruction & 0x7FFF;
 
                        pc++;
 
                } else if ((0xE000 & instruction) == 0x6000) { /* ALU */
 
                        uw_t n = m[sp], T = t;
 
                        pc = instruction & 0x10 ? m[rp] >> 1 : pc + 1;
 
 
 
                        switch((instruction >> 8u) & 0x1f) {
 
                        case  0: /*T = t;*/                break;
 
                        case  1: T = n;                    break;
 
                        case  2: T = m[rp];                break;
 
                        case  3: T = m[t>>1];              break;
 
                        case  4: m[t>>1] = n; T = m[--sp]; break;
 
                        case  5: d = (ud_t)t + (ud_t)n; T = d >> 16; m[sp] = d; n = d; break;
 
                        case  6: d = (ud_t)t * (ud_t)n; T = d >> 16; m[sp] = d; n = d; break;
 
                        case  7: T &= n;                   break;
 
                        case  8: T |= n;                   break;
 
                        case  9: T ^= n;                   break;
 
                        case 10: T = ~t;                   break;
 
                        case 11: T--;                      break;
 
                        case 12: T = -(t == 0);            break;
 
                        case 13: T = -(t == n);            break;
 
                        case 14: T = -(n < t);             break;
 
                        case 15: T = -((sw_t)n < (sw_t)t); break;
 
                        case 16: T = n >> t;               break;
 
                        case 17: T = n << t;               break;
 
                        case 18: T = sp << 1;              break;
 
                        case 19: T = rp << 1;              break;
 
                        case 20: sp = t >> 1;              break;
 
                        case 21: rp = t >> 1; T = n;       break;
 
                        case 22: T = embed_save(h, block, n>>1, ((ud_t)T+1)>>1); break;
 
                        case 23: T = fputc(t, out);        break;
 
                        case 24: T = fgetc(in);            break;
 
                        case 25: if(t) { T=n/t; t=n%t; n=t; } else { pc=1; T=10; } break;
 
                        case 26: if(t) { T=(sw_t)n/(sw_t)t; t=(sw_t)n%(sw_t)t; n=t; } else { pc=1; T=10; } break;
 
                        case 27: goto finished;
 
                        }
 
                        sp += delta[ instruction       & 0x3];
 
                        rp -= delta[(instruction >> 2) & 0x3];
 
                        if(instruction & 0x20)
 
                                T = n;
 
                        if(instruction & 0x40)
 
                                m[rp] = t;
 
                        if(instruction & 0x80)
 
                                m[sp] = t;
 
                        t = T;
 
                } else if (0x4000 & instruction) { /* call */
 
                        m[--rp] = (pc + 1) << 1;
 
                        pc      = instruction & 0x1FFF;
 
                } else if (0x2000 & instruction) { /* 0branch */
 
                        pc = !t ? instruction & 0x1FFF : pc + 1;
 
                        t  = m[sp--];
 
                } else { /* branch */
 
                        pc = instruction & 0x1FFF;
 
                }
 
        }
 
finished: h->pc = pc; h->sp = sp; h->rp = rp; h->t = t;
 
        return (int16_t)t;
 
}
 
 
 
/* ========================== Embed Forth Virtual Machine ================== */
 
 
 
/* ========================== Assembler ==================================== */
 
 
 
 
 
/* ========================== Main ========================================= */
 
 
 
#ifndef NO_MAIN
 
typedef enum {
 
        DEFAULT_COMMAND,
 
        DISASSEMBLE_COMMAND,
 
        ASSEMBLE_COMMAND,
 
        RUN_COMMAND,
 
        ASSEMBLE_RUN_COMMAND
 
} command_e;
 
 
 
typedef struct {
 
        command_e cmd;
 
        long steps;
 
        bool full_disassembly;
 
        bool debug_mode;
 
        bool hacks;
 
        disassemble_color_method_e dcm;
 
        const char *nvram;
 
} command_args_t;
 
 
 
static const char *help = "\
static const char *help = "\
usage ./h2 [-hvdDarRTH] [-sc number] [-L symbol.file] [-S symbol.file] (file.hex|file.fth)\n\n\
usage ./h2 [-hvdDarRTH] [-sc number] [-L symbol.file] [-S symbol.file] [-e file.fth] (file.hex|file.fth)\n\n\
Brief:     A H2 CPU Assembler, disassembler and Simulator.\n\
Brief:     A H2 CPU Assembler, disassembler and Simulator.\n\
Author:    Richard James Howe\n\
Author:    Richard James Howe\n\
Site:      https://github.com/howerj/forth-cpu\n\
Site:      https://github.com/howerj/forth-cpu\n\
License:   MIT\n\
License:   MIT\n\
Copyright: Richard James Howe (2017)\n\
Copyright: Richard James Howe (2017,2018)\n\
Options:\n\n\
Options:\n\n\
\t-\tstop processing options, following arguments are files\n\
\t-\tstop processing options, following arguments are files\n\
\t-h\tprint this help message and exit\n\
\t-h\tprint this help message and exit\n\
\t-v\tincrease logging level\n\
\t-v\tincrease logging level\n\
\t-d\tdisassemble input files (default)\n\
\t-d\tdisassemble input files (default)\n\
\t-D\tfull disassembly of input files\n\
\t-D\tfull disassembly of input files\n\
\t-T\tEnter debug mode when running simulation\n\
\t-T\tEnter debug mode when running simulation\n\
\t-a\tassemble file\n\
 
\t-H\tenable hacks to make the simulation easier to use\n\
\t-H\tenable hacks to make the simulation easier to use\n\
\t-r\trun hex file\n\
\t-r\trun hex file\n\
\t-R\tassemble file then run it\n\
 
\t-e #\tRun Embed Forth Virtual Machine\n\
 
\t-L #\tload symbol file\n\
\t-L #\tload symbol file\n\
\t-S #\tsave symbols to file\n\
\t-S #\tsave symbols to file\n\
\t-s #\tnumber of steps to run simulation (0 = forever)\n\
\t-s #\tnumber of steps to run simulation (0 = forever)\n\
\t-n #\tspecify nvram file\n\
\t-n #\tspecify nvram file\n\
\t-H #\tenable certain hacks for simulation purposes\n\
\t-H #\tenable certain hacks for simulation purposes\n\
Line 4208... Line 2417...
Options must precede any files given, if a file has not been\n\
Options must precede any files given, if a file has not been\n\
given as arguments input is taken from stdin. Output is to\n\
given as arguments input is taken from stdin. Output is to\n\
stdout. Program returns zero on success, non zero on failure.\n\n\
stdout. Program returns zero on success, non zero on failure.\n\n\
";
";
 
 
static void debug_note(command_args_t *cmd)
static void debug_note(const command_args_t * const cmd) {
{
        assert(cmd);
        if(cmd->debug_mode)
        if(cmd->debug_mode)
                note("entering debug mode");
                note("entering debug mode");
        else
        else
                note("running for %u cycles (0 = forever)", (unsigned)cmd->steps);
                note("running for %u cycles (0 = forever)", (unsigned)cmd->steps);
}
}
 
 
static int assemble_run_command(command_args_t *cmd, FILE *input, FILE *output, symbol_table_t *symbols, bool assemble, uint16_t *vga_initial_contents)
static int run_command(const command_args_t * const cmd, FILE *input, FILE *output, symbol_table_t *symbols, uint16_t *vga_initial_contents) {
{
 
        assert(input);
        assert(input);
        assert(output);
        assert(output);
        assert(cmd);
        assert(cmd);
        assert(cmd->nvram);
        assert(cmd->nvram);
        h2_t *h = NULL;
 
        h2_io_t *io = NULL;
 
        int r = 0;
        int r = 0;
 
 
        if(assemble) {
        h2_t *h = h2_new(START_ADDR);
                h = h2_assemble_core(input, symbols);
 
        } else {
 
                h = h2_new(START_ADDR);
 
                if(h2_load(h, input) < 0)
 
                        return -1;
 
        }
 
 
 
        if(!h)
        if(!h)
                return -1;
                return -1;
 
        if (h2_load(h, input) < 0) {
 
                h2_free(h);
 
                return -1;
 
        }
 
 
        io = h2_io_new();
        h2_io_t * const io = h2_io_new();
        assert(VGA_BUFFER_LENGTH <= VT100_MAX_SIZE);
        assert(VGA_BUFFER_LENGTH <= VT100_MAX_SIZE);
        for(size_t i = 0; i < VGA_BUFFER_LENGTH; i++) {
        for(size_t i = 0; i < VGA_BUFFER_LENGTH; i++) {
                vt100_attribute_t attr;
                vt100_attribute_t attr;
                memset(&attr, 0, sizeof(attr));
                memset(&attr, 0, sizeof(attr));
                io->soc->vt100.m[i]   =  vga_initial_contents[i] & 0xff;
                io->soc->vt100.m[i]   =  vga_initial_contents[i] & 0xff;
Line 4251... Line 2454...
        }
        }
 
 
        nvram_load_and_transfer(io, cmd->nvram, cmd->hacks);
        nvram_load_and_transfer(io, cmd->nvram, cmd->hacks);
        h->pc = START_ADDR;
        h->pc = START_ADDR;
        debug_note(cmd);
        debug_note(cmd);
        r = h2_run(h, io, output, cmd->steps, symbols, cmd->debug_mode);
        r = h2_run(h, io, output, cmd->steps, symbols, cmd->debug_mode, NULL);
        nvram_save(io, cmd->nvram);
        nvram_save(io, cmd->nvram);
 
 
        h2_free(h);
        h2_free(h);
        h2_io_free(io);
        h2_io_free(io);
        return r;
        return r;
}
}
 
 
int command(command_args_t *cmd, FILE *input, FILE *output, symbol_table_t *symbols, uint16_t *vga_initial_contents)
int command(const command_args_t * const cmd, FILE *input, FILE *output, symbol_table_t *symbols, uint16_t *vga_initial_contents) {
{
 
        assert(input);
        assert(input);
        assert(output);
        assert(output);
        assert(cmd);
        assert(cmd);
 
        assert(vga_initial_contents);
        switch(cmd->cmd) {
        switch(cmd->cmd) {
        case DEFAULT_COMMAND:      /* fall through */
        case DEFAULT_COMMAND:      /* fall through */
        case DISASSEMBLE_COMMAND:  return h2_disassemble(cmd->dcm, input, output, symbols);
        case DISASSEMBLE_COMMAND:  return h2_disassemble(cmd->dcm, input, output, symbols);
        case ASSEMBLE_COMMAND:     return h2_assemble_file(input, output, symbols);
        case RUN_COMMAND:          return run_command(cmd, input, output, symbols, vga_initial_contents);
        case RUN_COMMAND:          return assemble_run_command(cmd, input, output, symbols, false, vga_initial_contents);
 
        case ASSEMBLE_RUN_COMMAND: return assemble_run_command(cmd, input, output, symbols, true,  vga_initial_contents);
 
        default:                   fatal("invalid command: %d", cmd->cmd);
        default:                   fatal("invalid command: %d", cmd->cmd);
        }
        }
        return -1;
        return -1;
}
}
 
 
static const char *nvram_file = FLASH_INIT_FILE;
static const char *nvram_file = FLASH_INIT_FILE;
 
 
int h2_main(int argc, char **argv)
int h2_main(int argc, char **argv) {
{
 
        int i;
        int i;
        const char *optarg = NULL;
        const char *optarg = NULL;
        command_args_t cmd;
        command_args_t cmd;
        symbol_table_t *symbols = NULL;
        symbol_table_t *symbols = NULL;
        FILE *symfile = NULL;
        FILE *symfile = NULL;
        FILE *newsymfile = NULL;
 
        FILE *input = NULL;
        FILE *input = NULL;
        memset(&cmd, 0, sizeof(cmd));
        memset(&cmd, 0, sizeof(cmd));
        cmd.steps = DEFAULT_STEPS;
        cmd.steps = DEFAULT_STEPS;
        cmd.nvram = nvram_file;
        cmd.nvram = nvram_file;
        cmd.dcm   = DCM_X11;
        cmd.dcm   = DCM_X11;
Line 4332... Line 2531...
                case 'd':
                case 'd':
                        if(cmd.cmd)
                        if(cmd.cmd)
                                goto fail;
                                goto fail;
                        cmd.cmd = DISASSEMBLE_COMMAND;
                        cmd.cmd = DISASSEMBLE_COMMAND;
                        break;
                        break;
                case 'e':
 
                {
 
                        forth_t *f = embed_new();
 
                        if(i >= (argc - 1)) {
 
                                embed_free(f);
 
                                goto fail;
 
                        }
 
                        optarg = argv[++i];
 
                        int r = embed_forth(f, stdin, stdout, optarg);
 
                        embed_free(f);
 
                        if(r < 0)
 
                                fatal("embed run failed: %u\n", r);
 
                        break;
 
                }
 
                case 'a':
 
                        if(cmd.cmd)
 
                                goto fail;
 
                        cmd.cmd = ASSEMBLE_COMMAND;
 
                        break;
 
                case 'r':
                case 'r':
                        if(cmd.cmd)
                        if(cmd.cmd)
                                goto fail;
                                goto fail;
                        cmd.cmd = RUN_COMMAND;
                        cmd.cmd = RUN_COMMAND;
                        break;
                        break;
                case 'T':
                case 'T':
                        cmd.debug_mode = true;
                        cmd.debug_mode = true;
                        break;
                        break;
                case 'R':
 
                        if(cmd.cmd)
 
                                goto fail;
 
                        cmd.cmd = ASSEMBLE_RUN_COMMAND;
 
                        break;
 
                case 'L':
                case 'L':
                        if(i >= (argc - 1) || symfile)
                        if(i >= (argc - 1) || symfile)
                                goto fail;
                                goto fail;
                        optarg = argv[++i];
                        optarg = argv[++i];
                        /* NB. Cannot merge symbol tables */
                        /* NB. Cannot merge symbol tables */
                        symfile = fopen_or_die(optarg, "rb");
                        symfile = fopen_or_die(optarg, "rb");
                        symbols = symbol_table_load(symfile);
                        symbols = symbol_table_load(symfile);
                        break;
                        break;
                case 'S':
 
                        if(i >= (argc - 1) || newsymfile)
 
                                goto fail;
 
                        optarg = argv[++i];
 
                        newsymfile = fopen_or_die(optarg, "wb");
 
                        break;
 
                case 'c':
                case 'c':
                {
                {
                        long dcm = DCM_NONE;
                        long dcm = DCM_NONE;
                        if(i >= (argc - 1))
                        if(i >= (argc - 1))
                                goto fail;
                                goto fail;
                        optarg = argv[++i];
                        optarg = argv[++i];
                        if(string_to_long(0, &dcm, optarg))
                        if(string_to_long(0, &dcm, optarg))
                                goto fail;
                                goto fail;
                        cmd.dcm = dcm;
                        cmd.dcm = dcm;
                        if(cmd.dcm >= DCM_MAX_DCM) {
                        if(cmd.dcm >= DCM_MAX_DCM) {
                                fprintf(stderr, "Invalid Colorization Method: %u\n", cmd.dcm);
                                fprintf(stderr, "Invalid Colorization Method: %u\n", (unsigned)cmd.dcm);
                                goto fail;
                                goto fail;
                        }
                        }
                        break;
                        break;
                }
                }
                case 's':
                case 's':
Line 4430... Line 2599...
                fatal("more than one file argument given");
                fatal("more than one file argument given");
 
 
        input = fopen_or_die(argv[i], "rb");
        input = fopen_or_die(argv[i], "rb");
        if(command(&cmd, input, stdout, symbols, vga_initial_contents) < 0)
        if(command(&cmd, input, stdout, symbols, vga_initial_contents) < 0)
                fatal("failed to process file: %s", argv[i]);
                fatal("failed to process file: %s", argv[i]);
        /**@note keeping "input" open until the command exits locks the
 
         * file for longer than is necessary under Windows */
 
        fclose(input);
        fclose(input);
 
 
        if(newsymfile) {
 
                symbol_table_print(symbols, newsymfile);
 
                fclose(newsymfile);
 
        }
 
        symbol_table_free(symbols);
        symbol_table_free(symbols);
        if(symfile)
        if(symfile)
                fclose(symfile);
                fclose(symfile);
        return 0;
        return 0;
}
}
 
 
int main(int argc, char **argv)
int main(int argc, char **argv) {
{
 
        return h2_main(argc, argv);
        return h2_main(argc, argv);
}
}
#endif
#endif
 
 
/* ========================== Main ========================================= */
/* ========================== Main ========================================= */
 
 
 No newline at end of file
 No newline at end of file
 
 
 No newline at end of file
 No newline at end of file

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.