fixed instruction length issue
[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 = vm->cb_table_;
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, one exists for either potential instruction operand
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 >= 0x10 && value <= 0x17) || 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 (!lit_a) {
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 (!lit_a) {
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 (!lit_a) {
406 *a = acc;
407 }
408 d->o = (acc > 0xffff);
409 d->cycle += 2;
410
411 ACCT_W(ev_a);
412 }
413
414 OP_IMPL(mul) {
415 OP_BASIC(mul);
416 /* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
417 unsigned int acc = *a * *b;
418
419 ACCT_R(ev_b);
420 ACCT_R(ev_a);
421
422 if (!lit_a) {
423 *a = acc;
424 }
425 d->o = acc >> 16;
426 d->cycle += 2;
427
428 ACCT_W(ev_a);
429 }
430
431 OP_IMPL(div) {
432 OP_BASIC(div);
433 /* sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead. */
434
435 ACCT_R(ev_b);
436 ACCT_R(ev_a);
437
438 if (*b == 0) {
439 if (!lit_a) {
440 *a = 0;
441 }
442 d->o = 0;
443 } else {
444 unsigned int acc = *a / *b;
445
446 if (!lit_a) {
447 *a = acc;
448 }
449
450 acc = (*a << 16) / *b;
451 d->o = acc;
452 }
453
454 d->cycle += 3;
455
456 ACCT_W(ev_a);
457 }
458
459 OP_IMPL(mod) {
460 OP_BASIC(mod);
461 /* sets a to a%b. if b==0, sets a to 0 instead. */
462
463 ACCT_R(ev_b);
464 ACCT_R(ev_a);
465
466 if (*b == 0) {
467 if (!lit_a) {
468 *a = 0;
469 }
470 } else {
471 if (!lit_a) {
472 *a = *a % *b;
473 }
474 }
475
476 d->cycle += 3;
477
478 ACCT_W(ev_a);
479 }
480
481 OP_IMPL(shl) {
482 OP_BASIC(shl);
483 /* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
484 unsigned int acc = *a << *b;
485
486 ACCT_R(ev_b);
487 ACCT_R(ev_a);
488
489 if (!lit_a) {
490 *a = acc;
491 }
492 d->o = acc >> 16;
493
494 d->cycle += 2;
495
496 ACCT_W(ev_a);
497 }
498
499 OP_IMPL(shr) {
500 OP_BASIC(shr);
501 /* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
502 unsigned int acc = *a >> *b;
503
504 ACCT_R(ev_b);
505 ACCT_R(ev_a);
506
507 if (!lit_a) {
508 *a = acc;
509 }
510 d->o = (*a << 16) >> *b;
511
512 d->cycle += 2;
513
514 ACCT_W(ev_a);
515 }
516
517 OP_IMPL(and) {
518 OP_BASIC(and);
519 /* sets a to a&b */
520
521 ACCT_R(ev_b);
522 ACCT_R(ev_a);
523
524 if (!lit_a) {
525 *a = *a & *b;
526 }
527
528 d->cycle += 1;
529
530 ACCT_W(ev_a);
531 }
532
533 OP_IMPL(bor) {
534 OP_BASIC(bor);
535 /* sets a to a|b */
536
537 ACCT_R(ev_b);
538 ACCT_R(ev_a);
539
540 if (!lit_a) {
541 *a = *a | *b;
542 }
543
544 d->cycle += 1;
545
546 ACCT_W(ev_a);
547 }
548
549 OP_IMPL(xor) {
550 OP_BASIC(xor);
551 /* sets a to a^b */
552
553 ACCT_R(ev_b);
554 ACCT_R(ev_a);
555
556 if (!lit_a) {
557 *a = *a ^ *b;
558 }
559
560 ACCT_W(ev_a);
561
562 d->cycle += 1;
563 }
564
565 OP_IMPL(ife) {
566 OP_BASIC(ife);
567 /* performs next instruction only if a==b */
568
569 ACCT_R(ev_b);
570 ACCT_R(ev_a);
571
572 if (*a == *b) {
573 /* */
574 } else {
575 d->skip_ = 1;
576 d->cycle++;
577 }
578
579 d->cycle += 2;
580 }
581
582 OP_IMPL(ifn) {
583 OP_BASIC(ifn);
584 /* performs next instruction only if a!=b */
585
586 ACCT_R(ev_b);
587 ACCT_R(ev_a);
588
589 if (*a != *b) {
590 /* */
591 } else {
592 d->skip_ = 1;
593 d->cycle++;
594 }
595
596 d->cycle += 2;
597 }
598
599 OP_IMPL(ifg) {
600 OP_BASIC(ifg);
601 /* performs next instruction only if a>b */
602
603 ACCT_R(ev_b);
604 ACCT_R(ev_a);
605
606 if (*a > *b) {
607 /* */
608 } else {
609 d->skip_ = 1;
610 d->cycle++;
611 }
612
613 d->cycle += 2;
614 }
615
616 OP_IMPL(ifb) {
617 OP_BASIC(ifb);
618 /* performs next instruction only if (a&b)!=0 */
619
620 ACCT_R(ev_b);
621 ACCT_R(ev_a);
622
623 if ((*a & *b) != 0) {
624 /* */
625 } else {
626 d->skip_ = 1;
627 d->cycle++;
628 }
629
630 d->cycle += 2;
631 }
632
633 static const struct opcode_entry opcode_basic_entries[] = {
634 {0x0, "(nbi)", op__nbi_},
635 {0x1, "SET", op_set },
636 {0x2, "ADD", op_add },
637 {0x3, "SUB", op_sub },
638 {0x4, "MUL", op_mul },
639 {0x5, "DIV", op_div },
640 {0x6, "MOD", op_mod },
641 {0x7, "SHL", op_shl },
642 {0x8, "SHR", op_shr },
643 {0x9, "AND", op_and },
644 {0xa, "BOR", op_bor },
645 {0xb, "XOR", op_xor },
646 {0xc, "IFE", op_ife },
647 {0xd, "IFN", op_ifn },
648 {0xe, "IFG", op_ifg },
649 {0xf, "IFB", op_ifb },
650 {0x0, "", NULL }
651 };
652
653 static inline
654 void dump_operand_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 /* split a word into the parts of an instruction, and determine how many words it takes up in total */
675 static inline
676 void instruction_decode_(struct dcpu16 *d, WORD addr, WORD *opcode, WORD *a, WORD *b, unsigned int *instr_len) {
677 *opcode = d->ram[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
678 *a = (d->ram[addr] >> OPCODE_BASIC_BITS) & ((1 << 6) - 1);
679 *b = (d->ram[addr] >> (OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1);
680 *instr_len = 1;
681 /* both basic and nbi opcodes use their b operand */
682 if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f )
683 *instr_len += 1;
684 /* but only basic uses a */
685 if (*opcode
686 && ((*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) )
687 *instr_len += 1;
688 }
689
690 /* dcpu16_disassemble_print
691 print the words of the instruction at addr, followed by its assembly
692 7c01 0030 ; SET A,
693 7de1 1000 0020 ; SET [0x1000], 0x0020
694 7803 1000 ; SUB A,
695 c00d ; IFN A,
696 */
697 WORD dcpu16_disassemble_print(struct dcpu16 *d, WORD addr) {
698 WORD opcode, a, b;
699 unsigned int instr_len, i;
700 const struct opcode_entry *e;
701 unsigned int indent = 0;
702 unsigned int partial = 0;
703
704 /*
705 Check the previous instruction, to see if this one should be
706 indented. This check isn't foolproof, as preceeding addresses
707 could be data which happen to match instructions..
708 */
709 for (i = 3; i; i--) {
710 instruction_decode_(d, addr - i, &opcode, &a, &b, &instr_len);
711 if (instr_len > i)
712 partial++;
713 if (instr_len == i && opcode >= 0xc) {
714 indent++;
715 break;
716 }
717 }
718
719 /* now get what we're really interested in */
720 instruction_decode_(d, addr, &opcode, &a, &b, &instr_len);
721
722 if (opcode)
723 e = opcode_basic_entries + opcode;
724 else
725 e = opcode_nbi_entries + ( (a < OPCODE_NBI_MAX) ? a : (OPCODE_NBI_MAX - 1) );
726
727 /* show the raw words */
728 printf("%04x", d->ram[addr]);
729 for (i = 1; i < instr_len; i++) {
730 printf(" %04x", d->ram[addr + i]);
731 }
732
733 /* align things neatly, show the instruction */
734 printf("%s%s ;%s%s%s",
735 instr_len < 3 ? " " : "",
736 instr_len < 2 ? " " : "",
737 partial ? "*" : " ",
738 indent ? " " : "",
739 e->name);
740
741 /* show the operands */
742 i = 0;
743 if (opcode) {
744 dump_operand_value_(a, d->ram[addr + 1]);
745 if ((a >= 0x10 && a <= 0x17) || a == 0x1e || a == 0x1f)
746 addr++;
747 printf(",");
748 }
749
750 if (opcode || a)
751 dump_operand_value_(b, d->ram[addr + 1]);
752
753 return instr_len;
754 }
755
756 void dcpu16_step(struct dcpu16 *d) {
757 WORD opcode;
758 WORD val_a, val_b;
759 const struct opcode_entry *e;
760
761 /* decode opcode and invoke */
762
763 opcode = (d->ram[ d->pc ] >> OPCODE_BASIC_SHIFT) & ((1 << OPCODE_BASIC_BITS) - 1);
764 val_a = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS)) & ((1 << 6) - 1);
765 val_b = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1);
766
767 d->pc++;
768
769 for (e = opcode_basic_entries; e->impl; e++) {
770 if (e->value == opcode) {
771 TRACE(">> %s 0x%04x, 0x%04x", e->name, val_a, val_b);
772 e->impl(d, val_a, val_b);
773 break;
774 }
775 }
776 }
777
778 void dcpu16_state_print(struct dcpu16 *d) {
779 unsigned int i;
780
781 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
782 d->cycle,
783 "O", d->o,
784 "SP", d->sp,
785 "PC", d->pc,
786 "PC");
787
788 dcpu16_disassemble_print(d, d->pc);
789 printf("\n ");
790
791 for (i = 0; i < 8; i++)
792 printf(" %c:0x%04x", regnames_[i], d->reg[i]);
793 printf("\n");
794 }
795
796 /* dcpu16_dump_ram
797 * print raw ram contents from start to stop
798 */
799 void dcpu16_dump_ram(struct dcpu16 *d, WORD start, WORD end) {
800 unsigned int i, j;
801 const unsigned int n = 8; /* words per line */
802
803 for (i = start, j = 0; i <= end; i++, j++) {
804 if (j % n == 0)
805 printf("0x%04x:\t", i);
806 printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
807 }
808 if ((j % n) != (n - 1))
809 printf("\n");
810 }
811
812 /* dcpu16_reset
813 * resets a dcpu16 instance to initial state
814 */
815 void dcpu16_reset(struct dcpu16 *d) {
816 memset(d, 0, sizeof *d);
817 }
818
819 /* dcpu16_new
820 * allocate a new dcpu16 instance
821 */
822 struct dcpu16 *dcpu16_new(void) {
823 struct dcpu16 *vm;
824
825 vm = calloc(1, sizeof *vm);
826 if (vm == NULL)
827 WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
828
829 return vm;
830 }
831
832 /* dcpu16_delete
833 * release a dcpu16 instance
834 */
835 void dcpu16_delete(struct dcpu16 **vm) {
836 free(*vm);
837 *vm = NULL;
838 }