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 04 06 - first functionality achieved
22 * 2012 04 09 - minor cleanups
23 * 2012 04 10 - moved cli to separate module
24 * 2012 04 12 - added basic callback support for address accesses
25 * 2012 05 05 - start of v1.7 revisions
26 * 2012 05 08 - v1.7 revisions mostly complete
29 * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
30 * !! v1.7 hardware interface needs to be finished
31 * !! v1.7 interrupts need to be finished
32 * change api to print into buffers rather than stdio
33 * refactor opcode functiontables into switch statements
34 * let callbacks determine whether to override events, or just observe
35 * sort init callbacks by base addr, to call in-order
36 * make all callbacks register addr range of interest
39 static const char * const src_id_
= "$Id$";
41 #define OPCODE_BASIC_BITS 5
42 #define OPCODE_OPERAND_B_BITS 5
43 #define OPCODE_OPERAND_A_BITS 6
45 static const char * const regnames_
= "ABCXYZIJ";
47 /* some default warning and debug reporting functions, which can be overridden by clients */
48 #define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
49 static inline void warn_(char *fmt
, ...) __attribute__((format(printf
, 1, 2)));
51 void warn_(char *fmt
, ...) {
54 fprintf(stderr
, "[warning] ");
56 vfprintf(stderr
, fmt
, ap
);
58 fprintf(stderr
, "\n");
61 static void (*warn_cb_
)(char *fmt
, ...) = warn_
;
62 void dcpu16_warn_cb_set(void (*fn
)(char *fmt
, ...)) {
67 #define TRACE(...) do { if (trace_cb_) trace_cb_(__VA_ARGS__); } while (0)
68 static inline void trace_(char *fmt
, ...) __attribute__((format(printf
, 1, 2)));
70 void trace_(char *fmt
, ...) {
73 fprintf(stdout
, "[debug] ");
75 vfprintf(stdout
, fmt
, ap
);
77 fprintf(stdout
, "\n");
81 #define TRACE(...) do {} while(0)
83 static void (*trace_cb_
)(char *fmt
, ...) =
90 void dcpu16_trace_cb_set(void (*fn
)(char *fmt
, ...)) {
96 * invokes callbacks for specified event
99 void acct_event_(struct dcpu16
*vm
, dcpu16_acct_event ev
, DCPU16_WORD addr
) {
100 struct dcpu16_acct_cb
*cb
= vm
->cb_table_
;
103 for (i
= 0; i
< vm
->cb_table_entries_
; i
++) {
104 if ( (cb
[i
].mask
& ev
) )
105 cb
[i
].fn(vm
, ev
, addr
, cb
[i
].data
);
109 /* convert register name to index into register array */
111 off_t
reg_index_(int reg
) {
112 return strchr(regnames_
, reg
) - regnames_
;
115 /* add an entry to the interrupt queue */
117 int interrupt_enqueue_(struct dcpu16
*d
, DCPU16_WORD message
) {
118 d
->interrupts_
[d
->interrupts_tail_
] = message
;
119 d
->interrupts_tail_
+= 1;
120 d
->interrupts_tail_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
122 if (d
->interrupts_tail_
== d
->interrupts_head_
) {
124 WARN("interrupt queue overflow (system is now on fire)");
132 DCPU16_WORD
interrupt_dequeue_(struct dcpu16
*d
) {
135 if (d
->interrupts_tail_
== d
->interrupts_head_
) {
136 WARN("interrupt underflow");
140 message
= d
->interrupts_
[d
->interrupts_head_
];
141 d
->interrupts_head_
+= 1;
142 d
->interrupts_head_
%= DCPU16_INTERRUPT_QUEUE_SIZE
;
148 * sets *v to be the address of the represented value
149 * value_is_a is 0 for b, 1 for a, alters behavior of some operands
150 * value_data is 'nextword' for this operand, ignored if unused
151 * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
152 * e_addr is set to a referenced address, for accounting callback
153 * pc_adjust is set to how to change the program counter
154 * stack_adjust is set to how to change the stack pointer
155 * cycles is set to number of cycles spent looking up operand
157 * zero all adjustables before decoding first operand, and pass in these values when
158 * decoding next operand..
162 void value_decode_(struct dcpu16
*d
, DCPU16_WORD value
, unsigned int value_is_a
, DCPU16_WORD value_data
,
163 DCPU16_WORD
*work_v
, DCPU16_WORD
**v
, DCPU16_WORD
*e_addr
,
164 short *pc_adjust
, short *sp_adjust
, unsigned int *cycle_adjust
) {
165 assert(value
<= 0x3f);
167 DCPU16_WORD pc
= (DCPU16_WORD
)(d
->pc
+ *pc_adjust
),
168 sp
= (DCPU16_WORD
)(d
->sp
+ *sp_adjust
);
170 TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n",
176 if (value
<= 0x07) { /* register */
178 TRACE("%s>> %c (0x%04x)",
185 if (value
<= 0x0f) { /* [register] */
186 *v
= &(d
->ram
[ d
->reg
[value
& 0x07] ]);
187 *e_addr
= d
->reg
[value
& 0x07];
188 TRACE("%s>> [%c] [0x%04x] (0x%04x)",
190 regnames_
[value
& 0x07],
191 d
->reg
[value
& 0x07],
196 if (value
<= 0x17) { /* [next word + register] */
197 *pc_adjust
+= 1; /* consume next word */
199 *e_addr
= value_data
+ d
->reg
[value
& 0x07];
200 *v
= d
->ram
+ *e_addr
;
201 TRACE("%s>> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
203 regnames_
[value
& 0x07],
205 d
->reg
[value
& 0x07],
211 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
212 if (value_is_a
== 0) { /* b */
213 *v
= &(d
->ram
[sp
- 1]);
216 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__
, sp
- 1, **v
);
221 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__
, sp
, **v
);
225 case 0x19: /* PEEK/[SP] */
228 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
234 case 0x1a: /* PICK n */
237 *e_addr
= sp
+ value_data
;
238 *v
= d
->ram
+ *e_addr
;
239 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
248 TRACE("%s>> %s (0x%04x)",
256 TRACE("%s>> %s (0x%04x)",
264 TRACE("%s>> %s (0x%04x)",
270 case 0x1e: /* [next word] / [[pc++]] */
273 *e_addr
= value_data
;
274 *v
= d
->ram
+ *e_addr
;
275 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
281 case 0x1f: /* next word (literal) / [pc++] */
284 *work_v
= value_data
;
286 TRACE("%s>> nextword (0x%04x)",
291 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
292 *work_v
= (value
& 0x1f) - 1;
294 TRACE("%s>> literal (0x%04x)",
301 #define OPCODE_NAME_LEN 16
302 struct opcode_entry
{
303 unsigned short value
;
304 char name
[OPCODE_NAME_LEN
];
305 void (*impl
)(struct dcpu16
*, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
, DCPU16_WORD
);
308 /* messy boilerplate for opcode handlers */
310 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
311 /* opcode does adjust stack and cycle count */
313 #define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
315 #define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
316 DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
317 short pc_adjust = 0, sp_adjust = 0;\
318 unsigned int cycle_adjust = 0;\
321 value_decode_(d, val_a, 1, val_a_data,\
322 &d->reg_work_[1], &a, &ev_a_addr,\
323 &pc_adjust, &sp_adjust, &cycle_adjust);\
325 d->cycle += cycle_adjust;\
327 #define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
328 #define OP_BASIC_ value_decode_(d, val_b, 0, val_b_data,\
329 &d->reg_work_[0], &b, &ev_b_addr,\
330 &pc_adjust, &sp_adjust, &cycle_adjust)
331 #define OP_BASIC(x) OP_TYPE(OP_BASIC_)
332 #define OP_NBI(x) OP_TYPE(OP_NBI_)
334 /* 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 */
337 accounting helpers, these fire off the related callbacks for memory reads,
338 memory writes, and execution of reserved instructions
340 #define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
341 #define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
342 #define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
344 /* extended opcodes */
347 N.B. this next function currently decodes values -- id est, it is
348 an opcode processing terminus; however, if 'future instruction set
349 expansion' happens, this will probably need to behave more like
350 the OP_IMPL(_nbi_) function which invoked it, if those instructions
351 have zero or differently styled operands.
353 OP_IMPL(nbi__reserved_
) {
354 OP_NBI(nbi__reserved_
);
355 /* reserved for future expansion */
357 /* fire an illegal instruction event for current instruction */
358 DCPU16_WORD future_opcode
= (d
->ram
[d
->pc
- pc_adjust
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
));
359 WARN("reserved future opcode 0x%04x invoked", future_opcode
);
361 ACCT_ILL(d
->pc
- pc_adjust
);
366 /* pushes the address of the next instruction to the stack, then sets PC to a */
370 d
->ram
[ --d
->sp
] = d
->pc
;
378 OP_IMPL(nbi__reserved2_
) {
379 OP_NBI(nbi__reserved2_
);
382 WARN("reserved nbi opcode invoked");
384 ACCT_ILL(d
->pc
- pc_adjust
);
393 if ( interrupt_enqueue_(d
, *a
) ) {
394 WARN("failed to queue interrupt");
398 if (d
->interrupts_deferred_
)
401 d
->interrupts_deferred_
= 1;
402 d
->ram
[--d
->sp
] = d
->pc
;
403 d
->ram
[--d
->sp
] = d
->reg
[reg_index_('A')];
427 /* does this just ignore its operand? */
431 d
->interrupts_deferred_
= 0;
432 d
->reg
[reg_index_('A')] = d
->ram
[d
->sp
++];
433 d
->pc
= d
->ram
[d
->sp
++];
440 d
->interrupts_deferred_
= 1;
442 d
->interrupts_deferred_
= 0;
453 *a
= d
->hw_table_entries_
;
463 if (*a
>= d
->hw_table_entries_
) {
464 WARN("hardware query for non-extant device 0x%04x", *a
);
465 d
->reg
[reg_index_('A')] = 0;
466 d
->reg
[reg_index_('B')] = 0;
467 d
->reg
[reg_index_('C')] = 0;
468 d
->reg
[reg_index_('X')] = 0;
469 d
->reg
[reg_index_('Y')] = 0;
473 d
->reg
[reg_index_('A')] = d
->hw_table_
[*a
].id_l
;
474 d
->reg
[reg_index_('B')] = d
->hw_table_
[*a
].id_h
;
475 d
->reg
[reg_index_('C')] = d
->hw_table_
[*a
].ver
;
476 d
->reg
[reg_index_('X')] = d
->hw_table_
[*a
].mfg_l
;
477 d
->reg
[reg_index_('Y')] = d
->hw_table_
[*a
].mfg_h
;
487 if (*a
> d
->hw_table_entries_
) {
488 WARN("interrupt for non-extant device 0x%04x", *a
);
493 d
->hw_table_
[*a
].int_fn(d
, d
->hw_table_
[*a
].data
);
502 WARN("system on fire");
507 static const struct opcode_entry opcode_nbi_entries
[] = {
508 {0x00, "(reserved)", op_nbi__reserved_
},
509 {0x01, "JSR", op_nbi_jsr
},
510 {0x02, "(reserved)", op_nbi__reserved2_
},
511 {0x03, "(reserved)", op_nbi__reserved2_
},
512 {0x04, "(reserved)", op_nbi__reserved2_
},
513 {0x05, "(reserved)", op_nbi__reserved2_
},
514 {0x06, "(reserved)", op_nbi__reserved2_
},
515 {0x07, "HCF", op_nbi_hcf
}, /* undocumented */
516 {0x08, "INT", op_nbi_int
},
517 {0x09, "IAG", op_nbi_iag
},
518 {0x0a, "IAS", op_nbi_ias
},
519 {0x0b, "RFI", op_nbi_rfi
},
520 {0x0c, "IAQ", op_nbi_iaq
},
521 {0x0d, "(reserved)", op_nbi__reserved2_
},
522 {0x0e, "(reserved)", op_nbi__reserved2_
},
523 {0x0f, "(reserved)", op_nbi__reserved2_
},
524 {0x10, "HWN", op_nbi_hwn
},
525 {0x11, "HWQ", op_nbi_hwq
},
526 {0x12, "HWI", op_nbi_hwi
},
527 {0x13, "(reserved)", op_nbi__reserved2_
},
530 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
536 N.B. the following function does not decode values, (thus does not advance sp)
537 Decoding is handled by the secondary opcode functions it calls.
540 /* non-basic instruction */
542 /* don't do normal value decoding here */
544 DCPU16_WORD nbi_opcode
= val_b
;
545 const struct opcode_entry
*e
= opcode_nbi_entries
;
549 e
= opcode_nbi_entries
+ ( (nbi_opcode
< OPCODE_NBI_MAX
) ? nbi_opcode
: (OPCODE_NBI_MAX
- 1) );
551 assert(e
->impl
!= NULL
);
553 TRACE(">> %s 0x%04x", e
->name
, val_b
);
554 e
->impl(d
, 0, 0, val_a
, val_a_data
);
564 if b is a literal, it's aimed at a scratch register,
565 so it's fine to update, as it won't have any effect.
576 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
577 unsigned int acc
= *b
+ *a
;
583 d
->ex
= (acc
> 0xffff);
592 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
593 unsigned int acc
= *b
- *a
;
599 d
->ex
= (acc
> 0xffff);
608 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
609 unsigned int acc
= *b
* *a
;
624 /* sets b to b*a, signed */
625 int acc
= (short)*b
* (short)*a
;
640 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
650 d
->ex
= (*b
<< 16) / *a
;
660 /* sets b to b/a, signed, round towards 0 */
669 *b
= (short)*b
/ (short)*a
;
670 d
->ex
= (short)(*b
<< 16) / (short)*a
;
680 /* sets b to b%a. if a==0, sets b to 0 instead. */
698 /* sets b to b%a, signed */
706 *b
= (short)*b
% (short)*a
;
758 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
759 unsigned int acc
= *b
>> *a
;
765 d
->ex
= (*b
<< 16) >> *a
;
776 /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
777 unsigned int acc
= *b
<< *a
;
783 d
->ex
= (*b
<< 16) >> *a
;
794 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
795 unsigned int acc
= *b
<< *a
;
811 /* performs next instruction only if (b&a)!=0 */
816 if ((*b
& *a
) != 0) {
828 /* performs next instruction only if (b&a)==0 */
833 if ((*b
& *a
) == 0) {
845 /* performs next instruction only if b==a */
862 /* performs next instruction only if b!=a */
879 /* performs next instruction only if b>a */
896 /* performs next instruction only if b>a (signed) */
913 /* performs next instruction only if b<a */
930 /* performs next instruction only if b<a (signed) */
947 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
953 acc
= *b
+ *a
+ d
->ex
;
967 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
973 acc
= *b
- *a
+ d
->ex
;
987 /* sets b to a, then increases I and J by 1 */
993 d
->reg
[reg_index_('I')] += 1;
994 d
->reg
[reg_index_('J')] += 1;
1003 /* sets b to a, then decreases I and J by 1 */
1009 d
->reg
[reg_index_('I')] -= 1;
1010 d
->reg
[reg_index_('J')] -= 1;
1017 OP_IMPL(_reserved_
) {
1018 OP_BASIC(_reserved_
);
1020 WARN("reserved opcode invoked");
1022 ACCT_ILL(d
->pc
- pc_adjust
);
1025 static const struct opcode_entry opcode_basic_entries
[] = {
1026 {0x00, "(nbi)", op__nbi_
},
1027 {0x01, "SET", op_set
},
1028 {0x02, "ADD", op_add
},
1029 {0x03, "SUB", op_sub
},
1030 {0x04, "MUL", op_mul
},
1031 {0x05, "MLI", op_mli
},
1032 {0x06, "DIV", op_div
},
1033 {0x07, "DVI", op_dvi
},
1034 {0x08, "MOD", op_mod
},
1035 {0x09, "MDI", op_mdi
},
1036 {0x0a, "AND", op_and
},
1037 {0x0b, "BOR", op_bor
},
1038 {0x0c, "XOR", op_xor
},
1039 {0x0d, "SHR", op_shr
},
1040 {0x0e, "ASR", op_asr
},
1041 {0x0f, "SHL", op_shl
},
1042 {0x10, "IFB", op_ifb
},
1043 {0x11, "IFC", op_ifc
},
1044 {0x12, "IFE", op_ife
},
1045 {0x13, "IFN", op_ifn
},
1046 {0x14, "IFG", op_ifg
},
1047 {0x15, "IFA", op_ifa
},
1048 {0x16, "IFL", op_ifl
},
1049 {0x17, "IFU", op_ifu
},
1050 {0x18, "(reserved)", op__reserved_
},
1051 {0x19, "(reserved)", op__reserved_
},
1052 {0x1a, "ADX", op_adx
},
1053 {0x1b, "SBX", op_sbx
},
1054 {0x1c, "(reserved)", op__reserved_
},
1055 {0x1d, "(reserved)", op__reserved_
},
1056 {0x1e, "STI", op_sti
},
1057 {0x1f, "STD", op_std
},
1062 void dump_operand_value_(DCPU16_WORD value
, DCPU16_WORD nextword
, unsigned int value_position
) {
1063 if (value
<= 0x07) {
1064 printf(" %c", regnames_
[value
]);
1065 } else if (value
<= 0x0f) {
1066 printf(" [%c]", regnames_
[value
& 0x07]);
1067 } else if (value
<= 0x17) {
1068 printf(" [0x%04x + %c]", nextword
, regnames_
[value
& 0x07]);
1069 } else switch (value
) {
1071 if (value_position
== 0) { /* b */
1077 case 0x19: printf(" PEEK"); break;
1078 case 0x1a: printf(" PICK 0x%04x", nextword
); break;
1079 case 0x1b: printf(" SP"); break;
1080 case 0x1c: printf(" PC"); break;
1081 case 0x1d: printf(" EX"); break;
1082 case 0x1e: printf(" [0x%04x]", nextword
); break;
1083 case 0x1f: printf(" 0x%04x", nextword
); break;
1084 default: printf(" 0x%02x", value
- 0x21);
1089 /* split a sequence of (one to three) words into the components of an instruction */
1091 void instruction_decode_(DCPU16_WORD
*mem
, DCPU16_WORD addr
,
1092 DCPU16_WORD
*opcode
, DCPU16_WORD
*b
, DCPU16_WORD
**b_data
, DCPU16_WORD
*a
, DCPU16_WORD
**a_data
,
1093 DCPU16_WORD
*instr_len
) {
1094 *opcode
= *a
= *b
= mem
[addr
];
1095 *opcode
= mem
[addr
] & ((1 << OPCODE_BASIC_BITS
) - 1);
1096 *b
= (mem
[addr
] >> OPCODE_BASIC_BITS
) & ((1 << OPCODE_OPERAND_B_BITS
) - 1);
1097 *a
= (mem
[addr
] >> (OPCODE_BASIC_BITS
+ OPCODE_OPERAND_B_BITS
)) & ((1 << OPCODE_OPERAND_A_BITS
) - 1);
1100 if ( (*b
>= 0x10 && *b
<= 0x17) || *b
== 0x1e || *b
== 0x1f ) {
1101 *b_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1102 TRACE("**b_data:%hu", **b_data
);
1108 if ( (*opcode
!= 0x0000 || (*opcode
== 0 && *b
!= 0x0000) )
1109 && ( (*a
>= 0x10 && *a
<= 0x17) || *a
== 0x1e || *a
== 0x1f) ) {
1110 *a_data
= mem
+ (DCPU16_WORD
)(addr
+ *instr_len
);
1111 TRACE("**a_data:%hu", **a_data
);
1118 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",
1124 *b_data
? **b_data
: 0,
1126 *a_data
? **a_data
: 0,
1131 /* dcpu16_mnemonify_buf
1132 print words as words
1134 DCPU16_WORD
dcpu16_mnemonify_buf(DCPU16_WORD
*buf
) {
1135 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1136 const struct opcode_entry
*e
;
1138 instruction_decode_(buf
, 0, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1140 if (opcode
== 0x0000)
1141 e
= opcode_nbi_entries
+
1142 ( (b
< OPCODE_NBI_MAX
) ? b
: (OPCODE_NBI_MAX
- 1) );
1144 e
= opcode_basic_entries
+ opcode
;
1145 printf("%s", e
->name
);
1148 dump_operand_value_(b
, b_data
? *b_data
: 0, 0);
1153 dump_operand_value_(a
, a_data
? *a_data
: 0, 1);
1159 /* dcpu16_disassemble_print
1160 print the words of the instruction at addr, followed by its assembly representation
1161 returns the length of the instruction in words
1163 DCPU16_WORD
dcpu16_disassemble_print(struct dcpu16
*d
, DCPU16_WORD addr
) {
1164 DCPU16_WORD opcode
, b
, a
, instr_len
, i
, *b_data
, *a_data
;
1165 DCPU16_WORD buf
[3] = { d
->ram
[addr
], d
->ram
[(DCPU16_WORD
)(addr
+ 1)], d
->ram
[(DCPU16_WORD
)(addr
+ 2)] };
1166 unsigned int indent
= 0;
1167 unsigned int partial
= 0;
1173 Check the previous instruction, to see if this one should be
1174 indented. This check isn't foolproof, as preceeding addresses
1175 could be data which happen to match instructions..
1177 for (i
= 3; i
; i
--) {
1178 instruction_decode_(d
->ram
, (DCPU16_WORD
)(addr
- i
), &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1182 && (opcode
>= 0x10 && opcode
<= 0x17) ) {
1189 /* just need instr_len */
1190 instruction_decode_(d
->ram
, addr
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1192 /* show the raw words */
1193 printf("%04x", d
->ram
[addr
]);
1194 for (i
= 1; i
< instr_len
; i
++) {
1195 printf(" %04x", d
->ram
[addr
+ i
]);
1198 /* align things neatly, show the instruction */
1199 printf("%s%s ;%s%s",
1200 instr_len
< 3 ? " " : "",
1201 instr_len
< 2 ? " " : "",
1202 partial
? "*" : " ",
1205 dcpu16_mnemonify_buf(buf
);
1210 /* execute the next instruction */
1211 void dcpu16_step(struct dcpu16
*d
) {
1212 DCPU16_WORD opcode
, b
, a
, instr_len
, *b_data
, *a_data
;
1213 const struct opcode_entry
*e
;
1217 acct_event_(d
, DCPU16_ACCT_EV_CYCLE
, d
->pc
);
1219 /* if we're currently servicing interrupts */
1220 if (d
->interrupts_deferred_
== 0) {
1221 /* and there are interrupts to be serviced */
1222 if (d
->interrupts_head_
!= d
->interrupts_tail_
) {
1223 DCPU16_WORD message
;
1224 message
= interrupt_dequeue_(d
);
1227 TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", d
->ia
, message
);
1228 /* then service the next interrupt */
1229 d
->interrupts_deferred_
= 1;
1230 d
->ram
[--d
->sp
] = d
->pc
;
1231 d
->ram
[--d
->sp
] = d
->reg
[reg_index_('A')];
1233 d
->reg
[0] = message
;
1235 TRACE("ignoring interrupt IA:0");
1240 /* and make sure to execute an instruction after an interrupt */
1242 instruction_decode_(d
->ram
, d
->pc
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1244 for (e
= opcode_basic_entries
; e
->impl
; e
++) {
1245 if (e
->value
== opcode
) {
1246 TRACE("%s>> %s 0x%04x, 0x%04x", __func__
, e
->name
, b
, a
);
1247 e
->impl(d
, b
, b_data
? *b_data
: 0, a
, a_data
? *a_data
: 0);
1252 /* get ready for the next one */
1255 /* and jump over next instr(s) if needed */
1257 instruction_decode_(d
->ram
, d
->pc
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1259 TRACE("++ SKIPPED %x words", instr_len
);
1260 if (opcode
>= 0x10 && opcode
<= 0x17) {
1261 /* skipping a branch instruction? skip branch's skippable instruction as well */
1263 instruction_decode_(d
->ram
, d
->pc
, &opcode
, &b
, &b_data
, &a
, &a_data
, &instr_len
);
1265 TRACE("++ SKIPPED %x words", instr_len
);
1272 print the current state of the machine
1273 shows current cycle count, registers, and next instruction
1275 void dcpu16_state_print(struct dcpu16
*d
) {
1281 for (i
= 0; i
< 8; i
++)
1282 printf(" %c:0x%04x", regnames_
[i
], d
->reg
[i
]);
1285 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1293 dcpu16_disassemble_print(d
, d
->pc
);
1298 * print raw ram contents from start to stop
1300 void dcpu16_dump_ram(struct dcpu16
*d
, DCPU16_WORD start
, DCPU16_WORD end
) {
1302 const unsigned int n
= 8; /* words per line */
1306 for (i
= start
, j
= 0; i
<= end
; i
++, j
++) {
1308 printf("0x%04x:\t", i
);
1309 printf(" %04x%s", d
->ram
[i
], (j
% n
) == (n
- 1) ? "\n" : "");
1311 if ((j
% n
) != (n
- 1))
1316 * registers new 'hardware' device with system
1318 int dcpu16_hw_add(struct dcpu16
*vm
, struct dcpu16_hw
*hw
) {
1322 if (vm
->hw_table_entries_
== vm
->hw_table_allocated_
) {
1323 size_t new_entries
= vm
->hw_table_allocated_
+ 32;
1324 void *tmp_ptr
= realloc(vm
->hw_table_
, new_entries
* sizeof * (vm
->hw_table_
));
1325 if (tmp_ptr
== NULL
) {
1326 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1329 vm
->hw_table_
= tmp_ptr
;
1330 vm
->hw_table_allocated_
+= 32;
1333 memcpy(vm
->hw_table_
+ vm
->hw_table_entries_
, hw
, sizeof *hw
);
1334 vm
->hw_table_entries_
++;
1340 * Register callback fn to be triggered whenever event matching any events
1341 * in bitwise mask occur.
1343 int dcpu16_acct_add(struct dcpu16
*vm
, dcpu16_acct_event mask
, dcpu16_ev_cb_t
*fn
, void *data
) {
1344 struct dcpu16_acct_cb cb
;
1350 if (vm
->cb_table_entries_
== vm
->cb_table_allocated_
) {
1351 size_t new_entries
= vm
->cb_table_allocated_
+ 32;
1352 void *tmp_ptr
= realloc(vm
->cb_table_
, new_entries
* sizeof *(vm
->cb_table_
));
1353 if (tmp_ptr
== NULL
) {
1354 fprintf(stderr
, "%s():%s", "realloc", strerror(errno
));
1357 vm
->cb_table_
= tmp_ptr
;
1358 vm
->cb_table_allocated_
+= 32;
1361 memcpy(vm
->cb_table_
+ vm
->cb_table_entries_
, &cb
, sizeof cb
);
1362 vm
->cb_table_entries_
++;
1368 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1370 void dcpu16_reset(struct dcpu16
*d
) {
1374 memset(d
->reg
, 0, sizeof d
->reg
);
1380 memset(d
->ram
, 0, sizeof d
->ram
);
1382 acct_event_(d
, DCPU16_ACCT_EV_RESET
, 0);
1386 * allocate a new dcpu16 instance
1388 struct dcpu16
*dcpu16_new(void) {
1391 vm
= calloc(1, sizeof *vm
);
1393 WARN("%s: %s(%zu): %s", __func__
, "calloc", strerror(errno
));
1399 * release a dcpu16 instance
1401 void dcpu16_delete(struct dcpu16
**vm
) {
1402 if (!vm
|| !*vm
) return;