v1.7 spec mostly implemented, mostly untested
[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 * 2012 05 05 - start of v1.7 revisions
26 * 2012 05 08 - v1.7 revisions mostly complete
27 *
28 * TODO
29 * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
30 * !! v1.7 hardware interface needs to be finished
31 * !! v1.7 interrupts need to be finished
32 * change api to print into buffers rather than stdio
33 * refactor opcode functiontables into switch statements
34 * let callbacks determine whether to override events, or just observe
35 * sort init callbacks by base addr, to call in-order
36 * make all callbacks register addr range of interest
37 */
38
39 static const char * const src_id_ = "$Id$";
40
41 #define OPCODE_BASIC_BITS 5
42 #define OPCODE_OPERAND_B_BITS 5
43 #define OPCODE_OPERAND_A_BITS 6
44
45 static const char * const regnames_ = "ABCXYZIJ";
46
47 /* some default warning and debug reporting functions, which can be overridden by clients */
48 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
49 static inline void warn_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
50 static inline
51 void warn_(char *fmt, ...) {
52 va_list ap;
53
54 fprintf(stderr, "[warning] ");
55 va_start(ap, fmt);
56 vfprintf(stderr, fmt, ap);
57 va_end(ap);
58 fprintf(stderr, "\n");
59 fflush(stderr);
60 }
61 static void (*warn_cb_)(char *fmt, ...) = warn_;
62 void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
63 warn_cb_ = fn;
64 }
65
66 #ifdef DEBUG
67 #define TRACE(...) do { if (trace_cb_) trace_cb_(__VA_ARGS__); } while (0)
68 static inline void trace_(char *fmt, ...) __attribute__((format(printf, 1, 2)));
69 static inline
70 void trace_(char *fmt, ...) {
71 va_list ap;
72
73 fprintf(stdout, "[debug] ");
74 va_start(ap, fmt);
75 vfprintf(stdout, fmt, ap);
76 va_end(ap);
77 fprintf(stdout, "\n");
78 fflush(stdout);
79 }
80 #else /* DEBUG */
81 #define TRACE(...) do {} while(0)
82 #endif /* DEBUG */
83 static void (*trace_cb_)(char *fmt, ...) =
84 #ifdef DEBUG
85 trace_
86 #else /* DEBUG */
87 NULL
88 #endif
89 ;
90 void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
91 trace_cb_ = fn;
92 }
93
94
95 /* acct_event_
96 * invokes callbacks for specified event
97 */
98 static inline
99 void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
100 struct dcpu16_acct_cb *cb = vm->cb_table_;
101 size_t i;
102
103 for (i = 0; i < vm->cb_table_entries_; i++) {
104 if ( (cb[i].mask & ev) )
105 cb[i].fn(vm, ev, addr, cb[i].data);
106 }
107 }
108
109 /* convert register name to index into register array */
110 static inline
111 off_t reg_index_(int reg) {
112 return strchr(regnames_, reg) - regnames_;
113 }
114
115 /* add an entry to the interrupt queue */
116 static
117 int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) {
118 d->interrupts_[d->interrupts_tail_] = message;
119 d->interrupts_tail_ += 1;
120 d->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
121
122 if (d->interrupts_tail_ == d->interrupts_head_) {
123 d->on_fire_ = 1;
124 WARN("interrupt queue overflow (system is now on fire)");
125 return -1;
126 }
127
128 return 0;
129 }
130
131 static
132 DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) {
133 DCPU16_WORD message;
134
135 if (d->interrupts_tail_ == d->interrupts_head_) {
136 WARN("interrupt underflow");
137 return 0;
138 }
139
140 message = d->interrupts_[d->interrupts_head_];
141 d->interrupts_head_ += 1;
142 d->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
143
144 return message;
145 }
146
147 /* value_decode_
148 * sets *v to be the address of the represented value
149 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
150 * value_data is 'nextword' for this operand, ignored if unused
151 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
152 * e_addr is set to a referenced address, for accounting callback
153 * pc_adjust is set to how to change the program counter
154 * stack_adjust is set to how to change the stack pointer
155 * cycles is set to number of cycles spent looking up operand
156 *
157 * zero all adjustables before decoding first operand, and pass in these values when
158 * decoding next operand..
159 *
160 */
161 static inline
162 void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
163 DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr,
164 short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) {
165 assert(value <= 0x3f);
166
167 DCPU16_WORD pc = (DCPU16_WORD)(d->pc + *pc_adjust),
168 sp = (DCPU16_WORD)(d->sp + *sp_adjust);
169
170 TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n",
171 __func__,
172 pc,
173 sp,
174 value_data);
175
176 if (value <= 0x07) { /* register */
177 *v = d->reg + value;
178 TRACE("%s>> %c (0x%04x)",
179 __func__,
180 regnames_[value],
181 **v);
182 return;
183 }
184
185 if (value <= 0x0f) { /* [register] */
186 *v = &(d->ram[ d->reg[value & 0x07] ]);
187 *e_addr = d->reg[value & 0x07];
188 TRACE("%s>> [%c] [0x%04x] (0x%04x)",
189 __func__,
190 regnames_[value & 0x07],
191 d->reg[value & 0x07],
192 **v);
193 return;
194 }
195
196 if (value <= 0x17) { /* [next word + register] */
197 *pc_adjust += 1; /* consume next word */
198 *cycle_adjust += 1;
199 *e_addr = value_data + d->reg[value & 0x07];
200 *v = d->ram + *e_addr;
201 TRACE("%s>> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
202 __func__,
203 regnames_[value & 0x07],
204 value_data,
205 d->reg[value & 0x07],
206 **v);
207 return;
208 }
209
210 switch (value) {
211 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
212 if (value_is_a == 0) { /* b */
213 *v = &(d->ram[sp - 1]);
214 *sp_adjust -= 1;
215 *e_addr = sp - 1;
216 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v);
217 } else { /* a */
218 *v = &(d->ram[sp]);
219 *sp_adjust += 1;
220 *e_addr = sp;
221 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__, sp, **v);
222 }
223 break;
224
225 case 0x19: /* PEEK/[SP] */
226 *v = &(d->ram[sp]);
227 *e_addr = sp;
228 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
229 __func__,
230 sp,
231 **v);
232 break;
233
234 case 0x1a: /* PICK n */
235 *pc_adjust += 1;
236 *cycle_adjust += 1;
237 *e_addr = sp + value_data;
238 *v = d->ram + *e_addr;
239 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
240 __func__,
241 value_data,
242 sp + value_data,
243 **v);
244 break;
245
246 case 0x1b: /* SP */
247 *v = &(d->sp);
248 TRACE("%s>> %s (0x%04x)",
249 __func__,
250 "SP",
251 **v);
252 break;
253
254 case 0x1c: /* PC */
255 *v = &(d->pc);
256 TRACE("%s>> %s (0x%04x)",
257 __func__,
258 "PC",
259 **v);
260 break;
261
262 case 0x1d: /* EX */
263 *v = &(d->ex);
264 TRACE("%s>> %s (0x%04x)",
265 __func__,
266 "EX",
267 **v);
268 break;
269
270 case 0x1e: /* [next word] / [[pc++]] */
271 *pc_adjust += 1;
272 *cycle_adjust += 1;
273 *e_addr = value_data;
274 *v = d->ram + *e_addr;
275 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
276 __func__,
277 value_data,
278 **v);
279 break;
280
281 case 0x1f: /* next word (literal) / [pc++] */
282 *pc_adjust += 1;
283 *cycle_adjust += 1;
284 *work_v = value_data;
285 *v = work_v;
286 TRACE("%s>> nextword (0x%04x)",
287 __func__,
288 **v);
289 break;
290
291 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
292 *work_v = (value & 0x1f) - 1;
293 *v = work_v;
294 TRACE("%s>> literal (0x%04x)",
295 __func__,
296 **v);
297 }
298 }
299
300
301 #define OPCODE_NAME_LEN 16
302 struct opcode_entry {
303 unsigned short value;
304 char name[OPCODE_NAME_LEN];
305 void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD);
306 };
307
308 /* messy boilerplate for opcode handlers */
309
310 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
311 /* opcode does adjust stack and cycle count */
312
313 #define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
314
315 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
316 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
317 short pc_adjust = 0, sp_adjust = 0;\
318 unsigned int cycle_adjust = 0;\
319 do {\
320 op_type;\
321 value_decode_(d, val_a, 1, val_a_data,\
322 &d->reg_work_[1], &a, &ev_a_addr,\
323 &pc_adjust, &sp_adjust, &cycle_adjust);\
324 d->sp += sp_adjust;\
325 d->cycle += cycle_adjust;\
326 } while (0)
327 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
328 #define OP_BASIC_ value_decode_(d, val_b, 0, val_b_data,\
329 &d->reg_work_[0], &b, &ev_b_addr,\
330 &pc_adjust, &sp_adjust, &cycle_adjust)
331 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
332 #define OP_NBI(x) OP_TYPE(OP_NBI_)
333
334 /* after invoking one of these header macros, the instruction and operands will have been decoded, and the control registers have been adjusted to the next instruction et cetera */
335
336 /*
337 accounting helpers, these fire off the related callbacks for memory reads,
338 memory writes, and execution of reserved instructions
339 */
340 #define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
341 #define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
342 #define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
343
344 /* extended opcodes */
345
346 /*
347 N.B. this next function currently decodes values -- id est, it is
348 an opcode processing terminus; however, if 'future instruction set
349 expansion' happens, this will probably need to behave more like
350 the OP_IMPL(_nbi_) function which invoked it, if those instructions
351 have zero or differently styled operands.
352 */
353 OP_IMPL(nbi__reserved_) {
354 OP_NBI(nbi__reserved_);
355 /* reserved for future expansion */
356
357 /* fire an illegal instruction event for current instruction */
358 DCPU16_WORD future_opcode = (d->ram[d->pc - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
359 WARN("reserved future opcode 0x%04x invoked", future_opcode);
360
361 ACCT_ILL(d->pc - pc_adjust);
362 }
363
364 OP_IMPL(nbi_jsr) {
365 OP_NBI(nbi_jsr);
366 /* pushes the address of the next instruction to the stack, then sets PC to a */
367
368 ACCT_R(ev_a_addr);
369
370 d->ram[ --d->sp ] = d->pc;
371 d->pc = *a;
372
373 d->cycle += 2;
374
375 ACCT_W(d->sp + 1);
376 }
377
378 OP_IMPL(nbi__reserved2_) {
379 OP_NBI(nbi__reserved2_);
380 /* reserved */
381
382 WARN("reserved nbi opcode invoked");
383
384 ACCT_ILL(d->pc - pc_adjust);
385 }
386
387 OP_IMPL(nbi_int) {
388 OP_NBI(nbi_int);
389
390 ACCT_R(ev_a_addr);
391
392 if (d->ia) {
393 if ( interrupt_enqueue_(d, *a) ) {
394 WARN("failed to queue interrupt");
395 return;
396 }
397
398 if (d->interrupts_deferred_)
399 return;
400
401 d->interrupts_deferred_ = 1;
402 d->ram[--d->sp] = d->pc;
403 d->ram[--d->sp] = d->reg[reg_index_('A')];
404 d->pc = d->ia;
405 d->reg[0] = *a;
406 }
407
408 d->cycle += 4;
409 }
410
411 OP_IMPL(nbi_iag) {
412 OP_NBI(nbi_iag);
413
414 *a = d->ia;
415
416 ACCT_W(ev_a_addr);
417 }
418
419 OP_IMPL(nbi_ias) {
420 OP_NBI(nbi_ias);
421
422 d->ia = *a;
423
424 ACCT_R(ev_a_addr);
425 }
426
427 /* does this just ignore its operand? */
428 OP_IMPL(nbi_rfi) {
429 OP_NBI(nbi_rfi);
430
431 d->interrupts_deferred_ = 0;
432 d->reg[reg_index_('A')] = d->ram[d->sp++];
433 d->pc = d->ram[d->sp++];
434 }
435
436 OP_IMPL(nbi_iaq) {
437 OP_NBI(nbi_iaq);
438
439 if (*a) {
440 d->interrupts_deferred_ = 1;
441 } else {
442 d->interrupts_deferred_ = 0;
443 }
444
445 ACCT_R(ev_a_addr);
446 }
447
448 OP_IMPL(nbi_hwn) {
449 OP_NBI(nbi_hwn);
450
451 ACCT_W(ev_a_addr);
452
453 *a = d->hw_table_entries_;
454
455 d->cycle += 2;
456 }
457
458 OP_IMPL(nbi_hwq) {
459 OP_NBI(nbi_hwq);
460
461 ACCT_R(ev_a_addr);
462
463 if (*a >= d->hw_table_entries_) {
464 WARN("hardware query for non-extant device 0x%04x", *a);
465 d->reg[reg_index_('A')] = 0;
466 d->reg[reg_index_('B')] = 0;
467 d->reg[reg_index_('C')] = 0;
468 d->reg[reg_index_('X')] = 0;
469 d->reg[reg_index_('Y')] = 0;
470 return;
471 }
472
473 d->reg[reg_index_('A')] = d->hw_table_[*a].id_l;
474 d->reg[reg_index_('B')] = d->hw_table_[*a].id_h;
475 d->reg[reg_index_('C')] = d->hw_table_[*a].ver;
476 d->reg[reg_index_('X')] = d->hw_table_[*a].mfg_l;
477 d->reg[reg_index_('Y')] = d->hw_table_[*a].mfg_h;
478
479 d->cycle += 4;
480 }
481
482 OP_IMPL(nbi_hwi) {
483 OP_NBI(nbi_hwi);
484
485 ACCT_R(ev_a_addr);
486
487 if (*a > d->hw_table_entries_) {
488 WARN("interrupt for non-extant device 0x%04x", *a);
489 return;
490 }
491
492 d->cycle += 4;
493 d->hw_table_[*a].int_fn(d, d->hw_table_[*a].data);
494 }
495
496 OP_IMPL(nbi_hcf) {
497 OP_NBI(nbi_hcf);
498
499 ACCT_R(ev_a_addr);
500
501 d->on_fire_ = 1;
502 WARN("system on fire");
503
504 d->cycle += 9;
505 }
506
507 static const struct opcode_entry opcode_nbi_entries[] = {
508 {0x00, "(reserved)", op_nbi__reserved_},
509 {0x01, "JSR", op_nbi_jsr},
510 {0x02, "(reserved)", op_nbi__reserved2_},
511 {0x03, "(reserved)", op_nbi__reserved2_},
512 {0x04, "(reserved)", op_nbi__reserved2_},
513 {0x05, "(reserved)", op_nbi__reserved2_},
514 {0x06, "(reserved)", op_nbi__reserved2_},
515 {0x07, "HCF", op_nbi_hcf}, /* undocumented */
516 {0x08, "INT", op_nbi_int},
517 {0x09, "IAG", op_nbi_iag},
518 {0x0a, "IAS", op_nbi_ias},
519 {0x0b, "RFI", op_nbi_rfi},
520 {0x0c, "IAQ", op_nbi_iaq},
521 {0x0d, "(reserved)", op_nbi__reserved2_},
522 {0x0e, "(reserved)", op_nbi__reserved2_},
523 {0x0f, "(reserved)", op_nbi__reserved2_},
524 {0x10, "HWN", op_nbi_hwn},
525 {0x11, "HWQ", op_nbi_hwq},
526 {0x12, "HWI", op_nbi_hwi},
527 {0x13, "(reserved)", op_nbi__reserved2_},
528 {0x00, "", NULL}
529 };
530 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
531
532
533 /* basic opcodes */
534
535 /*
536 N.B. the following function does not decode values, (thus does not advance sp)
537 Decoding is handled by the secondary opcode functions it calls.
538 */
539 OP_IMPL(_nbi_) {
540 /* non-basic instruction */
541
542 /* don't do normal value decoding here */
543
544 DCPU16_WORD nbi_opcode = val_b;
545 const struct opcode_entry *e = opcode_nbi_entries;
546
547 (void)val_b_data;
548
549 e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
550
551 assert(e->impl != NULL);
552
553 TRACE(">> %s 0x%04x", e->name, val_b);
554 e->impl(d, 0, 0, val_a, val_a_data);
555 }
556
557 OP_IMPL(set) {
558 OP_BASIC(set);
559 /* sets b to a */
560
561 ACCT_R(ev_a_addr);
562
563 /*
564 if b is a literal, it's aimed at a scratch register,
565 so it's fine to update, as it won't have any effect.
566 */
567 *b = *a;
568
569 d->cycle += 1;
570
571 ACCT_W(ev_b_addr);
572 }
573
574 OP_IMPL(add) {
575 OP_BASIC(add);
576 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
577 unsigned int acc = *b + *a;
578
579 ACCT_R(ev_b_addr);
580 ACCT_R(ev_a_addr);
581
582 *b = acc;
583 d->ex = (acc > 0xffff);
584
585 d->cycle += 2;
586
587 ACCT_W(ev_b_addr);
588 }
589
590 OP_IMPL(sub) {
591 OP_BASIC(sub);
592 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
593 unsigned int acc = *b - *a;
594
595 ACCT_R(ev_b_addr);
596 ACCT_R(ev_a_addr);
597
598 *b = acc;
599 d->ex = (acc > 0xffff);
600
601 d->cycle += 2;
602
603 ACCT_W(ev_b_addr);
604 }
605
606 OP_IMPL(mul) {
607 OP_BASIC(mul);
608 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
609 unsigned int acc = *b * *a;
610
611 ACCT_R(ev_b_addr);
612 ACCT_R(ev_a_addr);
613
614 *b = acc;
615 d->ex = acc >> 16;
616
617 d->cycle += 2;
618
619 ACCT_W(ev_b_addr);
620 }
621
622 OP_IMPL(mli) {
623 OP_BASIC(mli);
624 /* sets b to b*a, signed */
625 int acc = (short)*b * (short)*a;
626
627 ACCT_R(ev_b_addr);
628 ACCT_R(ev_a_addr);
629
630 *b = acc;
631 d->ex = acc >> 16;
632
633 d->cycle += 2;
634
635 ACCT_W(ev_b_addr);
636 }
637
638 OP_IMPL(div) {
639 OP_BASIC(div);
640 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
641
642 ACCT_R(ev_b_addr);
643 ACCT_R(ev_a_addr);
644
645 if (*a == 0) {
646 *b = 0;
647 d->ex = 0;
648 } else {
649 *b = *b / *a;
650 d->ex = (*b << 16) / *a;
651 }
652
653 d->cycle += 3;
654
655 ACCT_W(ev_b_addr);
656 }
657
658 OP_IMPL(dvi) {
659 OP_BASIC(dvi);
660 /* sets b to b/a, signed, round towards 0 */
661
662 ACCT_R(ev_b_addr);
663 ACCT_R(ev_a_addr);
664
665 if (*a == 0) {
666 *b = 0;
667 d->ex = 0;
668 } else {
669 *b = (short)*b / (short)*a;
670 d->ex = (short)(*b << 16) / (short)*a;
671 }
672
673 d->cycle += 3;
674
675 ACCT_W(ev_b_addr);
676 }
677
678 OP_IMPL(mod) {
679 OP_BASIC(mod);
680 /* sets b to b%a. if a==0, sets b to 0 instead. */
681
682 ACCT_R(ev_b_addr);
683 ACCT_R(ev_a_addr);
684
685 if (*a == 0) {
686 *b = 0;
687 } else {
688 *b = *b % *a;
689 }
690
691 d->cycle += 3;
692
693 ACCT_W(ev_a_addr);
694 }
695
696 OP_IMPL(mdi) {
697 OP_BASIC(mdi);
698 /* sets b to b%a, signed */
699
700 ACCT_R(ev_b_addr);
701 ACCT_R(ev_a_addr);
702
703 if (*a == 0) {
704 *b = 0;
705 } else {
706 *b = (short)*b % (short)*a;
707 }
708
709 d->cycle += 3;
710
711 ACCT_W(ev_b_addr);
712 }
713
714 OP_IMPL(and) {
715 OP_BASIC(and);
716 /* sets b to b&a */
717
718 ACCT_R(ev_b_addr);
719 ACCT_R(ev_a_addr);
720
721 *b = *b & *a;
722
723 d->cycle += 1;
724
725 ACCT_W(ev_b_addr);
726 }
727
728 OP_IMPL(bor) {
729 OP_BASIC(bor);
730 /* sets b to b|a */
731
732 ACCT_R(ev_b_addr);
733 ACCT_R(ev_a_addr);
734
735 *b = *b | *a;
736
737 d->cycle += 1;
738
739 ACCT_W(ev_b_addr);
740 }
741
742 OP_IMPL(xor) {
743 OP_BASIC(xor);
744 /* sets b to b^a */
745
746 ACCT_R(ev_b_addr);
747 ACCT_R(ev_a_addr);
748
749 *b = *b ^ *a;
750
751 d->cycle += 1;
752
753 ACCT_W(ev_b_addr);
754 }
755
756 OP_IMPL(shr) {
757 OP_BASIC(shr);
758 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
759 unsigned int acc = *b >> *a;
760
761 ACCT_R(ev_b_addr);
762 ACCT_R(ev_a_addr);
763
764 *b = acc & 0xffff;
765 d->ex = (*b << 16) >> *a;
766
767 d->cycle += 2;
768
769 WARN("IMPLEMENT");
770
771 ACCT_W(ev_b_addr);
772 }
773
774 OP_IMPL(asr) {
775 OP_BASIC(asr);
776 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
777 unsigned int acc = *b << *a;
778
779 ACCT_R(ev_b_addr);
780 ACCT_R(ev_a_addr);
781
782 *b = acc & 0xffff;
783 d->ex = (*b << 16) >> *a;
784
785 d->cycle += 2;
786
787 WARN("IMPLEMENT");
788
789 ACCT_W(ev_b_addr);
790 }
791
792 OP_IMPL(shl) {
793 OP_BASIC(shl);
794 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
795 unsigned int acc = *b << *a;
796
797 ACCT_R(ev_b_addr);
798 ACCT_R(ev_a_addr);
799
800 *b = acc;
801
802 d->ex = acc >> 16;
803
804 d->cycle += 2;
805
806 ACCT_W(ev_b_addr);
807 }
808
809 OP_IMPL(ifb) {
810 OP_BASIC(ifb);
811 /* performs next instruction only if (b&a)!=0 */
812
813 ACCT_R(ev_b_addr);
814 ACCT_R(ev_a_addr);
815
816 if ((*b & *a) != 0) {
817 /* */
818 } else {
819 d->skip_ = 1;
820 d->cycle += 1;
821 }
822
823 d->cycle += 2;
824 }
825
826 OP_IMPL(ifc) {
827 OP_BASIC(ifc);
828 /* performs next instruction only if (b&a)==0 */
829
830 ACCT_R(ev_b_addr);
831 ACCT_R(ev_a_addr);
832
833 if ((*b & *a) == 0) {
834
835 } else {
836 d->skip_ = 1;
837 d->cycle += 1;
838 }
839
840 d->cycle += 2;
841 }
842
843 OP_IMPL(ife) {
844 OP_BASIC(ife);
845 /* performs next instruction only if b==a */
846
847 ACCT_R(ev_b_addr);
848 ACCT_R(ev_a_addr);
849
850 if (*b == *a) {
851 /* */
852 } else {
853 d->skip_ = 1;
854 d->cycle += 1;
855 }
856
857 d->cycle += 2;
858 }
859
860 OP_IMPL(ifn) {
861 OP_BASIC(ifn);
862 /* performs next instruction only if b!=a */
863
864 ACCT_R(ev_b_addr);
865 ACCT_R(ev_a_addr);
866
867 if (*b != *a) {
868 /* */
869 } else {
870 d->skip_ = 1;
871 d->cycle++;
872 }
873
874 d->cycle += 2;
875 }
876
877 OP_IMPL(ifg) {
878 OP_BASIC(ifg);
879 /* performs next instruction only if b>a */
880
881 ACCT_R(ev_b_addr);
882 ACCT_R(ev_a_addr);
883
884 if (*b > *a) {
885 /* */
886 } else {
887 d->skip_ = 1;
888 d->cycle++;
889 }
890
891 d->cycle += 2;
892 }
893
894 OP_IMPL(ifa) {
895 OP_BASIC(ifa);
896 /* performs next instruction only if b>a (signed) */
897
898 ACCT_R(ev_b_addr);
899 ACCT_R(ev_a_addr);
900
901 if (*b > *a) {
902 /* */
903 } else {
904 d->skip_ = 1;
905 d->cycle += 1;
906 }
907
908 d->cycle += 2;
909 }
910
911 OP_IMPL(ifl) {
912 OP_BASIC(ifl);
913 /* performs next instruction only if b<a */
914
915 ACCT_R(ev_b_addr);
916 ACCT_R(ev_a_addr);
917
918 if (*b < *a) {
919 /* */
920 } else {
921 d->skip_ = 1;
922 d->cycle++;
923 }
924
925 d->cycle += 2;
926 }
927
928 OP_IMPL(ifu) {
929 OP_BASIC(ifu);
930 /* performs next instruction only if b<a (signed) */
931
932 ACCT_R(ev_b_addr);
933 ACCT_R(ev_a_addr);
934
935 if (*b < *a) {
936 /* */
937 } else {
938 d->skip_ = 1;
939 d->cycle += 1;
940 }
941
942 d->cycle += 2;
943 }
944
945 OP_IMPL(adx) {
946 OP_BASIC(adx);
947 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
948 unsigned int acc;
949
950 ACCT_R(ev_b_addr);
951 ACCT_R(ev_a_addr);
952
953 acc = *b + *a + d->ex;
954 *b = acc & 0xffff;
955 if (acc > 0xffff)
956 d->ex = 0x0001;
957 else
958 d->ex = 0x0000;
959
960 d->cycle += 3;
961
962 ACCT_W(ev_b_addr);
963 }
964
965 OP_IMPL(sbx) {
966 OP_BASIC(sbx);
967 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
968 unsigned int acc;
969
970 ACCT_R(ev_b_addr);
971 ACCT_R(ev_a_addr);
972
973 acc = *b - *a + d->ex;
974 *b = acc & 0xffff;
975 if (acc > 0xffff)
976 d->ex = 0xffff;
977 else
978 d->ex = 0;
979
980 d->cycle += 3;
981
982 ACCT_W(ev_b_addr);
983 }
984
985 OP_IMPL(sti) {
986 OP_BASIC(sti);
987 /* sets b to a, then increases I and J by 1 */
988
989 ACCT_R(ev_b_addr);
990 ACCT_R(ev_a_addr);
991
992 *b = *a;
993 d->reg[reg_index_('I')] += 1;
994 d->reg[reg_index_('J')] += 1;
995
996 d->cycle += 2;
997
998 ACCT_W(ev_b_addr);
999 }
1000
1001 OP_IMPL(std) {
1002 OP_BASIC(std);
1003 /* sets b to a, then decreases I and J by 1 */
1004
1005 ACCT_R(ev_b_addr);
1006 ACCT_R(ev_a_addr);
1007
1008 *b = *a;
1009 d->reg[reg_index_('I')] -= 1;
1010 d->reg[reg_index_('J')] -= 1;
1011
1012 d->cycle += 2;
1013
1014 ACCT_W(ev_b_addr);
1015 }
1016
1017 OP_IMPL(_reserved_) {
1018 OP_BASIC(_reserved_);
1019
1020 WARN("reserved opcode invoked");
1021
1022 ACCT_ILL(d->pc - pc_adjust);
1023 }
1024
1025 static const struct opcode_entry opcode_basic_entries[] = {
1026 {0x00, "(nbi)", op__nbi_},
1027 {0x01, "SET", op_set },
1028 {0x02, "ADD", op_add },
1029 {0x03, "SUB", op_sub },
1030 {0x04, "MUL", op_mul },
1031 {0x05, "MLI", op_mli },
1032 {0x06, "DIV", op_div },
1033 {0x07, "DVI", op_dvi },
1034 {0x08, "MOD", op_mod },
1035 {0x09, "MDI", op_mdi },
1036 {0x0a, "AND", op_and },
1037 {0x0b, "BOR", op_bor },
1038 {0x0c, "XOR", op_xor },
1039 {0x0d, "SHR", op_shr },
1040 {0x0e, "ASR", op_asr },
1041 {0x0f, "SHL", op_shl },
1042 {0x10, "IFB", op_ifb },
1043 {0x11, "IFC", op_ifc },
1044 {0x12, "IFE", op_ife },
1045 {0x13, "IFN", op_ifn },
1046 {0x14, "IFG", op_ifg },
1047 {0x15, "IFA", op_ifa },
1048 {0x16, "IFL", op_ifl },
1049 {0x17, "IFU", op_ifu },
1050 {0x18, "(reserved)", op__reserved_ },
1051 {0x19, "(reserved)", op__reserved_ },
1052 {0x1a, "ADX", op_adx },
1053 {0x1b, "SBX", op_sbx },
1054 {0x1c, "(reserved)", op__reserved_ },
1055 {0x1d, "(reserved)", op__reserved_ },
1056 {0x1e, "STI", op_sti },
1057 {0x1f, "STD", op_std },
1058 {0x00, "", NULL }
1059 };
1060
1061 static inline
1062 void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword, unsigned int value_position) {
1063 if (value <= 0x07) {
1064 printf(" %c", regnames_[value]);
1065 } else if (value <= 0x0f) {
1066 printf(" [%c]", regnames_[value & 0x07]);
1067 } else if (value <= 0x17) {
1068 printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
1069 } else switch (value) {
1070 case 0x18:
1071 if (value_position == 0) { /* b */
1072 printf(" PUSH");
1073 } else {
1074 printf(" POP");
1075 }
1076 break;
1077 case 0x19: printf(" PEEK"); break;
1078 case 0x1a: printf(" PICK 0x%04x", nextword); break;
1079 case 0x1b: printf(" SP"); break;
1080 case 0x1c: printf(" PC"); break;
1081 case 0x1d: printf(" EX"); break;
1082 case 0x1e: printf(" [0x%04x]", nextword); break;
1083 case 0x1f: printf(" 0x%04x", nextword); break;
1084 default: printf(" 0x%02x", value - 0x21);
1085 }
1086 }
1087
1088
1089 /* split a sequence of (one to three) words into the components of an instruction */
1090 static inline
1091 void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr,
1092 DCPU16_WORD *opcode, DCPU16_WORD *b, DCPU16_WORD **b_data, DCPU16_WORD *a, DCPU16_WORD **a_data,
1093 DCPU16_WORD *instr_len) {
1094 *opcode = *a = *b = mem[addr];
1095 *opcode = mem[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
1096 *b = (mem[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_B_BITS) - 1);
1097 *a = (mem[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)) & ((1 << OPCODE_OPERAND_A_BITS) - 1);
1098 *instr_len = 1;
1099
1100 if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) {
1101 *b_data = mem + (DCPU16_WORD)(addr + *instr_len);
1102 TRACE("**b_data:%hu", **b_data);
1103 *instr_len += 1;
1104 } else {
1105 *b_data = NULL;
1106 }
1107
1108 if ( (*opcode != 0x0000 || (*opcode == 0 && *b != 0x0000) )
1109 && ( (*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) ) {
1110 *a_data = mem + (DCPU16_WORD)(addr + *instr_len);
1111 TRACE("**a_data:%hu", **a_data);
1112 *instr_len += 1;
1113 } else {
1114 *a_data = NULL;
1115 }
1116
1117 #if 0
1118 TRACE("\n%s: [0x%04x]:0x%04x op:0x%02x b:0x%02x (b_data:0x%04x) a:0x%02x (a_data:0x%04x) len:0x%02x\n",
1119 __func__,
1120 addr,
1121 mem[addr],
1122 *opcode,
1123 *b,
1124 *b_data ? **b_data : 0,
1125 *a,
1126 *a_data ? **a_data : 0,
1127 *instr_len);
1128 #endif
1129 }
1130
1131 /* dcpu16_mnemonify_buf
1132 print words as words
1133 */
1134 DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *buf) {
1135 DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
1136 const struct opcode_entry *e;
1137
1138 instruction_decode_(buf, 0, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1139
1140 if (opcode == 0x0000)
1141 e = opcode_nbi_entries +
1142 ( (b < OPCODE_NBI_MAX) ? b : (OPCODE_NBI_MAX - 1) );
1143 else
1144 e = opcode_basic_entries + opcode;
1145 printf("%s", e->name);
1146
1147 if (opcode) {
1148 dump_operand_value_(b, b_data ? *b_data : 0, 0);
1149 printf(",");
1150 }
1151
1152 if (opcode || b) {
1153 dump_operand_value_(a, a_data ? *a_data : 0, 1);
1154 }
1155
1156 return instr_len;
1157 }
1158
1159 /* dcpu16_disassemble_print
1160 print the words of the instruction at addr, followed by its assembly representation
1161 returns the length of the instruction in words
1162 */
1163 DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
1164 DCPU16_WORD opcode, b, a, instr_len, i, *b_data, *a_data;
1165 DCPU16_WORD buf[3] = { d->ram[addr], d->ram[(DCPU16_WORD)(addr + 1)], d->ram[(DCPU16_WORD)(addr + 2)] };
1166 unsigned int indent = 0;
1167 unsigned int partial = 0;
1168
1169 if (!d) return 0;
1170
1171 #if 0
1172 /*
1173 Check the previous instruction, to see if this one should be
1174 indented. This check isn't foolproof, as preceeding addresses
1175 could be data which happen to match instructions..
1176 */
1177 for (i = 3; i; i--) {
1178 instruction_decode_(d->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
1179 if (instr_len > i)
1180 partial++;
1181 if (instr_len == i
1182 && (opcode >= 0x10 && opcode <= 0x17) ) {
1183 indent++;
1184 break;
1185 }
1186 }
1187 #endif
1188
1189 /* just need instr_len */
1190 instruction_decode_(d->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1191
1192 /* show the raw words */
1193 printf("%04x", d->ram[addr]);
1194 for (i = 1; i < instr_len; i++) {
1195 printf(" %04x", d->ram[addr + i]);
1196 }
1197
1198 /* align things neatly, show the instruction */
1199 printf("%s%s ;%s%s",
1200 instr_len < 3 ? " " : "",
1201 instr_len < 2 ? " " : "",
1202 partial ? "*" : " ",
1203 indent ? " " : "");
1204
1205 dcpu16_mnemonify_buf(buf);
1206
1207 return instr_len;
1208 }
1209
1210 /* execute the next instruction */
1211 void dcpu16_step(struct dcpu16 *d) {
1212 DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
1213 const struct opcode_entry *e;
1214
1215 if (!d) return;
1216
1217 acct_event_(d, DCPU16_ACCT_EV_CYCLE, d->pc);
1218
1219 /* if we're currently servicing interrupts */
1220 if (d->interrupts_deferred_ == 0) {
1221 /* and there are interrupts to be serviced */
1222 if (d->interrupts_head_ != d->interrupts_tail_) {
1223 DCPU16_WORD message;
1224 message = interrupt_dequeue_(d);
1225
1226 if (d->ia) {
1227 TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", d->ia, message);
1228 /* then service the next interrupt */
1229 d->interrupts_deferred_ = 1;
1230 d->ram[--d->sp] = d->pc;
1231 d->ram[--d->sp] = d->reg[reg_index_('A')];
1232 d->pc = d->ia;
1233 d->reg[0] = message;
1234 } else {
1235 TRACE("ignoring interrupt IA:0");
1236 }
1237 }
1238 }
1239
1240 /* and make sure to execute an instruction after an interrupt */
1241
1242 instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1243
1244 for (e = opcode_basic_entries; e->impl; e++) {
1245 if (e->value == opcode) {
1246 TRACE("%s>> %s 0x%04x, 0x%04x", __func__, e->name, b, a);
1247 e->impl(d, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
1248 break;
1249 }
1250 }
1251
1252 /* get ready for the next one */
1253 d->pc += instr_len;
1254
1255 /* and jump over next instr(s) if needed */
1256 if (d->skip_) {
1257 instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1258 d->pc += instr_len;
1259 TRACE("++ SKIPPED %x words", instr_len);
1260 if (opcode >= 0x10 && opcode <= 0x17) {
1261 /* skipping a branch instruction? skip branch's skippable instruction as well */
1262 d->cycle += 1;
1263 instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1264 d->pc += instr_len;
1265 TRACE("++ SKIPPED %x words", instr_len);
1266 }
1267 d->skip_ = 0;
1268 }
1269 }
1270
1271 /*
1272 print the current state of the machine
1273 shows current cycle count, registers, and next instruction
1274 */
1275 void dcpu16_state_print(struct dcpu16 *d) {
1276 unsigned int i;
1277
1278 if (!d) return;
1279
1280 printf(" ");
1281 for (i = 0; i < 8; i++)
1282 printf(" %c:0x%04x", regnames_[i], d->reg[i]);
1283 printf("\n");
1284
1285 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1286 d->cycle,
1287 "EX", d->ex,
1288 "SP", d->sp,
1289 "PC", d->pc,
1290 "IA", d->ia,
1291 "PC");
1292
1293 dcpu16_disassemble_print(d, d->pc);
1294 printf("\n");
1295 }
1296
1297 /* dcpu16_dump_ram
1298 * print raw ram contents from start to stop
1299 */
1300 void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
1301 unsigned int i, j;
1302 const unsigned int n = 8; /* words per line */
1303
1304 if (!d) return;
1305
1306 for (i = start, j = 0; i <= end; i++, j++) {
1307 if (j % n == 0)
1308 printf("0x%04x:\t", i);
1309 printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
1310 }
1311 if ((j % n) != (n - 1))
1312 printf("\n");
1313 }
1314
1315 /* dcpu16_hw_add
1316 * registers new 'hardware' device with system
1317 */
1318 int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) {
1319 if (!vm || !hw)
1320 return -1;
1321
1322 if (vm->hw_table_entries_ == vm->hw_table_allocated_) {
1323 size_t new_entries = vm->hw_table_allocated_ + 32;
1324 void *tmp_ptr = realloc(vm->hw_table_, new_entries * sizeof * (vm->hw_table_));
1325 if (tmp_ptr == NULL) {
1326 fprintf(stderr, "%s():%s", "realloc", strerror(errno));
1327 return -1;
1328 }
1329 vm->hw_table_ = tmp_ptr;
1330 vm->hw_table_allocated_ += 32;
1331 }
1332
1333 memcpy(vm->hw_table_ + vm->hw_table_entries_, hw, sizeof *hw);
1334 vm->hw_table_entries_++;
1335
1336 return 0;
1337 }
1338
1339 /* dcpu16_acct_add
1340 * Register callback fn to be triggered whenever event matching any events
1341 * in bitwise mask occur.
1342 */
1343 int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) {
1344 struct dcpu16_acct_cb cb;
1345
1346 cb.mask = mask;
1347 cb.fn = fn;
1348 cb.data = data;
1349
1350 if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
1351 size_t new_entries = vm->cb_table_allocated_ + 32;
1352 void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
1353 if (tmp_ptr == NULL) {
1354 fprintf(stderr, "%s():%s", "realloc", strerror(errno));
1355 return -1;
1356 }
1357 vm->cb_table_ = tmp_ptr;
1358 vm->cb_table_allocated_ += 32;
1359 }
1360
1361 memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
1362 vm->cb_table_entries_++;
1363
1364 return 0;
1365 }
1366
1367 /* dcpu16_reset
1368 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1369 */
1370 void dcpu16_reset(struct dcpu16 *d) {
1371 if (!d) return;
1372
1373 d->cycle = 0;
1374 memset(d->reg, 0, sizeof d->reg);
1375 d->pc = 0;
1376 d->sp = 0;
1377 d->ex = 0;
1378 d->ia = 0;
1379 d->skip_ = 0;
1380 memset(d->ram, 0, sizeof d->ram);
1381
1382 acct_event_(d, DCPU16_ACCT_EV_RESET, 0);
1383 }
1384
1385 /* dcpu16_new
1386 * allocate a new dcpu16 instance
1387 */
1388 struct dcpu16 *dcpu16_new(void) {
1389 struct dcpu16 *vm;
1390
1391 vm = calloc(1, sizeof *vm);
1392 if (vm == NULL)
1393 WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
1394
1395 return vm;
1396 }
1397
1398 /* dcpu16_delete
1399 * release a dcpu16 instance
1400 */
1401 void dcpu16_delete(struct dcpu16 **vm) {
1402 if (!vm || !*vm) return;
1403
1404 free(*vm);
1405 *vm = NULL;
1406 }