13 * emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
15 * I couldn't remember ever implementing an emulator before, so this
16 * happened. As such, consider this a toy in progress.
17 * There are likely many improvable aspects.
19 * Justin Wind <justin.wind@gmail.com>
20 * 2012 04 05 - implementation started
21 * 2012 05 05 - start of v1.7 revisions
22 * 2012 05 08 - v1.7 revisions mostly complete
25 * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
26 * !! v1.7 hardware interface needs to be finished
27 * !! v1.7 interrupts need to be finished
28 * change api to print into buffers rather than stdio
29 * refactor opcode functiontables into switch statements
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 * sets *v to be the address of the represented value
162 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
163 * value_data is 'nextword' for this operand, ignored if unused
164 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
165 * e_addr is set to a referenced address, for accounting callback
166 * pc_adjust is set to how to change the program counter
167 * stack_adjust is set to how to change the stack pointer
168 * cycle_adjust is set to number of cycles spent looking up operand
170 * zero all adjustables before decoding first operand, and pass in these values when
171 * decoding next operand..
175 void value_decode_(struct dcpu16
*vm
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
176 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
,
177 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
178 assert(value
<= 0x3f);
180 DCPU16_WORD pc
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *pc_adjust
),
181 sp
= (DCPU16_WORD
)(vm
->reg
[DCPU16_REG_PC
] + *sp_adjust
);
183 TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n",
189 if (value
<= 0x07) { /* register */
190 *v
= vm
->reg
+ value
;
191 TRACE("%s>> %s (0x%04x)",
193 dcpu16_reg_names
[value
],
198 if (value
<= 0x0f) { /* [register] */
199 *v
= &(vm
->ram
[ vm
->reg
[value
& 0x07] ]);
200 *e_addr
= vm
->reg
[value
& 0x07];
201 TRACE("%s>> [%s] [0x%04x] (0x%04x)",
203 dcpu16_reg_names
[value
& 0x07],
204 vm
->reg
[value
& 0x07],
209 if (value
<= 0x17) { /* [next word + register] */
210 *pc_adjust
+= 1; /* consume next word */
212 *e_addr
= value_data
+ vm
->reg
[value
& 0x07];
213 *v
= vm
->ram
+ *e_addr
;
214 TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
216 dcpu16_reg_names
[value
& 0x07],
218 vm
->reg
[value
& 0x07],
224 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
225 if (value_is_a
== 0) { /* b */
226 *v
= &(vm
->ram
[sp
- 1]);
229 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
234 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
238 case 0x19: /* PEEK/[SP] */
241 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
247 case 0x1a: /* PICK n */
250 *e_addr
= sp
+ value_data
;
251 *v
= vm
->ram
+ *e_addr
;
252 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
260 *v
= &(vm
->reg
[DCPU16_REG_SP
]);
261 TRACE("%s>> %s (0x%04x)",
263 dcpu16_reg_names
[DCPU16_REG_SP
],
268 *v
= &(vm
->reg
[DCPU16_REG_PC
]);
269 TRACE("%s>> %s (0x%04x)",
271 dcpu16_reg_names
[DCPU16_REG_PC
],
276 *v
= &(vm
->reg
[DCPU16_REG_EX
]);
277 TRACE("%s>> %s (0x%04x)",
279 dcpu16_reg_names
[DCPU16_REG_EX
],
283 case 0x1e: /* [next word] / [[pc++]] */
286 *e_addr
= value_data
;
287 *v
= vm
->ram
+ *e_addr
;
288 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
294 case 0x1f: /* next word (literal) / [pc++] */
297 *work_v
= value_data
;
299 TRACE("%s>> nextword (0x%04x)",
304 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
305 *work_v
= (value
& 0x1f) - 1;
307 TRACE("%s>> literal (0x%04x)",
314 #define OPCODE_NAME_LEN 16
315 struct opcode_entry
{
316 unsigned short value
;
317 char name
[OPCODE_NAME_LEN
];
318 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
321 /* messy boilerplate for opcode handlers */
323 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
324 /* opcode does adjust stack and cycle count */
326 #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)
328 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
329 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
330 short pc_adjust = 0, sp_adjust = 0;\
331 unsigned int cycle_adjust = 0;\
334 value_decode_(vm, val_a, 1, val_a_data,\
335 &vm->reg_work_[1], &a, &ev_a_addr,\
336 &pc_adjust, &sp_adjust, &cycle_adjust);\
337 vm->reg[DCPU16_REG_SP] += sp_adjust;\
338 vm->cycle += cycle_adjust;\
340 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
341 #define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
342 &vm->reg_work_[0], &b, &ev_b_addr,\
343 &pc_adjust, &sp_adjust, &cycle_adjust)
344 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
345 #define OP_NBI(x) OP_TYPE(OP_NBI_)
347 /* 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 */
350 accounting helpers, these fire off the related callbacks for memory reads,
351 memory writes, and execution of reserved instructions
353 #define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
354 #define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
355 #define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
357 /* extended opcodes */
360 N.B. this next function currently decodes values -- id est, it is
361 an opcode processing terminus; however, if 'future instruction set
362 expansion' happens, this will probably need to behave more like
363 the OP_IMPL(_nbi_) function which invoked it, if those instructions
364 have zero or differently styled operands.
366 OP_IMPL(nbi__reserved_
) {
367 OP_NBI(nbi__reserved_
);
368 /* reserved for future expansion */
370 /* fire an illegal instruction event for current instruction */
371 DCPU16_WORD future_opcode
= (vm
->ram
[vm
->reg
[DCPU16_REG_PC
] - pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
372 WARN("reserved future opcode 0x%04x invoked", future_opcode
);
374 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
379 /* pushes the address of the next instruction to the stack, then sets PC to a */
383 vm
->ram
[ --vm
->reg
[DCPU16_REG_SP
] ] = vm
->reg
[DCPU16_REG_PC
];
384 vm
->reg
[DCPU16_REG_PC
] = *a
;
388 ACCT_W(vm
->reg
[DCPU16_REG_SP
] + 1);
391 OP_IMPL(nbi__reserved2_
) {
392 OP_NBI(nbi__reserved2_
);
395 WARN("reserved nbi opcode invoked");
397 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
405 if (vm
->reg
[DCPU16_REG_IA
]) {
406 if ( interrupt_enqueue_(vm
, *a
) ) {
407 WARN("failed to queue interrupt");
411 if (vm
->interrupts_deferred_
)
414 vm
->interrupts_deferred_
= 1;
415 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
416 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
417 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
427 *a
= vm
->reg
[DCPU16_REG_IA
];
435 vm
->reg
[DCPU16_REG_IA
] = *a
;
440 /* does this just ignore its operand? */
444 vm
->interrupts_deferred_
= 0;
445 vm
->reg
[DCPU16_REG_A
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
446 vm
->reg
[DCPU16_REG_PC
] = vm
->ram
[vm
->reg
[DCPU16_REG_SP
]++];
453 vm
->interrupts_deferred_
= 1;
455 vm
->interrupts_deferred_
= 0;
466 *a
= vm
->hw_table_entries_
;
476 if (*a
>= vm
->hw_table_entries_
) {
477 WARN("hardware query for non-extant device 0x%04x", *a
);
478 vm
->reg
[DCPU16_REG_A
] = 0;
479 vm
->reg
[DCPU16_REG_B
] = 0;
480 vm
->reg
[DCPU16_REG_C
] = 0;
481 vm
->reg
[DCPU16_REG_X
] = 0;
482 vm
->reg
[DCPU16_REG_Y
] = 0;
486 vm
->reg
[DCPU16_REG_A
] = vm
->hw_table_
[*a
].id_l
;
487 vm
->reg
[DCPU16_REG_B
] = vm
->hw_table_
[*a
].id_h
;
488 vm
->reg
[DCPU16_REG_C
] = vm
->hw_table_
[*a
].ver
;
489 vm
->reg
[DCPU16_REG_X
] = vm
->hw_table_
[*a
].mfg_l
;
490 vm
->reg
[DCPU16_REG_Y
] = vm
->hw_table_
[*a
].mfg_h
;
500 if (*a
> vm
->hw_table_entries_
) {
501 WARN("interrupt for non-extant device 0x%04x", *a
);
506 if (vm
->hw_table_
[*a
].hwi
)
507 vm
->hw_table_
[*a
].hwi(vm
, vm
->hw_table_
[*a
].data
);
509 WARN("hardware 0x%04x has no interrupt handler", *a
);
518 WARN("system on fire");
523 static const struct opcode_entry opcode_nbi_entries
[] = {
524 {0x00, "(reserved)", op_nbi__reserved_
},
525 {0x01, "JSR", op_nbi_jsr
},
526 {0x02, "(reserved)", op_nbi__reserved2_
},
527 {0x03, "(reserved)", op_nbi__reserved2_
},
528 {0x04, "(reserved)", op_nbi__reserved2_
},
529 {0x05, "(reserved)", op_nbi__reserved2_
},
530 {0x06, "(reserved)", op_nbi__reserved2_
},
531 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
532 {0x08, "INT", op_nbi_int
},
533 {0x09, "IAG", op_nbi_iag
},
534 {0x0a, "IAS", op_nbi_ias
},
535 {0x0b, "RFI", op_nbi_rfi
},
536 {0x0c, "IAQ", op_nbi_iaq
},
537 {0x0d, "(reserved)", op_nbi__reserved2_
},
538 {0x0e, "(reserved)", op_nbi__reserved2_
},
539 {0x0f, "(reserved)", op_nbi__reserved2_
},
540 {0x10, "HWN", op_nbi_hwn
},
541 {0x11, "HWQ", op_nbi_hwq
},
542 {0x12, "HWI", op_nbi_hwi
},
543 {0x13, "(reserved)", op_nbi__reserved2_
},
546 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
552 N.B. the following function does not decode values.
553 Decoding is handled by the secondary opcode functions it calls.
556 /* non-basic instruction */
558 /* don't do normal value decoding here */
560 DCPU16_WORD nbi_opcode
= val_b
;
561 const struct opcode_entry
*e
= opcode_nbi_entries
;
565 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
567 assert(e
->impl
!= NULL
);
569 TRACE(">> %s 0x%04x", e
->name
, val_b
);
570 e
->impl(vm
, 0, 0, val_a
, val_a_data
);
580 if b is a literal, it's aimed at a scratch register,
581 so it's fine to update, as it won't have any effect.
592 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
593 unsigned int acc
= *b
+ *a
;
599 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
608 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
609 unsigned int acc
= *b
- *a
;
615 vm
->reg
[DCPU16_REG_EX
] = (acc
> 0xffff);
624 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
625 unsigned int acc
= *b
* *a
;
631 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
640 /* sets b to b*a, signed */
641 int acc
= (short)*b
* (short)*a
;
647 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
656 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
663 vm
->reg
[DCPU16_REG_EX
] = 0;
666 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) / *a
;
676 /* sets b to b/a, signed, round towards 0 */
683 vm
->reg
[DCPU16_REG_EX
] = 0;
685 *b
= (short)*b
/ (short)*a
;
686 vm
->reg
[DCPU16_REG_EX
] = (short)(*b
<< 16) / (short)*a
;
696 /* sets b to b%a. if a==0, sets b to 0 instead. */
714 /* sets b to b%a, signed */
722 *b
= (short)*b
% (short)*a
;
774 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
775 unsigned int acc
= *b
>> *a
;
781 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
792 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
793 unsigned int acc
= *b
<< *a
;
799 vm
->reg
[DCPU16_REG_EX
] = (*b
<< 16) >> *a
;
810 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
811 unsigned int acc
= *b
<< *a
;
818 vm
->reg
[DCPU16_REG_EX
] = acc
>> 16;
827 /* performs next instruction only if (b&a)!=0 */
832 if ((*b
& *a
) != 0) {
844 /* performs next instruction only if (b&a)==0 */
849 if ((*b
& *a
) == 0) {
861 /* performs next instruction only if b==a */
878 /* performs next instruction only if b!=a */
895 /* performs next instruction only if b>a */
912 /* performs next instruction only if b>a (signed) */
929 /* performs next instruction only if b<a */
946 /* performs next instruction only if b<a (signed) */
963 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
969 acc
= *b
+ *a
+ vm
->reg
[DCPU16_REG_EX
];
972 vm
->reg
[DCPU16_REG_EX
] = 0x0001;
974 vm
->reg
[DCPU16_REG_EX
] = 0x0000;
983 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
989 acc
= *b
- *a
+ vm
->reg
[DCPU16_REG_EX
];
992 vm
->reg
[DCPU16_REG_EX
] = 0xffff;
994 vm
->reg
[DCPU16_REG_EX
] = 0;
1003 /* sets b to a, then increases I and J by 1 */
1009 vm
->reg
[DCPU16_REG_I
] += 1;
1010 vm
->reg
[DCPU16_REG_J
] += 1;
1019 /* sets b to a, then decreases I and J by 1 */
1025 vm
->reg
[DCPU16_REG_I
] -= 1;
1026 vm
->reg
[DCPU16_REG_J
] -= 1;
1033 OP_IMPL(_reserved_
) {
1034 OP_BASIC(_reserved_
);
1036 WARN("reserved opcode invoked");
1038 ACCT_ILL(vm
->reg
[DCPU16_REG_PC
] - pc_adjust
);
1041 static const struct opcode_entry opcode_basic_entries
[] = {
1042 {0x00, "(nbi)", op__nbi_
},
1043 {0x01, "SET", op_set
},
1044 {0x02, "ADD", op_add
},
1045 {0x03, "SUB", op_sub
},
1046 {0x04, "MUL", op_mul
},
1047 {0x05, "MLI", op_mli
},
1048 {0x06, "DIV", op_div
},
1049 {0x07, "DVI", op_dvi
},
1050 {0x08, "MOD", op_mod
},
1051 {0x09, "MDI", op_mdi
},
1052 {0x0a, "AND", op_and
},
1053 {0x0b, "BOR", op_bor
},
1054 {0x0c, "XOR", op_xor
},
1055 {0x0d, "SHR", op_shr
},
1056 {0x0e, "ASR", op_asr
},
1057 {0x0f, "SHL", op_shl
},
1058 {0x10, "IFB", op_ifb
},
1059 {0x11, "IFC", op_ifc
},
1060 {0x12, "IFE", op_ife
},
1061 {0x13, "IFN", op_ifn
},
1062 {0x14, "IFG", op_ifg
},
1063 {0x15, "IFA", op_ifa
},
1064 {0x16, "IFL", op_ifl
},
1065 {0x17, "IFU", op_ifu
},
1066 {0x18, "(reserved)", op__reserved_
},
1067 {0x19, "(reserved)", op__reserved_
},
1068 {0x1a, "ADX", op_adx
},
1069 {0x1b, "SBX", op_sbx
},
1070 {0x1c, "(reserved)", op__reserved_
},
1071 {0x1d, "(reserved)", op__reserved_
},
1072 {0x1e, "STI", op_sti
},
1073 {0x1f, "STD", op_std
},
1078 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1079 if (value
<= 0x07) {
1080 printf(" %s", dcpu16_reg_names
[value
]);
1081 } else if (value
<= 0x0f) {
1082 printf(" [%s]", dcpu16_reg_names
[value
& 0x07]);
1083 } else if (value
<= 0x17) {
1084 printf(" [0x%04x + %s]", nextword
, dcpu16_reg_names
[value
& 0x07]);
1085 } else switch (value
) {
1087 if (value_position
== 0) { /* b */
1093 case 0x19: printf(" PEEK"); break;
1094 case 0x1a: printf(" PICK 0x%04x", nextword
); break;
1095 case 0x1b: printf(" SP"); break;
1096 case 0x1c: printf(" PC"); break;
1097 case 0x1d: printf(" EX"); break;
1098 case 0x1e: printf(" [0x%04x]", nextword
); break;
1099 case 0x1f: printf(" 0x%04x", nextword
); break;
1100 default: printf(" 0x%02x", value
- 0x21);
1105 /* split a sequence of (one to three) words into the components of an instruction */
1107 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1108 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1109 DCPU16_WORD
*instr_len
) {
1110 *opcode
= *a
= *b
= mem
[addr
];
1111 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1112 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1113 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1116 if ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) {
1117 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1118 TRACE("**b_data:%hu", **b_data
);
1124 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1125 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1126 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1127 TRACE("**a_data:%hu", **a_data
);
1134 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",
1140 *b_data
? **b_data
: 0,
1142 *a_data
? **a_data
: 0,
1147 /* dcpu16_mnemonify_buf
1148 print words as words
1150 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1151 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1152 const struct opcode_entry
*e
;
1154 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1156 if (opcode
== 0x0000)
1157 e
= opcode_nbi_entries
+
1158 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1160 e
= opcode_basic_entries
+ opcode
;
1161 printf("%s", e
->name
);
1164 dump_operand_value_(b
, b_data
? *b_data
: 0, 0);
1169 dump_operand_value_(a
, a_data
? *a_data
: 0, 1);
1175 /* dcpu16_disassemble_print
1176 print the words of the instruction at addr, followed by its assembly representation
1177 returns the length of the instruction in words
1179 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*vm
, DCPU16_WORD addr
) {
1180 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1181 DCPU16_WORD buf
[3] = { vm
->ram
[addr
], vm
->ram
[(DCPU16_WORD
)(addr
+ 1)], vm
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1182 unsigned int indent
= 0;
1183 unsigned int partial
= 0;
1189 Check the previous instruction, to see if this one should be
1190 indented. This check isn't foolproof, as preceeding addresses
1191 could be data which happen to match instructions..
1193 for (i
= 3; i
; i
--) {
1194 instruction_decode_(vm
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1198 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1205 /* just need instr_len */
1206 instruction_decode_(vm
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1208 /* show the raw words */
1209 printf("%04x", vm
->ram
[addr
]);
1210 for (i
= 1; i
< instr_len
; i
++) {
1211 printf(" %04x", vm
->ram
[addr
+ i
]);
1214 /* align things neatly, show the instruction */
1215 printf("%s%s ;%s%s",
1216 instr_len
< 3 ? " " : "",
1217 instr_len
< 2 ? " " : "",
1218 partial
? "*" : " ",
1221 dcpu16_mnemonify_buf(buf
);
1226 int dcpu16_interrupt(struct dcpu16
*vm
, DCPU16_WORD message
) {
1227 TRACE("%s>> message:0x%04x", __func__
, message
);
1228 return interrupt_enqueue_(vm
, message
);
1231 /* execute the next instruction */
1232 void dcpu16_step(struct dcpu16
*vm
) {
1233 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1235 const struct opcode_entry
*e
;
1239 /* signal interested parties that a new cycle has ticked */
1240 TRACE("%s: sending global cycle event", __func__
);
1241 acct_event_(vm
, DCPU16_ACCT_EV_CYCLE
, vm
->reg
[DCPU16_REG_PC
]);
1243 /* signal attached hardware */
1244 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1245 if (vm
->hw_table_
[i
].cycle
) {
1246 TRACE("%s: sending cycle to %s", __func__
, vm
->hw_table_
[i
].name_
);
1247 vm
->hw_table_
[i
].cycle(vm
, vm
->hw_table_
[i
].data
);
1251 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1253 /* consume what we decoded */
1254 /* this happens immediately as PC might be re-set as an operation */
1255 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1257 /* run the operation */
1258 for (e
= opcode_basic_entries
; e
->impl
; e
++) {
1259 if (e
->value
== opcode
) {
1260 TRACE("%s>> %s 0x%04x, 0x%04x", __func__
, e
->name
, b
, a
);
1261 e
->impl(vm
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1266 /* and jump over next instr(s) if needed */
1268 instruction_decode_(vm
->ram
, vm
->reg
[DCPU16_REG_PC
], &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1269 vm
->reg
[DCPU16_REG_PC
] += instr_len
;
1270 TRACE("++ SKIPPED %x words", instr_len
);
1271 if (opcode
>= 0x10 && opcode
<= 0x17) {
1272 /* skipping a branch instruction? skip branch's skippable instruction as well */
1279 /* if we're currently servicing interrupts */
1280 if (vm
->interrupts_deferred_
== 0) {
1281 /* and there are interrupts to be serviced */
1282 if (vm
->interrupts_head_
!= vm
->interrupts_tail_
) {
1283 DCPU16_WORD message
;
1284 message
= interrupt_dequeue_(vm
);
1286 if (vm
->reg
[DCPU16_REG_IA
]) {
1287 TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", vm
->reg
[DCPU16_REG_IA
], message
);
1288 /* then service the next interrupt */
1289 vm
->interrupts_deferred_
= 1;
1290 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_PC
];
1291 vm
->ram
[--vm
->reg
[DCPU16_REG_SP
]] = vm
->reg
[DCPU16_REG_A
];
1292 vm
->reg
[DCPU16_REG_PC
] = vm
->reg
[DCPU16_REG_IA
];
1293 vm
->reg
[DCPU16_REG_A
] = message
;
1295 TRACE("ignoring interrupt IA:0");
1302 print the current state of the machine
1303 shows current cycle count, registers, and next instruction
1305 void dcpu16_state_print(struct dcpu16
*vm
) {
1311 for (i
= 0; i
< 8; i
++)
1312 printf(" %s:0x%04x", dcpu16_reg_names
[i
], vm
->reg
[i
]);
1315 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1317 dcpu16_reg_names
[DCPU16_REG_EX
], vm
->reg
[DCPU16_REG_EX
],
1318 dcpu16_reg_names
[DCPU16_REG_SP
], vm
->reg
[DCPU16_REG_SP
],
1319 dcpu16_reg_names
[DCPU16_REG_PC
], vm
->reg
[DCPU16_REG_PC
],
1320 dcpu16_reg_names
[DCPU16_REG_IA
], vm
->reg
[DCPU16_REG_IA
],
1323 dcpu16_disassemble_print(vm
, vm
->reg
[DCPU16_REG_PC
]);
1328 * print raw ram contents from start to stop
1330 void dcpu16_dump_ram(struct dcpu16
*vm
, DCPU16_WORD start
, DCPU16_WORD end
) {
1332 const unsigned int n
= 8; /* words per line */
1336 for (i
= start
, j
= 0; i
<= end
; i
++, j
++) {
1338 printf("0x%04x:\t", i
);
1339 printf(" %04x%s", vm
->ram
[i
], (j
% n
) == (n
- 1) ? "\n" : "");
1341 if ((j
% n
) != (n
- 1))
1346 * registers new 'hardware' device with system
1348 int dcpu16_hw_add(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1352 if (vm
->hw_table_entries_
== 0xffff) {
1353 WARN("maximum hardware entries reached");
1357 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1358 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1359 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1360 if (tmp_ptr
== NULL
) {
1361 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1364 vm
->hw_table_
= tmp_ptr
;
1365 vm
->hw_table_allocated_
+= 32;
1368 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1369 vm
->hw_table_entries_
++;
1375 * Register callback fn to be triggered whenever event matching any events
1376 * in bitwise mask occur.
1378 int dcpu16_acct_add(struct dcpu16
*vm
, dcpu16_acct_event mask
, dcpu16_ev_cb_t
*fn
, void *data
) {
1379 struct dcpu16_acct_cb cb
;
1388 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1389 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1390 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1391 if (tmp_ptr
== NULL
) {
1392 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1395 vm
->cb_table_
= tmp_ptr
;
1396 vm
->cb_table_allocated_
+= 32;
1399 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1400 vm
->cb_table_entries_
++;
1406 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1408 void dcpu16_reset(struct dcpu16
*vm
) {
1415 vm
->interrupts_deferred_
= 0;
1417 memset(vm
->interrupts_
, 0, sizeof vm
->interrupts_
);
1418 vm
->interrupts_head_
= 0;
1419 vm
->interrupts_tail_
= 0;
1421 /* signal attached hardware */
1422 for (i
= 0; i
< vm
->hw_table_entries_
; i
++) {
1423 if (vm
->hw_table_
[i
].reset
)
1424 vm
->hw_table_
[i
].reset(vm
, vm
->hw_table_
[i
].data
);
1427 memset(vm
->reg
, 0, sizeof vm
->reg
);
1428 memset(vm
->ram
, 0, sizeof vm
->ram
);
1431 acct_event_(vm
, DCPU16_ACCT_EV_RESET
, 0);
1435 * allocate a new dcpu16 instance
1437 struct dcpu16
*dcpu16_new(void) {
1440 vm
= calloc(1, sizeof *vm
);
1442 WARN("%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1444 vm
->warn_cb_
= warn_cb_
;
1445 vm
->trace_cb_
= trace_cb_
;
1451 * release a dcpu16 instance
1453 void dcpu16_delete(struct dcpu16
**vm
) {