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 cb
[i
].fn(vm
, ev
, addr
, cb
[i
].data
);
128 /* add an entry to the interrupt queue */
130 int interrupt_enqueue_(struct dcpu16
*vm
, DCPU16_WORD message
) {
131 vm
->interrupts_
[vm
->interrupts_tail_
] = message
;
132 vm
->interrupts_tail_
+= 1;
133 vm
->interrupts_tail_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
135 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
137 WARN("interrupt queue overflow (system is now on fire)");
145 DCPU16_WORD
interrupt_dequeue_(struct dcpu16
*vm
) {
148 if (vm
->interrupts_tail_
== vm
->interrupts_head_
) {
149 WARN("interrupt underflow");
153 message
= vm
->interrupts_
[vm
->interrupts_head_
];
154 vm
->interrupts_head_
+= 1;
155 vm
->interrupts_head_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
161 void dcpu16_cycle_inc(struct dcpu16
*vm
, unsigned int n
) {
167 TRACE("%s>> starting cycle %llu", vm
->cycle_
);
169 /* signal interested cycle hooks */
170 acct_event_(vm
, DCPU16_ACCT_EV_CYCLE
, vm
->reg
[DCPU16_REG_PC
]);
172 /* signal attached hardware */
173 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
174 TRACE("%s>> notifying %s", __func__
, vm
->hw_table_
[i
].name_
);
175 vm
->hw_table_
[i
].cycle(vm
, &vm
->hw_table_
[i
]);
181 * sets *v to be the address of the represented value
182 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
183 * value_data is 'nextword' for this operand, ignored if unused
184 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
185 * e_addr is set to a referenced address, for accounting callback
186 * pc_adjust is set to how to change the program counter
187 * stack_adjust is set to how to change the stack pointer
188 * cycle_adjust is set to number of cycles spent looking up operand
190 * zero all adjustables before decoding first operand, and pass in these values when
191 * decoding next operand..
195 void value_decode_(struct dcpu16
*vm
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
196 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
,
197 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
198 assert(value
<= 0x3f);
200 DCPU16_WORD pc
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *pc_adjust
),
201 sp
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_SP
] + *sp_adjust
);
203 TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
210 if (value
<= 0x07) { /* register */
211 *v
= vm
->reg
+ value
;
212 TRACE("%s>> %s (0x%04x)",
214 dcpu16_reg_names
[value
],
219 if (value
<= 0x0f) { /* [register] */
220 *v
= &(vm
->ram
[ vm
->reg
[value
& 0x07] ]);
221 *e_addr
= vm
->reg
[value
& 0x07];
222 TRACE("%s>> [%s] [0x%04x] (0x%04x)",
224 dcpu16_reg_names
[value
& 0x07],
225 vm
->reg
[value
& 0x07],
230 if (value
<= 0x17) { /* [next word + register] */
231 *pc_adjust
+= 1; /* consume next word */
233 *e_addr
= value_data
+ vm
->reg
[value
& 0x07];
234 *v
= vm
->ram
+ *e_addr
;
235 TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
237 dcpu16_reg_names
[value
& 0x07],
239 vm
->reg
[value
& 0x07],
245 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
246 if (value_is_a
== 0) { /* b */
247 *v
= &(vm
->ram
[sp
- 1]);
250 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
255 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
259 case 0x19: /* PEEK/[SP] */
262 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
268 case 0x1a: /* PICK n */
271 *e_addr
= sp
+ value_data
;
272 *v
= vm
->ram
+ *e_addr
;
273 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
281 *v
= &(vm
->reg
[DCPU16_REG_SP
]);
282 TRACE("%s>> %s (0x%04x)",
284 dcpu16_reg_names
[DCPU16_REG_SP
],
289 *v
= &(vm
->reg
[DCPU16_REG_PC
]);
290 TRACE("%s>> %s (0x%04x)",
292 dcpu16_reg_names
[DCPU16_REG_PC
],
297 *v
= &(vm
->reg
[DCPU16_REG_EX
]);
298 TRACE("%s>> %s (0x%04x)",
300 dcpu16_reg_names
[DCPU16_REG_EX
],
304 case 0x1e: /* [next word] / [[pc++]] */
307 *e_addr
= value_data
;
308 *v
= vm
->ram
+ *e_addr
;
309 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
315 case 0x1f: /* next word (literal) / [pc++] */
318 *work_v
= value_data
;
320 TRACE("%s>> nextword (0x%04x)",
325 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
326 *work_v
= (value
& 0x1f) - 1;
328 TRACE("%s>> literal (0x%04x)",
335 #define OPCODE_NAME_LEN 16
336 struct opcode_entry
{
337 unsigned short value
;
338 char name
[OPCODE_NAME_LEN
];
339 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
342 /* messy boilerplate for opcode handlers */
344 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
345 /* opcode does adjust stack and cycle count */
347 #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)
349 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
350 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
351 short pc_adjust = 0, sp_adjust = 0;\
352 unsigned int cycle_adjust = 0;\
355 value_decode_(vm, val_a, 1, val_a_data,\
356 &vm->reg_work_[1], &a, &ev_a_addr,\
357 &pc_adjust, &sp_adjust, &cycle_adjust);\
358 vm->reg[DCPU16_REG_SP] += sp_adjust;\
359 dcpu16_cycle_inc(vm, cycle_adjust);\
361 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
362 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
363 &vm->reg_work_[0], &b, &ev_b_addr,\
364 &pc_adjust, &sp_adjust, &cycle_adjust)
365 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
366 #define OP_NBI(x) OP_TYPE(OP_NBI_)
368 /* 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 */
371 accounting helpers, these fire off the related callbacks for memory reads,
372 memory writes, and execution of reserved instructions
374 #define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
375 #define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
376 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
378 /* extended opcodes */
381 N.B. this next function currently decodes values -- id est, it is
382 an opcode processing terminus; however, if 'future instruction set
383 expansion' happens, this will probably need to behave more like
384 the OP_IMPL(_nbi_) function which invoked it, if those instructions
385 have zero or differently styled operands.
387 OP_IMPL(nbi__reserved_
) {
388 OP_NBI(nbi__reserved_
);
389 /* reserved for future expansion */
391 /* fire an illegal instruction event for current instruction */
392 DCPU16_WORD future_opcode
= (vm
->ram
[vm
->reg
[DCPU16_REG_PC
] - pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
393 WARN("reserved future opcode 0x%04x invoked", future_opcode
);
395 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
400 /* pushes the address of the next instruction to the stack, then sets PC to a */
404 vm
->ram
[ --vm
->reg
[DCPU16_REG_SP
] ] = vm
->reg
[DCPU16_REG_PC
];
405 vm
->reg
[DCPU16_REG_PC
] = *a
;
407 dcpu16_cycle_inc(vm
, 2);
409 ACCT_W(vm
->reg
[DCPU16_REG_SP
] + 1);
412 OP_IMPL(nbi__reserved2_
) {
413 OP_NBI(nbi__reserved2_
);
416 WARN("reserved nbi opcode invoked");
418 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
426 if (vm
->reg
[DCPU16_REG_IA
]) {
427 if ( interrupt_enqueue_(vm
, *a
) ) {
428 WARN("failed to queue interrupt");
432 if (vm
->interrupts_deferred_
)
435 vm
->interrupts_deferred_
= 1;
436 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
437 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
438 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
442 dcpu16_cycle_inc(vm
, 4);
448 *a
= vm
->reg
[DCPU16_REG_IA
];
450 dcpu16_cycle_inc(vm
, 1);
458 vm
->reg
[DCPU16_REG_IA
] = *a
;
460 dcpu16_cycle_inc(vm
, 1);
465 /* does this just ignore its operand? */
469 vm
->interrupts_deferred_
= 0;
470 vm
->reg
[DCPU16_REG_A
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
471 vm
->reg
[DCPU16_REG_PC
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
473 dcpu16_cycle_inc(vm
, 3);
480 vm
->interrupts_deferred_
= 1;
482 vm
->interrupts_deferred_
= 0;
485 dcpu16_cycle_inc(vm
, 2);
495 *a
= vm
->hw_table_entries_
;
497 dcpu16_cycle_inc(vm
, 2);
505 if (*a
>= vm
->hw_table_entries_
) {
506 WARN("hardware query for non-extant device 0x%04x", *a
);
507 vm
->reg
[DCPU16_REG_A
] = 0;
508 vm
->reg
[DCPU16_REG_B
] = 0;
509 vm
->reg
[DCPU16_REG_C
] = 0;
510 vm
->reg
[DCPU16_REG_X
] = 0;
511 vm
->reg
[DCPU16_REG_Y
] = 0;
515 vm
->reg
[DCPU16_REG_A
] = vm
->hw_table_
[*a
].id_l
;
516 vm
->reg
[DCPU16_REG_B
] = vm
->hw_table_
[*a
].id_h
;
517 vm
->reg
[DCPU16_REG_C
] = vm
->hw_table_
[*a
].ver
;
518 vm
->reg
[DCPU16_REG_X
] = vm
->hw_table_
[*a
].mfg_l
;
519 vm
->reg
[DCPU16_REG_Y
] = vm
->hw_table_
[*a
].mfg_h
;
521 dcpu16_cycle_inc(vm
, 4);
529 if (*a
> vm
->hw_table_entries_
) {
530 WARN("interrupt for non-extant device 0x%04x", *a
);
534 dcpu16_cycle_inc(vm
, 4);
535 if (vm
->hw_table_
[*a
].hwi
)
536 vm
->hw_table_
[*a
].hwi(vm
, &vm
->hw_table_
[*a
]);
538 WARN("hardware 0x%04x has no interrupt handler", *a
);
547 WARN("system on fire");
549 dcpu16_cycle_inc(vm
, 9);
552 static const struct opcode_entry opcode_nbi_entries
[] = {
553 {0x00, "(reserved)", op_nbi__reserved_
},
554 {0x01, "JSR", op_nbi_jsr
},
555 {0x02, "(reserved)", op_nbi__reserved2_
},
556 {0x03, "(reserved)", op_nbi__reserved2_
},
557 {0x04, "(reserved)", op_nbi__reserved2_
},
558 {0x05, "(reserved)", op_nbi__reserved2_
},
559 {0x06, "(reserved)", op_nbi__reserved2_
},
560 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
561 {0x08, "INT", op_nbi_int
},
562 {0x09, "IAG", op_nbi_iag
},
563 {0x0a, "IAS", op_nbi_ias
},
564 {0x0b, "RFI", op_nbi_rfi
},
565 {0x0c, "IAQ", op_nbi_iaq
},
566 {0x0d, "(reserved)", op_nbi__reserved2_
},
567 {0x0e, "(reserved)", op_nbi__reserved2_
},
568 {0x0f, "(reserved)", op_nbi__reserved2_
},
569 {0x10, "HWN", op_nbi_hwn
},
570 {0x11, "HWQ", op_nbi_hwq
},
571 {0x12, "HWI", op_nbi_hwi
},
572 {0x13, "(reserved)", op_nbi__reserved2_
},
575 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
581 N.B. the following function does not decode values.
582 Decoding is handled by the secondary opcode functions it calls.
585 /* non-basic instruction */
587 /* don't do normal value decoding here */
589 DCPU16_WORD nbi_opcode
= val_b
;
590 const struct opcode_entry
*e
= opcode_nbi_entries
;
594 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
596 assert(e
->impl
!= NULL
);
598 TRACE(">> %s 0x%04x", e
->name
, val_b
);
599 e
->impl(vm
, 0, 0, val_a
, val_a_data
);
609 if b is a literal, it's aimed at a scratch register,
610 so it's fine to update, as it won't have any effect.
614 dcpu16_cycle_inc(vm
, 1);
621 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
622 unsigned int acc
= *b
+ *a
;
628 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
630 dcpu16_cycle_inc(vm
, 2);
637 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
638 unsigned int acc
= *b
- *a
;
644 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
646 dcpu16_cycle_inc(vm
, 2);
653 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
654 unsigned int acc
= *b
* *a
;
660 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
662 dcpu16_cycle_inc(vm
, 2);
669 /* sets b to b*a, signed */
670 int acc
= (short)*b
* (short)*a
;
676 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
678 dcpu16_cycle_inc(vm
, 2);
685 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
692 vm
->reg
[DCPU16_REG_EX
] = 0;
695 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) / *a
;
698 dcpu16_cycle_inc(vm
, 3);
705 /* sets b to b/a, signed, round towards 0 */
712 vm
->reg
[DCPU16_REG_EX
] = 0;
714 *b
= (short)*b
/ (short)*a
;
715 vm
->reg
[DCPU16_REG_EX
] = (short)(*b
<< 16) / (short)*a
;
718 dcpu16_cycle_inc(vm
, 3);
725 /* sets b to b%a. if a==0, sets b to 0 instead. */
736 dcpu16_cycle_inc(vm
, 3);
743 /* sets b to b%a, signed */
751 *b
= (short)*b
% (short)*a
;
754 dcpu16_cycle_inc(vm
, 3);
768 dcpu16_cycle_inc(vm
, 1);
782 dcpu16_cycle_inc(vm
, 1);
796 dcpu16_cycle_inc(vm
, 1);
803 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
804 unsigned int acc
= *b
>> *a
;
810 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
812 dcpu16_cycle_inc(vm
, 2);
821 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
822 unsigned int acc
= *b
<< *a
;
828 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
830 dcpu16_cycle_inc(vm
, 2);
839 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
840 unsigned int acc
= *b
<< *a
;
847 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
849 dcpu16_cycle_inc(vm
, 2);
856 /* performs next instruction only if (b&a)!=0 */
861 if ((*b
& *a
) != 0) {
865 dcpu16_cycle_inc(vm
, 1);
868 dcpu16_cycle_inc(vm
, 2);
873 /* performs next instruction only if (b&a)==0 */
878 if ((*b
& *a
) == 0) {
882 dcpu16_cycle_inc(vm
, 1);
885 dcpu16_cycle_inc(vm
, 2);
890 /* performs next instruction only if b==a */
899 dcpu16_cycle_inc(vm
, 1);
902 dcpu16_cycle_inc(vm
, 2);
907 /* performs next instruction only if b!=a */
916 dcpu16_cycle_inc(vm
, 1);
919 dcpu16_cycle_inc(vm
, 2);
924 /* performs next instruction only if b>a */
933 dcpu16_cycle_inc(vm
, 1);
936 dcpu16_cycle_inc(vm
, 2);
941 /* performs next instruction only if b>a (signed) */
950 dcpu16_cycle_inc(vm
, 1);
953 dcpu16_cycle_inc(vm
, 2);
958 /* performs next instruction only if b<a */
967 dcpu16_cycle_inc(vm
, 1);
970 dcpu16_cycle_inc(vm
, 2);
975 /* performs next instruction only if b<a (signed) */
984 dcpu16_cycle_inc(vm
, 1);
987 dcpu16_cycle_inc(vm
, 2);
992 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
998 acc
= *b
+ *a
+ vm
->reg
[DCPU16_REG_EX
];
1001 vm
->reg
[DCPU16_REG_EX
] = 0x0001;
1003 vm
->reg
[DCPU16_REG_EX
] = 0x0000;
1005 dcpu16_cycle_inc(vm
, 3);
1012 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
1018 acc
= *b
- *a
+ vm
->reg
[DCPU16_REG_EX
];
1021 vm
->reg
[DCPU16_REG_EX
] = 0xffff;
1023 vm
->reg
[DCPU16_REG_EX
] = 0;
1025 dcpu16_cycle_inc(vm
, 3);
1032 /* sets b to a, then increases I and J by 1 */
1038 vm
->reg
[DCPU16_REG_I
] += 1;
1039 vm
->reg
[DCPU16_REG_J
] += 1;
1041 dcpu16_cycle_inc(vm
, 2);
1048 /* sets b to a, then decreases I and J by 1 */
1054 vm
->reg
[DCPU16_REG_I
] -= 1;
1055 vm
->reg
[DCPU16_REG_J
] -= 1;
1057 dcpu16_cycle_inc(vm
, 2);
1062 OP_IMPL(_reserved_
) {
1063 OP_BASIC(_reserved_
);
1065 WARN("reserved opcode invoked");
1067 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
1070 static const struct opcode_entry opcode_basic_entries
[] = {
1071 {0x00, "(nbi)", op__nbi_
},
1072 {0x01, "SET", op_set
},
1073 {0x02, "ADD", op_add
},
1074 {0x03, "SUB", op_sub
},
1075 {0x04, "MUL", op_mul
},
1076 {0x05, "MLI", op_mli
},
1077 {0x06, "DIV", op_div
},
1078 {0x07, "DVI", op_dvi
},
1079 {0x08, "MOD", op_mod
},
1080 {0x09, "MDI", op_mdi
},
1081 {0x0a, "AND", op_and
},
1082 {0x0b, "BOR", op_bor
},
1083 {0x0c, "XOR", op_xor
},
1084 {0x0d, "SHR", op_shr
},
1085 {0x0e, "ASR", op_asr
},
1086 {0x0f, "SHL", op_shl
},
1087 {0x10, "IFB", op_ifb
},
1088 {0x11, "IFC", op_ifc
},
1089 {0x12, "IFE", op_ife
},
1090 {0x13, "IFN", op_ifn
},
1091 {0x14, "IFG", op_ifg
},
1092 {0x15, "IFA", op_ifa
},
1093 {0x16, "IFL", op_ifl
},
1094 {0x17, "IFU", op_ifu
},
1095 {0x18, "(reserved)", op__reserved_
},
1096 {0x19, "(reserved)", op__reserved_
},
1097 {0x1a, "ADX", op_adx
},
1098 {0x1b, "SBX", op_sbx
},
1099 {0x1c, "(reserved)", op__reserved_
},
1100 {0x1d, "(reserved)", op__reserved_
},
1101 {0x1e, "STI", op_sti
},
1102 {0x1f, "STD", op_std
},
1107 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1108 if (value
<= 0x07) {
1109 printf(" %s", dcpu16_reg_names
[value
]);
1110 } else if (value
<= 0x0f) {
1111 printf(" [%s]", dcpu16_reg_names
[value
& 0x07]);
1112 } else if (value
<= 0x17) {
1113 printf(" [0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1114 } else switch (value
) {
1116 if (value_position
== 0) { /* b */
1122 case 0x19: printf(" PEEK"); break;
1123 case 0x1a: printf(" PICK 0x%04x", nextword
); break;
1124 case 0x1b: printf(" SP"); break;
1125 case 0x1c: printf(" PC"); break;
1126 case 0x1d: printf(" EX"); break;
1127 case 0x1e: printf(" [0x%04x]", nextword
); break;
1128 case 0x1f: printf(" 0x%04x", nextword
); break;
1129 default: printf(" 0x%02x", value
- 0x21);
1134 /* split a sequence of (one to three) words into the components of an instruction */
1136 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1137 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1138 DCPU16_WORD
*instr_len
) {
1139 *opcode
= *a
= *b
= mem
[addr
];
1140 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1141 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1142 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1145 if ((*opcode
!= 0x0000) &&
1146 ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) ) {
1147 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1153 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1154 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1155 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1161 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",
1167 *b_data
? **b_data
: 0,
1169 *a_data
? **a_data
: 0,
1173 /* dcpu16_mnemonify_buf
1174 print words as words
1176 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1177 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1178 const struct opcode_entry
*e
;
1180 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1182 if (opcode
== 0x0000)
1183 e
= opcode_nbi_entries
+
1184 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1186 e
= opcode_basic_entries
+ opcode
;
1187 printf("%s", e
->name
);
1190 dump_operand_value_(b
, b_data
? *b_data
: 0, 0);
1195 dump_operand_value_(a
, a_data
? *a_data
: 0, 1);
1201 /* dcpu16_disassemble_print
1202 print the words of the instruction at addr, followed by its assembly representation
1203 returns the length of the instruction in words
1205 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*vm
, DCPU16_WORD addr
) {
1206 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1207 DCPU16_WORD buf
[3] = { vm
->ram
[addr
], vm
->ram
[(DCPU16_WORD
)(addr
+ 1)], vm
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1208 unsigned int indent
= 0;
1209 unsigned int partial
= 0;
1215 Check the previous instruction, to see if this one should be
1216 indented. This check isn't foolproof, as preceeding addresses
1217 could be data which happen to match instructions..
1219 for (i
= 3; i
; i
--) {
1220 instruction_decode_(vm
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1224 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1231 /* just need instr_len */
1232 instruction_decode_(vm
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1234 /* show the raw words */
1235 printf("%04x", vm
->ram
[addr
]);
1236 for (i
= 1; i
< instr_len
; i
++) {
1237 printf(" %04x", vm
->ram
[addr
+ i
]);
1240 /* align things neatly, show the instruction */
1241 printf("%s%s ;%s%s",
1242 instr_len
< 3 ? " " : "",
1243 instr_len
< 2 ? " " : "",
1244 partial
? "*" : " ",
1247 dcpu16_mnemonify_buf(buf
);
1252 int dcpu16_interrupt(struct dcpu16
*vm
, DCPU16_WORD message
) {
1253 TRACE("%s>> message:0x%04x", __func__
, message
);
1254 return interrupt_enqueue_(vm
, message
);
1257 /* execute the next instruction */
1258 void dcpu16_step(struct dcpu16
*vm
) {
1259 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1260 const struct opcode_entry
*e
;
1265 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1267 /* consume what we decoded */
1268 /* this happens immediately as PC might be re-set as an operation */
1269 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1271 /* run the operation */
1272 for (e
= opcode_basic_entries
; e
->impl
; e
++) {
1273 if (e
->value
== opcode
) {
1274 TRACE("%s>> %s 0x%04x, 0x%04x", __func__
, e
->name
, b
, a
);
1275 e
->impl(vm
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1280 /* and jump over next instr(s) if needed */
1282 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1283 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1284 TRACE("++ SKIPPED %x words", instr_len
);
1285 if (opcode
>= 0x10 && opcode
<= 0x17) {
1286 /* skipping a branch instruction? skip branch's skippable instruction as well */
1287 dcpu16_cycle_inc(vm
, 1);
1293 /* if we're currently servicing interrupts */
1294 if (vm
->interrupts_deferred_
== 0) {
1295 /* and there are interrupts to be serviced */
1296 if (vm
->interrupts_head_
!= vm
->interrupts_tail_
) {
1297 DCPU16_WORD message
;
1298 message
= interrupt_dequeue_(vm
);
1300 TRACE("%s>> %s interrupt IA:0x%04x message:0x%04x",
1302 vm
->reg
[DCPU16_REG_IA
] ? "servicing" : "ignoring",
1303 vm
->reg
[DCPU16_REG_IA
],
1305 if (vm
->reg
[DCPU16_REG_IA
]) {
1306 /* then service the next interrupt */
1307 vm
->interrupts_deferred_
= 1;
1308 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
1309 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
1310 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
1311 vm
->reg
[DCPU16_REG_A
] = message
;
1318 print the current state of the machine
1319 shows current cycle count, registers, and next instruction
1321 void dcpu16_state_print(struct dcpu16
*vm
) {
1327 for (i
= 0; i
< 8; i
++)
1328 printf(" %s:0x%04x", dcpu16_reg_names
[i
], vm
->reg
[i
]);
1331 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1333 dcpu16_reg_names
[DCPU16_REG_EX
], vm
->reg
[DCPU16_REG_EX
],
1334 dcpu16_reg_names
[DCPU16_REG_SP
], vm
->reg
[DCPU16_REG_SP
],
1335 dcpu16_reg_names
[DCPU16_REG_PC
], vm
->reg
[DCPU16_REG_PC
],
1336 dcpu16_reg_names
[DCPU16_REG_IA
], vm
->reg
[DCPU16_REG_IA
],
1339 dcpu16_disassemble_print(vm
, vm
->reg
[DCPU16_REG_PC
]);
1344 * print raw ram contents from start to stop
1346 void dcpu16_dump_ram(struct dcpu16
*vm
, DCPU16_WORD start
, DCPU16_WORD end
) {
1348 const unsigned int n
= 8; /* words per line */
1352 for (i
= start
, j
= 0; i
<= end
; i
++, j
++) {
1354 printf("0x%04x:\t", i
);
1355 printf(" %04x%s", vm
->ram
[i
], (j
% n
) == (n
- 1) ? "\n" : "");
1357 if ((j
% n
) != (n
- 1))
1361 /* instantiate a new 'hardware' device */
1362 struct dcpu16_hw
*dcpu16_hw_new(struct dcpu16
*vm
, struct dcpu16_hw_module
*mod
, void *data
) {
1363 struct dcpu16_hw
*hw
;
1365 hw
= calloc(1, sizeof *hw
);
1367 vm
->warn_cb_("%s():%s", "calloc", strerror(errno
));
1370 memcpy(hw
, mod
->template, sizeof *hw
);
1374 if (mod
->data_init(hw
, data
)) {
1375 vm
->warn_cb_("failed to init hw module data");
1383 /* destroy a 'hardware' device */
1384 void dcpu16_hw_del(struct dcpu16_hw
**hw
) {
1387 if ((*hw
)->mod
->data_free
) {
1388 (*hw
)->mod
->data_free(*hw
);
1397 * invokes per-module controls for hw device
1399 int dcpu16_hw_ctl(struct dcpu16_hw
*hw
, const char *cmd
, void *data_in
, void *data_out
) {
1404 return hw
->mod
->ctl(hw
, cmd
, data_in
, data_out
);
1414 * registers new 'hardware' device with system
1416 int dcpu16_hw_attach(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1420 TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1424 hw
->mfg_l
, hw
->mfg_h
,
1427 if (vm
->hw_table_entries_
== 0xffff) {
1428 WARN("maximum hardware entries reached");
1432 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1433 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1434 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1435 if (tmp_ptr
== NULL
) {
1436 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1439 vm
->hw_table_
= tmp_ptr
;
1440 vm
->hw_table_allocated_
+= 32;
1443 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1444 vm
->hw_table_entries_
++;
1446 TRACE("%s>> added hw entry %zu", __func__
, vm
->hw_table_entries_
);
1452 * Register callback fn to be triggered whenever event matching any events
1453 * in bitwise mask occur.
1455 int dcpu16_acct_add(struct dcpu16
*vm
, dcpu16_acct_event mask
, dcpu16_ev_cb_t
*fn
, void *data
) {
1456 struct dcpu16_acct_cb cb
;
1465 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1466 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1467 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1468 if (tmp_ptr
== NULL
) {
1469 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1472 vm
->cb_table_
= tmp_ptr
;
1473 vm
->cb_table_allocated_
+= 32;
1476 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1477 vm
->cb_table_entries_
++;
1479 TRACE("%s>> attached event callback %zu", __func__
, vm
->cb_table_entries_
);
1485 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1487 void dcpu16_reset(struct dcpu16
*vm
) {
1493 TRACE("%s>> reset", __func__
);
1496 vm
->interrupts_deferred_
= 0;
1498 memset(vm
->interrupts_
, 0, sizeof vm
->interrupts_
);
1499 vm
->interrupts_head_
= 0;
1500 vm
->interrupts_tail_
= 0;
1502 /* signal attached hardware */
1503 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1504 if (vm
->hw_table_
[i
].reset
)
1505 vm
->hw_table_
[i
].reset(vm
, &vm
->hw_table_
[i
]);
1508 memset(vm
->reg
, 0, sizeof vm
->reg
);
1509 memset(vm
->ram
, 0, sizeof vm
->ram
);
1512 acct_event_(vm
, DCPU16_ACCT_EV_RESET
, 0);
1516 * allocate a new dcpu16 instance
1518 struct dcpu16
*dcpu16_new(void) {
1521 vm
= calloc(1, sizeof *vm
);
1523 WARN("%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1525 vm
->warn_cb_
= warn_cb_
;
1526 vm
->trace_cb_
= trace_cb_
;
1532 * release a dcpu16 instance
1534 void dcpu16_delete(struct dcpu16
**vm
) {