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