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

Subversion Repositories forth-cpu

[/] [forth-cpu/] [trunk/] [h2.c] - Blame information for rev 5

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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