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