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 #define MSG_(__level__, __vm__, ...) do { ((__vm__) ? ((struct dcpu16 *)(__vm__))->msg_cb_ : dcpu16_msg_)((__level__), __VA_ARGS__); } while (0)
58 #define MSG_INFO(__vm__,...) MSG_(DCPU16_MSG_INFO, __vm__, __VA_ARGS__)
59 #define MSG_ERROR(__vm__,...) MSG_(DCPU16_MSG_ERROR, __vm__, __VA_ARGS__)
61 #define MSG_DEBUG(__vm__,...) MSG_(DCPU16_MSG_DEBUG, __vm__, __VA_ARGS__)
63 #define MSG_DEBUG(__vm__,...) do {} while (0)
66 #define MSG_DEBUG_DECODE (DCPU16_MSG_DEBUG + 2)
67 #endif /* DEBUG_DECODE
69 /* messages could be sent nowhere */
70 static void msg_null_(unsigned int l
, char *fmt
, ...) { (void)l
, (void)fmt
; }
72 /* messages default to standard streams */
73 static void msg_default_(unsigned int, char *, ...) __attribute__((format(printf
, 2, 3)));
75 void msg_default_(unsigned int l
, char *fmt
, ...) {
76 static const char * const msg_tag_
[] = { "info", "error", "debug" };
77 FILE *f
= (l
<= DCPU16_MSG_INFO
) ? stderr
: stdout
;
80 if (l
< sizeof msg_tag_
/ sizeof *msg_tag_
)
81 fprintf(f
, "[%s] ", msg_tag_
[l
]);
83 fprintf(f
, "[%u] ", l
);
94 /* dcpu16 message callback
95 * This function pointer is copied into newly instantiated dcpu16 structures,
96 * and is invoked directly for messages independant of a dcpu16 context.
98 dcpu16_msg_cb_t
*dcpu16_msg_
= msg_default_
;
100 /* set a new default message callback */
101 /* returns the previous setting */
102 dcpu16_msg_cb_t
*dcpu16_msg_set_default(dcpu16_msg_cb_t
*msg_cb
) {
103 dcpu16_msg_cb_t
*r
= dcpu16_msg_
;
104 dcpu16_msg_
= msg_cb
? msg_cb
: msg_null_
;
107 /* set a new callback */
108 dcpu16_msg_cb_t
*dcpu16_msg_set(struct dcpu16
*vm
, dcpu16_msg_cb_t
*msg_cb
) {
109 dcpu16_msg_cb_t
*r
= vm
->msg_cb_
;
110 vm
->msg_cb_
= msg_cb
? msg_cb
: msg_null_
;
115 * invokes callbacks for specified event
118 void acct_event_(struct dcpu16
*vm
, dcpu16_acct_event ev
, DCPU16_WORD addr
) {
119 struct dcpu16_acct_cb
*cb
= vm
->cb_table_
;
122 for (i
= 0; i
< vm
->cb_table_entries_
; i
++) {
123 if ( (cb
[i
].mask
& ev
) )
124 if (addr
>= cb
[i
].addr_l
&& addr
<= cb
[i
].addr_h
)
125 cb
[i
].fn(vm
, ev
, addr
, cb
[i
].data
);
130 /* add an entry to the interrupt queue */
132 int interrupt_enqueue_(struct dcpu16
*vm
, DCPU16_WORD message
) {
133 vm
->interrupts_
[vm
->interrupts_tail_
] = message
;
134 vm
->interrupts_tail_
+= 1;
135 vm
->interrupts_tail_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
137 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
139 MSG_INFO(vm
, "interrupt queue overflow (system is now on fire)");
147 DCPU16_WORD
interrupt_dequeue_(struct dcpu16
*vm
) {
150 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
151 MSG_INFO(vm
, "interrupt underflow");
155 message
= vm
->interrupts_
[vm
->interrupts_head_
];
156 vm
->interrupts_head_
+= 1;
157 vm
->interrupts_head_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
163 void dcpu16_cycle_inc(struct dcpu16
*vm
, unsigned int n
) {
169 MSG_DEBUG(vm
, "%s>> starting cycle %llu", __func__
, vm
->cycle_
);
171 /* signal interested cycle hooks */
172 acct_event_(vm
, DCPU16_ACCT_EV_CYCLE
, vm
->reg
[DCPU16_REG_PC
]);
174 /* signal attached hardware */
175 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
176 MSG_DEBUG(vm
, "%s>> notifying %s", __func__
, vm
->hw_table_
[i
].mod
->name_
);
177 if (vm
->hw_table_
[i
].mod
->cycle
)
178 vm
->hw_table_
[i
].mod
->cycle(vm
, &vm
->hw_table_
[i
]);
184 * sets *v to be the address of the represented value
185 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
186 * value_data is 'nextword' for this operand, ignored if unused
187 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
188 * e_addr is set to a referenced address, for accounting callback
189 * pc_adjust is set to how to change the program counter
190 * stack_adjust is set to how to change the stack pointer
191 * cycle_adjust is set to number of cycles spent looking up operand
193 * zero all adjustables before decoding first operand, and pass in these values when
194 * decoding next operand..
197 #define EWHAT_NONE (0)
198 #define EWHAT_REG (1<<1)
199 #define EWHAT_RAM (1<<2)
201 void value_decode_(struct dcpu16
*vm
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
202 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
, enum dcpu16_register_indexes
*e_reg
, unsigned int *e_what
,
203 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
204 assert(value
<= 0x3f);
206 DCPU16_WORD pc
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *pc_adjust
),
207 sp
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_SP
] + *sp_adjust
);
212 MSG_(MSG_DEBUG_DECODE
, vm
,"%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
218 #endif /* DEBUG_DECODE */
220 if (value
<= 0x07) { /* register */
222 *e_reg
= value
& 0x07;
223 *v
= vm
->reg
+ *e_reg
;
226 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> %s (0x%04x)",
228 dcpu16_reg_names
[value
],
230 #endif /* DEBUG_DECODE */
235 if (value
<= 0x0f) { /* [register] */
237 *e_addr
= vm
->reg
[value
& 0x07];
238 *v
= &(vm
->ram
[ *e_addr
]);
239 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, value
& 0x07);
242 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> [%s] [0x%04x] (0x%04x)",
244 dcpu16_reg_names
[value
& 0x07],
245 vm
->reg
[value
& 0x07],
247 #endif /* DEBUG_DECODE */
252 if (value
<= 0x17) { /* [next word + register] */
253 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
254 *pc_adjust
+= 1; /* consume next word */
257 *e_addr
= value_data
+ vm
->reg
[value
& 0x07];
258 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, value
& 0x07);
259 *v
= vm
->ram
+ *e_addr
;
262 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
264 dcpu16_reg_names
[value
& 0x07],
266 vm
->reg
[value
& 0x07],
268 #endif /* DEBUG_DECODE */
274 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
276 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
277 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_SP
);
278 if (value_is_a
== 0) { /* b */
279 *v
= &(vm
->ram
[sp
- 1]);
284 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
285 #endif /* DEBUG_DECODE */
293 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
294 #endif /* DEBUG_DECODE */
299 case 0x19: /* PEEK/[SP] */
300 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
306 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> PEEK [0x%04x] (0x%04x)",
310 #endif /* DEBUG_DECODE */
313 case 0x1a: /* PICK n */
314 acct_event_(vm
, DCPU16_ACCT_EV_REG_READ
, DCPU16_REG_SP
);
318 *e_addr
= sp
+ value_data
;
319 *v
= vm
->ram
+ *e_addr
;
322 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> PICK 0x%04x [0x%04x] (0x%04x)",
327 #endif /* DEBUG_DECODE */
331 *e_reg
= DCPU16_REG_SP
;
333 *v
= &(vm
->reg
[DCPU16_REG_SP
]);
336 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> %s (0x%04x)",
338 dcpu16_reg_names
[DCPU16_REG_SP
],
340 #endif /* DEBUG_DECODE */
344 *e_reg
= DCPU16_REG_PC
;
346 *v
= &(vm
->reg
[DCPU16_REG_PC
]);
349 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> %s (0x%04x)",
351 dcpu16_reg_names
[DCPU16_REG_PC
],
353 #endif /* DEBUG_DECODE */
357 *e_reg
= DCPU16_REG_EX
;
359 *v
= &(vm
->reg
[DCPU16_REG_EX
]);
362 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> %s (0x%04x)",
364 dcpu16_reg_names
[DCPU16_REG_EX
],
366 #endif /* DEBUG_DECODE */
369 case 0x1e: /* [next word] / [[pc++]] */
371 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
374 *e_addr
= value_data
;
375 *v
= vm
->ram
+ *e_addr
;
378 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> [nextword] [0x%04x] (0x%04x)",
382 #endif /* DEBUG_DECODE */
385 case 0x1f: /* next word (literal) / [pc++] */
386 *e_what
= EWHAT_NONE
;
387 acct_event_(vm
, DCPU16_ACCT_EV_REG_WRITE
, DCPU16_REG_PC
);
390 *work_v
= value_data
;
394 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> nextword (0x%04x)",
397 #endif /* DEBUG_DECODE */
400 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
401 *e_what
= EWHAT_NONE
;
402 *work_v
= (value
& 0x1f) - 1;
406 MSG_(MSG_DEBUG_DECODE
, vm
, "%s>> literal (0x%04x)",
409 #endif /* DEBUG_DECODE */
414 #define OPCODE_NAME_LEN 16
415 struct opcode_entry
{
416 unsigned short value
;
417 char name
[OPCODE_NAME_LEN
];
418 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
421 /* messy boilerplate for opcode handlers */
423 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
424 /* opcode does adjust stack and cycle count */
426 #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)
428 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
429 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
430 enum dcpu16_register_indexes ev_a_reg = DCPU16_REG__NUM, ev_b_reg = DCPU16_REG__NUM;\
431 unsigned int ev_a_what = 0, ev_b_what = 0;\
432 short pc_adjust = 0, sp_adjust = 0;\
433 unsigned int cycle_adjust = 0;\
436 value_decode_(vm, val_a, 1, val_a_data,\
437 &vm->reg_work_[1], &a, &ev_a_addr, &ev_a_reg, &ev_a_what,\
438 &pc_adjust, &sp_adjust, &cycle_adjust);\
439 vm->reg[DCPU16_REG_SP] += sp_adjust;\
440 if (cycle_adjust) dcpu16_cycle_inc(vm, cycle_adjust);\
442 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data, (void)ev_b_reg, (void)ev_b_what
443 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
444 &vm->reg_work_[0], &b, &ev_b_addr, &ev_b_reg, &ev_b_what,\
445 &pc_adjust, &sp_adjust, &cycle_adjust)
446 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
447 #define OP_NBI(x) OP_TYPE(OP_NBI_)
449 /* 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 */
452 accounting helpers, these fire off the related callbacks for memory reads,
453 memory writes, and execution of reserved instructions
455 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
456 #define ACCT_RAM_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
457 #define ACCT_RAM_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
458 #define ACCT_REG_R(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_READ, reg); } while (0)
459 #define ACCT_REG_W(reg) do { acct_event_(vm, DCPU16_ACCT_EV_REG_WRITE, reg); } while (0)
461 #define ACCT_R(__x__) do {\
462 if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_R(ev_##__x__##_reg);\
463 if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_R(ev_##__x__##_addr);\
465 #define ACCT_W(__x__) do {\
466 if (ev_##__x__##_what & EWHAT_REG) ACCT_REG_W(ev_##__x__##_reg);\
467 if (ev_##__x__##_what & EWHAT_RAM) ACCT_RAM_W(ev_##__x__##_addr);\
470 /* extended opcodes */
473 N.B. this next function currently decodes values -- id est, it is
474 an opcode processing terminus; however, if 'future instruction set
475 expansion' happens, this will probably need to behave more like
476 the OP_IMPL(_nbi_) function which invoked it, if those instructions
477 have zero or differently styled operands.
479 OP_IMPL(nbi__reserved_
) {
480 OP_NBI(nbi__reserved_
);
481 /* reserved for future expansion */
483 /* fire an illegal instruction event for current instruction */
484 DCPU16_WORD future_opcode
= (vm
->ram
[vm
->reg
[DCPU16_REG_PC
] - pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
486 MSG_INFO(vm
, "reserved future opcode 0x%04x invoked", future_opcode
);
488 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
490 dcpu16_cycle_inc(vm
, 1);
495 /* pushes the address of the next instruction to the stack, then sets PC to a */
499 ACCT_REG_R(DCPU16_REG_PC
);
500 ACCT_REG_R(DCPU16_REG_SP
);
501 vm
->ram
[ --vm
->reg
[DCPU16_REG_SP
] ] = vm
->reg
[DCPU16_REG_PC
];
502 ACCT_REG_W(DCPU16_REG_SP
);
503 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
505 vm
->reg
[DCPU16_REG_PC
] = *a
;
506 ACCT_REG_W(DCPU16_REG_PC
);
509 dcpu16_cycle_inc(vm
, 2);
512 OP_IMPL(nbi__reserved2_
) {
513 OP_NBI(nbi__reserved2_
);
516 MSG_INFO(vm
, "reserved nbi opcode invoked");
518 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
520 dcpu16_cycle_inc(vm
, 1);
528 ACCT_REG_R(DCPU16_REG_IA
);
529 if (vm
->reg
[DCPU16_REG_IA
]) {
530 if ( interrupt_enqueue_(vm
, *a
) ) {
531 MSG_INFO(vm
, "failed to queue interrupt");
535 if (vm
->interrupts_deferred_
)
538 vm
->interrupts_deferred_
= 1;
540 ACCT_REG_R(DCPU16_REG_PC
);
541 ACCT_REG_R(DCPU16_REG_SP
);
542 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
543 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
544 ACCT_REG_W(DCPU16_REG_SP
);
546 ACCT_REG_R(DCPU16_REG_A
);
547 ACCT_REG_R(DCPU16_REG_SP
);
548 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
549 ACCT_RAM_W(vm
->reg
[DCPU16_REG_SP
] + 1);
550 ACCT_REG_W(DCPU16_REG_SP
);
552 ACCT_REG_R(DCPU16_REG_IA
);
553 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
554 ACCT_REG_W(DCPU16_REG_PC
);
556 vm
->reg
[DCPU16_REG_A
] = *a
;
557 ACCT_REG_W(DCPU16_REG_A
);
560 dcpu16_cycle_inc(vm
, 4);
566 ACCT_REG_R(DCPU16_REG_IA
);
567 *a
= vm
->reg
[DCPU16_REG_IA
];
571 dcpu16_cycle_inc(vm
, 1);
579 vm
->reg
[DCPU16_REG_IA
] = *a
;
580 ACCT_REG_W(DCPU16_REG_IA
);
582 dcpu16_cycle_inc(vm
, 1);
585 /* does this just ignore its operand? */
589 /* well, it consumes the argument, currently, so I guess pretend like we care */
592 vm
->interrupts_deferred_
= 0;
594 ACCT_REG_R(DCPU16_REG_SP
);
595 ACCT_RAM_R(vm
->reg
[DCPU16_REG_SP
]);
596 vm
->reg
[DCPU16_REG_A
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
597 ACCT_REG_W(DCPU16_REG_A
);
598 ACCT_REG_W(DCPU16_REG_SP
);
600 ACCT_REG_R(DCPU16_REG_SP
);
601 ACCT_RAM_R(vm
->reg
[DCPU16_REG_SP
]);
602 vm
->reg
[DCPU16_REG_PC
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
603 ACCT_REG_W(DCPU16_REG_PC
);
604 ACCT_REG_W(DCPU16_REG_SP
);
606 dcpu16_cycle_inc(vm
, 3);
615 vm
->interrupts_deferred_
= 1;
617 vm
->interrupts_deferred_
= 0;
620 dcpu16_cycle_inc(vm
, 2);
626 *a
= vm
->hw_table_entries_
;
629 dcpu16_cycle_inc(vm
, 2);
637 if (*a
>= vm
->hw_table_entries_
) {
638 MSG_INFO(vm
, "hardware query for non-extant device 0x%04x", *a
);
639 vm
->reg
[DCPU16_REG_A
] = 0;
640 vm
->reg
[DCPU16_REG_B
] = 0;
641 vm
->reg
[DCPU16_REG_C
] = 0;
642 vm
->reg
[DCPU16_REG_X
] = 0;
643 vm
->reg
[DCPU16_REG_Y
] = 0;
645 vm
->reg
[DCPU16_REG_A
] = vm
->hw_table_
[*a
].mod
->id_l
;
646 vm
->reg
[DCPU16_REG_B
] = vm
->hw_table_
[*a
].mod
->id_h
;
647 vm
->reg
[DCPU16_REG_C
] = vm
->hw_table_
[*a
].mod
->ver
;
648 vm
->reg
[DCPU16_REG_X
] = vm
->hw_table_
[*a
].mod
->mfg_l
;
649 vm
->reg
[DCPU16_REG_Y
] = vm
->hw_table_
[*a
].mod
->mfg_h
;
652 ACCT_REG_W(DCPU16_REG_A
);
653 ACCT_REG_W(DCPU16_REG_B
);
654 ACCT_REG_W(DCPU16_REG_C
);
655 ACCT_REG_W(DCPU16_REG_X
);
656 ACCT_REG_W(DCPU16_REG_Y
);
658 dcpu16_cycle_inc(vm
, 4);
666 if (*a
> vm
->hw_table_entries_
) {
667 MSG_INFO(vm
, "interrupt for non-extant device 0x%04x", *a
);
671 if (vm
->hw_table_
[*a
].mod
->hwi
)
672 vm
->hw_table_
[*a
].mod
->hwi(vm
, &vm
->hw_table_
[*a
]);
674 MSG_INFO(vm
, "hardware 0x%04x has no interrupt handler", *a
);
676 dcpu16_cycle_inc(vm
, 4);
685 MSG_INFO(vm
, "system on fire");
687 dcpu16_cycle_inc(vm
, 9);
690 static const struct opcode_entry opcode_nbi_entries
[] = {
691 {0x00, "(reserved)", op_nbi__reserved_
},
692 {0x01, "JSR", op_nbi_jsr
},
693 {0x02, "(reserved)", op_nbi__reserved2_
},
694 {0x03, "(reserved)", op_nbi__reserved2_
},
695 {0x04, "(reserved)", op_nbi__reserved2_
},
696 {0x05, "(reserved)", op_nbi__reserved2_
},
697 {0x06, "(reserved)", op_nbi__reserved2_
},
698 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
699 {0x08, "INT", op_nbi_int
},
700 {0x09, "IAG", op_nbi_iag
},
701 {0x0a, "IAS", op_nbi_ias
},
702 {0x0b, "RFI", op_nbi_rfi
},
703 {0x0c, "IAQ", op_nbi_iaq
},
704 {0x0d, "(reserved)", op_nbi__reserved2_
},
705 {0x0e, "(reserved)", op_nbi__reserved2_
},
706 {0x0f, "(reserved)", op_nbi__reserved2_
},
707 {0x10, "HWN", op_nbi_hwn
},
708 {0x11, "HWQ", op_nbi_hwq
},
709 {0x12, "HWI", op_nbi_hwi
},
710 {0x13, "(reserved)", op_nbi__reserved2_
},
713 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
719 N.B. the following function does not decode values.
720 Decoding is handled by the secondary opcode functions it calls.
723 /* non-basic instruction */
725 /* don't do normal value decoding here */
727 DCPU16_WORD nbi_opcode
= val_b
;
728 const struct opcode_entry
*e
= opcode_nbi_entries
;
732 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
734 assert(e
->impl
!= NULL
);
736 MSG_DEBUG(vm
, "%s>> %s 0x%04x", __func__
, e
->name
, val_b
);
737 e
->impl(vm
, 0, 0, val_a
, val_a_data
);
747 if b is a literal, it's aimed at a scratch register,
748 so it's fine to update, as it won't have any effect.
754 dcpu16_cycle_inc(vm
, 1);
759 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
760 unsigned int acc
= *b
+ *a
;
766 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
768 ACCT_REG_W(DCPU16_REG_EX
);
772 dcpu16_cycle_inc(vm
, 2);
777 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
778 unsigned int acc
= *b
- *a
;
784 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
786 ACCT_REG_W(DCPU16_REG_EX
);
790 dcpu16_cycle_inc(vm
, 2);
795 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
796 unsigned int acc
= *b
* *a
;
802 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
804 ACCT_REG_W(DCPU16_REG_EX
);
808 dcpu16_cycle_inc(vm
, 2);
813 /* sets b to b*a, signed */
814 int acc
= (short)*b
* (short)*a
;
820 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
822 ACCT_REG_W(DCPU16_REG_EX
);
826 dcpu16_cycle_inc(vm
, 2);
831 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
838 vm
->reg
[DCPU16_REG_EX
] = 0;
841 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) / *a
;
844 ACCT_REG_W(DCPU16_REG_EX
);
848 dcpu16_cycle_inc(vm
, 3);
853 /* sets b to b/a, signed, round towards 0 */
860 vm
->reg
[DCPU16_REG_EX
] = 0;
862 *b
= (short)*b
/ (short)*a
;
863 vm
->reg
[DCPU16_REG_EX
] = (short)(*b
<< 16) / (short)*a
;
866 ACCT_REG_W(DCPU16_REG_EX
);
869 dcpu16_cycle_inc(vm
, 3);
874 /* sets b to b%a. if a==0, sets b to 0 instead. */
887 dcpu16_cycle_inc(vm
, 3);
892 /* sets b to b%a, signed */
900 *b
= (short)*b
% (short)*a
;
905 dcpu16_cycle_inc(vm
, 3);
919 dcpu16_cycle_inc(vm
, 1);
933 dcpu16_cycle_inc(vm
, 1);
945 dcpu16_cycle_inc(vm
, 1);
952 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
953 unsigned int acc
= *b
>> *a
;
959 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
961 MSG_ERROR(vm
, "IMPLEMENT");
963 ACCT_REG_W(DCPU16_REG_EX
);
966 dcpu16_cycle_inc(vm
, 2);
971 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
972 unsigned int acc
= *b
<< *a
;
978 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
980 MSG_ERROR(vm
, "IMPLEMENT");
982 ACCT_REG_W(DCPU16_REG_EX
);
985 dcpu16_cycle_inc(vm
, 2);
990 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
991 unsigned int acc
= *b
<< *a
;
998 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
1000 ACCT_REG_W(DCPU16_REG_EX
);
1003 dcpu16_cycle_inc(vm
, 2);
1008 /* performs next instruction only if (b&a)!=0 */
1013 if ((*b
& *a
) != 0) {
1017 dcpu16_cycle_inc(vm
, 1);
1020 dcpu16_cycle_inc(vm
, 2);
1025 /* performs next instruction only if (b&a)==0 */
1030 if ((*b
& *a
) == 0) {
1034 dcpu16_cycle_inc(vm
, 1);
1037 dcpu16_cycle_inc(vm
, 2);
1042 /* performs next instruction only if b==a */
1051 dcpu16_cycle_inc(vm
, 1);
1054 dcpu16_cycle_inc(vm
, 2);
1059 /* performs next instruction only if b!=a */
1068 dcpu16_cycle_inc(vm
, 1);
1071 dcpu16_cycle_inc(vm
, 2);
1076 /* performs next instruction only if b>a */
1085 dcpu16_cycle_inc(vm
, 1);
1088 dcpu16_cycle_inc(vm
, 2);
1093 /* performs next instruction only if b>a (signed) */
1102 dcpu16_cycle_inc(vm
, 1);
1105 dcpu16_cycle_inc(vm
, 2);
1110 /* performs next instruction only if b<a */
1119 dcpu16_cycle_inc(vm
, 1);
1122 dcpu16_cycle_inc(vm
, 2);
1127 /* performs next instruction only if b<a (signed) */
1136 dcpu16_cycle_inc(vm
, 1);
1139 dcpu16_cycle_inc(vm
, 2);
1144 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
1150 ACCT_REG_R(DCPU16_REG_EX
);
1152 acc
= *b
+ *a
+ vm
->reg
[DCPU16_REG_EX
];
1155 vm
->reg
[DCPU16_REG_EX
] = 0x0001;
1157 vm
->reg
[DCPU16_REG_EX
] = 0x0000;
1159 ACCT_REG_W(DCPU16_REG_EX
);
1162 dcpu16_cycle_inc(vm
, 3);
1167 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
1173 ACCT_REG_R(DCPU16_REG_EX
);
1175 acc
= *b
- *a
+ vm
->reg
[DCPU16_REG_EX
];
1178 vm
->reg
[DCPU16_REG_EX
] = 0xffff;
1180 vm
->reg
[DCPU16_REG_EX
] = 0;
1182 ACCT_REG_W(DCPU16_REG_EX
);
1186 dcpu16_cycle_inc(vm
, 3);
1191 /* sets b to a, then increases I and J by 1 */
1197 vm
->reg
[DCPU16_REG_I
] += 1;
1198 vm
->reg
[DCPU16_REG_J
] += 1;
1200 ACCT_REG_W(DCPU16_REG_I
);
1201 ACCT_REG_W(DCPU16_REG_J
);
1205 dcpu16_cycle_inc(vm
, 2);
1210 /* sets b to a, then decreases I and J by 1 */
1216 vm
->reg
[DCPU16_REG_I
] -= 1;
1217 vm
->reg
[DCPU16_REG_J
] -= 1;
1219 ACCT_REG_W(DCPU16_REG_I
);
1220 ACCT_REG_W(DCPU16_REG_J
);
1224 dcpu16_cycle_inc(vm
, 2);
1227 OP_IMPL(_reserved_
) {
1228 OP_BASIC(_reserved_
);
1230 MSG_INFO(vm
, "reserved opcode invoked");
1232 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
1235 static const struct opcode_entry opcode_basic_entries
[] = {
1236 {0x00, "(nbi)", op__nbi_
},
1237 {0x01, "SET", op_set
},
1238 {0x02, "ADD", op_add
},
1239 {0x03, "SUB", op_sub
},
1240 {0x04, "MUL", op_mul
},
1241 {0x05, "MLI", op_mli
},
1242 {0x06, "DIV", op_div
},
1243 {0x07, "DVI", op_dvi
},
1244 {0x08, "MOD", op_mod
},
1245 {0x09, "MDI", op_mdi
},
1246 {0x0a, "AND", op_and
},
1247 {0x0b, "BOR", op_bor
},
1248 {0x0c, "XOR", op_xor
},
1249 {0x0d, "SHR", op_shr
},
1250 {0x0e, "ASR", op_asr
},
1251 {0x0f, "SHL", op_shl
},
1252 {0x10, "IFB", op_ifb
},
1253 {0x11, "IFC", op_ifc
},
1254 {0x12, "IFE", op_ife
},
1255 {0x13, "IFN", op_ifn
},
1256 {0x14, "IFG", op_ifg
},
1257 {0x15, "IFA", op_ifa
},
1258 {0x16, "IFL", op_ifl
},
1259 {0x17, "IFU", op_ifu
},
1260 {0x18, "(reserved)", op__reserved_
},
1261 {0x19, "(reserved)", op__reserved_
},
1262 {0x1a, "ADX", op_adx
},
1263 {0x1b, "SBX", op_sbx
},
1264 {0x1c, "(reserved)", op__reserved_
},
1265 {0x1d, "(reserved)", op__reserved_
},
1266 {0x1e, "STI", op_sti
},
1267 {0x1f, "STD", op_std
},
1270 #define OPCODE_BASIC_MAX (((sizeof(opcode_basic_entries)) / (sizeof(struct opcode_entry))) - 1)
1273 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1275 if (value
<= 0x07) {
1276 printf("%s", dcpu16_reg_names
[value
]);
1277 } else if (value
<= 0x0f) {
1278 printf("[%s]", dcpu16_reg_names
[value
& 0x07]);
1279 } else if (value
<= 0x17) {
1280 printf("[0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1281 } else switch (value
) {
1283 if (value_position
== 0) { /* b */
1289 case 0x19: printf("PEEK"); break;
1290 case 0x1a: printf("PICK 0x%04x", nextword
); break;
1291 case 0x1b: printf("SP"); break;
1292 case 0x1c: printf("PC"); break;
1293 case 0x1d: printf("EX"); break;
1294 case 0x1e: printf("[0x%04x]", nextword
); break;
1295 case 0x1f: printf("0x%04x", nextword
); break;
1296 default: printf("0x%02x", value
- 0x21);
1301 int operand_snprint_(char *buf
, size_t buf_sz
, DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int operand_is_a
) {
1304 len
= snprintf(buf
, buf_sz
, " ");
1305 if ((size_t)len
>= buf_sz
)
1308 buf
+= len
, buf_sz
-= len
;
1310 if (value
<= 0x07) {
1311 len
= snprintf(buf
, buf_sz
, "%s", dcpu16_reg_names
[value
]);
1312 } else if (value
<= 0x0f) {
1313 len
= snprintf(buf
, buf_sz
, "[%s]", dcpu16_reg_names
[value
& 0x07]);
1314 } else if (value
<= 0x17) {
1315 len
= snprintf(buf
, buf_sz
, "[0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1316 } else switch (value
) {
1318 if (operand_is_a
== 0) { /* b */
1319 len
= snprintf(buf
, buf_sz
, "PUSH");
1321 len
= snprintf(buf
, buf_sz
, "POP");
1324 case 0x19: len
= snprintf(buf
, buf_sz
, "PEEK"); break;
1325 case 0x1a: len
= snprintf(buf
, buf_sz
, "PICK 0x%04x", nextword
); break;
1326 case 0x1b: len
= snprintf(buf
, buf_sz
, "SP"); break;
1327 case 0x1c: len
= snprintf(buf
, buf_sz
, "PC"); break;
1328 case 0x1d: len
= snprintf(buf
, buf_sz
, "EX"); break;
1329 case 0x1e: len
= snprintf(buf
, buf_sz
, "[0x%04x]", nextword
); break;
1330 case 0x1f: len
= snprintf(buf
, buf_sz
, "0x%04x", nextword
); break;
1331 default: len
= snprintf(buf
, buf_sz
, "0x%02x", (short)(value
- 0x21));
1334 if ((size_t)len
>= buf_sz
)
1341 /* split a sequence of (one to three) words into the components of an instruction */
1343 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1344 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1345 DCPU16_WORD
*instr_len
) {
1346 *opcode
= *a
= *b
= mem
[addr
];
1347 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1348 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1349 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1352 if ((*opcode
!= 0x0000) &&
1353 ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) ) {
1354 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1360 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1361 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1362 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1369 MSG_(MSG_DEBUG_DECODE
, NULL
, "\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",
1375 *b_data
? **b_data
: 0,
1377 *a_data
? **a_data
: 0,
1379 #endif /* DEBUG_DECODE */
1382 /* dcpu16_mnemonify_buf
1383 print words as words
1385 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1386 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1387 const struct opcode_entry
*e
;
1390 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1392 if (opcode
== 0x0000)
1393 e
= opcode_nbi_entries
+
1394 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1396 e
= opcode_basic_entries
+ opcode
;
1397 printf("%s", e
->name
);
1400 operand_snprint_(operand
, sizeof operand
, b
, b_data
? *b_data
: 0, 0);
1401 printf("%s,", operand
);
1405 operand_snprint_(operand
, sizeof operand
, a
, a_data
? *a_data
: 0, 1);
1406 printf("%s", operand
);
1412 /* dcpu16_disassemble_print
1413 print the words of the instruction at addr, followed by its assembly representation
1414 returns the length of the instruction in words
1416 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*vm
, DCPU16_WORD addr
) {
1417 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1418 DCPU16_WORD buf
[3] = { vm
->ram
[addr
], vm
->ram
[(DCPU16_WORD
)(addr
+ 1)], vm
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1419 unsigned int indent
= 0;
1420 unsigned int partial
= 0;
1426 Check the previous instruction, to see if this one should be
1427 indented. This check isn't foolproof, as preceeding addresses
1428 could be data which happen to match instructions..
1430 for (i
= 3; i
; i
--) {
1431 instruction_decode_(vm
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1435 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1442 /* just need instr_len */
1443 instruction_decode_(vm
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1445 /* show the raw words */
1446 printf("%04x", vm
->ram
[addr
]);
1447 for (i
= 1; i
< instr_len
; i
++) {
1448 printf(" %04x", vm
->ram
[addr
+ i
]);
1451 /* align things neatly, show the instruction */
1452 printf("%s%s ;%s%s",
1453 instr_len
< 3 ? " " : "",
1454 instr_len
< 2 ? " " : "",
1455 partial
? "*" : " ",
1458 dcpu16_mnemonify_buf(buf
);
1463 int dcpu16_interrupt(struct dcpu16
*vm
, DCPU16_WORD message
) {
1464 MSG_DEBUG(vm
, "%s>> message:0x%04x", __func__
, message
);
1465 return interrupt_enqueue_(vm
, message
);
1468 /* execute the next instruction */
1469 void dcpu16_step(struct dcpu16
*vm
) {
1470 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1471 const struct opcode_entry
*e
;
1476 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1478 /* consume what we decoded */
1479 /* this happens immediately as PC might be re-set as an operation */
1480 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1482 /* run the operation */
1483 e
= opcode_basic_entries
+ opcode
;
1484 MSG_DEBUG(vm
, "%s", e
->name
? e
->name
: "???");
1485 e
->impl(vm
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1488 /* and jump over next instr(s) if needed */
1490 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1491 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1492 MSG_DEBUG(vm
, "%s>> ++ SKIPPED %x words", __func__
, instr_len
);
1493 if (opcode
>= 0x10 && opcode
<= 0x17) {
1494 /* skipping a branch instruction? skip branch's skippable instruction as well */
1495 dcpu16_cycle_inc(vm
, 1);
1501 /* if we're currently servicing interrupts */
1502 if (vm
->interrupts_deferred_
== 0) {
1503 /* and there are interrupts to be serviced */
1504 if (vm
->interrupts_head_
!= vm
->interrupts_tail_
) {
1505 DCPU16_WORD message
;
1506 message
= interrupt_dequeue_(vm
);
1508 MSG_DEBUG(vm
, "%s>> %s interrupt IA:0x%04x message:0x%04x",
1510 vm
->reg
[DCPU16_REG_IA
] ? "servicing" : "ignoring",
1511 vm
->reg
[DCPU16_REG_IA
],
1514 if (vm
->reg
[DCPU16_REG_IA
]) {
1515 /* then service the next interrupt */
1516 vm
->interrupts_deferred_
= 1;
1517 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
1518 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
1519 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
1520 vm
->reg
[DCPU16_REG_A
] = message
;
1527 /* instantiate a new 'hardware' device */
1528 struct dcpu16_hw
*dcpu16_hw_new(struct dcpu16
*vm
, struct dcpu16_hw_module
*mod
, void *data
) {
1529 struct dcpu16_hw
*hw
;
1531 MSG_DEBUG(vm
, "%s>> mod:%p data:%p", __func__
, mod
, data
);
1533 hw
= malloc(sizeof *hw
);
1535 MSG_ERROR(vm
, "%s():%s", "malloc", strerror(errno
));
1541 if (mod
->data_init
) {
1542 if (mod
->data_init(hw
, data
)) {
1543 MSG_ERROR(vm
, "failed to init hw module data");
1554 /* destroy a 'hardware' device */
1555 void dcpu16_hw_del(struct dcpu16_hw
**hw
) {
1558 MSG_DEBUG((*hw
)->vm
, "%s>> hw:%p",
1562 if ((*hw
)->mod
->data_free
) {
1563 (*hw
)->mod
->data_free(*hw
);
1572 * invokes per-module controls for hw device
1574 int dcpu16_hw_ctl(struct dcpu16_hw
*hw
, const char *cmd
, void *data_in
, void *data_out
) {
1578 MSG_DEBUG(hw
->vm
, "%s>> name:%s cmd:%s in:%p out:%p",
1588 return hw
->mod
->ctl(hw
, cmd
, data_in
, data_out
);
1598 * registers new 'hardware' device with system
1600 int dcpu16_hw_attach(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1604 MSG_DEBUG(vm
, "%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1607 hw
->mod
->id_h
, hw
->mod
->id_l
,
1608 hw
->mod
->mfg_l
, hw
->mod
->mfg_h
,
1611 if (vm
->hw_table_entries_
== 0xffff) {
1612 MSG_ERROR(vm
, "maximum hardware entries reached");
1616 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1617 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1618 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1619 if (tmp_ptr
== NULL
) {
1620 MSG_ERROR(vm
, "%s():%s", "realloc", strerror(errno
));
1623 vm
->hw_table_
= tmp_ptr
;
1624 vm
->hw_table_allocated_
+= 32;
1627 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1628 vm
->hw_table_entries_
++;
1630 MSG_DEBUG(vm
, "%s>> added hw entry %zu", __func__
, vm
->hw_table_entries_
);
1636 * Register callback fn to be triggered whenever event matching any events
1637 * in bitwise mask occur.
1639 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
) {
1640 struct dcpu16_acct_cb cb
= {
1657 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1658 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1659 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1660 if (tmp_ptr
== NULL
) {
1661 MSG_ERROR(vm
, "%s():%s", "realloc", strerror(errno
));
1664 vm
->cb_table_
= tmp_ptr
;
1665 vm
->cb_table_allocated_
+= 32;
1668 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1669 vm
->cb_table_entries_
++;
1671 MSG_DEBUG(vm
, "%s>> attached event callback %zu", __func__
, vm
->cb_table_entries_
);
1677 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1679 void dcpu16_reset(struct dcpu16
*vm
) {
1685 MSG_DEBUG(vm
, "%s>> reset", __func__
);
1688 vm
->interrupts_deferred_
= 0;
1690 memset(vm
->interrupts_
, 0, sizeof vm
->interrupts_
);
1691 vm
->interrupts_head_
= 0;
1692 vm
->interrupts_tail_
= 0;
1694 /* signal attached hardware */
1695 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1696 if (vm
->hw_table_
[i
].mod
->reset
)
1697 vm
->hw_table_
[i
].mod
->reset(vm
, &vm
->hw_table_
[i
]);
1700 memset(vm
->reg
, 0, sizeof vm
->reg
);
1701 memset(vm
->ram
, 0, sizeof vm
->ram
);
1704 acct_event_(vm
, DCPU16_ACCT_EV_RESET
, 0);
1708 * allocate a new dcpu16 instance
1710 struct dcpu16
*dcpu16_new(void) {
1713 vm
= calloc(1, sizeof *vm
);
1715 MSG_ERROR(NULL
, "%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1717 vm
->msg_cb_
= dcpu16_msg_
;
1723 * release a dcpu16 instance
1725 void dcpu16_delete(struct dcpu16
**vm
) {