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