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