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