13 * emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
14 * currently emulates '1.7' spec from http://pastebin.com/Q4JvQvnM
16 * I couldn't remember ever implementing an emulator before, so this
17 * happened. As such, consider this a toy in progress.
18 * There are likely many improvable aspects.
20 * Justin Wind <justin.wind@gmail.com>
21 * 2012 04 05 - implementation started
22 * 2012 05 05 - start of v1.7 revisions
23 * 2012 05 08 - v1.7 revisions mostly complete
26 * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
27 * !! v1.7 hardware interface needs to be finished
28 * !! v1.7 interrupts need to be finished
29 * change api to print into buffers rather than stdio
30 * let callbacks determine whether to override events, or just observe
31 * sort init callbacks by base addr, to call in-order
32 * make all callbacks register addr range of interest
35 static const char * const src_id_
= "$Id$";
37 #define OPCODE_BASIC_BITS 5
38 #define OPCODE_OPERAND_B_BITS 5
39 #define OPCODE_OPERAND_A_BITS 6
41 const char * const dcpu16_reg_names
[] = {
57 static void printf_null_(char *fmt
, ...) { (void)fmt
; }
59 /* some default warning and debug reporting functions, which can be overridden by clients */
60 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
61 static inline void warn_(char *fmt
, ...) __attribute__((format(printf
, 1, 2)));
63 void warn_(char *fmt
, ...) {
66 fprintf(stderr
, "[warning] ");
68 vfprintf(stderr
, fmt
, ap
);
70 fprintf(stderr
, "\n");
73 static void (*warn_cb_
)(char *fmt
, ...) = warn_
;
74 void dcpu16_warn_cb_set(void (*fn
)(char *fmt
, ...)) {
78 warn_cb_
= printf_null_
;
82 #define TRACE(...) do { if (trace_cb_) trace_cb_(__VA_ARGS__); } while (0)
83 static inline void trace_(char *fmt
, ...) __attribute__((format(printf
, 1, 2)));
85 void trace_(char *fmt
, ...) {
88 fprintf(stdout
, "[debug] ");
90 vfprintf(stdout
, fmt
, ap
);
92 fprintf(stdout
, "\n");
96 #define TRACE(...) do {} while(0)
98 static void (*trace_cb_
)(char *fmt
, ...) =
105 void dcpu16_trace_cb_set(void (*fn
)(char *fmt
, ...)) {
109 trace_cb_
= printf_null_
;
114 * invokes callbacks for specified event
117 void acct_event_(struct dcpu16
*vm
, dcpu16_acct_event ev
, DCPU16_WORD addr
) {
118 struct dcpu16_acct_cb
*cb
= vm
->cb_table_
;
121 for (i
= 0; i
< vm
->cb_table_entries_
; i
++) {
122 if ( (cb
[i
].mask
& ev
) )
123 if (addr
>= cb
[i
].addr_l
&& addr
<= cb
[i
].addr_h
)
124 cb
[i
].fn(vm
, ev
, addr
, cb
[i
].data
);
129 /* add an entry to the interrupt queue */
131 int interrupt_enqueue_(struct dcpu16
*vm
, DCPU16_WORD message
) {
132 vm
->interrupts_
[vm
->interrupts_tail_
] = message
;
133 vm
->interrupts_tail_
+= 1;
134 vm
->interrupts_tail_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
136 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
138 WARN("interrupt queue overflow (system is now on fire)");
146 DCPU16_WORD
interrupt_dequeue_(struct dcpu16
*vm
) {
149 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
150 WARN("interrupt underflow");
154 message
= vm
->interrupts_
[vm
->interrupts_head_
];
155 vm
->interrupts_head_
+= 1;
156 vm
->interrupts_head_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
162 void dcpu16_cycle_inc(struct dcpu16
*vm
, unsigned int n
) {
168 TRACE("%s>> starting cycle %llu", vm
->cycle_
);
170 /* signal interested cycle hooks */
171 acct_event_(vm
, DCPU16_ACCT_EV_CYCLE
, vm
->reg
[DCPU16_REG_PC
]);
173 /* signal attached hardware */
174 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
175 TRACE("%s>> notifying %s", __func__
, vm
->hw_table_
[i
].mod
->name_
);
176 if (vm
->hw_table_
[i
].mod
->cycle
)
177 vm
->hw_table_
[i
].mod
->cycle(vm
, &vm
->hw_table_
[i
]);
183 * sets *v to be the address of the represented value
184 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
185 * value_data is 'nextword' for this operand, ignored if unused
186 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
187 * e_addr is set to a referenced address, for accounting callback
188 * pc_adjust is set to how to change the program counter
189 * stack_adjust is set to how to change the stack pointer
190 * cycle_adjust is set to number of cycles spent looking up operand
192 * zero all adjustables before decoding first operand, and pass in these values when
193 * decoding next operand..
196 #define EWHAT_NONE (0)
197 #define EWHAT_REG (1<<1)
198 #define EWHAT_RAM (1<<2)
200 void value_decode_(struct dcpu16
*vm
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
201 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
, enum dcpu16_register_indexes
*e_reg
, unsigned int *e_what
,
202 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
203 assert(value
<= 0x3f);
205 DCPU16_WORD pc
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *pc_adjust
),
206 sp
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_SP
] + *sp_adjust
);
209 TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
216 if (value
<= 0x07) { /* register */
218 *e_reg
= value
& 0x07;
219 *v
= vm
->reg
+ *e_reg
;
220 TRACE("%s>> %s (0x%04x)",
222 dcpu16_reg_names
[value
],
227 if (value
<= 0x0f) { /* [register] */
229 *e_addr
= vm
->reg
[value
& 0x07];
230 *v
= &(vm
->ram
[ *e_addr
]);
231 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, value
& 0x07);
232 TRACE("%s>> [%s] [0x%04x] (0x%04x)",
234 dcpu16_reg_names
[value
& 0x07],
235 vm
->reg
[value
& 0x07],
240 if (value
<= 0x17) { /* [next word + register] */
241 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
242 *pc_adjust
+= 1; /* consume next word */
245 *e_addr
= value_data
+ vm
->reg
[value
& 0x07];
246 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, value
& 0x07);
247 *v
= vm
->ram
+ *e_addr
;
248 TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
250 dcpu16_reg_names
[value
& 0x07],
252 vm
->reg
[value
& 0x07],
258 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
260 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
261 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_SP
);
262 if (value_is_a
== 0) { /* b */
263 *v
= &(vm
->ram
[sp
- 1]);
266 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
271 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
275 case 0x19: /* PEEK/[SP] */
276 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
280 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
286 case 0x1a: /* PICK n */
287 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
291 *e_addr
= sp
+ value_data
;
292 *v
= vm
->ram
+ *e_addr
;
293 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
301 *e_reg
= DCPU16_REG_SP
;
303 *v
= &(vm
->reg
[DCPU16_REG_SP
]);
304 TRACE("%s>> %s (0x%04x)",
306 dcpu16_reg_names
[DCPU16_REG_SP
],
311 *e_reg
= DCPU16_REG_PC
;
313 *v
= &(vm
->reg
[DCPU16_REG_PC
]);
314 TRACE("%s>> %s (0x%04x)",
316 dcpu16_reg_names
[DCPU16_REG_PC
],
321 *e_reg
= DCPU16_REG_EX
;
323 *v
= &(vm
->reg
[DCPU16_REG_EX
]);
324 TRACE("%s>> %s (0x%04x)",
326 dcpu16_reg_names
[DCPU16_REG_EX
],
330 case 0x1e: /* [next word] / [[pc++]] */
332 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
335 *e_addr
= value_data
;
336 *v
= vm
->ram
+ *e_addr
;
337 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
343 case 0x1f: /* next word (literal) / [pc++] */
344 *e_what
= EWHAT_NONE
;
345 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
348 *work_v
= value_data
;
350 TRACE("%s>> nextword (0x%04x)",
355 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
356 *e_what
= EWHAT_NONE
;
357 *work_v
= (value
& 0x1f) - 1;
359 TRACE("%s>> literal (0x%04x)",
366 #define OPCODE_NAME_LEN 16
367 struct opcode_entry
{
368 unsigned short value
;
369 char name
[OPCODE_NAME_LEN
];
370 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
373 /* messy boilerplate for opcode handlers */
375 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
376 /* opcode does adjust stack and cycle count */
378 #define OP_IMPL(x) static void op_##x(struct dcpu16 *vm, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
380 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
381 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
382 enum dcpu16_register_indexes ev_a_reg = DCPU16_REG__NUM, ev_b_reg = DCPU16_REG__NUM;\
383 unsigned int ev_a_what = 0, ev_b_what = 0;\
384 short pc_adjust = 0, sp_adjust = 0;\
385 unsigned int cycle_adjust = 0;\
388 value_decode_(vm, val_a, 1, val_a_data,\
389 &vm->reg_work_[1], &a, &ev_a_addr, &ev_a_reg, &ev_a_what,\
390 &pc_adjust, &sp_adjust, &cycle_adjust);\
391 vm->reg[DCPU16_REG_SP] += sp_adjust;\
392 dcpu16_cycle_inc(vm, cycle_adjust);\
394 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data, (void)ev_b_reg, (void)ev_b_what
395 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
396 &vm->reg_work_[0], &b, &ev_b_addr, &ev_b_reg, &ev_b_what,\
397 &pc_adjust, &sp_adjust, &cycle_adjust)
398 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
399 #define OP_NBI(x) OP_TYPE(OP_NBI_)
401 /* 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 */
404 accounting helpers, these fire off the related callbacks for memory reads,
405 memory writes, and execution of reserved instructions
407 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
408 #define ACCT_RAM_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
409 #define ACCT_RAM_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
410 #define ACCT_REG_R(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_READ, reg); } while (0)
411 #define ACCT_REG_W(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, reg); } while (0)
413 #define ACCT_R(__x__) do {\
414 if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_R(ev_##__x__##_reg);\
415 if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_R(ev_##__x__##_addr);\
417 #define ACCT_W(__x__) do {\
418 if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_W(ev_##__x__##_reg);\
419 if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_W(ev_##__x__##_addr);\
422 /* extended opcodes */
425 N.B. this next function currently decodes values -- id est, it is
426 an opcode processing terminus; however, if 'future instruction set
427 expansion' happens, this will probably need to behave more like
428 the OP_IMPL(_nbi_) function which invoked it, if those instructions
429 have zero or differently styled operands.
431 OP_IMPL(nbi__reserved_
) {
432 OP_NBI(nbi__reserved_
);
433 /* reserved for future expansion */
435 /* fire an illegal instruction event for current instruction */
436 DCPU16_WORD future_opcode
= (vm
->ram
[vm
->reg
[DCPU16_REG_PC
] - pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
438 WARN("reserved future opcode 0x%04x invoked", future_opcode
);
440 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
442 dcpu16_cycle_inc(vm
, 1);
447 /* pushes the address of the next instruction to the stack, then sets PC to a */
451 ACCT_REG_R(DCPU16_REG_PC
);
452 ACCT_REG_R(DCPU16_REG_SP
);
453 vm
->ram
[ --vm
->reg
[DCPU16_REG_SP
] ] = vm
->reg
[DCPU16_REG_PC
];
454 ACCT_REG_W(DCPU16_REG_SP
);
455 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
457 vm
->reg
[DCPU16_REG_PC
] = *a
;
458 ACCT_REG_W(DCPU16_REG_PC
);
461 dcpu16_cycle_inc(vm
, 2);
464 OP_IMPL(nbi__reserved2_
) {
465 OP_NBI(nbi__reserved2_
);
468 WARN("reserved nbi opcode invoked");
470 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
472 dcpu16_cycle_inc(vm
, 1);
480 ACCT_REG_R(DCPU16_REG_IA
);
481 if (vm
->reg
[DCPU16_REG_IA
]) {
482 if ( interrupt_enqueue_(vm
, *a
) ) {
483 WARN("failed to queue interrupt");
487 if (vm
->interrupts_deferred_
)
490 vm
->interrupts_deferred_
= 1;
492 ACCT_REG_R(DCPU16_REG_PC
);
493 ACCT_REG_R(DCPU16_REG_SP
);
494 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
495 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
496 ACCT_REG_W(DCPU16_REG_SP
);
498 ACCT_REG_R(DCPU16_REG_A
);
499 ACCT_REG_R(DCPU16_REG_SP
);
500 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
501 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
502 ACCT_REG_W(DCPU16_REG_SP
);
504 ACCT_REG_R(DCPU16_REG_IA
);
505 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
506 ACCT_REG_W(DCPU16_REG_PC
);
508 vm
->reg
[DCPU16_REG_A
] = *a
;
509 ACCT_REG_W(DCPU16_REG_A
);
512 dcpu16_cycle_inc(vm
, 4);
518 ACCT_REG_R(DCPU16_REG_IA
);
519 *a
= vm
->reg
[DCPU16_REG_IA
];
523 dcpu16_cycle_inc(vm
, 1);
531 vm
->reg
[DCPU16_REG_IA
] = *a
;
532 ACCT_REG_W(DCPU16_REG_IA
);
534 dcpu16_cycle_inc(vm
, 1);
537 /* does this just ignore its operand? */
541 /* well, it consumes the argument, currently, so I guess pretend like we care */
544 vm
->interrupts_deferred_
= 0;
546 ACCT_REG_R(DCPU16_REG_SP
);
547 ACCT_RAM_R(vm
->reg
[DCPU16_REG_SP
]);
548 vm
->reg
[DCPU16_REG_A
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
549 ACCT_REG_W(DCPU16_REG_A
);
550 ACCT_REG_W(DCPU16_REG_SP
);
552 ACCT_REG_R(DCPU16_REG_SP
);
553 ACCT_RAM_R(vm
->reg
[DCPU16_REG_SP
]);
554 vm
->reg
[DCPU16_REG_PC
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
555 ACCT_REG_W(DCPU16_REG_PC
);
556 ACCT_REG_W(DCPU16_REG_SP
);
558 dcpu16_cycle_inc(vm
, 3);
567 vm
->interrupts_deferred_
= 1;
569 vm
->interrupts_deferred_
= 0;
572 dcpu16_cycle_inc(vm
, 2);
578 *a
= vm
->hw_table_entries_
;
581 dcpu16_cycle_inc(vm
, 2);
589 if (*a
>= vm
->hw_table_entries_
) {
590 WARN("hardware query for non-extant device 0x%04x", *a
);
591 vm
->reg
[DCPU16_REG_A
] = 0;
592 vm
->reg
[DCPU16_REG_B
] = 0;
593 vm
->reg
[DCPU16_REG_C
] = 0;
594 vm
->reg
[DCPU16_REG_X
] = 0;
595 vm
->reg
[DCPU16_REG_Y
] = 0;
597 vm
->reg
[DCPU16_REG_A
] = vm
->hw_table_
[*a
].mod
->id_l
;
598 vm
->reg
[DCPU16_REG_B
] = vm
->hw_table_
[*a
].mod
->id_h
;
599 vm
->reg
[DCPU16_REG_C
] = vm
->hw_table_
[*a
].mod
->ver
;
600 vm
->reg
[DCPU16_REG_X
] = vm
->hw_table_
[*a
].mod
->mfg_l
;
601 vm
->reg
[DCPU16_REG_Y
] = vm
->hw_table_
[*a
].mod
->mfg_h
;
604 ACCT_REG_W(DCPU16_REG_A
);
605 ACCT_REG_W(DCPU16_REG_B
);
606 ACCT_REG_W(DCPU16_REG_C
);
607 ACCT_REG_W(DCPU16_REG_X
);
608 ACCT_REG_W(DCPU16_REG_Y
);
610 dcpu16_cycle_inc(vm
, 4);
618 if (*a
> vm
->hw_table_entries_
) {
619 WARN("interrupt for non-extant device 0x%04x", *a
);
623 if (vm
->hw_table_
[*a
].mod
->hwi
)
624 vm
->hw_table_
[*a
].mod
->hwi(vm
, &vm
->hw_table_
[*a
]);
626 WARN("hardware 0x%04x has no interrupt handler", *a
);
628 dcpu16_cycle_inc(vm
, 4);
637 WARN("system on fire");
639 dcpu16_cycle_inc(vm
, 9);
642 static const struct opcode_entry opcode_nbi_entries
[] = {
643 {0x00, "(reserved)", op_nbi__reserved_
},
644 {0x01, "JSR", op_nbi_jsr
},
645 {0x02, "(reserved)", op_nbi__reserved2_
},
646 {0x03, "(reserved)", op_nbi__reserved2_
},
647 {0x04, "(reserved)", op_nbi__reserved2_
},
648 {0x05, "(reserved)", op_nbi__reserved2_
},
649 {0x06, "(reserved)", op_nbi__reserved2_
},
650 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
651 {0x08, "INT", op_nbi_int
},
652 {0x09, "IAG", op_nbi_iag
},
653 {0x0a, "IAS", op_nbi_ias
},
654 {0x0b, "RFI", op_nbi_rfi
},
655 {0x0c, "IAQ", op_nbi_iaq
},
656 {0x0d, "(reserved)", op_nbi__reserved2_
},
657 {0x0e, "(reserved)", op_nbi__reserved2_
},
658 {0x0f, "(reserved)", op_nbi__reserved2_
},
659 {0x10, "HWN", op_nbi_hwn
},
660 {0x11, "HWQ", op_nbi_hwq
},
661 {0x12, "HWI", op_nbi_hwi
},
662 {0x13, "(reserved)", op_nbi__reserved2_
},
665 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
671 N.B. the following function does not decode values.
672 Decoding is handled by the secondary opcode functions it calls.
675 /* non-basic instruction */
677 /* don't do normal value decoding here */
679 DCPU16_WORD nbi_opcode
= val_b
;
680 const struct opcode_entry
*e
= opcode_nbi_entries
;
684 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
686 assert(e
->impl
!= NULL
);
688 TRACE(">> %s 0x%04x", e
->name
, val_b
);
689 e
->impl(vm
, 0, 0, val_a
, val_a_data
);
699 if b is a literal, it's aimed at a scratch register,
700 so it's fine to update, as it won't have any effect.
706 dcpu16_cycle_inc(vm
, 1);
711 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
712 unsigned int acc
= *b
+ *a
;
718 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
720 ACCT_REG_W(DCPU16_REG_EX
);
724 dcpu16_cycle_inc(vm
, 2);
729 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
730 unsigned int acc
= *b
- *a
;
736 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
738 ACCT_REG_W(DCPU16_REG_EX
);
742 dcpu16_cycle_inc(vm
, 2);
747 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
748 unsigned int acc
= *b
* *a
;
754 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
756 ACCT_REG_W(DCPU16_REG_EX
);
760 dcpu16_cycle_inc(vm
, 2);
765 /* sets b to b*a, signed */
766 int acc
= (short)*b
* (short)*a
;
772 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
774 ACCT_REG_W(DCPU16_REG_EX
);
778 dcpu16_cycle_inc(vm
, 2);
783 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
790 vm
->reg
[DCPU16_REG_EX
] = 0;
793 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) / *a
;
796 ACCT_REG_W(DCPU16_REG_EX
);
800 dcpu16_cycle_inc(vm
, 3);
805 /* sets b to b/a, signed, round towards 0 */
812 vm
->reg
[DCPU16_REG_EX
] = 0;
814 *b
= (short)*b
/ (short)*a
;
815 vm
->reg
[DCPU16_REG_EX
] = (short)(*b
<< 16) / (short)*a
;
818 ACCT_REG_W(DCPU16_REG_EX
);
821 dcpu16_cycle_inc(vm
, 3);
826 /* sets b to b%a. if a==0, sets b to 0 instead. */
839 dcpu16_cycle_inc(vm
, 3);
844 /* sets b to b%a, signed */
852 *b
= (short)*b
% (short)*a
;
857 dcpu16_cycle_inc(vm
, 3);
871 dcpu16_cycle_inc(vm
, 1);
885 dcpu16_cycle_inc(vm
, 1);
897 dcpu16_cycle_inc(vm
, 1);
904 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
905 unsigned int acc
= *b
>> *a
;
911 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
915 ACCT_REG_W(DCPU16_REG_EX
);
918 dcpu16_cycle_inc(vm
, 2);
923 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
924 unsigned int acc
= *b
<< *a
;
930 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
934 ACCT_REG_W(DCPU16_REG_EX
);
937 dcpu16_cycle_inc(vm
, 2);
942 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
943 unsigned int acc
= *b
<< *a
;
950 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
952 ACCT_REG_W(DCPU16_REG_EX
);
955 dcpu16_cycle_inc(vm
, 2);
960 /* performs next instruction only if (b&a)!=0 */
965 if ((*b
& *a
) != 0) {
969 dcpu16_cycle_inc(vm
, 1);
972 dcpu16_cycle_inc(vm
, 2);
977 /* performs next instruction only if (b&a)==0 */
982 if ((*b
& *a
) == 0) {
986 dcpu16_cycle_inc(vm
, 1);
989 dcpu16_cycle_inc(vm
, 2);
994 /* performs next instruction only if b==a */
1003 dcpu16_cycle_inc(vm
, 1);
1006 dcpu16_cycle_inc(vm
, 2);
1011 /* performs next instruction only if b!=a */
1020 dcpu16_cycle_inc(vm
, 1);
1023 dcpu16_cycle_inc(vm
, 2);
1028 /* performs next instruction only if b>a */
1037 dcpu16_cycle_inc(vm
, 1);
1040 dcpu16_cycle_inc(vm
, 2);
1045 /* performs next instruction only if b>a (signed) */
1054 dcpu16_cycle_inc(vm
, 1);
1057 dcpu16_cycle_inc(vm
, 2);
1062 /* performs next instruction only if b<a */
1071 dcpu16_cycle_inc(vm
, 1);
1074 dcpu16_cycle_inc(vm
, 2);
1079 /* performs next instruction only if b<a (signed) */
1088 dcpu16_cycle_inc(vm
, 1);
1091 dcpu16_cycle_inc(vm
, 2);
1096 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
1102 ACCT_REG_R(DCPU16_REG_EX
);
1104 acc
= *b
+ *a
+ vm
->reg
[DCPU16_REG_EX
];
1107 vm
->reg
[DCPU16_REG_EX
] = 0x0001;
1109 vm
->reg
[DCPU16_REG_EX
] = 0x0000;
1111 ACCT_REG_W(DCPU16_REG_EX
);
1114 dcpu16_cycle_inc(vm
, 3);
1119 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
1125 ACCT_REG_R(DCPU16_REG_EX
);
1127 acc
= *b
- *a
+ vm
->reg
[DCPU16_REG_EX
];
1130 vm
->reg
[DCPU16_REG_EX
] = 0xffff;
1132 vm
->reg
[DCPU16_REG_EX
] = 0;
1134 ACCT_REG_W(DCPU16_REG_EX
);
1138 dcpu16_cycle_inc(vm
, 3);
1143 /* sets b to a, then increases I and J by 1 */
1149 vm
->reg
[DCPU16_REG_I
] += 1;
1150 vm
->reg
[DCPU16_REG_J
] += 1;
1152 ACCT_REG_W(DCPU16_REG_I
);
1153 ACCT_REG_W(DCPU16_REG_J
);
1157 dcpu16_cycle_inc(vm
, 2);
1162 /* sets b to a, then decreases I and J by 1 */
1168 vm
->reg
[DCPU16_REG_I
] -= 1;
1169 vm
->reg
[DCPU16_REG_J
] -= 1;
1171 ACCT_REG_W(DCPU16_REG_I
);
1172 ACCT_REG_W(DCPU16_REG_J
);
1176 dcpu16_cycle_inc(vm
, 2);
1179 OP_IMPL(_reserved_
) {
1180 OP_BASIC(_reserved_
);
1182 WARN("reserved opcode invoked");
1184 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
1187 static const struct opcode_entry opcode_basic_entries
[] = {
1188 {0x00, "(nbi)", op__nbi_
},
1189 {0x01, "SET", op_set
},
1190 {0x02, "ADD", op_add
},
1191 {0x03, "SUB", op_sub
},
1192 {0x04, "MUL", op_mul
},
1193 {0x05, "MLI", op_mli
},
1194 {0x06, "DIV", op_div
},
1195 {0x07, "DVI", op_dvi
},
1196 {0x08, "MOD", op_mod
},
1197 {0x09, "MDI", op_mdi
},
1198 {0x0a, "AND", op_and
},
1199 {0x0b, "BOR", op_bor
},
1200 {0x0c, "XOR", op_xor
},
1201 {0x0d, "SHR", op_shr
},
1202 {0x0e, "ASR", op_asr
},
1203 {0x0f, "SHL", op_shl
},
1204 {0x10, "IFB", op_ifb
},
1205 {0x11, "IFC", op_ifc
},
1206 {0x12, "IFE", op_ife
},
1207 {0x13, "IFN", op_ifn
},
1208 {0x14, "IFG", op_ifg
},
1209 {0x15, "IFA", op_ifa
},
1210 {0x16, "IFL", op_ifl
},
1211 {0x17, "IFU", op_ifu
},
1212 {0x18, "(reserved)", op__reserved_
},
1213 {0x19, "(reserved)", op__reserved_
},
1214 {0x1a, "ADX", op_adx
},
1215 {0x1b, "SBX", op_sbx
},
1216 {0x1c, "(reserved)", op__reserved_
},
1217 {0x1d, "(reserved)", op__reserved_
},
1218 {0x1e, "STI", op_sti
},
1219 {0x1f, "STD", op_std
},
1224 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1225 if (value
<= 0x07) {
1226 printf(" %s", dcpu16_reg_names
[value
]);
1227 } else if (value
<= 0x0f) {
1228 printf(" [%s]", dcpu16_reg_names
[value
& 0x07]);
1229 } else if (value
<= 0x17) {
1230 printf(" [0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1231 } else switch (value
) {
1233 if (value_position
== 0) { /* b */
1239 case 0x19: printf(" PEEK"); break;
1240 case 0x1a: printf(" PICK 0x%04x", nextword
); break;
1241 case 0x1b: printf(" SP"); break;
1242 case 0x1c: printf(" PC"); break;
1243 case 0x1d: printf(" EX"); break;
1244 case 0x1e: printf(" [0x%04x]", nextword
); break;
1245 case 0x1f: printf(" 0x%04x", nextword
); break;
1246 default: printf(" 0x%02x", value
- 0x21);
1251 /* split a sequence of (one to three) words into the components of an instruction */
1253 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1254 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1255 DCPU16_WORD
*instr_len
) {
1256 *opcode
= *a
= *b
= mem
[addr
];
1257 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1258 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1259 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1262 if ((*opcode
!= 0x0000) &&
1263 ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) ) {
1264 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1270 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1271 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1272 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1278 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",
1284 *b_data
? **b_data
: 0,
1286 *a_data
? **a_data
: 0,
1290 /* dcpu16_mnemonify_buf
1291 print words as words
1293 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1294 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1295 const struct opcode_entry
*e
;
1297 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1299 if (opcode
== 0x0000)
1300 e
= opcode_nbi_entries
+
1301 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1303 e
= opcode_basic_entries
+ opcode
;
1304 printf("%s", e
->name
);
1307 dump_operand_value_(b
, b_data
? *b_data
: 0, 0);
1312 dump_operand_value_(a
, a_data
? *a_data
: 0, 1);
1318 /* dcpu16_disassemble_print
1319 print the words of the instruction at addr, followed by its assembly representation
1320 returns the length of the instruction in words
1322 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*vm
, DCPU16_WORD addr
) {
1323 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1324 DCPU16_WORD buf
[3] = { vm
->ram
[addr
], vm
->ram
[(DCPU16_WORD
)(addr
+ 1)], vm
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1325 unsigned int indent
= 0;
1326 unsigned int partial
= 0;
1332 Check the previous instruction, to see if this one should be
1333 indented. This check isn't foolproof, as preceeding addresses
1334 could be data which happen to match instructions..
1336 for (i
= 3; i
; i
--) {
1337 instruction_decode_(vm
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1341 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1348 /* just need instr_len */
1349 instruction_decode_(vm
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1351 /* show the raw words */
1352 printf("%04x", vm
->ram
[addr
]);
1353 for (i
= 1; i
< instr_len
; i
++) {
1354 printf(" %04x", vm
->ram
[addr
+ i
]);
1357 /* align things neatly, show the instruction */
1358 printf("%s%s ;%s%s",
1359 instr_len
< 3 ? " " : "",
1360 instr_len
< 2 ? " " : "",
1361 partial
? "*" : " ",
1364 dcpu16_mnemonify_buf(buf
);
1369 int dcpu16_interrupt(struct dcpu16
*vm
, DCPU16_WORD message
) {
1370 TRACE("%s>> message:0x%04x", __func__
, message
);
1371 return interrupt_enqueue_(vm
, message
);
1374 /* execute the next instruction */
1375 void dcpu16_step(struct dcpu16
*vm
) {
1376 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1377 const struct opcode_entry
*e
;
1382 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1384 /* consume what we decoded */
1385 /* this happens immediately as PC might be re-set as an operation */
1386 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1388 /* run the operation */
1389 for (e
= opcode_basic_entries
; e
->impl
; e
++) {
1390 if (e
->value
== opcode
) {
1391 TRACE("%s>> %s 0x%04x, 0x%04x", __func__
, e
->name
, b
, a
);
1392 e
->impl(vm
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1397 /* and jump over next instr(s) if needed */
1399 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1400 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1401 TRACE("++ SKIPPED %x words", instr_len
);
1402 if (opcode
>= 0x10 && opcode
<= 0x17) {
1403 /* skipping a branch instruction? skip branch's skippable instruction as well */
1404 dcpu16_cycle_inc(vm
, 1);
1410 /* if we're currently servicing interrupts */
1411 if (vm
->interrupts_deferred_
== 0) {
1412 /* and there are interrupts to be serviced */
1413 if (vm
->interrupts_head_
!= vm
->interrupts_tail_
) {
1414 DCPU16_WORD message
;
1415 message
= interrupt_dequeue_(vm
);
1417 TRACE("%s>> %s interrupt IA:0x%04x message:0x%04x",
1419 vm
->reg
[DCPU16_REG_IA
] ? "servicing" : "ignoring",
1420 vm
->reg
[DCPU16_REG_IA
],
1422 if (vm
->reg
[DCPU16_REG_IA
]) {
1423 /* then service the next interrupt */
1424 vm
->interrupts_deferred_
= 1;
1425 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
1426 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
1427 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
1428 vm
->reg
[DCPU16_REG_A
] = message
;
1435 /* instantiate a new 'hardware' device */
1436 struct dcpu16_hw
*dcpu16_hw_new(struct dcpu16
*vm
, struct dcpu16_hw_module
*mod
, void *data
) {
1437 struct dcpu16_hw
*hw
;
1439 hw
= malloc(sizeof *hw
);
1441 vm
->warn_cb_("%s():%s", "malloc", strerror(errno
));
1447 if (mod
->data_init
) {
1448 if (mod
->data_init(hw
, data
)) {
1449 vm
->warn_cb_("failed to init hw module data");
1460 /* destroy a 'hardware' device */
1461 void dcpu16_hw_del(struct dcpu16_hw
**hw
) {
1464 if ((*hw
)->mod
->data_free
) {
1465 (*hw
)->mod
->data_free(*hw
);
1474 * invokes per-module controls for hw device
1476 int dcpu16_hw_ctl(struct dcpu16_hw
*hw
, const char *cmd
, void *data_in
, void *data_out
) {
1481 return hw
->mod
->ctl(hw
, cmd
, data_in
, data_out
);
1491 * registers new 'hardware' device with system
1493 int dcpu16_hw_attach(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1497 TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1500 hw
->mod
->id_h
, hw
->mod
->id_l
,
1501 hw
->mod
->mfg_l
, hw
->mod
->mfg_h
,
1504 if (vm
->hw_table_entries_
== 0xffff) {
1505 WARN("maximum hardware entries reached");
1509 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1510 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1511 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1512 if (tmp_ptr
== NULL
) {
1513 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1516 vm
->hw_table_
= tmp_ptr
;
1517 vm
->hw_table_allocated_
+= 32;
1520 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1521 vm
->hw_table_entries_
++;
1523 TRACE("%s>> added hw entry %zu", __func__
, vm
->hw_table_entries_
);
1529 * Register callback fn to be triggered whenever event matching any events
1530 * in bitwise mask occur.
1532 int dcpu16_acct_add(struct dcpu16
*vm
, dcpu16_acct_event mask
, dcpu16_ev_cb_t
*fn
, DCPU16_WORD addr_l
, DCPU16_WORD addr_h
, void *data
) {
1533 struct dcpu16_acct_cb cb
= {
1550 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1551 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1552 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1553 if (tmp_ptr
== NULL
) {
1554 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1557 vm
->cb_table_
= tmp_ptr
;
1558 vm
->cb_table_allocated_
+= 32;
1561 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1562 vm
->cb_table_entries_
++;
1564 TRACE("%s>> attached event callback %zu", __func__
, vm
->cb_table_entries_
);
1570 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1572 void dcpu16_reset(struct dcpu16
*vm
) {
1578 TRACE("%s>> reset", __func__
);
1581 vm
->interrupts_deferred_
= 0;
1583 memset(vm
->interrupts_
, 0, sizeof vm
->interrupts_
);
1584 vm
->interrupts_head_
= 0;
1585 vm
->interrupts_tail_
= 0;
1587 /* signal attached hardware */
1588 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1589 if (vm
->hw_table_
[i
].mod
->reset
)
1590 vm
->hw_table_
[i
].mod
->reset(vm
, &vm
->hw_table_
[i
]);
1593 memset(vm
->reg
, 0, sizeof vm
->reg
);
1594 memset(vm
->ram
, 0, sizeof vm
->ram
);
1597 acct_event_(vm
, DCPU16_ACCT_EV_RESET
, 0);
1601 * allocate a new dcpu16 instance
1603 struct dcpu16
*dcpu16_new(void) {
1606 vm
= calloc(1, sizeof *vm
);
1608 WARN("%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1610 vm
->warn_cb_
= warn_cb_
;
1611 vm
->trace_cb_
= trace_cb_
;
1617 * release a dcpu16 instance
1619 void dcpu16_delete(struct dcpu16
**vm
) {