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
].mod
->name_
);
175 if (vm
->hw_table_
[i
].mod
->cycle
)
176 vm
->hw_table_
[i
].mod
->cycle(vm
, &vm
->hw_table_
[i
]);
182 * sets *v to be the address of the represented value
183 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
184 * value_data is 'nextword' for this operand, ignored if unused
185 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
186 * e_addr is set to a referenced address, for accounting callback
187 * pc_adjust is set to how to change the program counter
188 * stack_adjust is set to how to change the stack pointer
189 * cycle_adjust is set to number of cycles spent looking up operand
191 * zero all adjustables before decoding first operand, and pass in these values when
192 * decoding next operand..
196 void value_decode_(struct dcpu16
*vm
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
197 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
,
198 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
199 assert(value
<= 0x3f);
201 DCPU16_WORD pc
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *pc_adjust
),
202 sp
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_SP
] + *sp_adjust
);
204 TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
211 if (value
<= 0x07) { /* register */
212 *v
= vm
->reg
+ value
;
213 TRACE("%s>> %s (0x%04x)",
215 dcpu16_reg_names
[value
],
220 if (value
<= 0x0f) { /* [register] */
221 *v
= &(vm
->ram
[ vm
->reg
[value
& 0x07] ]);
222 *e_addr
= vm
->reg
[value
& 0x07];
223 TRACE("%s>> [%s] [0x%04x] (0x%04x)",
225 dcpu16_reg_names
[value
& 0x07],
226 vm
->reg
[value
& 0x07],
231 if (value
<= 0x17) { /* [next word + register] */
232 *pc_adjust
+= 1; /* consume next word */
234 *e_addr
= value_data
+ vm
->reg
[value
& 0x07];
235 *v
= vm
->ram
+ *e_addr
;
236 TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
238 dcpu16_reg_names
[value
& 0x07],
240 vm
->reg
[value
& 0x07],
246 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
247 if (value_is_a
== 0) { /* b */
248 *v
= &(vm
->ram
[sp
- 1]);
251 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
256 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
260 case 0x19: /* PEEK/[SP] */
263 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
269 case 0x1a: /* PICK n */
272 *e_addr
= sp
+ value_data
;
273 *v
= vm
->ram
+ *e_addr
;
274 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
282 *v
= &(vm
->reg
[DCPU16_REG_SP
]);
283 TRACE("%s>> %s (0x%04x)",
285 dcpu16_reg_names
[DCPU16_REG_SP
],
290 *v
= &(vm
->reg
[DCPU16_REG_PC
]);
291 TRACE("%s>> %s (0x%04x)",
293 dcpu16_reg_names
[DCPU16_REG_PC
],
298 *v
= &(vm
->reg
[DCPU16_REG_EX
]);
299 TRACE("%s>> %s (0x%04x)",
301 dcpu16_reg_names
[DCPU16_REG_EX
],
305 case 0x1e: /* [next word] / [[pc++]] */
308 *e_addr
= value_data
;
309 *v
= vm
->ram
+ *e_addr
;
310 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
316 case 0x1f: /* next word (literal) / [pc++] */
319 *work_v
= value_data
;
321 TRACE("%s>> nextword (0x%04x)",
326 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
327 *work_v
= (value
& 0x1f) - 1;
329 TRACE("%s>> literal (0x%04x)",
336 #define OPCODE_NAME_LEN 16
337 struct opcode_entry
{
338 unsigned short value
;
339 char name
[OPCODE_NAME_LEN
];
340 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
343 /* messy boilerplate for opcode handlers */
345 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
346 /* opcode does adjust stack and cycle count */
348 #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)
350 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
351 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
352 short pc_adjust = 0, sp_adjust = 0;\
353 unsigned int cycle_adjust = 0;\
356 value_decode_(vm, val_a, 1, val_a_data,\
357 &vm->reg_work_[1], &a, &ev_a_addr,\
358 &pc_adjust, &sp_adjust, &cycle_adjust);\
359 vm->reg[DCPU16_REG_SP] += sp_adjust;\
360 dcpu16_cycle_inc(vm, cycle_adjust);\
362 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
363 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
364 &vm->reg_work_[0], &b, &ev_b_addr,\
365 &pc_adjust, &sp_adjust, &cycle_adjust)
366 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
367 #define OP_NBI(x) OP_TYPE(OP_NBI_)
369 /* 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 */
372 accounting helpers, these fire off the related callbacks for memory reads,
373 memory writes, and execution of reserved instructions
375 #define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
376 #define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
377 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
379 /* extended opcodes */
382 N.B. this next function currently decodes values -- id est, it is
383 an opcode processing terminus; however, if 'future instruction set
384 expansion' happens, this will probably need to behave more like
385 the OP_IMPL(_nbi_) function which invoked it, if those instructions
386 have zero or differently styled operands.
388 OP_IMPL(nbi__reserved_
) {
389 OP_NBI(nbi__reserved_
);
390 /* reserved for future expansion */
392 /* fire an illegal instruction event for current instruction */
393 DCPU16_WORD future_opcode
= (vm
->ram
[vm
->reg
[DCPU16_REG_PC
] - pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
394 WARN("reserved future opcode 0x%04x invoked", future_opcode
);
396 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
401 /* pushes the address of the next instruction to the stack, then sets PC to a */
405 vm
->ram
[ --vm
->reg
[DCPU16_REG_SP
] ] = vm
->reg
[DCPU16_REG_PC
];
406 vm
->reg
[DCPU16_REG_PC
] = *a
;
408 dcpu16_cycle_inc(vm
, 2);
410 ACCT_W(vm
->reg
[DCPU16_REG_SP
] + 1);
413 OP_IMPL(nbi__reserved2_
) {
414 OP_NBI(nbi__reserved2_
);
417 WARN("reserved nbi opcode invoked");
419 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
427 if (vm
->reg
[DCPU16_REG_IA
]) {
428 if ( interrupt_enqueue_(vm
, *a
) ) {
429 WARN("failed to queue interrupt");
433 if (vm
->interrupts_deferred_
)
436 vm
->interrupts_deferred_
= 1;
437 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
438 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
439 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
443 dcpu16_cycle_inc(vm
, 4);
449 *a
= vm
->reg
[DCPU16_REG_IA
];
451 dcpu16_cycle_inc(vm
, 1);
459 vm
->reg
[DCPU16_REG_IA
] = *a
;
461 dcpu16_cycle_inc(vm
, 1);
466 /* does this just ignore its operand? */
470 vm
->interrupts_deferred_
= 0;
471 vm
->reg
[DCPU16_REG_A
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
472 vm
->reg
[DCPU16_REG_PC
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
474 dcpu16_cycle_inc(vm
, 3);
481 vm
->interrupts_deferred_
= 1;
483 vm
->interrupts_deferred_
= 0;
486 dcpu16_cycle_inc(vm
, 2);
496 *a
= vm
->hw_table_entries_
;
498 dcpu16_cycle_inc(vm
, 2);
506 if (*a
>= vm
->hw_table_entries_
) {
507 WARN("hardware query for non-extant device 0x%04x", *a
);
508 vm
->reg
[DCPU16_REG_A
] = 0;
509 vm
->reg
[DCPU16_REG_B
] = 0;
510 vm
->reg
[DCPU16_REG_C
] = 0;
511 vm
->reg
[DCPU16_REG_X
] = 0;
512 vm
->reg
[DCPU16_REG_Y
] = 0;
516 vm
->reg
[DCPU16_REG_A
] = vm
->hw_table_
[*a
].mod
->id_l
;
517 vm
->reg
[DCPU16_REG_B
] = vm
->hw_table_
[*a
].mod
->id_h
;
518 vm
->reg
[DCPU16_REG_C
] = vm
->hw_table_
[*a
].mod
->ver
;
519 vm
->reg
[DCPU16_REG_X
] = vm
->hw_table_
[*a
].mod
->mfg_l
;
520 vm
->reg
[DCPU16_REG_Y
] = vm
->hw_table_
[*a
].mod
->mfg_h
;
522 dcpu16_cycle_inc(vm
, 4);
530 if (*a
> vm
->hw_table_entries_
) {
531 WARN("interrupt for non-extant device 0x%04x", *a
);
535 dcpu16_cycle_inc(vm
, 4);
536 if (vm
->hw_table_
[*a
].mod
->hwi
)
537 vm
->hw_table_
[*a
].mod
->hwi(vm
, &vm
->hw_table_
[*a
]);
539 WARN("hardware 0x%04x has no interrupt handler", *a
);
548 WARN("system on fire");
550 dcpu16_cycle_inc(vm
, 9);
553 static const struct opcode_entry opcode_nbi_entries
[] = {
554 {0x00, "(reserved)", op_nbi__reserved_
},
555 {0x01, "JSR", op_nbi_jsr
},
556 {0x02, "(reserved)", op_nbi__reserved2_
},
557 {0x03, "(reserved)", op_nbi__reserved2_
},
558 {0x04, "(reserved)", op_nbi__reserved2_
},
559 {0x05, "(reserved)", op_nbi__reserved2_
},
560 {0x06, "(reserved)", op_nbi__reserved2_
},
561 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
562 {0x08, "INT", op_nbi_int
},
563 {0x09, "IAG", op_nbi_iag
},
564 {0x0a, "IAS", op_nbi_ias
},
565 {0x0b, "RFI", op_nbi_rfi
},
566 {0x0c, "IAQ", op_nbi_iaq
},
567 {0x0d, "(reserved)", op_nbi__reserved2_
},
568 {0x0e, "(reserved)", op_nbi__reserved2_
},
569 {0x0f, "(reserved)", op_nbi__reserved2_
},
570 {0x10, "HWN", op_nbi_hwn
},
571 {0x11, "HWQ", op_nbi_hwq
},
572 {0x12, "HWI", op_nbi_hwi
},
573 {0x13, "(reserved)", op_nbi__reserved2_
},
576 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
582 N.B. the following function does not decode values.
583 Decoding is handled by the secondary opcode functions it calls.
586 /* non-basic instruction */
588 /* don't do normal value decoding here */
590 DCPU16_WORD nbi_opcode
= val_b
;
591 const struct opcode_entry
*e
= opcode_nbi_entries
;
595 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
597 assert(e
->impl
!= NULL
);
599 TRACE(">> %s 0x%04x", e
->name
, val_b
);
600 e
->impl(vm
, 0, 0, val_a
, val_a_data
);
610 if b is a literal, it's aimed at a scratch register,
611 so it's fine to update, as it won't have any effect.
615 dcpu16_cycle_inc(vm
, 1);
622 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
623 unsigned int acc
= *b
+ *a
;
629 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
631 dcpu16_cycle_inc(vm
, 2);
638 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
639 unsigned int acc
= *b
- *a
;
645 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
647 dcpu16_cycle_inc(vm
, 2);
654 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
655 unsigned int acc
= *b
* *a
;
661 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
663 dcpu16_cycle_inc(vm
, 2);
670 /* sets b to b*a, signed */
671 int acc
= (short)*b
* (short)*a
;
677 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
679 dcpu16_cycle_inc(vm
, 2);
686 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
693 vm
->reg
[DCPU16_REG_EX
] = 0;
696 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) / *a
;
699 dcpu16_cycle_inc(vm
, 3);
706 /* sets b to b/a, signed, round towards 0 */
713 vm
->reg
[DCPU16_REG_EX
] = 0;
715 *b
= (short)*b
/ (short)*a
;
716 vm
->reg
[DCPU16_REG_EX
] = (short)(*b
<< 16) / (short)*a
;
719 dcpu16_cycle_inc(vm
, 3);
726 /* sets b to b%a. if a==0, sets b to 0 instead. */
737 dcpu16_cycle_inc(vm
, 3);
744 /* sets b to b%a, signed */
752 *b
= (short)*b
% (short)*a
;
755 dcpu16_cycle_inc(vm
, 3);
769 dcpu16_cycle_inc(vm
, 1);
783 dcpu16_cycle_inc(vm
, 1);
797 dcpu16_cycle_inc(vm
, 1);
804 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
805 unsigned int acc
= *b
>> *a
;
811 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
813 dcpu16_cycle_inc(vm
, 2);
822 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
823 unsigned int acc
= *b
<< *a
;
829 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
831 dcpu16_cycle_inc(vm
, 2);
840 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
841 unsigned int acc
= *b
<< *a
;
848 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
850 dcpu16_cycle_inc(vm
, 2);
857 /* performs next instruction only if (b&a)!=0 */
862 if ((*b
& *a
) != 0) {
866 dcpu16_cycle_inc(vm
, 1);
869 dcpu16_cycle_inc(vm
, 2);
874 /* performs next instruction only if (b&a)==0 */
879 if ((*b
& *a
) == 0) {
883 dcpu16_cycle_inc(vm
, 1);
886 dcpu16_cycle_inc(vm
, 2);
891 /* performs next instruction only if b==a */
900 dcpu16_cycle_inc(vm
, 1);
903 dcpu16_cycle_inc(vm
, 2);
908 /* performs next instruction only if b!=a */
917 dcpu16_cycle_inc(vm
, 1);
920 dcpu16_cycle_inc(vm
, 2);
925 /* performs next instruction only if b>a */
934 dcpu16_cycle_inc(vm
, 1);
937 dcpu16_cycle_inc(vm
, 2);
942 /* performs next instruction only if b>a (signed) */
951 dcpu16_cycle_inc(vm
, 1);
954 dcpu16_cycle_inc(vm
, 2);
959 /* performs next instruction only if b<a */
968 dcpu16_cycle_inc(vm
, 1);
971 dcpu16_cycle_inc(vm
, 2);
976 /* performs next instruction only if b<a (signed) */
985 dcpu16_cycle_inc(vm
, 1);
988 dcpu16_cycle_inc(vm
, 2);
993 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
999 acc
= *b
+ *a
+ vm
->reg
[DCPU16_REG_EX
];
1002 vm
->reg
[DCPU16_REG_EX
] = 0x0001;
1004 vm
->reg
[DCPU16_REG_EX
] = 0x0000;
1006 dcpu16_cycle_inc(vm
, 3);
1013 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
1019 acc
= *b
- *a
+ vm
->reg
[DCPU16_REG_EX
];
1022 vm
->reg
[DCPU16_REG_EX
] = 0xffff;
1024 vm
->reg
[DCPU16_REG_EX
] = 0;
1026 dcpu16_cycle_inc(vm
, 3);
1033 /* sets b to a, then increases I and J by 1 */
1039 vm
->reg
[DCPU16_REG_I
] += 1;
1040 vm
->reg
[DCPU16_REG_J
] += 1;
1042 dcpu16_cycle_inc(vm
, 2);
1049 /* sets b to a, then decreases I and J by 1 */
1055 vm
->reg
[DCPU16_REG_I
] -= 1;
1056 vm
->reg
[DCPU16_REG_J
] -= 1;
1058 dcpu16_cycle_inc(vm
, 2);
1063 OP_IMPL(_reserved_
) {
1064 OP_BASIC(_reserved_
);
1066 WARN("reserved opcode invoked");
1068 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
1071 static const struct opcode_entry opcode_basic_entries
[] = {
1072 {0x00, "(nbi)", op__nbi_
},
1073 {0x01, "SET", op_set
},
1074 {0x02, "ADD", op_add
},
1075 {0x03, "SUB", op_sub
},
1076 {0x04, "MUL", op_mul
},
1077 {0x05, "MLI", op_mli
},
1078 {0x06, "DIV", op_div
},
1079 {0x07, "DVI", op_dvi
},
1080 {0x08, "MOD", op_mod
},
1081 {0x09, "MDI", op_mdi
},
1082 {0x0a, "AND", op_and
},
1083 {0x0b, "BOR", op_bor
},
1084 {0x0c, "XOR", op_xor
},
1085 {0x0d, "SHR", op_shr
},
1086 {0x0e, "ASR", op_asr
},
1087 {0x0f, "SHL", op_shl
},
1088 {0x10, "IFB", op_ifb
},
1089 {0x11, "IFC", op_ifc
},
1090 {0x12, "IFE", op_ife
},
1091 {0x13, "IFN", op_ifn
},
1092 {0x14, "IFG", op_ifg
},
1093 {0x15, "IFA", op_ifa
},
1094 {0x16, "IFL", op_ifl
},
1095 {0x17, "IFU", op_ifu
},
1096 {0x18, "(reserved)", op__reserved_
},
1097 {0x19, "(reserved)", op__reserved_
},
1098 {0x1a, "ADX", op_adx
},
1099 {0x1b, "SBX", op_sbx
},
1100 {0x1c, "(reserved)", op__reserved_
},
1101 {0x1d, "(reserved)", op__reserved_
},
1102 {0x1e, "STI", op_sti
},
1103 {0x1f, "STD", op_std
},
1108 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1109 if (value
<= 0x07) {
1110 printf(" %s", dcpu16_reg_names
[value
]);
1111 } else if (value
<= 0x0f) {
1112 printf(" [%s]", dcpu16_reg_names
[value
& 0x07]);
1113 } else if (value
<= 0x17) {
1114 printf(" [0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1115 } else switch (value
) {
1117 if (value_position
== 0) { /* b */
1123 case 0x19: printf(" PEEK"); break;
1124 case 0x1a: printf(" PICK 0x%04x", nextword
); break;
1125 case 0x1b: printf(" SP"); break;
1126 case 0x1c: printf(" PC"); break;
1127 case 0x1d: printf(" EX"); break;
1128 case 0x1e: printf(" [0x%04x]", nextword
); break;
1129 case 0x1f: printf(" 0x%04x", nextword
); break;
1130 default: printf(" 0x%02x", value
- 0x21);
1135 /* split a sequence of (one to three) words into the components of an instruction */
1137 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1138 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1139 DCPU16_WORD
*instr_len
) {
1140 *opcode
= *a
= *b
= mem
[addr
];
1141 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1142 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1143 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1146 if ((*opcode
!= 0x0000) &&
1147 ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) ) {
1148 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1154 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1155 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1156 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1162 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",
1168 *b_data
? **b_data
: 0,
1170 *a_data
? **a_data
: 0,
1174 /* dcpu16_mnemonify_buf
1175 print words as words
1177 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1178 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1179 const struct opcode_entry
*e
;
1181 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1183 if (opcode
== 0x0000)
1184 e
= opcode_nbi_entries
+
1185 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1187 e
= opcode_basic_entries
+ opcode
;
1188 printf("%s", e
->name
);
1191 dump_operand_value_(b
, b_data
? *b_data
: 0, 0);
1196 dump_operand_value_(a
, a_data
? *a_data
: 0, 1);
1202 /* dcpu16_disassemble_print
1203 print the words of the instruction at addr, followed by its assembly representation
1204 returns the length of the instruction in words
1206 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*vm
, DCPU16_WORD addr
) {
1207 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1208 DCPU16_WORD buf
[3] = { vm
->ram
[addr
], vm
->ram
[(DCPU16_WORD
)(addr
+ 1)], vm
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1209 unsigned int indent
= 0;
1210 unsigned int partial
= 0;
1216 Check the previous instruction, to see if this one should be
1217 indented. This check isn't foolproof, as preceeding addresses
1218 could be data which happen to match instructions..
1220 for (i
= 3; i
; i
--) {
1221 instruction_decode_(vm
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1225 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1232 /* just need instr_len */
1233 instruction_decode_(vm
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1235 /* show the raw words */
1236 printf("%04x", vm
->ram
[addr
]);
1237 for (i
= 1; i
< instr_len
; i
++) {
1238 printf(" %04x", vm
->ram
[addr
+ i
]);
1241 /* align things neatly, show the instruction */
1242 printf("%s%s ;%s%s",
1243 instr_len
< 3 ? " " : "",
1244 instr_len
< 2 ? " " : "",
1245 partial
? "*" : " ",
1248 dcpu16_mnemonify_buf(buf
);
1253 int dcpu16_interrupt(struct dcpu16
*vm
, DCPU16_WORD message
) {
1254 TRACE("%s>> message:0x%04x", __func__
, message
);
1255 return interrupt_enqueue_(vm
, message
);
1258 /* execute the next instruction */
1259 void dcpu16_step(struct dcpu16
*vm
) {
1260 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1261 const struct opcode_entry
*e
;
1266 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1268 /* consume what we decoded */
1269 /* this happens immediately as PC might be re-set as an operation */
1270 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1272 /* run the operation */
1273 for (e
= opcode_basic_entries
; e
->impl
; e
++) {
1274 if (e
->value
== opcode
) {
1275 TRACE("%s>> %s 0x%04x, 0x%04x", __func__
, e
->name
, b
, a
);
1276 e
->impl(vm
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1281 /* and jump over next instr(s) if needed */
1283 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1284 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1285 TRACE("++ SKIPPED %x words", instr_len
);
1286 if (opcode
>= 0x10 && opcode
<= 0x17) {
1287 /* skipping a branch instruction? skip branch's skippable instruction as well */
1288 dcpu16_cycle_inc(vm
, 1);
1294 /* if we're currently servicing interrupts */
1295 if (vm
->interrupts_deferred_
== 0) {
1296 /* and there are interrupts to be serviced */
1297 if (vm
->interrupts_head_
!= vm
->interrupts_tail_
) {
1298 DCPU16_WORD message
;
1299 message
= interrupt_dequeue_(vm
);
1301 TRACE("%s>> %s interrupt IA:0x%04x message:0x%04x",
1303 vm
->reg
[DCPU16_REG_IA
] ? "servicing" : "ignoring",
1304 vm
->reg
[DCPU16_REG_IA
],
1306 if (vm
->reg
[DCPU16_REG_IA
]) {
1307 /* then service the next interrupt */
1308 vm
->interrupts_deferred_
= 1;
1309 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
1310 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
1311 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
1312 vm
->reg
[DCPU16_REG_A
] = message
;
1319 /* instantiate a new 'hardware' device */
1320 struct dcpu16_hw
*dcpu16_hw_new(struct dcpu16
*vm
, struct dcpu16_hw_module
*mod
, void *data
) {
1321 struct dcpu16_hw
*hw
;
1323 hw
= malloc(sizeof *hw
);
1325 vm
->warn_cb_("%s():%s", "malloc", strerror(errno
));
1331 if (mod
->data_init
) {
1332 if (mod
->data_init(hw
, data
)) {
1333 vm
->warn_cb_("failed to init hw module data");
1344 /* destroy a 'hardware' device */
1345 void dcpu16_hw_del(struct dcpu16_hw
**hw
) {
1348 if ((*hw
)->mod
->data_free
) {
1349 (*hw
)->mod
->data_free(*hw
);
1358 * invokes per-module controls for hw device
1360 int dcpu16_hw_ctl(struct dcpu16_hw
*hw
, const char *cmd
, void *data_in
, void *data_out
) {
1365 return hw
->mod
->ctl(hw
, cmd
, data_in
, data_out
);
1375 * registers new 'hardware' device with system
1377 int dcpu16_hw_attach(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1381 TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1384 hw
->mod
->id_h
, hw
->mod
->id_l
,
1385 hw
->mod
->mfg_l
, hw
->mod
->mfg_h
,
1388 if (vm
->hw_table_entries_
== 0xffff) {
1389 WARN("maximum hardware entries reached");
1393 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1394 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1395 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1396 if (tmp_ptr
== NULL
) {
1397 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1400 vm
->hw_table_
= tmp_ptr
;
1401 vm
->hw_table_allocated_
+= 32;
1404 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1405 vm
->hw_table_entries_
++;
1407 TRACE("%s>> added hw entry %zu", __func__
, vm
->hw_table_entries_
);
1413 * Register callback fn to be triggered whenever event matching any events
1414 * in bitwise mask occur.
1416 int dcpu16_acct_add(struct dcpu16
*vm
, dcpu16_acct_event mask
, dcpu16_ev_cb_t
*fn
, void *data
) {
1417 struct dcpu16_acct_cb cb
;
1426 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1427 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1428 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1429 if (tmp_ptr
== NULL
) {
1430 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1433 vm
->cb_table_
= tmp_ptr
;
1434 vm
->cb_table_allocated_
+= 32;
1437 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1438 vm
->cb_table_entries_
++;
1440 TRACE("%s>> attached event callback %zu", __func__
, vm
->cb_table_entries_
);
1446 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1448 void dcpu16_reset(struct dcpu16
*vm
) {
1454 TRACE("%s>> reset", __func__
);
1457 vm
->interrupts_deferred_
= 0;
1459 memset(vm
->interrupts_
, 0, sizeof vm
->interrupts_
);
1460 vm
->interrupts_head_
= 0;
1461 vm
->interrupts_tail_
= 0;
1463 /* signal attached hardware */
1464 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1465 if (vm
->hw_table_
[i
].mod
->reset
)
1466 vm
->hw_table_
[i
].mod
->reset(vm
, &vm
->hw_table_
[i
]);
1469 memset(vm
->reg
, 0, sizeof vm
->reg
);
1470 memset(vm
->ram
, 0, sizeof vm
->ram
);
1473 acct_event_(vm
, DCPU16_ACCT_EV_RESET
, 0);
1477 * allocate a new dcpu16 instance
1479 struct dcpu16
*dcpu16_new(void) {
1482 vm
= calloc(1, sizeof *vm
);
1484 WARN("%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1486 vm
->warn_cb_
= warn_cb_
;
1487 vm
->trace_cb_
= trace_cb_
;
1493 * release a dcpu16 instance
1495 void dcpu16_delete(struct dcpu16
**vm
) {