fixed bug with instruction decoding in dcpu, fixed bugs in display
[dcpu16] / dcpu16.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <assert.h>
8 #include <sysexits.h>
9
10 #include "dcpu16.h"
11
12 /*
13 * emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
14 *
15 * I couldn't remember ever implementing an emulator before, so this
16 * happened. As such, consider this a toy in progress.
17 * There are likely many improvable aspects.
18 *
19 * Justin Wind <justin.wind@gmail.com>
20 * 2012 04 05 - implementation started
21 * 2012 05 05 - start of v1.7 revisions
22 * 2012 05 08 - v1.7 revisions mostly complete
23 *
24 * TODO
25 * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
26 * !! v1.7 hardware interface needs to be finished
27 * !! v1.7 interrupts need to be finished
28 * change api to print into buffers rather than stdio
29 * refactor opcode functiontables into switch statements
30 * let callbacks determine whether to override events, or just observe
31 * sort init callbacks by base addr, to call in-order
32 * make all callbacks register addr range of interest
33 */
34
35 static const char * const src_id_ = "$Id$";
36
37 #define OPCODE_BASIC_BITS 5
38 #define OPCODE_OPERAND_B_BITS 5
39 #define OPCODE_OPERAND_A_BITS 6
40
41 const char * const dcpu16_reg_names[] = {
42 "A",
43 "B",
44 "C",
45 "X",
46 "Y",
47 "Z",
48 "I",
49 "J",
50 "PC",
51 "SP",
52 "EX",
53 "IA",
54 NULL
55 };
56
57 static void printf_null_(char *fmt, ...) { (void)fmt; }
58
59 /* some default warning and debug reporting functions, which can be overridden by clients */
60 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
61 static inline void warn_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
62 static inline
63 void warn_(char *fmt, ...) {
64 va_list ap;
65
66 fprintf(stderr, "[warning] ");
67 va_start(ap, fmt);
68 vfprintf(stderr, fmt, ap);
69 va_end(ap);
70 fprintf(stderr, "\n");
71 fflush(stderr);
72 }
73 static void (*warn_cb_)(char *fmt, ...) = warn_;
74 void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
75 if (fn)
76 warn_cb_ = fn;
77 else
78 warn_cb_ = printf_null_;
79 }
80
81 #ifdef DEBUG
82 #define TRACE(...) do { if (trace_cb_) trace_cb_(__VA_ARGS__); } while (0)
83 static inline void trace_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
84 static inline
85 void trace_(char *fmt, ...) {
86 va_list ap;
87
88 fprintf(stdout, "[debug] ");
89 va_start(ap, fmt);
90 vfprintf(stdout, fmt, ap);
91 va_end(ap);
92 fprintf(stdout, "\n");
93 fflush(stdout);
94 }
95 #else /* DEBUG */
96 #define TRACE(...) do {} while(0)
97 #endif /* DEBUG */
98 static void (*trace_cb_)(char *fmt, ...) =
99 #ifdef DEBUG
100 trace_
101 #else /* DEBUG */
102 NULL
103 #endif
104 ;
105 void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
106 if (fn)
107 trace_cb_ = fn;
108 else
109 trace_cb_ = printf_null_;
110 }
111
112
113 /* acct_event_
114 * invokes callbacks for specified event
115 */
116 static inline
117 void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
118 struct dcpu16_acct_cb *cb = vm->cb_table_;
119 size_t i;
120
121 for (i = 0; i < vm->cb_table_entries_; i++) {
122 if ( (cb[i].mask & ev) )
123 cb[i].fn(vm, ev, addr, cb[i].data);
124 }
125 }
126
127
128 /* add an entry to the interrupt queue */
129 static
130 int interrupt_enqueue_(struct dcpu16 *vm, DCPU16_WORD message) {
131 vm->interrupts_[vm->interrupts_tail_] = message;
132 vm->interrupts_tail_ += 1;
133 vm->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
134
135 if (vm->interrupts_tail_ == vm->interrupts_head_) {
136 vm->on_fire_ = 1;
137 WARN("interrupt queue overflow (system is now on fire)");
138 return -1;
139 }
140
141 return 0;
142 }
143
144 static
145 DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) {
146 DCPU16_WORD message;
147
148 if (vm->interrupts_tail_ == vm->interrupts_head_) {
149 WARN("interrupt underflow");
150 return 0;
151 }
152
153 message = vm->interrupts_[vm->interrupts_head_];
154 vm->interrupts_head_ += 1;
155 vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
156
157 return message;
158 }
159
160 /* value_decode_
161 * sets *v to be the address of the represented value
162 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
163 * value_data is 'nextword' for this operand, ignored if unused
164 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
165 * e_addr is set to a referenced address, for accounting callback
166 * pc_adjust is set to how to change the program counter
167 * stack_adjust is set to how to change the stack pointer
168 * cycle_adjust is set to number of cycles spent looking up operand
169 *
170 * zero all adjustables before decoding first operand, and pass in these values when
171 * decoding next operand..
172 *
173 */
174 static inline
175 void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
176 DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr,
177 short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) {
178 assert(value <= 0x3f);
179
180 DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
181 sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *sp_adjust);
182
183 TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
184 __func__,
185 value_is_a,
186 pc,
187 sp,
188 value_data);
189
190 if (value <= 0x07) { /* register */
191 *v = vm->reg + value;
192 TRACE("%s>> %s (0x%04x)",
193 __func__,
194 dcpu16_reg_names[value],
195 **v);
196 return;
197 }
198
199 if (value <= 0x0f) { /* [register] */
200 *v = &(vm->ram[ vm->reg[value & 0x07] ]);
201 *e_addr = vm->reg[value & 0x07];
202 TRACE("%s>> [%s] [0x%04x] (0x%04x)",
203 __func__,
204 dcpu16_reg_names[value & 0x07],
205 vm->reg[value & 0x07],
206 **v);
207 return;
208 }
209
210 if (value <= 0x17) { /* [next word + register] */
211 *pc_adjust += 1; /* consume next word */
212 *cycle_adjust += 1;
213 *e_addr = value_data + vm->reg[value & 0x07];
214 *v = vm->ram + *e_addr;
215 TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
216 __func__,
217 dcpu16_reg_names[value & 0x07],
218 value_data,
219 vm->reg[value & 0x07],
220 **v);
221 return;
222 }
223
224 switch (value) {
225 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
226 if (value_is_a == 0) { /* b */
227 *v = &(vm->ram[sp - 1]);
228 *sp_adjust -= 1;
229 *e_addr = sp - 1;
230 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v);
231 } else { /* a */
232 *v = &(vm->ram[sp]);
233 *sp_adjust += 1;
234 *e_addr = sp;
235 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__, sp, **v);
236 }
237 break;
238
239 case 0x19: /* PEEK/[SP] */
240 *v = &(vm->ram[sp]);
241 *e_addr = sp;
242 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
243 __func__,
244 sp,
245 **v);
246 break;
247
248 case 0x1a: /* PICK n */
249 *pc_adjust += 1;
250 *cycle_adjust += 1;
251 *e_addr = sp + value_data;
252 *v = vm->ram + *e_addr;
253 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
254 __func__,
255 value_data,
256 sp + value_data,
257 **v);
258 break;
259
260 case 0x1b: /* SP */
261 *v = &(vm->reg[DCPU16_REG_SP]);
262 TRACE("%s>> %s (0x%04x)",
263 __func__,
264 dcpu16_reg_names[DCPU16_REG_SP],
265 **v);
266 break;
267
268 case 0x1c: /* PC */
269 *v = &(vm->reg[DCPU16_REG_PC]);
270 TRACE("%s>> %s (0x%04x)",
271 __func__,
272 dcpu16_reg_names[DCPU16_REG_PC],
273 **v);
274 break;
275
276 case 0x1d: /* EX */
277 *v = &(vm->reg[DCPU16_REG_EX]);
278 TRACE("%s>> %s (0x%04x)",
279 __func__,
280 dcpu16_reg_names[DCPU16_REG_EX],
281 **v);
282 break;
283
284 case 0x1e: /* [next word] / [[pc++]] */
285 *pc_adjust += 1;
286 *cycle_adjust += 1;
287 *e_addr = value_data;
288 *v = vm->ram + *e_addr;
289 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
290 __func__,
291 value_data,
292 **v);
293 break;
294
295 case 0x1f: /* next word (literal) / [pc++] */
296 *pc_adjust += 1;
297 *cycle_adjust += 1;
298 *work_v = value_data;
299 *v = work_v;
300 TRACE("%s>> nextword (0x%04x)",
301 __func__,
302 **v);
303 break;
304
305 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
306 *work_v = (value & 0x1f) - 1;
307 *v = work_v;
308 TRACE("%s>> literal (0x%04x)",
309 __func__,
310 **v);
311 }
312 }
313
314
315 #define OPCODE_NAME_LEN 16
316 struct opcode_entry {
317 unsigned short value;
318 char name[OPCODE_NAME_LEN];
319 void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD);
320 };
321
322 /* messy boilerplate for opcode handlers */
323
324 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
325 /* opcode does adjust stack and cycle count */
326
327 #define OP_IMPL(x) static void op_##x(struct dcpu16 *vm, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
328
329 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
330 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
331 short pc_adjust = 0, sp_adjust = 0;\
332 unsigned int cycle_adjust = 0;\
333 do {\
334 op_type;\
335 value_decode_(vm, val_a, 1, val_a_data,\
336 &vm->reg_work_[1], &a, &ev_a_addr,\
337 &pc_adjust, &sp_adjust, &cycle_adjust);\
338 vm->reg[DCPU16_REG_SP] += sp_adjust;\
339 vm->cycle += cycle_adjust;\
340 } while (0)
341 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
342 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
343 &vm->reg_work_[0], &b, &ev_b_addr,\
344 &pc_adjust, &sp_adjust, &cycle_adjust)
345 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
346 #define OP_NBI(x) OP_TYPE(OP_NBI_)
347
348 /* after invoking one of these header macros, the instruction and operands will have been decoded, and the control registers have been adjusted to the next instruction et cetera */
349
350 /*
351 accounting helpers, these fire off the related callbacks for memory reads,
352 memory writes, and execution of reserved instructions
353 */
354 #define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
355 #define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
356 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
357
358 /* extended opcodes */
359
360 /*
361 N.B. this next function currently decodes values -- id est, it is
362 an opcode processing terminus; however, if 'future instruction set
363 expansion' happens, this will probably need to behave more like
364 the OP_IMPL(_nbi_) function which invoked it, if those instructions
365 have zero or differently styled operands.
366 */
367 OP_IMPL(nbi__reserved_) {
368 OP_NBI(nbi__reserved_);
369 /* reserved for future expansion */
370
371 /* fire an illegal instruction event for current instruction */
372 DCPU16_WORD future_opcode = (vm->ram[vm->reg[DCPU16_REG_PC] - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
373 WARN("reserved future opcode 0x%04x invoked", future_opcode);
374
375 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
376 }
377
378 OP_IMPL(nbi_jsr) {
379 OP_NBI(nbi_jsr);
380 /* pushes the address of the next instruction to the stack, then sets PC to a */
381
382 ACCT_R(ev_a_addr);
383
384 vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC];
385 vm->reg[DCPU16_REG_PC] = *a;
386
387 vm->cycle += 2;
388
389 ACCT_W(vm->reg[DCPU16_REG_SP] + 1);
390 }
391
392 OP_IMPL(nbi__reserved2_) {
393 OP_NBI(nbi__reserved2_);
394 /* reserved */
395
396 WARN("reserved nbi opcode invoked");
397
398 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
399 }
400
401 OP_IMPL(nbi_int) {
402 OP_NBI(nbi_int);
403
404 ACCT_R(ev_a_addr);
405
406 if (vm->reg[DCPU16_REG_IA]) {
407 if ( interrupt_enqueue_(vm, *a) ) {
408 WARN("failed to queue interrupt");
409 return;
410 }
411
412 if (vm->interrupts_deferred_)
413 return;
414
415 vm->interrupts_deferred_ = 1;
416 vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
417 vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
418 vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
419 vm->reg[0] = *a;
420 }
421
422 vm->cycle += 4;
423 }
424
425 OP_IMPL(nbi_iag) {
426 OP_NBI(nbi_iag);
427
428 *a = vm->reg[DCPU16_REG_IA];
429
430 ACCT_W(ev_a_addr);
431 }
432
433 OP_IMPL(nbi_ias) {
434 OP_NBI(nbi_ias);
435
436 vm->reg[DCPU16_REG_IA] = *a;
437
438 ACCT_R(ev_a_addr);
439 }
440
441 /* does this just ignore its operand? */
442 OP_IMPL(nbi_rfi) {
443 OP_NBI(nbi_rfi);
444
445 vm->interrupts_deferred_ = 0;
446 vm->reg[DCPU16_REG_A] = vm->ram[vm->reg[DCPU16_REG_SP]++];
447 vm->reg[DCPU16_REG_PC] = vm->ram[vm->reg[DCPU16_REG_SP]++];
448 }
449
450 OP_IMPL(nbi_iaq) {
451 OP_NBI(nbi_iaq);
452
453 if (*a) {
454 vm->interrupts_deferred_ = 1;
455 } else {
456 vm->interrupts_deferred_ = 0;
457 }
458
459 ACCT_R(ev_a_addr);
460 }
461
462 OP_IMPL(nbi_hwn) {
463 OP_NBI(nbi_hwn);
464
465 ACCT_W(ev_a_addr);
466
467 *a = vm->hw_table_entries_;
468
469 vm->cycle += 2;
470 }
471
472 OP_IMPL(nbi_hwq) {
473 OP_NBI(nbi_hwq);
474
475 ACCT_R(ev_a_addr);
476
477 if (*a >= vm->hw_table_entries_) {
478 WARN("hardware query for non-extant device 0x%04x", *a);
479 vm->reg[DCPU16_REG_A] = 0;
480 vm->reg[DCPU16_REG_B] = 0;
481 vm->reg[DCPU16_REG_C] = 0;
482 vm->reg[DCPU16_REG_X] = 0;
483 vm->reg[DCPU16_REG_Y] = 0;
484 return;
485 }
486
487 vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].id_l;
488 vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].id_h;
489 vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].ver;
490 vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mfg_l;
491 vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mfg_h;
492
493 vm->cycle += 4;
494 }
495
496 OP_IMPL(nbi_hwi) {
497 OP_NBI(nbi_hwi);
498
499 ACCT_R(ev_a_addr);
500
501 if (*a > vm->hw_table_entries_) {
502 WARN("interrupt for non-extant device 0x%04x", *a);
503 return;
504 }
505
506 vm->cycle += 4;
507 if (vm->hw_table_[*a].hwi)
508 vm->hw_table_[*a].hwi(vm, vm->hw_table_[*a].data);
509 else
510 WARN("hardware 0x%04x has no interrupt handler", *a);
511 }
512
513 OP_IMPL(nbi_hcf) {
514 OP_NBI(nbi_hcf);
515
516 ACCT_R(ev_a_addr);
517
518 vm->on_fire_ = 1;
519 WARN("system on fire");
520
521 vm->cycle += 9;
522 }
523
524 static const struct opcode_entry opcode_nbi_entries[] = {
525 {0x00, "(reserved)", op_nbi__reserved_},
526 {0x01, "JSR", op_nbi_jsr},
527 {0x02, "(reserved)", op_nbi__reserved2_},
528 {0x03, "(reserved)", op_nbi__reserved2_},
529 {0x04, "(reserved)", op_nbi__reserved2_},
530 {0x05, "(reserved)", op_nbi__reserved2_},
531 {0x06, "(reserved)", op_nbi__reserved2_},
532 {0x07, "HCF", op_nbi_hcf}, /* undocumented */
533 {0x08, "INT", op_nbi_int},
534 {0x09, "IAG", op_nbi_iag},
535 {0x0a, "IAS", op_nbi_ias},
536 {0x0b, "RFI", op_nbi_rfi},
537 {0x0c, "IAQ", op_nbi_iaq},
538 {0x0d, "(reserved)", op_nbi__reserved2_},
539 {0x0e, "(reserved)", op_nbi__reserved2_},
540 {0x0f, "(reserved)", op_nbi__reserved2_},
541 {0x10, "HWN", op_nbi_hwn},
542 {0x11, "HWQ", op_nbi_hwq},
543 {0x12, "HWI", op_nbi_hwi},
544 {0x13, "(reserved)", op_nbi__reserved2_},
545 {0x00, "", NULL}
546 };
547 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
548
549
550 /* basic opcodes */
551
552 /*
553 N.B. the following function does not decode values.
554 Decoding is handled by the secondary opcode functions it calls.
555 */
556 OP_IMPL(_nbi_) {
557 /* non-basic instruction */
558
559 /* don't do normal value decoding here */
560
561 DCPU16_WORD nbi_opcode = val_b;
562 const struct opcode_entry *e = opcode_nbi_entries;
563
564 (void)val_b_data;
565
566 e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
567
568 assert(e->impl != NULL);
569
570 TRACE(">> %s 0x%04x", e->name, val_b);
571 e->impl(vm, 0, 0, val_a, val_a_data);
572 }
573
574 OP_IMPL(set) {
575 OP_BASIC(set);
576 /* sets b to a */
577
578 ACCT_R(ev_a_addr);
579
580 /*
581 if b is a literal, it's aimed at a scratch register,
582 so it's fine to update, as it won't have any effect.
583 */
584 *b = *a;
585
586 vm->cycle += 1;
587
588 ACCT_W(ev_b_addr);
589 }
590
591 OP_IMPL(add) {
592 OP_BASIC(add);
593 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
594 unsigned int acc = *b + *a;
595
596 ACCT_R(ev_b_addr);
597 ACCT_R(ev_a_addr);
598
599 *b = acc;
600 vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
601
602 vm->cycle += 2;
603
604 ACCT_W(ev_b_addr);
605 }
606
607 OP_IMPL(sub) {
608 OP_BASIC(sub);
609 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
610 unsigned int acc = *b - *a;
611
612 ACCT_R(ev_b_addr);
613 ACCT_R(ev_a_addr);
614
615 *b = acc;
616 vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
617
618 vm->cycle += 2;
619
620 ACCT_W(ev_b_addr);
621 }
622
623 OP_IMPL(mul) {
624 OP_BASIC(mul);
625 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
626 unsigned int acc = *b * *a;
627
628 ACCT_R(ev_b_addr);
629 ACCT_R(ev_a_addr);
630
631 *b = acc;
632 vm->reg[DCPU16_REG_EX] = acc >> 16;
633
634 vm->cycle += 2;
635
636 ACCT_W(ev_b_addr);
637 }
638
639 OP_IMPL(mli) {
640 OP_BASIC(mli);
641 /* sets b to b*a, signed */
642 int acc = (short)*b * (short)*a;
643
644 ACCT_R(ev_b_addr);
645 ACCT_R(ev_a_addr);
646
647 *b = acc;
648 vm->reg[DCPU16_REG_EX] = acc >> 16;
649
650 vm->cycle += 2;
651
652 ACCT_W(ev_b_addr);
653 }
654
655 OP_IMPL(div) {
656 OP_BASIC(div);
657 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
658
659 ACCT_R(ev_b_addr);
660 ACCT_R(ev_a_addr);
661
662 if (*a == 0) {
663 *b = 0;
664 vm->reg[DCPU16_REG_EX] = 0;
665 } else {
666 *b = *b / *a;
667 vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
668 }
669
670 vm->cycle += 3;
671
672 ACCT_W(ev_b_addr);
673 }
674
675 OP_IMPL(dvi) {
676 OP_BASIC(dvi);
677 /* sets b to b/a, signed, round towards 0 */
678
679 ACCT_R(ev_b_addr);
680 ACCT_R(ev_a_addr);
681
682 if (*a == 0) {
683 *b = 0;
684 vm->reg[DCPU16_REG_EX] = 0;
685 } else {
686 *b = (short)*b / (short)*a;
687 vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
688 }
689
690 vm->cycle += 3;
691
692 ACCT_W(ev_b_addr);
693 }
694
695 OP_IMPL(mod) {
696 OP_BASIC(mod);
697 /* sets b to b%a. if a==0, sets b to 0 instead. */
698
699 ACCT_R(ev_b_addr);
700 ACCT_R(ev_a_addr);
701
702 if (*a == 0) {
703 *b = 0;
704 } else {
705 *b = *b % *a;
706 }
707
708 vm->cycle += 3;
709
710 ACCT_W(ev_a_addr);
711 }
712
713 OP_IMPL(mdi) {
714 OP_BASIC(mdi);
715 /* sets b to b%a, signed */
716
717 ACCT_R(ev_b_addr);
718 ACCT_R(ev_a_addr);
719
720 if (*a == 0) {
721 *b = 0;
722 } else {
723 *b = (short)*b % (short)*a;
724 }
725
726 vm->cycle += 3;
727
728 ACCT_W(ev_b_addr);
729 }
730
731 OP_IMPL(and) {
732 OP_BASIC(and);
733 /* sets b to b&a */
734
735 ACCT_R(ev_b_addr);
736 ACCT_R(ev_a_addr);
737
738 *b = *b & *a;
739
740 vm->cycle += 1;
741
742 ACCT_W(ev_b_addr);
743 }
744
745 OP_IMPL(bor) {
746 OP_BASIC(bor);
747 /* sets b to b|a */
748
749 ACCT_R(ev_b_addr);
750 ACCT_R(ev_a_addr);
751
752 *b = *b | *a;
753
754 vm->cycle += 1;
755
756 ACCT_W(ev_b_addr);
757 }
758
759 OP_IMPL(xor) {
760 OP_BASIC(xor);
761 /* sets b to b^a */
762
763 ACCT_R(ev_b_addr);
764 ACCT_R(ev_a_addr);
765
766 *b = *b ^ *a;
767
768 vm->cycle += 1;
769
770 ACCT_W(ev_b_addr);
771 }
772
773 OP_IMPL(shr) {
774 OP_BASIC(shr);
775 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
776 unsigned int acc = *b >> *a;
777
778 ACCT_R(ev_b_addr);
779 ACCT_R(ev_a_addr);
780
781 *b = acc & 0xffff;
782 vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
783
784 vm->cycle += 2;
785
786 WARN("IMPLEMENT");
787
788 ACCT_W(ev_b_addr);
789 }
790
791 OP_IMPL(asr) {
792 OP_BASIC(asr);
793 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
794 unsigned int acc = *b << *a;
795
796 ACCT_R(ev_b_addr);
797 ACCT_R(ev_a_addr);
798
799 *b = acc & 0xffff;
800 vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
801
802 vm->cycle += 2;
803
804 WARN("IMPLEMENT");
805
806 ACCT_W(ev_b_addr);
807 }
808
809 OP_IMPL(shl) {
810 OP_BASIC(shl);
811 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
812 unsigned int acc = *b << *a;
813
814 ACCT_R(ev_b_addr);
815 ACCT_R(ev_a_addr);
816
817 *b = acc;
818
819 vm->reg[DCPU16_REG_EX] = acc >> 16;
820
821 vm->cycle += 2;
822
823 ACCT_W(ev_b_addr);
824 }
825
826 OP_IMPL(ifb) {
827 OP_BASIC(ifb);
828 /* performs next instruction only if (b&a)!=0 */
829
830 ACCT_R(ev_b_addr);
831 ACCT_R(ev_a_addr);
832
833 if ((*b & *a) != 0) {
834 /* */
835 } else {
836 vm->skip_ = 1;
837 vm->cycle += 1;
838 }
839
840 vm->cycle += 2;
841 }
842
843 OP_IMPL(ifc) {
844 OP_BASIC(ifc);
845 /* performs next instruction only if (b&a)==0 */
846
847 ACCT_R(ev_b_addr);
848 ACCT_R(ev_a_addr);
849
850 if ((*b & *a) == 0) {
851 /* */
852 } else {
853 vm->skip_ = 1;
854 vm->cycle += 1;
855 }
856
857 vm->cycle += 2;
858 }
859
860 OP_IMPL(ife) {
861 OP_BASIC(ife);
862 /* performs next instruction only if b==a */
863
864 ACCT_R(ev_b_addr);
865 ACCT_R(ev_a_addr);
866
867 if (*b == *a) {
868 /* */
869 } else {
870 vm->skip_ = 1;
871 vm->cycle += 1;
872 }
873
874 vm->cycle += 2;
875 }
876
877 OP_IMPL(ifn) {
878 OP_BASIC(ifn);
879 /* performs next instruction only if b!=a */
880
881 ACCT_R(ev_b_addr);
882 ACCT_R(ev_a_addr);
883
884 if (*b != *a) {
885 /* */
886 } else {
887 vm->skip_ = 1;
888 vm->cycle++;
889 }
890
891 vm->cycle += 2;
892 }
893
894 OP_IMPL(ifg) {
895 OP_BASIC(ifg);
896 /* performs next instruction only if b>a */
897
898 ACCT_R(ev_b_addr);
899 ACCT_R(ev_a_addr);
900
901 if (*b > *a) {
902 /* */
903 } else {
904 vm->skip_ = 1;
905 vm->cycle++;
906 }
907
908 vm->cycle += 2;
909 }
910
911 OP_IMPL(ifa) {
912 OP_BASIC(ifa);
913 /* performs next instruction only if b>a (signed) */
914
915 ACCT_R(ev_b_addr);
916 ACCT_R(ev_a_addr);
917
918 if (*b > *a) {
919 /* */
920 } else {
921 vm->skip_ = 1;
922 vm->cycle += 1;
923 }
924
925 vm->cycle += 2;
926 }
927
928 OP_IMPL(ifl) {
929 OP_BASIC(ifl);
930 /* performs next instruction only if b<a */
931
932 ACCT_R(ev_b_addr);
933 ACCT_R(ev_a_addr);
934
935 if (*b < *a) {
936 /* */
937 } else {
938 vm->skip_ = 1;
939 vm->cycle++;
940 }
941
942 vm->cycle += 2;
943 }
944
945 OP_IMPL(ifu) {
946 OP_BASIC(ifu);
947 /* performs next instruction only if b<a (signed) */
948
949 ACCT_R(ev_b_addr);
950 ACCT_R(ev_a_addr);
951
952 if (*b < *a) {
953 /* */
954 } else {
955 vm->skip_ = 1;
956 vm->cycle += 1;
957 }
958
959 vm->cycle += 2;
960 }
961
962 OP_IMPL(adx) {
963 OP_BASIC(adx);
964 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
965 unsigned int acc;
966
967 ACCT_R(ev_b_addr);
968 ACCT_R(ev_a_addr);
969
970 acc = *b + *a + vm->reg[DCPU16_REG_EX];
971 *b = acc & 0xffff;
972 if (acc > 0xffff)
973 vm->reg[DCPU16_REG_EX] = 0x0001;
974 else
975 vm->reg[DCPU16_REG_EX] = 0x0000;
976
977 vm->cycle += 3;
978
979 ACCT_W(ev_b_addr);
980 }
981
982 OP_IMPL(sbx) {
983 OP_BASIC(sbx);
984 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
985 unsigned int acc;
986
987 ACCT_R(ev_b_addr);
988 ACCT_R(ev_a_addr);
989
990 acc = *b - *a + vm->reg[DCPU16_REG_EX];
991 *b = acc & 0xffff;
992 if (acc > 0xffff)
993 vm->reg[DCPU16_REG_EX] = 0xffff;
994 else
995 vm->reg[DCPU16_REG_EX] = 0;
996
997 vm->cycle += 3;
998
999 ACCT_W(ev_b_addr);
1000 }
1001
1002 OP_IMPL(sti) {
1003 OP_BASIC(sti);
1004 /* sets b to a, then increases I and J by 1 */
1005
1006 ACCT_R(ev_b_addr);
1007 ACCT_R(ev_a_addr);
1008
1009 *b = *a;
1010 vm->reg[DCPU16_REG_I] += 1;
1011 vm->reg[DCPU16_REG_J] += 1;
1012
1013 vm->cycle += 2;
1014
1015 ACCT_W(ev_b_addr);
1016 }
1017
1018 OP_IMPL(std) {
1019 OP_BASIC(std);
1020 /* sets b to a, then decreases I and J by 1 */
1021
1022 ACCT_R(ev_b_addr);
1023 ACCT_R(ev_a_addr);
1024
1025 *b = *a;
1026 vm->reg[DCPU16_REG_I] -= 1;
1027 vm->reg[DCPU16_REG_J] -= 1;
1028
1029 vm->cycle += 2;
1030
1031 ACCT_W(ev_b_addr);
1032 }
1033
1034 OP_IMPL(_reserved_) {
1035 OP_BASIC(_reserved_);
1036
1037 WARN("reserved opcode invoked");
1038
1039 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
1040 }
1041
1042 static const struct opcode_entry opcode_basic_entries[] = {
1043 {0x00, "(nbi)", op__nbi_},
1044 {0x01, "SET", op_set },
1045 {0x02, "ADD", op_add },
1046 {0x03, "SUB", op_sub },
1047 {0x04, "MUL", op_mul },
1048 {0x05, "MLI", op_mli },
1049 {0x06, "DIV", op_div },
1050 {0x07, "DVI", op_dvi },
1051 {0x08, "MOD", op_mod },
1052 {0x09, "MDI", op_mdi },
1053 {0x0a, "AND", op_and },
1054 {0x0b, "BOR", op_bor },
1055 {0x0c, "XOR", op_xor },
1056 {0x0d, "SHR", op_shr },
1057 {0x0e, "ASR", op_asr },
1058 {0x0f, "SHL", op_shl },
1059 {0x10, "IFB", op_ifb },
1060 {0x11, "IFC", op_ifc },
1061 {0x12, "IFE", op_ife },
1062 {0x13, "IFN", op_ifn },
1063 {0x14, "IFG", op_ifg },
1064 {0x15, "IFA", op_ifa },
1065 {0x16, "IFL", op_ifl },
1066 {0x17, "IFU", op_ifu },
1067 {0x18, "(reserved)", op__reserved_ },
1068 {0x19, "(reserved)", op__reserved_ },
1069 {0x1a, "ADX", op_adx },
1070 {0x1b, "SBX", op_sbx },
1071 {0x1c, "(reserved)", op__reserved_ },
1072 {0x1d, "(reserved)", op__reserved_ },
1073 {0x1e, "STI", op_sti },
1074 {0x1f, "STD", op_std },
1075 {0x00, "", NULL }
1076 };
1077
1078 static inline
1079 void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword, unsigned int value_position) {
1080 if (value <= 0x07) {
1081 printf(" %s", dcpu16_reg_names[value]);
1082 } else if (value <= 0x0f) {
1083 printf(" [%s]", dcpu16_reg_names[value & 0x07]);
1084 } else if (value <= 0x17) {
1085 printf(" [0x%04x + %s]", nextword, dcpu16_reg_names[value & 0x07]);
1086 } else switch (value) {
1087 case 0x18:
1088 if (value_position == 0) { /* b */
1089 printf(" PUSH");
1090 } else {
1091 printf(" POP");
1092 }
1093 break;
1094 case 0x19: printf(" PEEK"); break;
1095 case 0x1a: printf(" PICK 0x%04x", nextword); break;
1096 case 0x1b: printf(" SP"); break;
1097 case 0x1c: printf(" PC"); break;
1098 case 0x1d: printf(" EX"); break;
1099 case 0x1e: printf(" [0x%04x]", nextword); break;
1100 case 0x1f: printf(" 0x%04x", nextword); break;
1101 default: printf(" 0x%02x", value - 0x21);
1102 }
1103 }
1104
1105
1106 /* split a sequence of (one to three) words into the components of an instruction */
1107 static inline
1108 void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr,
1109 DCPU16_WORD *opcode, DCPU16_WORD *b, DCPU16_WORD **b_data, DCPU16_WORD *a, DCPU16_WORD **a_data,
1110 DCPU16_WORD *instr_len) {
1111 *opcode = *a = *b = mem[addr];
1112 *opcode = mem[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
1113 *b = (mem[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_B_BITS) - 1);
1114 *a = (mem[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)) & ((1 << OPCODE_OPERAND_A_BITS) - 1);
1115 *instr_len = 1;
1116
1117 if ((*opcode != 0x0000) &&
1118 ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) ) {
1119 *b_data = mem + (DCPU16_WORD)(addr + *instr_len);
1120 *instr_len += 1;
1121 } else {
1122 *b_data = NULL;
1123 }
1124
1125 if ( (*opcode != 0x0000 || (*opcode == 0 && *b != 0x0000) )
1126 && ( (*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) ) {
1127 *a_data = mem + (DCPU16_WORD)(addr + *instr_len);
1128 *instr_len += 1;
1129 } else {
1130 *a_data = NULL;
1131 }
1132
1133 TRACE("\n%s: [0x%04x]:0x%04x op:0x%02x b:0x%02x (b_data:0x%04x) a:0x%02x (a_data:0x%04x) len:0x%02x\n",
1134 __func__,
1135 addr,
1136 mem[addr],
1137 *opcode,
1138 *b,
1139 *b_data ? **b_data : 0,
1140 *a,
1141 *a_data ? **a_data : 0,
1142 *instr_len);
1143 }
1144
1145 /* dcpu16_mnemonify_buf
1146 print words as words
1147 */
1148 DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *buf) {
1149 DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
1150 const struct opcode_entry *e;
1151
1152 instruction_decode_(buf, 0, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1153
1154 if (opcode == 0x0000)
1155 e = opcode_nbi_entries +
1156 ( (b < OPCODE_NBI_MAX) ? b : (OPCODE_NBI_MAX - 1) );
1157 else
1158 e = opcode_basic_entries + opcode;
1159 printf("%s", e->name);
1160
1161 if (opcode) {
1162 dump_operand_value_(b, b_data ? *b_data : 0, 0);
1163 printf(",");
1164 }
1165
1166 if (opcode || b) {
1167 dump_operand_value_(a, a_data ? *a_data : 0, 1);
1168 }
1169
1170 return instr_len;
1171 }
1172
1173 /* dcpu16_disassemble_print
1174 print the words of the instruction at addr, followed by its assembly representation
1175 returns the length of the instruction in words
1176 */
1177 DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *vm, DCPU16_WORD addr) {
1178 DCPU16_WORD opcode, b, a, instr_len, i, *b_data, *a_data;
1179 DCPU16_WORD buf[3] = { vm->ram[addr], vm->ram[(DCPU16_WORD)(addr + 1)], vm->ram[(DCPU16_WORD)(addr + 2)] };
1180 unsigned int indent = 0;
1181 unsigned int partial = 0;
1182
1183 if (!vm) return 0;
1184
1185 #if 0
1186 /*
1187 Check the previous instruction, to see if this one should be
1188 indented. This check isn't foolproof, as preceeding addresses
1189 could be data which happen to match instructions..
1190 */
1191 for (i = 3; i; i--) {
1192 instruction_decode_(vm->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
1193 if (instr_len > i)
1194 partial++;
1195 if (instr_len == i
1196 && (opcode >= 0x10 && opcode <= 0x17) ) {
1197 indent++;
1198 break;
1199 }
1200 }
1201 #endif
1202
1203 /* just need instr_len */
1204 instruction_decode_(vm->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1205
1206 /* show the raw words */
1207 printf("%04x", vm->ram[addr]);
1208 for (i = 1; i < instr_len; i++) {
1209 printf(" %04x", vm->ram[addr + i]);
1210 }
1211
1212 /* align things neatly, show the instruction */
1213 printf("%s%s ;%s%s",
1214 instr_len < 3 ? " " : "",
1215 instr_len < 2 ? " " : "",
1216 partial ? "*" : " ",
1217 indent ? " " : "");
1218
1219 dcpu16_mnemonify_buf(buf);
1220
1221 return instr_len;
1222 }
1223
1224 int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) {
1225 TRACE("%s>> message:0x%04x", __func__, message);
1226 return interrupt_enqueue_(vm, message);
1227 }
1228
1229 /* execute the next instruction */
1230 void dcpu16_step(struct dcpu16 *vm) {
1231 DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
1232 size_t i;
1233 const struct opcode_entry *e;
1234
1235 if (!vm)
1236 return;
1237
1238 /* signal interested parties that a new cycle has ticked */
1239 TRACE("%s>> sending global cycle event", __func__);
1240 acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]);
1241
1242 /* signal attached hardware */
1243 for (i = 0; i < vm->hw_table_entries_; i++) {
1244 if (vm->hw_table_[i].cycle) {
1245 TRACE("%s>> sending cycle to %s", __func__, vm->hw_table_[i].name_);
1246 vm->hw_table_[i].cycle(vm, vm->hw_table_[i].data);
1247 }
1248 }
1249
1250 instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
1251
1252 /* consume what we decoded */
1253 /* this happens immediately as PC might be re-set as an operation */
1254 vm->reg[DCPU16_REG_PC] += instr_len;
1255
1256 /* run the operation */
1257 for (e = opcode_basic_entries; e->impl; e++) {
1258 if (e->value == opcode) {
1259 TRACE("%s>> %s 0x%04x, 0x%04x", __func__, e->name, b, a);
1260 e->impl(vm, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
1261 break;
1262 }
1263 }
1264
1265 /* and jump over next instr(s) if needed */
1266 while (vm->skip_) {
1267 instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
1268 vm->reg[DCPU16_REG_PC] += instr_len;
1269 TRACE("++ SKIPPED %x words", instr_len);
1270 if (opcode >= 0x10 && opcode <= 0x17) {
1271 /* skipping a branch instruction? skip branch's skippable instruction as well */
1272 vm->cycle += 1;
1273 } else {
1274 vm->skip_ = 0;
1275 }
1276 }
1277
1278 /* if we're currently servicing interrupts */
1279 if (vm->interrupts_deferred_ == 0) {
1280 /* and there are interrupts to be serviced */
1281 if (vm->interrupts_head_ != vm->interrupts_tail_) {
1282 DCPU16_WORD message;
1283 message = interrupt_dequeue_(vm);
1284
1285 if (vm->reg[DCPU16_REG_IA]) {
1286 TRACE("%s>> servicing interrupt IA:0x%04x message:0x%04x \n", __func__, vm->reg[DCPU16_REG_IA], message);
1287 /* then service the next interrupt */
1288 vm->interrupts_deferred_ = 1;
1289 vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
1290 vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
1291 vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
1292 vm->reg[DCPU16_REG_A] = message;
1293 } else {
1294 TRACE("%s>> ignoring interrupt IA:0", __func__);
1295 }
1296 }
1297 }
1298 }
1299
1300 /*
1301 print the current state of the machine
1302 shows current cycle count, registers, and next instruction
1303 */
1304 void dcpu16_state_print(struct dcpu16 *vm) {
1305 unsigned int i;
1306
1307 if (!vm) return;
1308
1309 printf(" ");
1310 for (i = 0; i < 8; i++)
1311 printf(" %s:0x%04x", dcpu16_reg_names[i], vm->reg[i]);
1312 printf("\n");
1313
1314 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1315 vm->cycle,
1316 dcpu16_reg_names[DCPU16_REG_EX], vm->reg[DCPU16_REG_EX],
1317 dcpu16_reg_names[DCPU16_REG_SP], vm->reg[DCPU16_REG_SP],
1318 dcpu16_reg_names[DCPU16_REG_PC], vm->reg[DCPU16_REG_PC],
1319 dcpu16_reg_names[DCPU16_REG_IA], vm->reg[DCPU16_REG_IA],
1320 "PC");
1321
1322 dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
1323 printf("\n");
1324 }
1325
1326 /* dcpu16_dump_ram
1327 * print raw ram contents from start to stop
1328 */
1329 void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) {
1330 unsigned int i, j;
1331 const unsigned int n = 8; /* words per line */
1332
1333 if (!vm) return;
1334
1335 for (i = start, j = 0; i <= end; i++, j++) {
1336 if (j % n == 0)
1337 printf("0x%04x:\t", i);
1338 printf(" %04x%s", vm->ram[i], (j % n) == (n - 1) ? "\n" : "");
1339 }
1340 if ((j % n) != (n - 1))
1341 printf("\n");
1342 }
1343
1344 /* dcpu16_hw_add
1345 * registers new 'hardware' device with system
1346 */
1347 int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) {
1348 if (!vm || !hw)
1349 return -1;
1350
1351 TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1352 __func__,
1353 hw->name_,
1354 hw->id_h, hw->id_l,
1355 hw->mfg_l, hw->mfg_h,
1356 hw->ver);
1357
1358 if (vm->hw_table_entries_ == 0xffff) {
1359 WARN("maximum hardware entries reached");
1360 return -1;
1361 }
1362
1363 if (vm->hw_table_entries_ == vm->hw_table_allocated_) {
1364 size_t new_entries = vm->hw_table_allocated_ + 32;
1365 void *tmp_ptr = realloc(vm->hw_table_, new_entries * sizeof * (vm->hw_table_));
1366 if (tmp_ptr == NULL) {
1367 fprintf(stderr, "%s():%s", "realloc", strerror(errno));
1368 return -1;
1369 }
1370 vm->hw_table_ = tmp_ptr;
1371 vm->hw_table_allocated_ += 32;
1372 }
1373
1374 memcpy(vm->hw_table_ + vm->hw_table_entries_, hw, sizeof *hw);
1375 vm->hw_table_entries_++;
1376
1377 TRACE("%s>> added hw entry %zu", __func__, vm->hw_table_entries_);
1378
1379 return 0;
1380 }
1381
1382 /* dcpu16_acct_add
1383 * Register callback fn to be triggered whenever event matching any events
1384 * in bitwise mask occur.
1385 */
1386 int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) {
1387 struct dcpu16_acct_cb cb;
1388
1389 if (!vm)
1390 return -1;
1391
1392 cb.mask = mask;
1393 cb.fn = fn;
1394 cb.data = data;
1395
1396 if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
1397 size_t new_entries = vm->cb_table_allocated_ + 32;
1398 void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
1399 if (tmp_ptr == NULL) {
1400 fprintf(stderr, "%s():%s", "realloc", strerror(errno));
1401 return -1;
1402 }
1403 vm->cb_table_ = tmp_ptr;
1404 vm->cb_table_allocated_ += 32;
1405 }
1406
1407 memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
1408 vm->cb_table_entries_++;
1409
1410 TRACE("%s>> attached event callback %zu", __func__, vm->cb_table_entries_);
1411
1412 return 0;
1413 }
1414
1415 /* dcpu16_reset
1416 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1417 */
1418 void dcpu16_reset(struct dcpu16 *vm) {
1419 size_t i;
1420
1421 if (!vm)
1422 return;
1423
1424 TRACE("%s>> reset", __func__);
1425
1426 vm->skip_ = 0;
1427 vm->interrupts_deferred_ = 0;
1428 vm->on_fire_ = 0;
1429 memset(vm->interrupts_, 0, sizeof vm->interrupts_);
1430 vm->interrupts_head_ = 0;
1431 vm->interrupts_tail_ = 0;
1432
1433 /* signal attached hardware */
1434 for (i = 0; i < vm->hw_table_entries_; i++) {
1435 if (vm->hw_table_[i].reset)
1436 vm->hw_table_[i].reset(vm, vm->hw_table_[i].data);
1437 }
1438
1439 memset(vm->reg, 0, sizeof vm->reg);
1440 memset(vm->ram, 0, sizeof vm->ram);
1441 vm->cycle = 0;
1442
1443 acct_event_(vm, DCPU16_ACCT_EV_RESET, 0);
1444 }
1445
1446 /* dcpu16_new
1447 * allocate a new dcpu16 instance
1448 */
1449 struct dcpu16 *dcpu16_new(void) {
1450 struct dcpu16 *vm;
1451
1452 vm = calloc(1, sizeof *vm);
1453 if (vm == NULL)
1454 WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
1455
1456 vm->warn_cb_ = warn_cb_;
1457 vm->trace_cb_ = trace_cb_;
1458
1459 return vm;
1460 }
1461
1462 /* dcpu16_delete
1463 * release a dcpu16 instance
1464 */
1465 void dcpu16_delete(struct dcpu16 **vm) {
1466 if (!vm || !*vm)
1467 return;
1468
1469 free(*vm);
1470 *vm = NULL;
1471 }