cleanup, simplified event callbacks, step now consumes branch-skipped instructions
[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 04 06 - first functionality achieved
22 * 2012 04 09 - minor cleanups
23 * 2012 04 10 - moved cli to separate module
24 * 2012 04 12 - added basic callback support for address accesses
25 *
26 * TODO
27 * change api to print into buffers rather than stdio
28 * drop checks for assigning to literals -- it won't affect anything anyhow
29 * refactor opcode functiontables into switch statements
30 */
31
32 static const char * const src_id_ = "$Id$";
33
34 #define OPCODE_BASIC_BITS 4
35 #define OPCODE_OPERAND_BITS 6
36
37 static const char regnames_[] = "ABCXYZIJ";
38
39 /* some default warning and debug reporting functions, which can be overridden by clients */
40 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
41 static inline void warn_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
42 static inline
43 void warn_(char *fmt, ...) {
44 va_list ap;
45
46 fprintf(stderr, "[warning] ");
47 va_start(ap, fmt);
48 vfprintf(stderr, fmt, ap);
49 va_end(ap);
50 fprintf(stderr, "\n");
51 fflush(stderr);
52 }
53 static void (*warn_cb_)(char *fmt, ...) = warn_;
54 void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
55 warn_cb_ = fn;
56 }
57
58 #ifdef DEBUG
59 #define TRACE(...) do { if (trace_cb_) trace_cb_(__VA_ARGS__); } while (0)
60 static inline void trace_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
61 static inline
62 void trace_(char *fmt, ...) {
63 va_list ap;
64
65 fprintf(stdout, "[debug] ");
66 va_start(ap, fmt);
67 vfprintf(stdout, fmt, ap);
68 va_end(ap);
69 fprintf(stdout, "\n");
70 fflush(stdout);
71 }
72 #else /* DEBUG */
73 #define TRACE(...) do {} while(0)
74 #endif /* DEBUG */
75 static void (*trace_cb_)(char *fmt, ...) =
76 #ifdef DEBUG
77 trace_
78 #else /* DEBUG */
79 NULL
80 #endif
81 ;
82 void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
83 trace_cb_ = fn;
84 }
85
86
87 /* acct_event_
88 * invokes callbacks for specified event
89 */
90 static inline
91 void acct_event_(struct dcpu16 *vm, dcpu16_acct_event_ ev, DCPU16_WORD addr) {
92 struct dcpu16_acct_cb *cb = vm->cb_table_;
93 size_t i;
94
95 for (i = 0; i < vm->cb_table_entries_; i++) {
96 if ( (cb[i].mask & ev) )
97 cb[i].fn(ev, addr);
98 }
99 }
100
101
102 /* value_decode_
103 * sets *v to be the destination of the value
104 * advances d->pc if necessary
105 * workv is buffer to use to accumulate literal value before use, one exists for either potential instruction operand
106 * e_addr is for accounting callback
107 * returns true if destination points to literal (id est *v should ignore writes)
108 */
109 static
110 unsigned int value_decode_(struct dcpu16 *d, DCPU16_WORD value, DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr) {
111 DCPU16_WORD nextword;
112 unsigned int retval = 0;
113
114 assert(value <= 0x3f);
115
116 /* does this value indicate a literal */
117 if (value >= 0x1f)
118 retval = 1;
119
120 /* if we're skipping this instruction, just advance the pc if needed */
121 if (d->skip_) {
122 TRACE(">> SKIP decode");
123 if ((value >= 0x10 && value <= 0x17) || value == 0x1e || value == 0x1f)
124 d->pc++;
125 return retval;
126 }
127
128 if (value <= 0x07) { /* register */
129 *v = d->reg + value;
130 TRACE(">> %c (0x%04x)",
131 regnames_[value],
132 **v);
133
134 } else if (value <= 0x0f) { /* [register] */
135 *v = &(d->ram[ d->reg[(value & 0x07)] ]);
136 TRACE(">> [%c] [0x%04x] (0x%04x)",
137 regnames_[value&0x07],
138 d->reg[value&0x07],
139 **v);
140 *e_addr = d->reg[(value & 0x07)];
141
142 } else if (value <= 0x17) { /* [next word + register] */
143 nextword = d->ram[ d->pc++ ];
144 d->cycle++;
145 *v = &(d->ram[ nextword + d->reg[(value & 0x07)] ]);
146 TRACE(">> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
147 regnames_[(value & 0x07)],
148 nextword,
149 d->reg[(value & 0x07)],
150 **v);
151 *e_addr = nextword + d->reg[(value & 0x07)];
152
153 } else switch (value) {
154 case 0x18: /* POP / [sp++] */
155 *v = &(d->ram[ d->sp++ ]);
156 TRACE(">> POP [0x%04x] (0x%04x)",
157 d->sp - 1,
158 **v);
159 *e_addr = d->sp - 1;
160 break;
161
162 case 0x19: /* PEEK / [sp] */
163 *v = &(d->ram[ d->sp ]);
164 TRACE(">> PEEK [0x%04x] (0x%04x)",
165 d->sp,
166 **v);
167 *e_addr = d->sp;
168 break;
169
170 case 0x1a: /* PUSH / [--sp] */
171 *v = &(d->ram[ --d->sp ]);
172 TRACE(">> PUSH [0x%04x] (0x%04x)",
173 d->sp + 1,
174 **v);
175 *e_addr = d->sp + 1;
176 break;
177
178 case 0x1b: /* SP */
179 *v = &(d->sp);
180 TRACE(">> SP (0x%04x)",
181 **v);
182 break;
183
184 case 0x1c: /* PC */
185 *v = &(d->pc);
186 TRACE(">> PC (0x%04x)", **v);
187 break;
188
189 case 0x1d: /* O */
190 *v = &(d->o);
191 TRACE(">> O (0x%04x)", **v);
192 break;
193
194 case 0x1e: /* [next word] / [[pc++]] */
195 nextword = d->ram[ d->pc++ ];
196 d->cycle++;
197 *v = &(d->ram[ nextword ]);
198 TRACE(">> [nextword] [0x%04x] (0x%04x)",
199 nextword,
200 **v);
201 *e_addr = nextword;
202 break;
203
204 case 0x1f: /* next word (literal) / [pc++] */
205 nextword = d->ram[ d->pc++ ];
206 d->cycle++;
207 *work_v = nextword;
208 *v = work_v;
209 TRACE(">> nextword (0x%04x)", **v);
210 break;
211
212 default: /* 0x20-0x3f: literal values 0x00-0x1f */
213 *work_v = value & 0x1f;
214 *v = work_v;
215 TRACE(">> literal (0x%04x)", **v);
216 }
217
218 return retval;
219 }
220
221
222 #define OPCODE_NAME_LEN 16
223 struct opcode_entry {
224 unsigned short value;
225 char name[OPCODE_NAME_LEN];
226 void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD);
227 };
228
229 /* messy boilerplate for opcode handlers */
230
231 #define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_a, DCPU16_WORD val_b)
232
233 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
234 unsigned int lit_a;\
235 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
236 do {\
237 lit_a = value_decode_(d, val_a, &d->reg_work_[0], &a, &ev_a_addr);\
238 op_type;\
239 } while (0)
240 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr
241 #define OP_BASIC_ (void)value_decode_(d, val_b, &d->reg_work_[0], &b, &ev_b_addr)
242 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
243 #define OP_NBI(x) OP_TYPE(OP_NBI_)
244
245 /*
246 accounting helpers, these fire off the related callbacks for memory reads,
247 memory writes, and execution of reserved instructions
248 */
249 #define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
250 #define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
251 #define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
252
253 /* extended opcodes */
254
255 /*
256 N.B. this next function currently decodes values -- id est, it is
257 an opcode processing terminus; however, if 'future instruction set
258 expansion' happens, this will probably need to behave more like
259 the OP_IMPL(_nbi_) function which invoked it, if those instructions
260 have zero or differently styled operands.
261 */
262 OP_IMPL(nbi__reserved_) {
263 OP_NBI(nbi__reserved_);
264 /* reserved for future expansion */
265
266 DCPU16_WORD future_opcode = (d->ram[d->pc] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS));
267 WARN("reserved future opcode 0x%04x invoked", future_opcode);
268
269 ACCT_ILL(d->pc);
270 }
271
272 OP_IMPL(nbi_jsr) {
273 OP_NBI(nbi_jsr);
274 /* pushes the address of the next instruction to the stack, then sets PC to a */
275
276 ACCT_R(ev_a_addr);
277
278 d->ram[ --d->sp ] = d->pc;
279 d->pc = *a;
280
281 d->cycle += 2;
282
283 ACCT_W(d->sp + 1);
284 }
285
286 OP_IMPL(nbi__reserved2_) {
287 OP_NBI(nbi__reserved2_);
288 /* reserved */
289
290 WARN("reserved nbi opcode invoked");
291
292 ACCT_ILL(d->pc);
293 }
294
295 static const struct opcode_entry opcode_nbi_entries[] = {
296 {0x0, "(reserved)", op_nbi__reserved_},
297 {0x1, "JSR", op_nbi_jsr},
298 {0x2, "(reserved)", op_nbi__reserved2_},
299 {0x0, "", NULL}
300 };
301 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
302
303
304 /* basic opcodes */
305
306 /*
307 N.B. the following function does not decode values, as the nbi
308 instructions only have one operand.
309 */
310 OP_IMPL(_nbi_) {
311 /* non-basic instruction */
312
313 /* don't do normal value decoding here */
314
315 DCPU16_WORD nbi_opcode = val_a;
316 const struct opcode_entry *e = opcode_nbi_entries;
317
318 e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
319
320 assert(e->impl != NULL);
321
322 TRACE(">> %s 0x%04x", e->name, val_b);
323 e->impl(d, val_b, 0);
324 }
325
326 OP_IMPL(set) {
327 OP_BASIC(set);
328 /* sets a to b */
329
330 ACCT_R(ev_b_addr);
331 ACCT_W(ev_a_addr);
332
333 /* only set non-literal target */
334 if (!lit_a) {
335 *a = *b;
336 }
337
338 d->cycle += 1;
339 }
340
341 OP_IMPL(add) {
342 OP_BASIC(add);
343 /* sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise */
344 unsigned int acc = *a + *b;
345
346 ACCT_R(ev_b_addr);
347 ACCT_R(ev_a_addr);
348
349 if (!lit_a) {
350 *a = acc;
351 }
352 d->o = (acc > 0xffff);
353
354 d->cycle += 2;
355
356 ACCT_W(ev_a_addr);
357 }
358
359 OP_IMPL(sub) {
360 OP_BASIC(sub);
361 /* sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise */
362 unsigned int acc = *a - *b;
363
364 ACCT_R(ev_b_addr);
365 ACCT_R(ev_a_addr);
366
367 if (!lit_a) {
368 *a = acc;
369 }
370 d->o = (acc > 0xffff);
371 d->cycle += 2;
372
373 ACCT_W(ev_a_addr);
374 }
375
376 OP_IMPL(mul) {
377 OP_BASIC(mul);
378 /* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
379 unsigned int acc = *a * *b;
380
381 ACCT_R(ev_b_addr);
382 ACCT_R(ev_a_addr);
383
384 if (!lit_a) {
385 *a = acc;
386 }
387 d->o = acc >> 16;
388 d->cycle += 2;
389
390 ACCT_W(ev_a_addr);
391 }
392
393 OP_IMPL(div) {
394 OP_BASIC(div);
395 /* sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead. */
396
397 ACCT_R(ev_b_addr);
398 ACCT_R(ev_a_addr);
399
400 if (*b == 0) {
401 if (!lit_a) {
402 *a = 0;
403 }
404 d->o = 0;
405 } else {
406 unsigned int acc = *a / *b;
407
408 if (!lit_a) {
409 *a = acc;
410 }
411
412 acc = (*a << 16) / *b;
413 d->o = acc;
414 }
415
416 d->cycle += 3;
417
418 ACCT_W(ev_a_addr);
419 }
420
421 OP_IMPL(mod) {
422 OP_BASIC(mod);
423 /* sets a to a%b. if b==0, sets a to 0 instead. */
424
425 ACCT_R(ev_b_addr);
426 ACCT_R(ev_a_addr);
427
428 if (*b == 0) {
429 if (!lit_a) {
430 *a = 0;
431 }
432 } else {
433 if (!lit_a) {
434 *a = *a % *b;
435 }
436 }
437
438 d->cycle += 3;
439
440 ACCT_W(ev_a_addr);
441 }
442
443 OP_IMPL(shl) {
444 OP_BASIC(shl);
445 /* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
446 unsigned int acc = *a << *b;
447
448 ACCT_R(ev_b_addr);
449 ACCT_R(ev_a_addr);
450
451 if (!lit_a) {
452 *a = acc;
453 }
454 d->o = acc >> 16;
455
456 d->cycle += 2;
457
458 ACCT_W(ev_a_addr);
459 }
460
461 OP_IMPL(shr) {
462 OP_BASIC(shr);
463 /* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
464 unsigned int acc = *a >> *b;
465
466 ACCT_R(ev_b_addr);
467 ACCT_R(ev_a_addr);
468
469 if (!lit_a) {
470 *a = acc;
471 }
472 d->o = (*a << 16) >> *b;
473
474 d->cycle += 2;
475
476 ACCT_W(ev_a_addr);
477 }
478
479 OP_IMPL(and) {
480 OP_BASIC(and);
481 /* sets a to a&b */
482
483 ACCT_R(ev_b_addr);
484 ACCT_R(ev_a_addr);
485
486 if (!lit_a) {
487 *a = *a & *b;
488 }
489
490 d->cycle += 1;
491
492 ACCT_W(ev_a_addr);
493 }
494
495 OP_IMPL(bor) {
496 OP_BASIC(bor);
497 /* sets a to a|b */
498
499 ACCT_R(ev_b_addr);
500 ACCT_R(ev_a_addr);
501
502 if (!lit_a) {
503 *a = *a | *b;
504 }
505
506 d->cycle += 1;
507
508 ACCT_W(ev_a_addr);
509 }
510
511 OP_IMPL(xor) {
512 OP_BASIC(xor);
513 /* sets a to a^b */
514
515 ACCT_R(ev_b_addr);
516 ACCT_R(ev_a_addr);
517
518 if (!lit_a) {
519 *a = *a ^ *b;
520 }
521
522 ACCT_W(ev_a_addr);
523
524 d->cycle += 1;
525 }
526
527 OP_IMPL(ife) {
528 OP_BASIC(ife);
529 /* performs next instruction only if a==b */
530
531 ACCT_R(ev_b_addr);
532 ACCT_R(ev_a_addr);
533
534 if (*a == *b) {
535 /* */
536 } else {
537 d->skip_ = 1;
538 d->cycle++;
539 }
540
541 d->cycle += 2;
542 }
543
544 OP_IMPL(ifn) {
545 OP_BASIC(ifn);
546 /* performs next instruction only if a!=b */
547
548 ACCT_R(ev_b_addr);
549 ACCT_R(ev_a_addr);
550
551 if (*a != *b) {
552 /* */
553 } else {
554 d->skip_ = 1;
555 d->cycle++;
556 }
557
558 d->cycle += 2;
559 }
560
561 OP_IMPL(ifg) {
562 OP_BASIC(ifg);
563 /* performs next instruction only if a>b */
564
565 ACCT_R(ev_b_addr);
566 ACCT_R(ev_a_addr);
567
568 if (*a > *b) {
569 /* */
570 } else {
571 d->skip_ = 1;
572 d->cycle++;
573 }
574
575 d->cycle += 2;
576 }
577
578 OP_IMPL(ifb) {
579 OP_BASIC(ifb);
580 /* performs next instruction only if (a&b)!=0 */
581
582 ACCT_R(ev_b_addr);
583 ACCT_R(ev_a_addr);
584
585 if ((*a & *b) != 0) {
586 /* */
587 } else {
588 d->skip_ = 1;
589 d->cycle++;
590 }
591
592 d->cycle += 2;
593 }
594
595 static const struct opcode_entry opcode_basic_entries[] = {
596 {0x0, "(nbi)", op__nbi_},
597 {0x1, "SET", op_set },
598 {0x2, "ADD", op_add },
599 {0x3, "SUB", op_sub },
600 {0x4, "MUL", op_mul },
601 {0x5, "DIV", op_div },
602 {0x6, "MOD", op_mod },
603 {0x7, "SHL", op_shl },
604 {0x8, "SHR", op_shr },
605 {0x9, "AND", op_and },
606 {0xa, "BOR", op_bor },
607 {0xb, "XOR", op_xor },
608 {0xc, "IFE", op_ife },
609 {0xd, "IFN", op_ifn },
610 {0xe, "IFG", op_ifg },
611 {0xf, "IFB", op_ifb },
612 {0x0, "", NULL }
613 };
614
615 static inline
616 void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword) {
617 if (value <= 0x07) {
618 printf(" %c", regnames_[value]);
619 } else if (value <= 0x0f) {
620 printf(" [%c]", regnames_[value & 0x07]);
621 } else if (value <= 0x17) {
622 printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
623 } else switch (value) {
624 case 0x18: printf(" POP"); break;
625 case 0x19: printf(" PEEK"); break;
626 case 0x1a: printf(" PUSH"); break;
627 case 0x1b: printf(" SP"); break;
628 case 0x1c: printf(" PC"); break;
629 case 0x1d: printf(" O"); break;
630 case 0x1e: printf(" [0x%04x]", nextword); break;
631 case 0x1f: printf(" 0x%04x", nextword); break;
632 default: printf(" 0x%02x", value - 0x20);
633 }
634 }
635
636
637 /* split a word into the parts of an instruction, and determine how many words it takes up in total */
638 static inline
639 void instruction_decode_(struct dcpu16 *d, DCPU16_WORD addr, DCPU16_WORD *opcode, DCPU16_WORD *a, DCPU16_WORD *b, DCPU16_WORD *instr_len) {
640 *opcode = d->ram[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
641 *a = (d->ram[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_BITS) - 1);
642 *b = (d->ram[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS)) & ((1 << OPCODE_OPERAND_BITS) - 1);
643 if (instr_len) {
644 *instr_len = 1;
645 /* both basic and nbi opcodes use their b operand */
646 if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f )
647 *instr_len += 1;
648 /* but only basic uses a */
649 if (*opcode
650 && ((*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) )
651 *instr_len += 1;
652 }
653 }
654
655 /* dcpu16_disassemble_print
656 print the words of the instruction at addr, followed by its assembly representation
657 returns the length of the instruction in words
658 */
659 DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
660 DCPU16_WORD opcode, a, b, instr_len, i;
661 const struct opcode_entry *e;
662 unsigned int indent = 0;
663 unsigned int partial = 0;
664
665 if (!d) return 0;
666
667 /*
668 Check the previous instruction, to see if this one should be
669 indented. This check isn't foolproof, as preceeding addresses
670 could be data which happen to match instructions..
671 */
672 for (i = 3; i; i--) {
673 instruction_decode_(d, addr - i, &opcode, &a, &b, &instr_len);
674 if (instr_len > i)
675 partial++;
676 if (instr_len == i && opcode >= 0xc) {
677 indent++;
678 break;
679 }
680 }
681
682 /* now get what we're really interested in */
683 instruction_decode_(d, addr, &opcode, &a, &b, &instr_len);
684
685 if (opcode)
686 e = opcode_basic_entries + opcode;
687 else
688 e = opcode_nbi_entries + ( (a < OPCODE_NBI_MAX) ? a : (OPCODE_NBI_MAX - 1) );
689
690 /* show the raw words */
691 printf("%04x", d->ram[addr]);
692 for (i = 1; i < instr_len; i++) {
693 printf(" %04x", d->ram[addr + i]);
694 }
695
696 /* align things neatly, show the instruction */
697 printf("%s%s ;%s%s%s",
698 instr_len < 3 ? " " : "",
699 instr_len < 2 ? " " : "",
700 partial ? "*" : " ",
701 indent ? " " : "",
702 e->name);
703
704 /* show the operands */
705 i = 0;
706 if (opcode) {
707 dump_operand_value_(a, d->ram[addr + 1]);
708 if ((a >= 0x10 && a <= 0x17) || a == 0x1e || a == 0x1f)
709 addr++;
710 printf(",");
711 }
712
713 if (opcode || a)
714 dump_operand_value_(b, d->ram[addr + 1]);
715
716 return instr_len;
717 }
718
719 /* execute the next instruction */
720 void dcpu16_step(struct dcpu16 *d) {
721 DCPU16_WORD opcode, a, b, instr_len;
722 const struct opcode_entry *e;
723
724 if (!d) return;
725
726 /*
727 PC is advanced while decoding the operands by the opcode functions.
728 Things like this could be organized a little better..
729 */
730 instruction_decode_(d, d->pc, &opcode, &a, &b, NULL);
731
732 d->pc++; /* all instructions take at least one word */
733
734 for (e = opcode_basic_entries; e->impl; e++) {
735 if (e->value == opcode) {
736 TRACE(">> %s 0x%04x, 0x%04x", e->name, a, b);
737 e->impl(d, a, b);
738 break;
739 }
740 }
741
742 /* and jump over next instr if needed */
743 if (d->skip_) {
744 instruction_decode_(d, d->pc, &opcode, &a, &b, &instr_len);
745 d->pc += instr_len;
746 d->skip_ = 0;
747 TRACE("++ SKIPPED %x words", instr_len);
748 }
749 }
750
751 /*
752 print the current state of the machine
753 shows current cycle count, registers, and next instruction
754 */
755 void dcpu16_state_print(struct dcpu16 *d) {
756 unsigned int i;
757
758 if (!d) return;
759
760 printf(" ");
761 for (i = 0; i < 8; i++)
762 printf(" %c:0x%04x", regnames_[i], d->reg[i]);
763 printf("\n");
764
765 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
766 d->cycle,
767 "O", d->o,
768 "SP", d->sp,
769 "PC", d->pc,
770 "PC");
771
772 dcpu16_disassemble_print(d, d->pc);
773 printf("\n");
774 }
775
776 /* dcpu16_dump_ram
777 * print raw ram contents from start to stop
778 */
779 void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
780 unsigned int i, j;
781 const unsigned int n = 8; /* words per line */
782
783 if (!d) return;
784
785 for (i = start, j = 0; i <= end; i++, j++) {
786 if (j % n == 0)
787 printf("0x%04x:\t", i);
788 printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
789 }
790 if ((j % n) != (n - 1))
791 printf("\n");
792 }
793
794 /* dcpu16_acct_add
795 * Register callback fn to be triggered whenever event matching any events
796 * in bitwise mask occur.
797 */
798 int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ mask, void (*fn)(dcpu16_acct_event_, DCPU16_WORD)) {
799 struct dcpu16_acct_cb cb;
800
801 cb.mask = mask;
802 cb.fn = fn;
803
804 if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
805 size_t new_entries = vm->cb_table_allocated_ + 32;
806 void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
807 if (tmp_ptr == NULL) {
808 fprintf(stderr, "%s():%s", "realloc", strerror(errno));
809 return -1;
810 }
811 vm->cb_table_ = tmp_ptr;
812 vm->cb_table_allocated_ += 32;
813 }
814
815 memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
816 vm->cb_table_entries_++;
817
818 return 0;
819 }
820
821 /* dcpu16_reset
822 * resets a dcpu16 instance to initial state
823 */
824 void dcpu16_reset(struct dcpu16 *d) {
825 if (!d) return;
826
827 memset(d, 0, sizeof *d);
828 }
829
830 /* dcpu16_new
831 * allocate a new dcpu16 instance
832 */
833 struct dcpu16 *dcpu16_new(void) {
834 struct dcpu16 *vm;
835
836 vm = calloc(1, sizeof *vm);
837 if (vm == NULL)
838 WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
839
840 return vm;
841 }
842
843 /* dcpu16_delete
844 * release a dcpu16 instance
845 */
846 void dcpu16_delete(struct dcpu16 **vm) {
847 if (!vm || !*vm) return;
848
849 free(*vm);
850 *vm = NULL;
851 }