forth-cpu
/
h2.c
2616 строк · 78.5 Кб
1/** @file h2.c
2* @brief Simulate the H2 CPU and surrounding system
3* @copyright Richard James Howe (2017)
4* @license MIT
5*
6* This file contains the simulator and debugger for the H2,
7* The H2 is written in VHDL and
8* is based on the J1 processor (see http://excamera.com/sphinx/fpga-j1.html).
9*
10* The processor has been tested on an FPGA and is working.
11* The project can be found at: https://github.com/howerj/forth-cpu */
12
13/* ========================== Preamble: Types, Macros, Globals ============= */
14
15#include "h2.h"16#include <assert.h>17#include <ctype.h>18#include <errno.h>19#include <inttypes.h>20#include <setjmp.h>21#include <stdarg.h>22#include <stdlib.h>23#include <string.h>24
25#define UNUSED(VARIABLE) ((void)(VARIABLE))26
27#ifdef _WIN32 /* Making standard input streams on Windows binary */28#include <windows.h>29#include <io.h>30#include <fcntl.h>31extern int _fileno(FILE *stream);32static void binary(FILE *f) { _setmode(_fileno(f), _O_BINARY); }33#else34static inline void binary(FILE *f) { UNUSED(f); }35#endif36
37#define DEFAULT_STEPS (0) /*default is to run forever*/38#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))39#define MIN(X, Y) ((X) > (Y) ? (Y) : (X))40
41#define NUMBER_OF_INTERRUPTS (8u)42
43#define OP_BRANCH (0x0000)44#define OP_0BRANCH (0x2000)45#define OP_CALL (0x4000)46#define OP_ALU_OP (0x6000)47#define OP_LITERAL (0x8000)48
49#define IS_LITERAL(INST) (((INST) & 0x8000) == 0x8000)50#define IS_BRANCH(INST) (((INST) & 0xE000) == 0x0000)51#define IS_0BRANCH(INST) (((INST) & 0xE000) == 0x2000)52#define IS_CALL(INST) (((INST) & 0xE000) == 0x4000)53#define IS_ALU_OP(INST) (((INST) & 0xE000) == 0x6000)54
55#define ALU_OP_LENGTH (5u)56#define ALU_OP_START (8u)57#define ALU_OP(INST) (((INST) >> ALU_OP_START) & ((1 << ALU_OP_LENGTH) - 1))58
59#define DSTACK_LENGTH (2u)60#define DSTACK_START (0u)61#define DSTACK(INST) (((INST) >> DSTACK_START) & ((1 << DSTACK_LENGTH) - 1))62
63#define RSTACK_LENGTH (2u)64#define RSTACK_START (2u)65#define RSTACK(INST) (((INST) >> RSTACK_START) & ((1 << RSTACK_LENGTH) - 1))66
67#define R_TO_PC_BIT_INDEX (4u)68#define N_TO_ADDR_T_BIT_INDEX (5u)69#define T_TO_R_BIT_INDEX (6u)70#define T_TO_N_BIT_INDEX (7u)71
72#define R_TO_PC (1u << R_TO_PC_BIT_INDEX)73#define N_TO_ADDR_T (1u << N_TO_ADDR_T_BIT_INDEX)74#define T_TO_R (1u << T_TO_R_BIT_INDEX)75#define T_TO_N (1u << T_TO_N_BIT_INDEX)76
77typedef enum {78ALU_OP_T, /**< Top of Stack */79ALU_OP_N, /**< Copy T to N */80ALU_OP_T_PLUS_N, /**< Addition */81ALU_OP_T_AND_N, /**< Bitwise AND */82ALU_OP_T_OR_N, /**< Bitwise OR */83ALU_OP_T_XOR_N, /**< Bitwise XOR */84ALU_OP_T_INVERT, /**< Bitwise Inversion */85ALU_OP_T_EQUAL_N, /**< Equality test */86ALU_OP_N_LESS_T, /**< Signed comparison */87ALU_OP_N_RSHIFT_T, /**< Logical Right Shift */88ALU_OP_T_DECREMENT, /**< Decrement */89ALU_OP_R, /**< Top of return stack */90ALU_OP_T_LOAD, /**< Load from address */91ALU_OP_N_LSHIFT_T, /**< Logical Left Shift */92ALU_OP_DEPTH, /**< Depth of stack */93ALU_OP_N_ULESS_T, /**< Unsigned comparison */94ALU_OP_ENABLE_INTERRUPTS, /**< Enable interrupts */95
96ALU_OP_INTERRUPTS_ENABLED, /**< Are interrupts on? */97ALU_OP_RDEPTH, /**< R Stack Depth */98ALU_OP_T_EQUAL_0, /**< T == 0 */99ALU_OP_CPU_ID, /**< CPU Identifier */100
101ALU_OP_LITERAL, /**< undocumented; set T to instruction & $7fff */102} alu_code_e;103
104#define DELTA_0 (0)105#define DELTA_1 (1)106#define DELTA_N2 (2)107#define DELTA_N1 (3)108
109#define MK_DSTACK(DELTA) ((DELTA) << DSTACK_START)110#define MK_RSTACK(DELTA) ((DELTA) << RSTACK_START)111#define MK_CODE(CODE) ((CODE) << ALU_OP_START)112
113/**
114* @warning This table keeps most things synchronized when it comes
115* to instructions, the exception is in the lexer, which accepts
116* a range of tokens in one clause of the grammar from the first
117* instruction to the last. This must be manually updated
118* @note In the original J1 specification both r@ and r> both have
119* their T_TO_R bit set in their instruction description tables, this
120* appears to be incorrect */
121#define X_MACRO_INSTRUCTIONS \122X(DUP, "dup", true, (OP_ALU_OP | MK_CODE(ALU_OP_T) | T_TO_N | MK_DSTACK(DELTA_1)))\123X(OVER, "over", true, (OP_ALU_OP | MK_CODE(ALU_OP_N) | T_TO_N | MK_DSTACK(DELTA_1)))\124X(OVERADD,"over+", false, (OP_ALU_OP | MK_CODE(ALU_OP_T_PLUS_N)))\125X(OVERAND,"over-and", false, (OP_ALU_OP | MK_CODE(ALU_OP_T_AND_N)))\126X(INVERT, "invert", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_INVERT)))\127X(ADD, "+", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_PLUS_N) | MK_DSTACK(DELTA_N1)))\128X(SWAP, "swap", true, (OP_ALU_OP | MK_CODE(ALU_OP_N) | T_TO_N))\129X(NIP, "nip", true, (OP_ALU_OP | MK_CODE(ALU_OP_T) | MK_DSTACK(DELTA_N1)))\130X(DROP, "drop", true, (OP_ALU_OP | MK_CODE(ALU_OP_N) | MK_DSTACK(DELTA_N1)))\131X(EXIT, "exit", true, (OP_ALU_OP | MK_CODE(ALU_OP_T) | R_TO_PC | MK_RSTACK(DELTA_N1)))\132X(TOR, ">r", true, (OP_ALU_OP | MK_CODE(ALU_OP_N) | T_TO_R | MK_DSTACK(DELTA_N1) | MK_RSTACK(DELTA_1)))\133X(FROMR, "r>", true, (OP_ALU_OP | MK_CODE(ALU_OP_R) | T_TO_N | MK_DSTACK(DELTA_1) | MK_RSTACK(DELTA_N1)))\134X(RAT, "r@", true, (OP_ALU_OP | MK_CODE(ALU_OP_R) | T_TO_N | MK_DSTACK(DELTA_1)))\135X(LOAD, "@", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_LOAD)))\136X(STORE, "store", false, (OP_ALU_OP | MK_CODE(ALU_OP_N) | N_TO_ADDR_T | MK_DSTACK(DELTA_N1)))\137X(RSHIFT, "rshift", true, (OP_ALU_OP | MK_CODE(ALU_OP_N_RSHIFT_T) | MK_DSTACK(DELTA_N1)))\138X(LSHIFT, "lshift", true, (OP_ALU_OP | MK_CODE(ALU_OP_N_LSHIFT_T) | MK_DSTACK(DELTA_N1)))\139X(EQUAL, "=", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_EQUAL_N) | MK_DSTACK(DELTA_N1)))\140X(ULESS, "u<", true, (OP_ALU_OP | MK_CODE(ALU_OP_N_ULESS_T) | MK_DSTACK(DELTA_N1)))\141X(LESS, "<", true, (OP_ALU_OP | MK_CODE(ALU_OP_N_LESS_T) | MK_DSTACK(DELTA_N1)))\142X(AND, "and", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_AND_N) | MK_DSTACK(DELTA_N1)))\143X(XOR, "xor", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_XOR_N) | MK_DSTACK(DELTA_N1)))\144X(OR, "or", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_OR_N) | MK_DSTACK(DELTA_N1)))\145X(DEPTH, "sp@", true, (OP_ALU_OP | MK_CODE(ALU_OP_DEPTH) | T_TO_N | MK_DSTACK(DELTA_1)))\146X(T_N1, "1-", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_DECREMENT)))\147X(IEN, "ien!", true, (OP_ALU_OP | MK_CODE(ALU_OP_ENABLE_INTERRUPTS) | MK_DSTACK(DELTA_N1)))\148X(ISIEN, "ien?", true, (OP_ALU_OP | MK_CODE(ALU_OP_INTERRUPTS_ENABLED) | T_TO_N | MK_DSTACK(DELTA_1)))\149X(RDEPTH, "rp@", true, (OP_ALU_OP | MK_CODE(ALU_OP_RDEPTH) | T_TO_N | MK_DSTACK(DELTA_1)))\150X(TE0, "0=", true, (OP_ALU_OP | MK_CODE(ALU_OP_T_EQUAL_0)))\151X(NOP, "nop", false, (OP_ALU_OP | MK_CODE(ALU_OP_T)))\152X(CPU_ID, "cpu-id", true, (OP_ALU_OP | MK_CODE(ALU_OP_CPU_ID)) | MK_DSTACK(DELTA_1))\153X(RUP, "rup", false, (OP_ALU_OP | MK_CODE(ALU_OP_T)) | MK_RSTACK(DELTA_1))\154X(DUPTOR, "dup>r", false, (OP_ALU_OP | MK_CODE(ALU_OP_T)) | T_TO_R | MK_RSTACK(DELTA_1))\155X(RDROP, "rdrop", true, (OP_ALU_OP | MK_CODE(ALU_OP_T) | MK_RSTACK(DELTA_N1)))156
157
158typedef enum {159#define X(NAME, STRING, DEFINE, INSTRUCTION) CODE_ ## NAME = INSTRUCTION,160X_MACRO_INSTRUCTIONS
161#undef X162} forth_word_codes_e;163
164static const char *log_levels[] = {165#define X(ENUM, NAME) [ENUM] = NAME,166X_MACRO_LOGGING
167#undef X168};169
170log_level_e log_level = LOG_WARNING;171
172typedef struct {173int error;174int jmp_buf_valid;175jmp_buf j;176} error_t;177
178/* ========================== Preamble: Types, Macros, Globals ============= */
179
180/* ========================== Utilities ==================================== */
181
182int logger(log_level_e level, const char *func, const unsigned line, const char *fmt, ...) {183int r = 0;184assert(func);185assert(fmt);186assert(level <= LOG_ALL_MESSAGES);187if (level <= log_level) {188va_list ap;189fprintf(stderr, "[%s %u] %s: ", func, line, log_levels[level]);190va_start(ap, fmt);191r = vfprintf(stderr, fmt, ap);192va_end(ap);193fputc('\n', stderr);194fflush(stderr);195}196if (level == LOG_FATAL)197exit(EXIT_FAILURE);198return r;199}
200
201static const char *reason(void) {202static const char *unknown = "unknown reason";203if (errno == 0)204return unknown;205const char *r = strerror(errno);206if (!r)207return unknown;208return r;209}
210
211void *allocate_or_die(const size_t length) {212errno = 0;213void *r = calloc(1, length);214if (!r)215fatal("allocation of size %u failed: %s", (unsigned)length, reason());216return r;217}
218
219FILE *fopen_or_die(const char *file, const char *mode) {220assert(file);221assert(mode);222errno = 0;223FILE *f = fopen(file, mode);224if (!f)225fatal("failed to open file '%s' (mode %s): %s", file, mode, reason());226return f;227}
228
229static int string_to_long(const int base, long *n, const char *s) {230char *end = NULL;231assert(base >= 0);232assert(base != 1);233assert(base <= 36);234assert(n);235assert(s);236errno = 0;237*n = strtol(s, &end, base);238return errno || *s == '\0' || *end != '\0';239}
240
241static int string_to_cell(int base, uint16_t *n, const char *s) {242long n1 = 0;243const int r = string_to_long(base, &n1, s);244*n = n1;245return r;246}
247
248static char *duplicate(const char *str) {249assert(str);250const size_t length = strlen(str);251assert((length + 1) > length);252errno = 0;253char *r = malloc(length + 1);254if (!r)255fatal("duplicate of '%s' failed: %s", str, reason());256strcpy(r, str);257return r;258}
259
260static void ethrow(error_t *e) {261if (e && e->jmp_buf_valid) {262e->jmp_buf_valid = 0;263e->error = 1;264longjmp(e->j, 1);265}266exit(EXIT_FAILURE);267}
268
269h2_t *h2_new(const uint16_t start_address) {270h2_t *h = allocate_or_die(sizeof(h2_t));271h->pc = start_address;272for (uint16_t i = 0; i < start_address; i++)273h->core[i] = OP_BRANCH | start_address;274return h;275}
276
277void h2_free(h2_t * const h) {278if (!h)279return;280free(h->bp.points);281memset(h, 0, sizeof(*h));282free(h);283}
284
285int binary_memory_load(FILE *input, uint16_t *p, const size_t length) {286assert(input);287assert(p);288for (size_t i = 0; i < length; i++) {289errno = 0;290const int r1 = fgetc(input);291const int r2 = fgetc(input);292if (r1 < 0 || r2 < 0) {293debug("memory read failed: %s", strerror(errno));294return -1;295}296p[i] = (((unsigned)r1 & 0xffu)) | (((unsigned)r2 & 0xffu) << 8u);297}298return 0;299}
300
301int binary_memory_save(FILE *output, const uint16_t * const p, const size_t length) {302assert(output);303assert(p);304for (size_t i = 0; i < length; i++) {305errno = 0;306const int r1 = fputc((p[i]) & 0xff,output);307const int r2 = fputc((p[i] >> 8u) & 0xff, output);308if (r1 < 0 || r2 < 0) {309debug("memory write failed: %s", strerror(errno));310return -1;311}312}313return 0;314}
315
316int nvram_load_and_transfer(h2_io_t *io, const char *name, const bool transfer_to_sram) {317assert(io);318assert(name);319FILE *input = NULL;320int r = 0;321errno = 0;322if ((input = fopen(name, "rb"))) {323r = binary_memory_load(input, io->soc->flash.nvram, CHIP_MEMORY_SIZE);324if (transfer_to_sram)325memcpy(io->soc->vram, io->soc->flash.nvram, CHIP_MEMORY_SIZE);326fclose(input);327} else {328error("nvram file read (from %s) failed: %s", name, strerror(errno));329r = -1;330}331return r;332}
333
334int nvram_save(h2_io_t *io, const char *name) {335FILE *output = NULL;336int r = 0;337assert(io);338assert(name);339errno = 0;340if ((output = fopen(name, "wb"))) {341r = binary_memory_save(output, io->soc->flash.nvram, CHIP_MEMORY_SIZE);342fclose(output);343} else {344error("nvram file write (to %s) failed: %s", name, strerror(errno));345r = -1;346}347return r;348}
349
350int memory_load(FILE *input, uint16_t *p, const size_t length) {351assert(input);352assert(p);353char line[80] = {0}; /*more than enough!*/354size_t i = 0;355
356for (;fgets(line, sizeof(line), input); i++) {357int r;358if (i >= length) {359error("file contains too many lines: %u", (unsigned)i);360return -1;361}362r = string_to_cell(16, &p[i], line);363if (!r) {364error("invalid line - expected hex string: %s", line);365return -1;366}367debug("%u %u", (unsigned)i, (unsigned)p[i]);368}369
370return 0;371}
372
373int memory_save(FILE *output, const uint16_t * const p, const size_t length) {374assert(output);375assert(p);376for (size_t i = 0; i < length; i++)377if (fprintf(output, "%04"PRIx16"\n", p[i]) < 0) {378error("failed to write line: %lu", (unsigned long)i);379return -1;380}381return 0;382}
383
384int h2_load(h2_t *h, FILE *hexfile) {385assert(h);386assert(hexfile);387return memory_load(hexfile, h->core, MAX_CORE);388}
389
390int h2_save(const h2_t * const h, FILE *output, const bool full) {391assert(h);392assert(output);393return memory_save(output, h->core, full ? MAX_CORE : h->pc);394}
395
396/* From: https://stackoverflow.com/questions/215557/how-do-i-implement-a-circular-list-ring-buffer-in-c */
397
398fifo_t *fifo_new(const size_t size) {399assert(size >= 2); /* It does not make sense to have a FIFO less than this size */400fifo_data_t *buffer = allocate_or_die(size * sizeof(buffer[0]));401fifo_t *fifo = allocate_or_die(sizeof(fifo_t));402
403fifo->buffer = buffer;404fifo->head = 0;405fifo->tail = 0;406fifo->size = size;407
408return fifo;409}
410
411void fifo_free(fifo_t *fifo) {412if (!fifo)413return;414free(fifo->buffer);415free(fifo);416}
417
418bool fifo_is_full(const fifo_t * const fifo) {419assert(fifo);420return (fifo->head == (fifo->size - 1) && fifo->tail == 0)421|| (fifo->head == (fifo->tail - 1));422}
423
424bool fifo_is_empty(const fifo_t * const fifo) {425assert(fifo);426return fifo->head == fifo->tail;427}
428
429size_t fifo_count(const fifo_t * const fifo) {430assert(fifo);431if (fifo_is_empty(fifo))432return 0;433else if (fifo_is_full(fifo))434return fifo->size;435else if (fifo->head < fifo->tail)436return fifo->head + (fifo->size - fifo->tail);437else438return fifo->head - fifo->tail;439}
440
441size_t fifo_push(fifo_t * fifo, fifo_data_t data) {442assert(fifo);443if (fifo_is_full(fifo))444return 0;445
446fifo->buffer[fifo->head] = data;447
448fifo->head++;449if (fifo->head == fifo->size)450fifo->head = 0;451
452return 1;453}
454
455size_t fifo_pop(fifo_t * fifo, fifo_data_t * data) {456assert(fifo);457assert(data);458
459if (fifo_is_empty(fifo))460return 0;461
462*data = fifo->buffer[fifo->tail];463
464fifo->tail++;465if (fifo->tail == fifo->size)466fifo->tail = 0;467
468return 1;469}
470
471#ifdef __unix__472#include <unistd.h>473#include <termios.h>474static int getch(void) {475struct termios oldattr, newattr;476tcgetattr(STDIN_FILENO, &oldattr);477newattr = oldattr;478newattr.c_iflag &= ~(ICRNL);479newattr.c_lflag &= ~(ICANON | ECHO);480
481tcsetattr(STDIN_FILENO, TCSANOW, &newattr);482const int ch = getchar();483
484tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);485
486return ch;487}
488
489static int putch(int c) {490int res = putchar(c);491fflush(stdout);492return res;493}
494#else495#ifdef _WIN32496
497extern int getch(void);498extern int putch(int c);499
500#else501static int getch(void) {502return getchar();503}
504
505static int putch(const int c) {506return putchar(c);507}
508#endif509#endif /** __unix__ **/510
511static int wrap_getch(bool *debug_on) {512const int ch = getch();513assert(debug_on);514if (ch == EOF) {515note("End Of Input - exiting");516exit(EXIT_SUCCESS);517}518if (ch == ESCAPE && debug_on)519*debug_on = true;520
521return ch == DELETE ? BACKSPACE : ch;522}
523
524/* ========================== Utilities ==================================== */
525
526/* ========================== Symbol Table ================================= */
527
528static const char *symbol_names[] = {529[SYMBOL_TYPE_LABEL] = "label",530[SYMBOL_TYPE_CALL] = "call",531[SYMBOL_TYPE_CONSTANT] = "constant",532[SYMBOL_TYPE_VARIABLE] = "variable",533NULL534};535
536static symbol_t *symbol_new(const symbol_type_e type, const char *id, const uint16_t value) {537symbol_t *s = allocate_or_die(sizeof(*s));538assert(id);539s->id = duplicate(id);540s->value = value;541s->type = type;542return s;543}
544
545static void symbol_free(symbol_t *s) {546if (!s)547return;548free(s->id);549memset(s, 0, sizeof(*s));550free(s);551}
552
553static symbol_table_t *symbol_table_new(void) {554symbol_table_t *t = allocate_or_die(sizeof(*t));555return t;556}
557
558static void symbol_table_free(symbol_table_t *t) {559if (!t)560return;561for (size_t i = 0; i < t->length; i++)562symbol_free(t->symbols[i]);563free(t->symbols);564memset(t, 0, sizeof(*t));565free(t);566}
567
568static symbol_t *symbol_table_lookup(const symbol_table_t * const t, const char *id) {569for (size_t i = 0; i < t->length; i++)570if (!strcmp(t->symbols[i]->id, id))571return t->symbols[i];572return NULL;573}
574
575/** @note There can be multiple symbols with the same value of the same type */
576static const symbol_t *symbol_table_reverse_lookup(const symbol_table_t * const t, const symbol_type_e type, const uint16_t value) {577for (size_t i = 0; i < t->length; i++)578if (t->symbols[i]->type == type && t->symbols[i]->value == value)579return t->symbols[i];580return NULL;581}
582
583static 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) {584symbol_t *s = symbol_new(type, id, value);585symbol_t **xs = NULL;586assert(t);587
588if (symbol_table_lookup(t, id)) {589symbol_free(s);590error("redefinition of symbol: %s", id);591if (e)592ethrow(e);593else594return -1;595}596s->hidden = hidden;597s->used = used;598t->length++;599errno = 0;600xs = realloc(t->symbols, sizeof(*t->symbols) * t->length);601if (!xs)602fatal("reallocate of size %u failed: %s", (unsigned)t->length, reason());603t->symbols = xs;604t->symbols[t->length - 1] = s;605return 0;606}
607
608static int symbol_table_print(symbol_table_t *t, FILE *output) {609assert(t);610assert(output);611for (size_t i = 0; i < t->length; i++) {612symbol_t *s = t->symbols[i];613char *visibility = s->hidden ? "hidden" : "visible";614char *used = s->used ? "used" : "unused";615if (fprintf(output, "%s %s %"PRId16" %s %s\n", symbol_names[s->type], s->id, s->value, visibility, used) < 0)616return -1;617}618return 0;619}
620
621symbol_table_t *symbol_table_load(FILE *input) {622assert(input);623symbol_table_t *t = symbol_table_new();624char symbol[80] = { 0 };625char id[256] = { 0 };626char visibility[80] = { 0 };627uint16_t value = false;628
629while (!feof(input)) {630int r = 0;631memset(symbol, 0, sizeof(symbol));632memset(id, 0, sizeof(id));633memset(visibility, 0, sizeof(visibility));634value = 0;635r = fscanf(input, "%79s%255s%"SCNu16"%79s", symbol, id, &value, visibility);636if (r != 4 && r > 0) {637error("invalid symbol table: %d", r);638goto fail;639}640if (r == 4) {641size_t i = 0;642bool hidden = false;643if (!strcmp(visibility, "hidden")) {644hidden = true;645}else if (!strcmp(visibility, "visible")) {646error("invalid visibility value: %s", visibility);647goto fail;648}649
650for (i = 0; symbol_names[i] && strcmp(symbol_names[i], symbol); i++)651/*do nothing*/;652if (symbol_names[i]) {653if (symbol_table_add(t, i, id, value, NULL, hidden, false) < 0)654goto fail;655} else {656error("invalid symbol: %s", symbol);657goto fail;658}659}660}661if (log_level >= LOG_DEBUG)662symbol_table_print(t, stderr);663return t;664fail:665symbol_table_free(t);666return NULL;667}
668/* ========================== Symbol Table ================================= */
669
670/* ========================== Disassembler ================================= */
671
672static const char *instruction_to_string(const uint16_t i) {673switch (i) {674#define X(NAME, STRING, DEFINE, INSTRUCTION) case CODE_ ## NAME : return STRING ;675X_MACRO_INSTRUCTIONS
676#undef X677default: break;678}679return NULL;680}
681
682static const char *alu_op_to_string(const uint16_t instruction) {683switch (ALU_OP(instruction)) {684case ALU_OP_T: return "T";685case ALU_OP_N: return "N";686case ALU_OP_T_PLUS_N: return "T+N";687case ALU_OP_T_AND_N: return "T&N";688case ALU_OP_T_OR_N: return "T|N";689case ALU_OP_T_XOR_N: return "T^N";690case ALU_OP_T_INVERT: return "~T";691case ALU_OP_T_EQUAL_N: return "N=T";692case ALU_OP_N_LESS_T: return "T>N";693case ALU_OP_N_RSHIFT_T: return "N>>T";694case ALU_OP_T_DECREMENT: return "T-1";695case ALU_OP_R: return "R";696case ALU_OP_T_LOAD: return "[T]";697case ALU_OP_N_LSHIFT_T: return "N<<T";698case ALU_OP_DEPTH: return "depth";699case ALU_OP_N_ULESS_T: return "Tu>N";700case ALU_OP_ENABLE_INTERRUPTS: return "ien";701case ALU_OP_INTERRUPTS_ENABLED: return "ien?";702case ALU_OP_RDEPTH: return "rdepth";703case ALU_OP_T_EQUAL_0: return "0=";704case ALU_OP_CPU_ID: return "cpu-id";705case ALU_OP_LITERAL: return "literal";706default: return "unknown";707}708}
709
710static char *disassembler_alu(const uint16_t instruction) {711char buf[256] = {0};712const char *r = instruction_to_string(OP_ALU_OP | instruction);713if (r)714return duplicate(r);715sprintf(buf, "%04x:%s:%s:%s:%s:%s:%u:%u",716(unsigned)instruction,717alu_op_to_string(instruction),718(instruction & T_TO_N) ? "T->N" : "",719(instruction & T_TO_R) ? "T->R" : "",720(instruction & N_TO_ADDR_T) ? "N->[T]" : "",721(instruction & R_TO_PC) ? "R->PC" : "",722(unsigned)(instruction & 0x000C),723(unsigned)(instruction & 0x0003));724return duplicate(buf);725}
726
727static const char *disassemble_jump(const symbol_table_t * const symbols, const symbol_type_e type, const uint16_t address) {728if (!symbols)729return "";730const symbol_t * const found = symbol_table_reverse_lookup(symbols, type, address);731return found ? found->id : "";732}
733
734#define CSI "\033["735#define ANSI_RESET (CSI "0m")736#define ANSI_BLACK (CSI "30m")737#define ANSI_RED (CSI "31m")738#define ANSI_GREEN (CSI "32m")739#define ANSI_YELLOW (CSI "33m")740#define ANSI_BLUE (CSI "34m")741#define ANSI_MAGENTA (CSI "35m")742#define ANSI_CYAN (CSI "36m")743#define ANSI_WHITE (CSI "37m")744
745typedef enum {746DCM_NONE,747DCM_X11,748DCM_ANSI,749DCM_MAX_DCM
750} disassemble_color_method_e;751
752typedef enum {753DC_LITERAL,754DC_ALU,755DC_CALL,756DC_0BRANCH,757DC_BRANCH,758DC_ERROR, /* Invalid instruction */759DC_RESET, /* Reset color */760} decompilation_color_e;761
762static int disassemble_instruction(const uint16_t instruction, FILE *output, const symbol_table_t * const symbols, const disassemble_color_method_e dcm) {763int r = 0;764char *s = NULL;765assert(output);766assert(dcm < DCM_MAX_DCM);767
768static const char *colors[3][7] = { /* for colorizing decompilation stream with in-band signalling */769/* LITERAL ALU CALL 0BRANCH BRANCH ERROR RESET */770[DCM_NONE] = { "", "", "", "", "", "", "" }, /* No-Color */771[DCM_X11] = { "?HotPink?", "?SkyBlue?", "?GreenYellow?", "?Khaki?", "?MediumTurquoise?", "?FireBrick?", "" }, /* X11/GTKWave */772[DCM_ANSI] = { ANSI_MAGENTA, ANSI_BLUE, ANSI_GREEN, ANSI_YELLOW, ANSI_CYAN, ANSI_RED, ANSI_RESET }, /* ANSI Escape Sequences */773};774
775const char **color = colors[dcm];776const unsigned short literal = instruction & 0x7FFF;777const unsigned short address = instruction & 0x1FFF;778
779if (IS_LITERAL(instruction))780r = fprintf(output, "%s%hx%s", color[DC_LITERAL], literal, color[DC_RESET]);781else if (IS_ALU_OP(instruction))782r = fprintf(output, "%s%s%s", color[DC_ALU], s = disassembler_alu(instruction), color[DC_RESET]);783else if (IS_CALL(instruction))784r = fprintf(output, "%scall%s %hx %s", color[DC_CALL], color[DC_RESET], address, disassemble_jump(symbols, SYMBOL_TYPE_CALL, address));785else if (IS_0BRANCH(instruction))786r = fprintf(output, "%s0branch%s %hx %s", color[DC_0BRANCH], color[DC_RESET], address, disassemble_jump(symbols, SYMBOL_TYPE_LABEL, address));787else if (IS_BRANCH(instruction))788r = fprintf(output, "%sbranch%s %hx %s", color[DC_BRANCH], color[DC_RESET], address, disassemble_jump(symbols, SYMBOL_TYPE_LABEL, address));789else790r = fprintf(output, "%s?(%hx)%s", color[DC_ERROR], instruction, color[DC_RESET]);791free(s);792return r < 0 ? -1 : 0;793}
794
795int h2_disassemble(const disassemble_color_method_e dcm, FILE *input, FILE *output, const symbol_table_t * const symbols) {796assert(input);797assert(output);798assert(dcm < DCM_MAX_DCM);799while (!feof(input)) {800char line[80] = { 0 };801if (fscanf(input, "%79s", line) != 1)802return -1;803if (line[0]) {804uint16_t instruction = 0;805if (string_to_cell(16, &instruction, line)) {806error("invalid input to disassembler: %s", line);807return -1;808}809if (disassemble_instruction(instruction, output, symbols, dcm) < 0) {810error("disassembly failed");811return -1;812}813if (fputc('\n', output) != '\n') {814error("disassembly failed");815return -1;816}817fflush(output);818}819}820return 0;821}
822
823/* ========================== Disassembler ================================= */
824
825/* ========================== Simulation And Debugger ====================== */
826
827/* @note At the moment I/O is not cycle accurate, the UART behaves as if reads
828* and writes happen instantly, along with the PS/2 keyboard. Also the UART
829* has a FIFO which is not simulated. It should be easy enough to delay for
830* the roughly the right number of cycles, but not to get exact cycle
831* accurate timing. */
832
833static char to_char(const uint8_t c) {834return isprint(c) ? c : '.';835}
836
837static void memory_print(FILE *out, const uint16_t start, const uint16_t * const p, const uint16_t length, const bool chars) {838const uint16_t line_length = 16;839assert(out);840assert(p);841for (uint16_t i = 0; i < length; i += line_length) {842fprintf(out, "%04"PRIx16 ": ", i + start);843for (uint16_t j = 0; j < line_length && j + i < length; j++)844fprintf(out, "%04"PRIx16 " ", p[j + i]);845fputc('\t', out);846if (chars) /* correct endianess? */847for (uint16_t j = 0; j < line_length && j + i < length; j++)848fprintf(out, "%c%c", to_char(p[j + i] >> 8), to_char(p[j + i]));849
850putc('\n', out);851}852putc('\n', out);853}
854
855static bool break_point_find(const break_point_t * const bp, const uint16_t find_me) {856assert(bp);857for (size_t i = 0; i < bp->length; i++)858if (bp->points[i] == find_me)859return true;860return false;861}
862
863static void break_point_add(break_point_t *bp, const uint16_t point) {864assert(bp);865if (break_point_find(bp, point))866return;867const size_t a = (bp->length + 1) * sizeof(bp->points[0]);868uint16_t *r = realloc(bp->points, a);869if (!r || a < bp->length)870fatal("realloc of size %u failed", (unsigned)a);871r[bp->length] = point;872bp->length++;873bp->points = r;874}
875
876static int break_point_print(FILE *out, const break_point_t * const bp) {877assert(out);878assert(bp);879for (size_t i = 0; i < bp->length; i++)880if (fprintf(out, "\t0x%04"PRIx16 "\n", bp->points[i]) < 0)881return -1;882return 0;883}
884
885#define LED_7_SEGMENT_DISPLAY_CHARSET_HEX "0123456789AbCdEF"886#define LED_7_SEGMENT_DISPLAY_CHARSET_BCD "0123456789 .- "887
888static char l7seg(const uint8_t c) {889static const char *v = LED_7_SEGMENT_DISPLAY_CHARSET_HEX;890return v[c & 0xf];891}
892
893void soc_print(FILE *out, const h2_soc_state_t * const soc) {894assert(out);895assert(soc);896const unsigned char led0 = l7seg(soc->led_7_segments >> 12);897const unsigned char led1 = l7seg(soc->led_7_segments >> 8);898const unsigned char led2 = l7seg(soc->led_7_segments >> 4);899const unsigned char led3 = l7seg(soc->led_7_segments);900
901fprintf(out, "LEDS: %02"PRIx8"\n", soc->leds);902/*fprintf(out, "VGA Cursor: %04"PRIx16"\n", soc->vga_cursor);903fprintf(out, "VGA Control: %04"PRIx16"\n", soc->vga_control);*/
904fprintf(out, "Timer Control: %04"PRIx16"\n", soc->timer_control);905fprintf(out, "Timer: %04"PRIx16"\n", soc->timer);906fprintf(out, "IRC Mask: %04"PRIx16"\n", soc->irc_mask);907fprintf(out, "UART Input: %02"PRIx8"\n", soc->uart_getchar_register);908fprintf(out, "LED 7 segment: %c%c%c%c\n", led0, led1, led2, led3);909fprintf(out, "Switches: %04"PRIx16"\n", soc->switches);910fprintf(out, "Waiting: %s\n", soc->wait ? "true" : "false");911fprintf(out, "Flash Control: %04"PRIx16"\n", soc->mem_control);912fprintf(out, "Flash Address Lo: %04"PRIx16"\n", soc->mem_addr_low);913fprintf(out, "Flash Data Out: %04"PRIx16"\n", soc->mem_dout);914fprintf(out, "UART TX Baud: %04"PRIx16"\n", soc->uart_tx_baud);915fprintf(out, "UART RX Baud: %04"PRIx16"\n", soc->uart_rx_baud);916fprintf(out, "UART Control: %04"PRIx16"\n", soc->uart_control);917}
918
919static void terminal_default_command_sequence(vt100_t * const t) {920assert(t);921t->n1 = 1;922t->n2 = 1;923t->command_index = 0;924}
925
926static void terminal_at_xy(vt100_t * const t, unsigned x, unsigned y, const bool limit_not_wrap) {927assert(t);928if (limit_not_wrap) {929x = MAX(x, 0);930y = MAX(y, 0);931x = MIN(x, t->width - 1);932y = MIN(y, t->height - 1);933} else {934x %= t->width;935y %= t->height;936}937t->cursor = (y * t->width) + x;938}
939
940static int terminal_x_current(const vt100_t * const t) {941assert(t);942return t->cursor % t->width;943}
944
945static int terminal_y_current(const vt100_t * const t) {946assert(t);947return t->cursor / t->width;948}
949
950static void terminal_at_xy_relative(vt100_t *t, const int x, const int y, const bool limit_not_wrap) {951assert(t);952const int x_current = terminal_x_current(t);953const int y_current = terminal_y_current(t);954terminal_at_xy(t, x_current + x, y_current + y, limit_not_wrap);955}
956
957static void terminal_parse_attribute(vt100_attribute_t * const a, const unsigned v) {958assert(a);959switch (v) {960case 0:961memset(a, 0, sizeof(*a));962a->foreground_color = WHITE;963a->background_color = BLACK;964return;965case 1: a->bold = true; return;966case 22: a->bold = false; return;967case 4: a->under_score = true; return;968case 5: a->blink = true; return;969case 25: a->blink = false; return;970case 7: a->reverse_video = true; return;971case 39: a->reverse_video = false; return;972case 8: a->conceal = true; return;973case 28: a->conceal = false; return;974default:975if (v >= 30 && v <= 37)976a->foreground_color = v - 30;977if (v >= 40 && v <= 47)978a->background_color = v - 40;979}980}
981
982static const vt100_attribute_t vt100_default_attribute = {983.foreground_color = WHITE,984.background_color = BLACK,985};986
987static void terminal_attribute_block_set(vt100_t *t, const size_t size, const vt100_attribute_t * const a) {988assert(t);989assert(a);990for (size_t i = 0; i < size; i++)991memcpy(&t->attributes[i], a, sizeof(*a));992}
993
994static int terminal_escape_sequences(vt100_t * const t, const uint8_t c) {995assert(t);996assert(t->state != TERMINAL_NORMAL_MODE);997switch (t->state) {998case TERMINAL_CSI: /* process CSI and some non-CSI Escape Only commands */999switch (c) {1000case '[': t->state = TERMINAL_COMMAND; break;1001case 'c': goto eraser; /*reset display*/ break;1002case '7': t->cursor_saved = t->cursor; t->attribute_saved = t->attribute; break;1003case '8': t->cursor = t->cursor_saved; t->attribute = t->attribute_saved; break;1004default: goto fail;1005}1006
1007break;1008case TERMINAL_COMMAND:1009switch (c) {1010case 's':1011t->cursor_saved = t->cursor;1012goto success;1013case 'n':1014t->cursor = t->cursor_saved;1015goto success;1016case '?':1017terminal_default_command_sequence(t);1018t->state = TERMINAL_DECTCEM;1019break;1020case ';':1021terminal_default_command_sequence(t);1022t->state = TERMINAL_NUMBER_2;1023break;1024default:1025if (isdigit(c)) {1026terminal_default_command_sequence(t);1027t->command_index++;1028t->n1 = c - '0';1029t->state = TERMINAL_NUMBER_1;1030} else {1031goto fail;1032}1033}1034break;1035case TERMINAL_NUMBER_1:1036if (isdigit(c)) {1037if (t->command_index > 3)1038goto fail;1039t->n1 = (t->n1 * (t->command_index ? 10 : 0)) + (c - '0');1040t->command_index++;1041break;1042}1043
1044switch (c) {1045case 'A': terminal_at_xy_relative(t, 0, -t->n1, true); goto success;/* relative cursor up */1046case 'B': terminal_at_xy_relative(t, 0, t->n1, true); goto success;/* relative cursor down */1047case 'C': terminal_at_xy_relative(t, t->n1, 0, true); goto success;/* relative cursor forward */1048case 'D': terminal_at_xy_relative(t, -t->n1, 0, true); goto success;/* relative cursor back */1049case 'E': terminal_at_xy(t, 0, t->n1, false); goto success; /* relative cursor down, beginning of line */1050case 'F': terminal_at_xy(t, 0, -t->n1, false); goto success; /* relative cursor up, beginning of line */1051case 'G': terminal_at_xy(t, t->n1, terminal_y_current(t), true); goto success; /* move the cursor to column n */1052case 'm': /* set attribute, CSI number m */1053terminal_parse_attribute(&t->attribute, t->n1);1054t->attributes[t->cursor] = t->attribute;1055goto success;1056case 'i': /* AUX Port On == 5, AUX Port Off == 4 */1057if (t->n1 == 5 || t->n1 == 4)1058goto success;1059goto fail;1060case 'n': /* Device Status Report */1061/** @note This should transmit to the H2 system the1062* following "ESC[n;mR", where n is the row and m is the column,
1063* we're not going to do this as the hardware does not, although
1064* 'fifo_push()' on 'uart_rx_fifo' could be called to do this */
1065if (t->n1 == 6)1066goto success;1067goto fail;1068eraser: /* HAHA: This is clearly the best way of doing things. */1069t->command_index = 1;1070t->n1 = 3; /* fall-through */1071case 'J': /* reset */1072
1073switch (t->n1) {1074case 3: /* fall-through */1075case 2: t->cursor = 0; /* with cursor */ /* fall-through */1076case 1:1077if (t->command_index) {1078memset(t->m, ' ', t->size);1079terminal_attribute_block_set(t, t->size, &vt100_default_attribute);1080goto success;1081} /* fall through if number not supplied */ /* fall-through */1082case 0:1083memset(t->m, ' ', t->cursor);1084terminal_attribute_block_set(t, t->cursor, &vt100_default_attribute);1085goto success;1086}1087goto fail;1088case ';':1089t->command_index = 0;1090t->state = TERMINAL_NUMBER_2;1091break;1092default:1093goto fail;1094}1095break;1096case TERMINAL_NUMBER_2:1097if (isdigit(c)) {1098if (t->command_index > 3)1099goto fail;1100t->n2 = (t->n2 * (t->command_index ? 10 : 0)) + (c - '0');1101t->command_index++;1102} else {1103switch (c) {1104case 'm':1105terminal_parse_attribute(&t->attribute, t->n1);1106terminal_parse_attribute(&t->attribute, t->n2);1107t->attributes[t->cursor] = t->attribute;1108goto success;1109case 'H':1110case 'f':1111terminal_at_xy(t, MIN(t->n2-1,t->n2), MIN(t->n1-1,t->n1), true);1112goto success;1113}1114goto fail;1115}1116break;1117case TERMINAL_DECTCEM:1118if (isdigit(c)) {1119if (t->command_index > 1)1120goto fail;1121t->n1 = (t->n1 * (t->command_index ? 10 : 0)) + (c - '0');1122t->command_index++;1123break;1124}1125
1126if (t->n1 != 25)1127goto fail;1128switch (c) {1129case 'l': t->cursor_on = false; goto success;1130case 'h': t->cursor_on = true; goto success;1131default:1132goto fail;1133}1134case TERMINAL_STATE_END:1135t->state = TERMINAL_NORMAL_MODE;1136break;1137default:1138fatal("invalid terminal state: %u", (unsigned)t->state);1139}1140
1141return 0;1142success:1143t->state = TERMINAL_NORMAL_MODE;1144return 0;1145fail:1146t->state = TERMINAL_NORMAL_MODE;1147return -1;1148}
1149
1150void vt100_update(vt100_t *t, const uint8_t c) {1151assert(t);1152assert(t->size <= VT100_MAX_SIZE);1153assert((t->width * t->height) <= VT100_MAX_SIZE);1154
1155if (t->state != TERMINAL_NORMAL_MODE) {1156if (terminal_escape_sequences(t, c)) {1157t->state = TERMINAL_NORMAL_MODE;1158/*warning("invalid ANSI command sequence");*/1159}1160} else {1161switch (c) {1162case ESCAPE:1163t->state = TERMINAL_CSI;1164break;1165case '\t':1166t->cursor += 8;1167t->cursor &= ~0x7;1168break;1169case '\n':1170t->cursor += t->width;1171t->cursor = (t->cursor / t->width) * t->width;1172break;1173case '\r':1174break;1175case BACKSPACE:1176terminal_at_xy_relative(t, -1, 0, true);1177break;1178default:1179assert(t->cursor < t->size);1180t->m[t->cursor] = c;1181memcpy(&t->attributes[t->cursor], &t->attribute, sizeof(t->attribute));1182t->cursor++;1183}1184if (t->cursor >= t->size) {1185const vt100_attribute_t *a = &vt100_default_attribute;1186t->cursor -= t->width;1187memmove(t->m, t->m + t->width, t->size - t->width);1188memset((t->m + t->size) - t->width, ' ', t->width);1189for (size_t i = 0; i < (t->size - t->width); i++)1190t->attributes[i] = t->attributes[i + t->width];1191for (size_t i = t->size - t->width; i < t->size; i++)1192memcpy(&t->attributes[i], a, sizeof(*a));1193}1194t->cursor %= t->size;1195}1196}
1197
1198#define FLASH_WRITE_CYCLES (20) /* x10ns */1199#define FLASH_ERASE_CYCLES (200) /* x10ns */1200
1201typedef enum {1202FLASH_STATUS_RESERVED = 1u << 0,1203FLASH_STATUS_BLOCK_LOCKED = 1u << 1,1204FLASH_STATUS_PROGRAM_SUSPEND = 1u << 2,1205FLASH_STATUS_VPP = 1u << 3,1206FLASH_STATUS_PROGRAM = 1u << 4,1207FLASH_STATUS_ERASE_BLANK = 1u << 5,1208FLASH_STATUS_ERASE_SUSPEND = 1u << 6,1209FLASH_STATUS_DEVICE_READY = 1u << 7,1210} flash_status_register_t;1211
1212typedef enum {1213FLASH_READ_ARRAY,1214FLASH_QUERY,1215FLASH_READ_DEVICE_IDENTIFIER,1216FLASH_READ_STATUS_REGISTER,1217FLASH_WORD_PROGRAM,1218FLASH_WORD_PROGRAMMING,1219FLASH_LOCK_OPERATION,1220FLASH_LOCK_OPERATING,1221FLASH_BLOCK_ERASE,1222FLASH_BLOCK_ERASING,1223FLASH_BUFFERED_PROGRAM,1224FLASH_BUFFERED_PROGRAMMING,1225} flash_state_t;1226
1227/** @note read the PC28F128P33BF60 datasheet to decode this
1228* information, this table was actually acquired from reading
1229* the data from the actual device. */
1230static const uint16_t PC28F128P33BF60_CFI_Query_Table[0x200] = {12310x0089, 0x881E, 0x0000, 0xFFFF, 0x0089, 0xBFCF, 0x0000, 0xFFFF,12320x0089, 0x881E, 0x0000, 0x0000, 0x0089, 0xBFCF, 0x0000, 0xFFFF,12330x0051, 0x0052, 0x0059, 0x0001, 0x0000, 0x000A, 0x0001, 0x0000,12340x0000, 0x0000, 0x0000, 0x0023, 0x0036, 0x0085, 0x0095, 0x0006,12350x0009, 0x0009, 0x0000, 0x0002, 0x0002, 0x0003, 0x0000, 0x0018,12360x0001, 0x0000, 0x0009, 0x0000, 0x0002, 0x007E, 0x0000, 0x0000,12370x0002, 0x0003, 0x0000, 0x0080, 0x0000, 0x0000, 0x0000, 0x0000,12380x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12390xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12400xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12410xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12420xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12430xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12440xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12450xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12460xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12470xFFBE, 0x0396, 0x66A2, 0xA600, 0x395A, 0xFFFF, 0xFFFF, 0xFFFF,12480xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12490xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12500xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12510xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12520xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12530xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12540xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12550xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12560xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12570xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12580xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12590xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12600xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12610xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12620xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12630xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12640xFFFF, 0xFFFF, 0x0050, 0x0052, 0x0049, 0x0031, 0x0035, 0x00E6,12650x0001, 0x0000, 0x0000, 0x0001, 0x0003, 0x0000, 0x0030, 0x0090,12660x0002, 0x0080, 0x0000, 0x0003, 0x0003, 0x0089, 0x0000, 0x0000,12670x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000, 0x0004, 0x0004,12680x0004, 0x0001, 0x0002, 0x0003, 0x0007, 0x0001, 0x0024, 0x0000,12690x0001, 0x0000, 0x0011, 0x0000, 0x0000, 0x0002, 0x007E, 0x0000,12700x0000, 0x0002, 0x0064, 0x0000, 0x0002, 0x0003, 0x0000, 0x0080,12710x0000, 0x0000, 0x0000, 0x0080, 0x0003, 0x0000, 0x0080, 0x0000,12720x0064, 0x0000, 0x0002, 0x0003, 0x0000, 0x0080, 0x0000, 0x0000,12730x0000, 0x0080, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12740xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12750xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12760xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12770xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12780xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12790xF020, 0x4DBF, 0x838C, 0xFC08, 0x638F, 0x20E3, 0xFF03, 0xD8D7,12800xC838, 0xFFFF, 0xFFFF, 0xAFFF, 0x3352, 0xB333, 0x3004, 0x1353,12810x0003, 0xA000, 0x80D5, 0x8A03, 0xFF4A, 0xFFFF, 0xFFFF, 0x0FFF,12820x2000, 0x0000, 0x0004, 0x0080, 0x1000, 0x0000, 0x0002, 0x0040,12830x0000, 0x0008, 0x0000, 0x0001, 0x2000, 0x0000, 0x0400, 0x0000,12840x0080, 0x0000, 0x0010, 0x0000, 0x0002, 0x4000, 0x0000, 0x0800,12850x0000, 0x0100, 0x0000, 0x0020, 0x0000, 0x0004, 0x8000, 0x0000,12860x1000, 0x0000, 0x0200, 0x0000, 0x0040, 0x0000, 0x0008, 0x0000,12870x0001, 0x2000, 0x0000, 0x0800, 0x0000, 0x0200, 0x0000, 0x0040,12880x0000, 0x0008, 0x0000, 0x0001, 0x2000, 0x0000, 0x0400, 0x0000,12890x0080, 0x0000, 0x0010, 0x0000, 0x0002, 0x4000, 0x0000, 0x0800,12900x0000, 0x0100, 0x0000, 0x0020, 0x0000, 0x0004, 0x8000, 0x0000,12910x1000, 0x0000, 0x0200, 0x0000, 0x0040, 0x0000, 0x0008, 0x0000,12920x0001, 0x4000, 0x0000, 0x1000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12930xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,12940xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,1295};1296
1297uint16_t PC28F128P33BF60_CFI_Query_Read(uint32_t addr) {1298addr &= 0x3ff;1299if (addr > 0x1ff) {1300addr &= 0x7;1301static const uint16_t r[] = {13020x0089, 0x881E, 0x0000, 0x0000,13030x0089, 0xBFCF, 0x0000, 0xFFFF1304};1305return r[addr];1306}1307return PC28F128P33BF60_CFI_Query_Table[addr];1308}
1309
1310static uint16_t h2_io_flash_read(const flash_t * const f, const uint32_t addr, const bool oe, const bool we, const bool rst) {1311if (rst)1312return 0;1313
1314if (oe && we) {1315warning("OE and WE set at the same time");1316return 0;1317}1318
1319if (!oe) {1320warning("flash read with OE not selected");1321return 0;1322}1323
1324switch (f->mode) {1325case FLASH_READ_ARRAY: return f->nvram[0x7ffffff & addr];1326case FLASH_READ_DEVICE_IDENTIFIER:1327case FLASH_QUERY: return PC28F128P33BF60_CFI_Query_Read(addr);1328case FLASH_READ_STATUS_REGISTER: return f->status;1329case FLASH_WORD_PROGRAMMING:1330case FLASH_WORD_PROGRAM: return f->status;1331case FLASH_BLOCK_ERASING:1332case FLASH_BLOCK_ERASE: return f->status;1333case FLASH_LOCK_OPERATING:1334case FLASH_LOCK_OPERATION: return f->status; /* return what? */1335default:1336fatal("invalid flash state: %u", f->mode);1337}1338
1339return 0;1340}
1341
1342static unsigned addr_to_block(uint32_t addr) {1343const uint32_t lower_64k_blocks_highest_address = 127ul * 64ul * 1024ul; /* 0x7F000 */1344/*assert(addr < 0x7ffffff);*/1345if (addr < lower_64k_blocks_highest_address)1346return addr / (64ul * 1024ul);1347addr -= lower_64k_blocks_highest_address;1348addr /= (16ul * 1024ul);1349return addr + 127ul;1350}
1351
1352static unsigned block_size(const unsigned block) {1353if (block >= 127ul)1354return 16ul * 1024ul;1355return 64ul * 1024ul;1356}
1357
1358static bool block_locked(const flash_t * const f, const unsigned block) {1359assert(f);1360assert(block < FLASH_BLOCK_MAX);1361/* The locks block would probably be best be represented as a bit1362* vector, the functions to manipulate a bit vector can quite easily be
1363* turned into a useful header only library */
1364return !!(f->locks[block]);1365}
1366
1367static bool address_protected(const flash_t *const f, const uint32_t addr) {1368assert(f);1369return block_locked(f, addr_to_block(addr));1370}
1371
1372/* We could implement the full standard for the Common Flash Memory
1373* Interface, and make the timing based on a simulated calculated time
1374* instead multiples of 10us see:
1375* <https://en.wikipedia.org/wiki/Common_Flash_Memory_Interface> with the
1376* devices PC28F128P33BF60 and NP8P128A13T1760E. The lock status of a register
1377* should be read as well as checking f->arg1_address == f->arg2_address for
1378* commands which require this.
1379*
1380* We *could* do that, however this simulator is 'good enough' for our
1381* purposes. */
1382static 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) {1383assert(f);1384if (oe && we)1385warning("OE and WE set at the same time");1386
1387if (rst) {1388f->mode = FLASH_READ_ARRAY;1389return;1390}1391
1392switch (f->mode) {1393case FLASH_READ_ARRAY:1394case FLASH_READ_STATUS_REGISTER:1395case FLASH_QUERY:1396case FLASH_READ_DEVICE_IDENTIFIER:1397f->arg1_address = addr;1398f->cycle = 0;1399f->status |= FLASH_STATUS_DEVICE_READY;1400
1401if (!we && f->we && cs) {1402switch (f->data) {1403case 0x00: break;1404case 0xff: f->mode = FLASH_READ_ARRAY; break;1405case 0x90: f->mode = FLASH_READ_DEVICE_IDENTIFIER; break;1406case 0x98: f->mode = FLASH_QUERY; break;1407case 0x70: f->mode = FLASH_READ_STATUS_REGISTER; break;1408case 0x50: f->status = FLASH_STATUS_DEVICE_READY; break; /* changes state? */1409case 0x10:1410case 0x40: f->mode = FLASH_WORD_PROGRAM; break;1411case 0xE8: f->mode = FLASH_BUFFERED_PROGRAM; break;1412case 0x20: f->mode = FLASH_BLOCK_ERASE; break;1413/*case 0xB0: SUSPEND NOT IMPLEMENTED; break; */1414/*case 0xD0: RESUME NOT IMPLEMENTED; break; */1415case 0x60: f->mode = FLASH_LOCK_OPERATION; break;1416/*case 0xC0: PROTECTION PROGRAM NOT IMPLEMENTED; break; */1417default:1418warning("Common Flash Interface command not implemented: %x", (unsigned)(f->data));1419f->mode = FLASH_READ_ARRAY;1420}1421}1422break;1423case FLASH_WORD_PROGRAM:1424if (!we && f->we && cs) {1425f->cycle = 0;1426if (address_protected(f, f->arg1_address)) {1427warning("address locked: %u", (unsigned)f->arg1_address);1428f->status |= FLASH_STATUS_BLOCK_LOCKED;1429f->status |= FLASH_STATUS_PROGRAM;1430f->mode = FLASH_READ_STATUS_REGISTER;1431} else {1432f->status &= ~FLASH_STATUS_DEVICE_READY;1433f->mode = FLASH_WORD_PROGRAMMING;1434}1435} else if (we && cs) {1436f->arg2_address = addr;1437}1438break;1439case FLASH_WORD_PROGRAMMING:1440if (f->cycle++ > FLASH_WRITE_CYCLES) {1441f->nvram[f->arg1_address] &= f->data;1442f->mode = FLASH_READ_STATUS_REGISTER;1443f->cycle = 0;1444f->status |= FLASH_STATUS_DEVICE_READY;1445}1446break;1447case FLASH_LOCK_OPERATION:1448if (!we && f->we && cs) {1449f->mode = FLASH_LOCK_OPERATING;1450} else if (we && cs) {1451f->arg2_address = addr;1452}1453break;1454case FLASH_LOCK_OPERATING:1455if (f->arg1_address > FLASH_BLOCK_MAX) {1456warning("block address invalid: %u", (unsigned)f->arg1_address);1457f->mode = FLASH_READ_STATUS_REGISTER;1458break;1459}1460
1461switch (f->data) {1462case 0xD0:1463if (f->locks[f->arg1_address] != FLASH_LOCKED_DOWN)1464f->locks[f->arg1_address] = FLASH_UNLOCKED;1465else1466warning("block locked down: %u", (unsigned)f->arg1_address);1467break;1468case 0x01:1469if (f->locks[f->arg1_address] != FLASH_LOCKED_DOWN)1470f->locks[f->arg1_address] = FLASH_LOCKED;1471else1472warning("block locked down: %u", (unsigned)f->arg1_address);1473break;1474case 0x2F:1475f->locks[f->arg1_address] = FLASH_LOCKED_DOWN;1476break;1477default:1478warning("Unknown/Unimplemented Common Flash Interface Lock Operation: %x", (unsigned)(f->data));1479}1480f->mode = FLASH_READ_STATUS_REGISTER;1481break;1482case FLASH_BLOCK_ERASE:1483/*f->status &= ~FLASH_STATUS_DEVICE_READY;*/1484if (!we && f->we && cs) {1485if (addr != f->arg1_address)1486warning("block addresses differ: 1(%u) 2(%u)", f->arg1_address, addr);1487if (f->data != 0xD0) /* erase confirm */1488f->mode = FLASH_READ_STATUS_REGISTER;1489else1490f->mode = FLASH_BLOCK_ERASING;1491
1492if (f->mode == FLASH_BLOCK_ERASING && address_protected(f, f->arg1_address)) {1493warning("address locked: %u", (unsigned)f->arg1_address);1494f->status |= FLASH_STATUS_BLOCK_LOCKED;1495f->status |= FLASH_STATUS_ERASE_BLANK;1496f->mode = FLASH_READ_STATUS_REGISTER;1497}1498} else if (we && cs) {1499f->arg2_address = addr;1500}1501f->cycle = 0;1502break;1503case FLASH_BLOCK_ERASING:1504f->status &= ~FLASH_STATUS_DEVICE_READY;1505if (f->cycle++ > FLASH_ERASE_CYCLES) {1506const unsigned block = f->arg1_address;1507const unsigned size = block_size(block);1508if (block >= FLASH_BLOCK_MAX) {1509warning("block operation out of range: %u", block);1510f->status |= FLASH_STATUS_ERASE_BLANK;1511} else {1512memset(f->nvram+block*size, 0xff, sizeof(f->nvram[0])*size);1513}1514f->cycle = 0;1515f->mode = FLASH_READ_STATUS_REGISTER;1516f->status |= FLASH_STATUS_DEVICE_READY;1517}1518break;1519case FLASH_BUFFERED_PROGRAM:1520case FLASH_BUFFERED_PROGRAMMING:1521warning("block programming not implemented");1522f->status |= FLASH_STATUS_PROGRAM;1523f->mode = FLASH_READ_STATUS_REGISTER;1524break;1525default:1526fatal("invalid flash state: %u", f->mode);1527return;1528}1529if (we && !oe)1530f->data = data;1531f->we = we;1532f->cs = cs;1533}
1534
1535uint16_t h2_io_memory_read_operation(const h2_soc_state_t * const soc) {1536assert(soc);1537const uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;1538const bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;1539const bool flash_cs = soc->mem_control & FLASH_CHIP_SELECT;1540const bool sram_cs = soc->mem_control & SRAM_CHIP_SELECT;1541const bool oe = soc->mem_control & FLASH_MEMORY_OE;1542const bool we = soc->mem_control & FLASH_MEMORY_WE;1543
1544if (oe && we)1545return 0;1546
1547if (flash_cs && sram_cs)1548warning("SRAM and Flash Chip selects both high");1549
1550if (flash_cs)1551return h2_io_flash_read(&soc->flash, flash_addr >> 1, oe, we, flash_rst);1552
1553if (sram_cs && oe && !we)1554return soc->vram[flash_addr >> 1];1555return 0;1556}
1557
1558static uint16_t h2_io_get_default(h2_soc_state_t * const soc, const uint16_t addr, bool *debug_on) {1559assert(soc);1560debug("IO read addr: %"PRIx16, addr);1561(void)debug_on;1562switch (addr) {1563case iUart: return UART_TX_FIFO_EMPTY | soc->uart_getchar_register;1564case iVT100: return UART_TX_FIFO_EMPTY | soc->ps2_getchar_register;1565case iSwitches: return soc->switches;1566case iTimerDin: return soc->timer;1567case iMemDin: return h2_io_memory_read_operation(soc);1568default:1569warning("invalid read from %04"PRIx16, addr);1570}1571return 0;1572}
1573
1574static void h2_io_set_default(h2_soc_state_t *soc, const uint16_t addr, const uint16_t value, bool *debug_on) {1575assert(soc);1576debug("IO write addr/value: %"PRIx16"/%"PRIx16, addr, value);1577
1578switch (addr) {1579case oUart:1580if (value & UART_TX_WE)1581putch(0xFF & value);1582if (value & UART_RX_RE)1583soc->uart_getchar_register = wrap_getch(debug_on);1584break;1585case oLeds: soc->leds = value; break;1586case oTimerCtrl: soc->timer_control = value; break;1587case oVT100:1588if (value & UART_TX_WE)1589vt100_update(&soc->vt100, value);1590if (value & UART_RX_RE)1591soc->ps2_getchar_register = wrap_getch(debug_on);1592break;1593case o7SegLED: soc->led_7_segments = value; break;1594case oIrcMask: soc->irc_mask = value; break;1595case oMemControl:1596{1597soc->mem_control = value;1598const bool sram_cs = soc->mem_control & SRAM_CHIP_SELECT;1599const bool oe = soc->mem_control & FLASH_MEMORY_OE;1600const bool we = soc->mem_control & FLASH_MEMORY_WE;1601
1602if (sram_cs && !oe && we)1603soc->vram[(((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low) >> 1] = soc->mem_dout;1604break;1605}1606case oMemAddrLow: soc->mem_addr_low = value; break;1607case oMemDout: soc->mem_dout = value; break;1608case oUartTxBaud: soc->uart_tx_baud = value; break;1609case oUartRxBaud: soc->uart_rx_baud = value; break;1610case oUartControl: soc->uart_control = value; break;1611default:1612warning("invalid write to %04"PRIx16 ":%04"PRIx16, addr, value);1613}1614}
1615
1616static void h2_io_update_default(h2_soc_state_t * const soc) {1617assert(soc);1618
1619if (soc->timer_control & TIMER_ENABLE) {1620if (soc->timer_control & TIMER_RESET) {1621soc->timer = 0;1622soc->timer_control &= ~TIMER_RESET;1623} else {1624soc->timer++;1625if ((soc->timer > (soc->timer_control & 0x1FFF))) {1626if (soc->timer_control & TIMER_INTERRUPT_ENABLE) {1627soc->interrupt = soc->irc_mask & (1 << isrTimer);1628soc->interrupt_selector |= soc->irc_mask & (1 << isrTimer);1629}1630soc->timer = 0;1631}1632}1633}1634
1635/* DPAD interrupt on change state */1636const uint16_t prev = soc->switches_previous;1637const uint16_t cur = soc->switches;1638if ((prev & 0xff00) != (cur & 0xff00)) {1639soc->interrupt = soc->irc_mask & (1u << isrDPadButton);1640soc->interrupt_selector |= soc->irc_mask & (1u << isrDPadButton);1641}1642soc->switches_previous = soc->switches;1643
1644const uint32_t flash_addr = ((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low;1645const bool flash_rst = soc->mem_control & FLASH_MEMORY_RESET;1646const bool flash_cs = soc->mem_control & FLASH_CHIP_SELECT;1647const bool oe = soc->mem_control & FLASH_MEMORY_OE;1648const bool we = soc->mem_control & FLASH_MEMORY_WE;1649h2_io_flash_update(&soc->flash, flash_addr >> 1, soc->mem_dout, oe, we, flash_rst, flash_cs);1650}
1651
1652h2_soc_state_t *h2_soc_state_new(void) {1653h2_soc_state_t *r = allocate_or_die(sizeof(h2_soc_state_t));1654vt100_t *v = &r->vt100;1655memset(r->flash.nvram, 0xff, sizeof(r->flash.nvram[0])*FLASH_BLOCK_MAX);1656memset(r->flash.locks, FLASH_LOCKED, FLASH_BLOCK_MAX);1657
1658v->width = VGA_WIDTH;1659v->height = VGA_HEIGHT;1660v->size = VGA_WIDTH * VGA_HEIGHT;1661v->state = TERMINAL_NORMAL_MODE;1662v->cursor_on = true;1663v->blinks = false;1664v->n1 = 1;1665v->n2 = 1;1666v->attribute.foreground_color = WHITE;1667v->attribute.background_color = BLACK;1668for (size_t i = 0; i < v->size; i++)1669v->attributes[i] = v->attribute;1670return r;1671}
1672
1673void h2_soc_state_free(h2_soc_state_t *soc) {1674if (!soc)1675return;1676memset(soc, 0, sizeof(*soc));1677free(soc);1678}
1679
1680h2_io_t *h2_io_new(void) {1681h2_io_t *io = allocate_or_die(sizeof(*io));1682io->in = h2_io_get_default;1683io->out = h2_io_set_default;1684io->update = h2_io_update_default;1685io->soc = h2_soc_state_new();1686return io;1687}
1688
1689void h2_io_free(h2_io_t *io) {1690if (!io)1691return;1692h2_soc_state_free(io->soc);1693memset(io, 0, sizeof(*io));1694free(io);1695}
1696
1697static void dpush(h2_t * const h, const uint16_t v) {1698assert(h);1699h->sp++;1700h->dstk[h->sp % STK_SIZE] = h->tos;1701h->tos = v;1702if (h->sp >= STK_SIZE)1703warning("data stack overflow");1704h->sp %= STK_SIZE;1705}
1706
1707static uint16_t dpop(h2_t * const h) {1708assert(h);1709const uint16_t r = h->tos;1710h->tos = h->dstk[h->sp % STK_SIZE];1711h->sp--;1712if (h->sp >= STK_SIZE)1713warning("data stack underflow");1714h->sp %= STK_SIZE;1715return r;1716}
1717
1718static void rpush(h2_t *h, const uint16_t r) {1719assert(h);1720h->rp++;1721h->rstk[(h->rp) % STK_SIZE] = r;1722if (h->rp >= STK_SIZE)1723warning("return stack overflow");1724h->rp %= STK_SIZE;1725}
1726
1727static uint16_t stack_delta(const uint16_t d) {1728static const uint16_t i[4] = { 0x0000, 0x0001, 0xFFFE, 0xFFFF };1729assert((d & 0xFFFC) == 0);1730return i[d];1731}
1732
1733static inline void reverse(char * const r, const size_t length) {1734const size_t last = length - 1;1735for (size_t i = 0; i < length/2ul; i++) {1736const size_t t = r[i];1737r[i] = r[last - i];1738r[last - i] = t;1739}1740}
1741
1742static inline void unsigned_to_csv(char b[64], unsigned u, const char delimiter) {1743unsigned i = 0;1744do {1745const unsigned base = 10; /* bases 2-10 allowed */1746const unsigned q = u % base;1747const unsigned r = u / base;1748b[i++] = q + '0';1749u = r;1750} while (u);1751b[i] = delimiter;1752b[i+1] = '\0';1753reverse(b, i);1754}
1755
1756static inline void csv_value(FILE *o, const unsigned u) {1757char b[64] = { 0 };1758unsigned_to_csv(b, u, ',');1759fputs(b, o);1760}
1761
1762/* This is a fairly fast trace/CSV generation routine which avoids the use of fprintf,
1763* speeding up this routine would greatly improve the speed of the interpreter when
1764* tracing is on. The symbol table lookup can be disabled by passing in NULL, this
1765* also greatly speeds things up. It can be used in a roundabout way to generate
1766* a file viewable by GTKWave, its output can be fed into 'csv2vcd' after some
1767* minimal processing with AWK, turning it into a VCD file, which is viewable in
1768* the wave form viewer, see:
1769* <http://www.ic.unicamp.br/~ducatte/mc542/Docs/gtkwave.pdf> and
1770* <https://github.com/carlos-jenkins/csv2vcd> for more details. */
1771static int h2_log_csv(FILE *o, const h2_t * const h, const symbol_table_t * const symbols, const bool header) {1772if (!o)1773return 0;1774assert(h);1775if (header) {1776fputs("\"pc[15:0]\",", o);1777fputs("\"tos[15:0]\",", o);1778fputs("\"rp[7:0]\",", o);1779fputs("\"sp[7:0]\",", o);1780fputs("\"ie\",", o);1781fputs("\"instruction[15:0]\",", o);1782if (symbols)1783fputs("\"disassembled\",", o);1784fputs("\"Time\"", o);1785if (fputc('\n', o) != '\n')1786return -1;1787return 0;1788}1789
1790csv_value(o, h->pc);1791csv_value(o, h->tos);1792csv_value(o, h->rp);1793csv_value(o, h->sp);1794csv_value(o, h->ie);1795csv_value(o, h->core[h->pc]);1796if (symbols) {1797fputc('"', o);1798disassemble_instruction(h->core[h->pc], o, symbols, DCM_NONE);1799fputs("\",", o);1800}1801csv_value(o, h->time*10);1802
1803if (fputc('\n', o) != '\n')1804return -1;1805return 0;1806}
1807
1808typedef struct {1809FILE *input;1810FILE *output;1811bool step;1812bool trace_on;1813} debug_state_t;1814
1815static const char *debug_prompt = "debug> ";1816
1817static uint16_t map_char_to_number(int c) {1818if (c >= '0' && c <= '9')1819return c - '0';1820c = tolower(c);1821if (c >= 'a' && c <= 'z')1822return c + 10 - 'a';1823fatal("invalid numeric character: %c", c);1824return 0;1825}
1826
1827static bool numeric(const int c, const int base) {1828assert(base == 10 || base == 16);1829if (base == 10)1830return isdigit(c);1831return isxdigit(c);1832}
1833
1834static int number(const char * const s, uint16_t * const o, const size_t length) {1835size_t i = 0, start = 0;1836uint32_t out = 0;1837int base = 10;1838bool negate = false;1839assert(o);1840if (s[i] == '\0')1841return 0;1842
1843if (s[i] == '-') {1844if (s[i+1] == '\0')1845return 0;1846negate = true;1847start = ++i;1848}1849
1850if (s[i] == '$') {1851base = 16;1852if (s[i+1] == '\0')1853return 0;1854start = i + 1;1855}1856
1857for (i = start; i < length; i++)1858if (!numeric(s[i], base))1859return 0;1860
1861for (i = start; i < length; i++)1862out = out * base + map_char_to_number(s[i]);1863
1864*o = negate ? out * (uint16_t)-1 : out;1865return 1;1866}
1867static void h2_print(FILE *out, const h2_t *const h) {1868assert(h);1869fputs("Return Stack:\n", out);1870memory_print(out, 0, h->rstk, STK_SIZE, false);1871fputs("Variable Stack:\n", out);1872fprintf(out, "tos: %04"PRIx16"\n", h->tos);1873memory_print(out, 1, h->dstk, STK_SIZE, false);1874
1875fprintf(out, "pc: %04"PRIx16"\n", h->pc);1876fprintf(out, "rp: %04"PRIx16" (max %04"PRIx16")\n", h->rp, h->rpm);1877fprintf(out, "dp: %04"PRIx16" (max %04"PRIx16")\n", h->sp, h->spm);1878fprintf(out, "ie: %s\n", h->ie ? "true" : "false");1879}
1880
1881typedef enum {1882DBG_CMD_NO_ARG,1883DBG_CMD_NUMBER,1884DBG_CMD_STRING,1885DBG_CMD_EITHER,1886} debug_command_type_e;1887
1888typedef struct {1889int cmd;1890int argc;1891debug_command_type_e arg1;1892debug_command_type_e arg2;1893char *description;1894} debug_command_t;1895
1896static const debug_command_t debug_commands[] = {1897{ .cmd = 'b', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "set break point " },1898{ .cmd = 'c', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "continue " },1899{ .cmd = 'd', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "dump " },1900{ .cmd = 'f', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "save to file " },1901{ .cmd = 'g', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "goto address " },1902{ .cmd = 'h', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "help " },1903{ .cmd = 'i', .argc = 1, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NO_ARG, .description = "input (port) " },1904{ .cmd = 'k', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "list all breakpoints " },1905{ .cmd = 'l', .argc = 1, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NO_ARG, .description = "set debug level " },1906{ .cmd = 'o', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "output (port value) " },1907{ .cmd = 'p', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "print IO state " },1908{ .cmd = 'q', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "quit " },1909{ .cmd = 'r', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "remove all break points" },1910{ .cmd = 's', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "step " },1911{ .cmd = 't', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "toggle tracing " },1912{ .cmd = 'u', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "unassemble " },1913{ .cmd = 'y', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "list symbols " },1914{ .cmd = 'v', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "print VGA display " },1915{ .cmd = 'P', .argc = 1, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "push value " },1916{ .cmd = 'D', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "pop value " },1917{ .cmd = 'G', .argc = 1, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = "call function/location " },1918{ .cmd = '!', .argc = 2, .arg1 = DBG_CMD_NUMBER, .arg2 = DBG_CMD_NUMBER, .description = "set value " },1919{ .cmd = '.', .argc = 0, .arg1 = DBG_CMD_NO_ARG, .arg2 = DBG_CMD_NO_ARG, .description = "print H2 CPU state " },1920{ .cmd = -1, .argc = 0, .arg1 = DBG_CMD_EITHER, .arg2 = DBG_CMD_NO_ARG, .description = NULL },1921};1922
1923static void debug_command_print_help(FILE *out, const debug_command_t *dc) {1924assert(out);1925assert(dc);1926
1927static const char *debug_help = "\1928Debugger Help: \n\n\
1929Hit 'Escape' when the simulation is and is reading from input to exit \n\
1930into the back debugger.\n\n\
1931Command list:\n\n";1932
1933static const char *arg_type[] = {1934[DBG_CMD_NO_ARG] = " ",1935[DBG_CMD_NUMBER] = "number ",1936[DBG_CMD_STRING] = "string ",1937[DBG_CMD_EITHER] = "number/string"1938};1939fputs(debug_help, out);1940for (unsigned i = 0; dc[i].cmd != -1; i++)1941fprintf(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]);1942}
1943
1944static 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) {1945assert(out);1946assert(dc);1947for (unsigned i = 0; dc[i].cmd != -1 ; i++) {1948if (dc[i].cmd == cmd) {1949if (dc[i].argc != argc) {1950fprintf(out, "command '%c' expects %d arguments, got %d\n", cmd, dc[i].argc, argc);1951return -1;1952}1953
1954if (dc[i].argc == 0)1955return 0;1956
1957if (dc[i].arg1 == DBG_CMD_NUMBER && !is_numeric1) {1958fprintf(out, "command '%c' expects arguments one to be numeric\n", cmd);1959return -1;1960}1961
1962if (dc[i].argc == 1)1963return 0;1964
1965if (dc[i].arg2 == DBG_CMD_NUMBER && !is_numeric2) {1966fprintf(out, "command '%c' expects arguments two to be numeric\n", cmd);1967return -1;1968}1969
1970return 0;1971}1972}1973fprintf(out, "unrecognized command '%c'\n", cmd);1974return -1;1975}
1976
1977static int debug_resolve_symbol(FILE *out, const char *symbol, const symbol_table_t * const symbols, uint16_t * const value) {1978assert(out);1979assert(symbol);1980assert(symbols);1981assert(value);1982*value = 0;1983const symbol_t * const sym = symbol_table_lookup(symbols, symbol);1984if (!sym) {1985fprintf(out, "symbol '%s' not found\n", symbol);1986return -1;1987}1988if (sym->type != SYMBOL_TYPE_LABEL && sym->type != SYMBOL_TYPE_CALL) {1989fprintf(out, "symbol is not call or label\n");1990return -1;1991}1992*value = sym->value;1993return 0;1994}
1995
1996static int h2_debugger(debug_state_t *ds, h2_t *h, h2_io_t *io, symbol_table_t *symbols, const uint16_t point) {1997assert(h);1998assert(ds);1999
2000const bool breaks = break_point_find(&h->bp, point);2001if (breaks)2002fprintf(ds->output, "\n === BREAK(0x%04"PRIx16") ===\n", h->pc);2003
2004if (ds->step || breaks) {2005char line[256];2006char op[256], arg1[256], arg2[256];2007int argc;2008bool is_numeric1, is_numeric2;2009uint16_t num1, num2;2010
2011ds->step = true;2012
2013again:2014memset(line, 0, sizeof(line));2015memset(op, 0, sizeof(op));2016memset(arg1, 0, sizeof(arg1));2017memset(arg2, 0, sizeof(arg2));2018
2019fputs(debug_prompt, ds->output);2020if (!fgets(line, sizeof(line), ds->input)) {2021fputs("End Of Input - exiting\n", ds->output);2022return -1;2023}2024
2025argc = sscanf(line, "%255s %255s %255s", op, arg1, arg2);2026if (argc < 1)2027goto again;2028
2029is_numeric1 = number(arg1, &num1, strlen(arg1));2030is_numeric2 = number(arg2, &num2, strlen(arg2));2031
2032if (!(strlen(op) == 1)) {2033fprintf(ds->output, "invalid command '%s'\n", op);2034goto again;2035}2036
2037if (debug_command_check(ds->output, debug_commands, op[0], argc-1, is_numeric1, is_numeric2) < 0)2038goto again;2039
2040switch (op[0]) {2041case ' ':2042case '\t':2043case '\r':2044case '\n':2045break;2046case 'f':2047{2048FILE *o = fopen(arg1, "wb");2049if (!o) {2050fprintf(ds->output, "could not open file '%s 'for writing: %s", arg1, strerror(errno));2051break;2052}2053h2_save(h, o, true);2054fclose(o);2055break;2056}2057case 'd':2058if (((long)num1 + (long)num2) > MAX_CORE)2059fprintf(ds->output, "overflow in RAM dump\n");2060else2061memory_print(ds->output, num1, h->core + num1, num2, true);2062break;2063case 'l':2064if (!is_numeric1) {2065fprintf(ds->output, "set log level expects one numeric argument\n");2066break;2067}2068log_level = num1;2069break;2070case 'b':2071if (!is_numeric1) {2072if (debug_resolve_symbol(ds->output, arg1, symbols, &num1))2073break;2074}2075break_point_add(&h->bp, num1);2076break;2077
2078case 'g':2079if (!is_numeric1) {2080if (debug_resolve_symbol(ds->output, arg1, symbols, &num1))2081break;2082}2083h->pc = num1;2084break;2085case 'G':2086if (!is_numeric1) {2087if (debug_resolve_symbol(ds->output, arg1, symbols, &num1))2088break;2089}2090rpush(h, h->pc);2091h->pc = num1;2092break;2093case '.':2094h2_print(ds->output, h);2095break;2096
2097case '!':2098if (num1 >= MAX_CORE) {2099fprintf(ds->output, "invalid write\n");2100break;2101}2102h->core[num1] = num2;2103break;2104case 'P':2105dpush(h, num1);2106break;2107case 'D':2108fprintf(ds->output, "popped: %04u\n", (unsigned)dpop(h));2109break;2110
2111case 'r':2112free(h->bp.points);2113h->bp.points = NULL;2114h->bp.length = 0;2115break;2116case 'u':2117if (num2 >= MAX_CORE || num1 > num2) {2118fprintf(ds->output, "invalid range\n");2119break;2120}2121for (uint16_t i = num1; i < num2; i++) {2122fprintf(ds->output, "%04"PRIx16 ":\t", i);2123disassemble_instruction(h->core[i], ds->output, symbols, DCM_NONE);2124fputc('\n', ds->output);2125}2126break;2127
2128case 'o':2129if (!io) {2130fprintf(ds->output, "I/O unavailable\n");2131break;2132}2133io->out(io->soc, num1, num2, NULL);2134
2135break;2136
2137case 'i':2138if (!io) {2139fprintf(ds->output, "I/O unavailable\n");2140break;2141}2142fprintf(ds->output, "read: %"PRIx16"\n", io->in(io->soc, num1, NULL));2143break;2144
2145case 'k':2146break_point_print(ds->output, &h->bp);2147break;2148case 'h':2149debug_command_print_help(ds->output, debug_commands);2150break;2151case 's':2152return 0;2153case 'c':2154ds->step = false;2155return 0;2156case 't':2157ds->trace_on = !ds->trace_on;2158fprintf(ds->output, "trace %s\n", ds->trace_on ? "on" : "off");2159break;2160case 'y':2161if (symbols)2162symbol_table_print(symbols, ds->output);2163else2164fprintf(ds->output, "symbol table unavailable\n");2165break;2166case 'v':2167if (!io) {2168fprintf(ds->output, "I/O unavailable\n");2169break;2170}2171for (size_t i = 0; i < VGA_HEIGHT; i++) {2172for (size_t j = 0; j < VGA_WIDTH; j++) {2173unsigned char c = io->soc->vt100.m[i*VGA_WIDTH + j];2174fputc(c < 32 || c > 127 ? '?' : c, ds->output);2175}2176fputc('\n', ds->output);2177}2178
2179break;2180case 'p':2181if (io)2182soc_print(ds->output, io->soc);2183else2184fprintf(ds->output, "I/O unavailable\n");2185break;2186case 'q':2187fprintf(ds->output, "Quiting simulator\n");2188return -1;2189default:2190fprintf(ds->output, "unknown command '%c'\n", op[0]);2191}2192goto again;2193}2194return 0;2195}
2196
2197static uint16_t interrupt_decode(uint8_t *vector) {2198for (unsigned i = 0; i < NUMBER_OF_INTERRUPTS; i++)2199if (*vector & (1u << i)) {2200*vector ^= 1u << i;2201return i;2202}2203return 0;2204}
2205
2206int h2_run(h2_t *h, h2_io_t *io, FILE *output, const unsigned steps, symbol_table_t *symbols, bool run_debugger, FILE *trace) {2207bool turn_debug_on = false;2208assert(h);2209debug_state_t ds = { .input = stdin, .output = stderr, .step = run_debugger, .trace_on = false /*run_debugger*/ };2210
2211if (trace)2212h2_log_csv(trace, h, NULL, true);2213
2214if (run_debugger)2215fputs("Debugger running, type 'h' for a list of command\n", ds.output);2216
2217for (unsigned i = 0; i < steps || steps == 0 || run_debugger; i++) {2218uint16_t instruction,2219literal,2220address,2221pc_plus_one;2222if (log_level >= LOG_DEBUG || ds.trace_on)2223h2_log_csv(output, h, symbols, false);2224if (trace)2225h2_log_csv(trace, h, NULL, false);2226
2227if (run_debugger)2228if (h2_debugger(&ds, h, io, symbols, h->pc))2229return 0;2230
2231h->time++;2232
2233if (io) {2234io->update(io->soc);2235if (io->soc->wait)2236continue; /* wait only applies to the H2 core not the rest of the SoC */2237}2238
2239if (h->pc >= MAX_CORE) {2240error("invalid program counter: %04x > %04x", (unsigned)h->pc, MAX_CORE);2241return -1;2242}2243instruction = h->core[h->pc];2244
2245literal = instruction & 0x7FFF;2246address = instruction & 0x1FFF; /* NB. also used for ALU OP */2247
2248if (h->ie && io && io->soc->interrupt) {2249rpush(h, h->pc << 1);2250io->soc->interrupt = false;2251h->pc = interrupt_decode(&io->soc->interrupt_selector);2252continue;2253}2254
2255pc_plus_one = (h->pc + 1) % MAX_CORE;2256
2257/* NB. This is not quite what the hardware is doing, but it should be equivalent */2258/* decode / execute */2259if (IS_LITERAL(instruction)) { /* The hardware actually uses ALU_OP_LITERAL */2260dpush(h, literal);2261h->pc = pc_plus_one;2262} else if (IS_ALU_OP(instruction)) {2263const uint16_t rd = stack_delta(RSTACK(instruction));2264const uint16_t dd = stack_delta(DSTACK(instruction));2265const uint16_t nos = h->dstk[h->sp % STK_SIZE];2266uint16_t npc = pc_plus_one;2267uint16_t tos = h->tos;2268
2269if (instruction & R_TO_PC)2270npc = h->rstk[h->rp % STK_SIZE] >> 1;2271
2272switch (ALU_OP(instruction)) {2273case ALU_OP_T: /* tos = tos; */ break;2274case ALU_OP_N: tos = nos; break;2275case ALU_OP_T_PLUS_N: tos += nos; break;2276case ALU_OP_T_AND_N: tos &= nos; break;2277case ALU_OP_T_OR_N: tos |= nos; break;2278case ALU_OP_T_XOR_N: tos ^= nos; break;2279case ALU_OP_T_INVERT: tos = ~tos; break;2280case ALU_OP_T_EQUAL_N: tos = -(tos == nos); break;2281case ALU_OP_N_LESS_T: tos = -((int16_t)nos < (int16_t)tos); break;2282case ALU_OP_N_RSHIFT_T: tos = nos >> tos; break;2283case ALU_OP_T_DECREMENT: tos--; break;2284case ALU_OP_R: tos = h->rstk[h->rp % STK_SIZE]; break;2285case ALU_OP_T_LOAD:2286if (h->tos & 0x4000) {2287if (io) {2288if (h->tos & 0x1)2289warning("unaligned register read: %04x", (unsigned)h->tos);2290tos = io->in(io->soc, h->tos & ~0x1, &turn_debug_on);2291if (turn_debug_on) {2292ds.step = true;2293run_debugger = true;2294turn_debug_on = false;2295}2296} else {2297warning("I/O read attempted on addr: %"PRIx16, h->tos);2298}2299} else {2300tos = h->core[(h->tos >> 1) % MAX_CORE];2301}2302break;2303case ALU_OP_N_LSHIFT_T: tos = nos << tos; break;2304case ALU_OP_DEPTH: tos = h->sp; break;2305case ALU_OP_N_ULESS_T: tos = -(nos < tos); break;2306case ALU_OP_ENABLE_INTERRUPTS: h->ie = tos & 1; tos = nos; break;2307case ALU_OP_INTERRUPTS_ENABLED: tos = ((1 & h->ie) << 0); break;2308case ALU_OP_RDEPTH: tos = h->rp; break;2309case ALU_OP_T_EQUAL_0: tos = -(tos == 0); break;2310case ALU_OP_CPU_ID: tos = H2_CPU_ID_SIMULATION; break;2311case ALU_OP_LITERAL: tos = instruction & 0x7fffu; break; // This makes more sense in the hardware2312default:2313warning("unknown ALU operation: %u", (unsigned)ALU_OP(instruction));2314}2315
2316h->sp += dd;2317if (h->sp >= STK_SIZE)2318warning("data stack overflow");2319h->sp %= STK_SIZE;2320
2321h->rp += rd;2322if (h->rp >= STK_SIZE)2323warning("return stack overflow");2324h->rp %= STK_SIZE;2325
2326if (instruction & T_TO_R)2327h->rstk[h->rp % STK_SIZE] = h->tos;2328
2329if (instruction & T_TO_N)2330h->dstk[h->sp % STK_SIZE] = h->tos;2331
2332if (instruction & N_TO_ADDR_T) {2333if ((h->tos & 0x4000) && ALU_OP(instruction) != ALU_OP_T_LOAD) {2334if (io) {2335if (h->tos & 0x1)2336warning("unaligned register write: %04x <- %04x", (unsigned)h->tos, (unsigned)nos);2337io->out(io->soc, h->tos & ~0x1, nos, &turn_debug_on);2338if (turn_debug_on) {2339ds.step = true;2340run_debugger = true;2341turn_debug_on = false;2342}2343} else {2344warning("I/O write attempted with addr/value: %"PRIx16 "/%"PRIx16, tos, nos);2345}2346} else {2347h->core[(h->tos >> 1) % MAX_CORE] = nos;2348}2349}2350
2351h->tos = tos;2352h->pc = npc;2353} else if (IS_CALL(instruction)) {2354rpush(h, pc_plus_one << 1);2355h->pc = address;2356} else if (IS_0BRANCH(instruction)) {2357if (!dpop(h))2358h->pc = address % MAX_CORE;2359else2360h->pc = pc_plus_one;2361} else if (IS_BRANCH(instruction)) {2362h->pc = address;2363} else {2364error("invalid instruction: %"PRId16, instruction);2365}2366
2367h->rpm = MAX(h->rpm, h->rp);2368h->spm = MAX(h->spm, h->sp);2369}2370return 0;2371}
2372
2373/* ========================== Simulation And Debugger ====================== */
2374
2375/* ========================== Main ========================================= */
2376
2377#ifndef NO_MAIN2378typedef enum {2379DEFAULT_COMMAND,2380DISASSEMBLE_COMMAND,2381RUN_COMMAND,2382} command_e;2383
2384typedef struct {2385command_e cmd;2386long steps;2387bool full_disassembly;2388bool debug_mode;2389bool hacks;2390disassemble_color_method_e dcm;2391const char *nvram;2392} command_args_t;2393
2394static const char *help = "\2395usage ./h2 [-hvdDarRTH] [-sc number] [-L symbol.file] [-S symbol.file] [-e file.fth] (file.hex|file.fth)\n\n\
2396Brief: A H2 CPU Assembler, disassembler and Simulator.\n\
2397Author: Richard James Howe\n\
2398Site: https://github.com/howerj/forth-cpu\n\
2399License: MIT\n\
2400Copyright: Richard James Howe (2017,2018)\n\
2401Options:\n\n\
2402\t-\tstop processing options, following arguments are files\n\
2403\t-h\tprint this help message and exit\n\
2404\t-v\tincrease logging level\n\
2405\t-d\tdisassemble input files (default)\n\
2406\t-D\tfull disassembly of input files\n\
2407\t-T\tEnter debug mode when running simulation\n\
2408\t-H\tenable hacks to make the simulation easier to use\n\
2409\t-r\trun hex file\n\
2410\t-L #\tload symbol file\n\
2411\t-S #\tsave symbols to file\n\
2412\t-s #\tnumber of steps to run simulation (0 = forever)\n\
2413\t-n #\tspecify nvram file\n\
2414\t-H #\tenable certain hacks for simulation purposes\n\
2415\t-c #\tset colorization method for disassembly\n\
2416\tfile\thex or forth file to process\n\n\
2417Options must precede any files given, if a file has not been\n\
2418given as arguments input is taken from stdin. Output is to\n\
2419stdout. Program returns zero on success, non zero on failure.\n\n\
2420";2421
2422static void debug_note(const command_args_t * const cmd) {2423assert(cmd);2424if (cmd->debug_mode)2425note("entering debug mode");2426else2427note("running for %u cycles (0 = forever)", (unsigned)cmd->steps);2428}
2429
2430static int run_command(const command_args_t * const cmd, FILE *input, FILE *output, symbol_table_t *symbols, uint16_t *vga_initial_contents) {2431assert(input);2432assert(output);2433assert(cmd);2434assert(cmd->nvram);2435int r = 0;2436
2437h2_t *h = h2_new(START_ADDR);2438if (!h)2439return -1;2440if (h2_load(h, input) < 0) {2441h2_free(h);2442return -1;2443}2444
2445h2_io_t * const io = h2_io_new();2446assert(VGA_BUFFER_LENGTH <= VT100_MAX_SIZE);2447for (size_t i = 0; i < VGA_BUFFER_LENGTH; i++) {2448vt100_attribute_t attr;2449memset(&attr, 0, sizeof(attr));2450io->soc->vt100.m[i] = vga_initial_contents[i] & 0xff;2451attr.background_color = (vga_initial_contents[i] >> 8) & 0x7;2452attr.foreground_color = (vga_initial_contents[i] >> 11) & 0x7;2453memcpy(&io->soc->vt100.attributes[i], &attr, sizeof(attr));2454}2455
2456nvram_load_and_transfer(io, cmd->nvram, cmd->hacks);2457h->pc = START_ADDR;2458debug_note(cmd);2459r = h2_run(h, io, output, cmd->steps, symbols, cmd->debug_mode, NULL);2460nvram_save(io, cmd->nvram);2461
2462h2_free(h);2463h2_io_free(io);2464return r;2465}
2466
2467int command(const command_args_t * const cmd, FILE *input, FILE *output, symbol_table_t *symbols, uint16_t *vga_initial_contents) {2468assert(input);2469assert(output);2470assert(cmd);2471assert(vga_initial_contents);2472switch (cmd->cmd) {2473case DEFAULT_COMMAND: /* fall through */2474case DISASSEMBLE_COMMAND: return h2_disassemble(cmd->dcm, input, output, symbols);2475case RUN_COMMAND: return run_command(cmd, input, output, symbols, vga_initial_contents);2476default: fatal("invalid command: %d", cmd->cmd);2477}2478return -1;2479}
2480
2481static const char *nvram_file = FLASH_INIT_FILE;2482
2483int h2_main(int argc, char **argv) {2484int i;2485const char *optarg = NULL;2486command_args_t cmd;2487symbol_table_t *symbols = NULL;2488FILE *symfile = NULL;2489FILE *input = NULL;2490memset(&cmd, 0, sizeof(cmd));2491cmd.steps = DEFAULT_STEPS;2492cmd.nvram = nvram_file;2493cmd.dcm = DCM_X11;2494
2495static uint16_t vga_initial_contents[VGA_BUFFER_LENGTH] = { 0 };2496
2497binary(stdin);2498binary(stdout);2499binary(stderr);2500
2501{ /* attempt to load initial contents of VGA memory */2502errno = 0;2503FILE *vga_init = fopen(VGA_INIT_FILE, "rb");2504if (vga_init) {2505memory_load(vga_init, vga_initial_contents, VGA_BUFFER_LENGTH);2506fclose(vga_init);2507} else {2508warning("could not load initial VGA memory file %s: %s", VGA_INIT_FILE, strerror(errno));2509}2510}2511
2512for (i = 1; i < argc && argv[i][0] == '-'; i++) {2513
2514if (strlen(argv[i]) > 2) {2515error("Only one option allowed at a time (got %s)", argv[i]);2516goto fail;2517}2518
2519switch (argv[i][1]) {2520case '\0':2521goto done; /* stop processing options */2522case 'h':2523fprintf(stderr, "%s\n", help);2524return -1;2525case 'v': /* increase verbosity */2526log_level += log_level < LOG_ALL_MESSAGES ? 1 : 0;2527break;2528case 'D':2529cmd.full_disassembly = true;2530/* fall through */2531case 'd':2532if (cmd.cmd)2533goto fail;2534cmd.cmd = DISASSEMBLE_COMMAND;2535break;2536case 'r':2537if (cmd.cmd)2538goto fail;2539cmd.cmd = RUN_COMMAND;2540break;2541case 'T':2542cmd.debug_mode = true;2543break;2544case 'L':2545if (i >= (argc - 1) || symfile)2546goto fail;2547optarg = argv[++i];2548/* NB. Cannot merge symbol tables */2549symfile = fopen_or_die(optarg, "rb");2550symbols = symbol_table_load(symfile);2551break;2552case 'c':2553{2554long dcm = DCM_NONE;2555if (i >= (argc - 1))2556goto fail;2557optarg = argv[++i];2558if (string_to_long(0, &dcm, optarg))2559goto fail;2560cmd.dcm = dcm;2561if (cmd.dcm >= DCM_MAX_DCM) {2562fprintf(stderr, "Invalid Colorization Method: %u\n", (unsigned)cmd.dcm);2563goto fail;2564}2565break;2566}2567case 's':2568if (i >= (argc - 1))2569goto fail;2570optarg = argv[++i];2571if (string_to_long(0, &cmd.steps, optarg))2572goto fail;2573break;2574case 'n':2575if (i >= (argc - 1))2576goto fail;2577cmd.nvram = argv[++i];2578note("nvram file %s", cmd.nvram);2579break;2580case 'H':2581cmd.hacks = true;2582break;2583default:2584fail:2585fatal("invalid argument '%s'\n%s\n", argv[i], help);2586}2587}2588if (!symbols)2589symbols = symbol_table_new();2590
2591done:2592if (i == argc) {2593if (command(&cmd, stdin, stdout, symbols, vga_initial_contents) < 0)2594fatal("failed to process standard input");2595return 0;2596}2597
2598if (i < (argc - 1))2599fatal("more than one file argument given");2600
2601input = fopen_or_die(argv[i], "rb");2602if (command(&cmd, input, stdout, symbols, vga_initial_contents) < 0)2603fatal("failed to process file: %s", argv[i]);2604fclose(input);2605symbol_table_free(symbols);2606if (symfile)2607fclose(symfile);2608return 0;2609}
2610
2611int main(int argc, char **argv) {2612return h2_main(argc, argv);2613}
2614#endif2615
2616/* ========================== Main ========================================= */
2617
2618