further reorg of module abstraction and control interface
[dcpu16] / dcpu16.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <assert.h>
8 #include <sysexits.h>
9
10 #include "dcpu16.h"
11
12 /*
13 * emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
14 * currently emulates '1.7' spec from http://pastebin.com/Q4JvQvnM
15 *
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.
19 *
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
24 *
25 * TODO
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
33 */
34
35 static const char * const src_id_ = "$Id$";
36
37 #define OPCODE_BASIC_BITS 5
38 #define OPCODE_OPERAND_B_BITS 5
39 #define OPCODE_OPERAND_A_BITS 6
40
41 const char * const dcpu16_reg_names[] = {
42 "A",
43 "B",
44 "C",
45 "X",
46 "Y",
47 "Z",
48 "I",
49 "J",
50 "PC",
51 "SP",
52 "EX",
53 "IA",
54 NULL
55 };
56
57 static void printf_null_(char *fmt, ...) { (void)fmt; }
58
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)));
62 static inline
63 void warn_(char *fmt, ...) {
64 va_list ap;
65
66 fprintf(stderr, "[warning] ");
67 va_start(ap, fmt);
68 vfprintf(stderr, fmt, ap);
69 va_end(ap);
70 fprintf(stderr, "\n");
71 fflush(stderr);
72 }
73 static void (*warn_cb_)(char *fmt, ...) = warn_;
74 void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
75 if (fn)
76 warn_cb_ = fn;
77 else
78 warn_cb_ = printf_null_;
79 }
80
81 #ifdef DEBUG
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)));
84 static inline
85 void trace_(char *fmt, ...) {
86 va_list ap;
87
88 fprintf(stdout, "[debug] ");
89 va_start(ap, fmt);
90 vfprintf(stdout, fmt, ap);
91 va_end(ap);
92 fprintf(stdout, "\n");
93 fflush(stdout);
94 }
95 #else /* DEBUG */
96 #define TRACE(...) do {} while(0)
97 #endif /* DEBUG */
98 static void (*trace_cb_)(char *fmt, ...) =
99 #ifdef DEBUG
100 trace_
101 #else /* DEBUG */
102 NULL
103 #endif
104 ;
105 void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
106 if (fn)
107 trace_cb_ = fn;
108 else
109 trace_cb_ = printf_null_;
110 }
111
112
113 /* acct_event_
114 * invokes callbacks for specified event
115 */
116 static inline
117 void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
118 struct dcpu16_acct_cb *cb = vm->cb_table_;
119 size_t i;
120
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);
124 }
125 }
126
127
128 /* add an entry to the interrupt queue */
129 static
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;
134
135 if (vm->interrupts_tail_ == vm->interrupts_head_) {
136 vm->on_fire_ = 1;
137 WARN("interrupt queue overflow (system is now on fire)");
138 return -1;
139 }
140
141 return 0;
142 }
143
144 static
145 DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) {
146 DCPU16_WORD message;
147
148 if (vm->interrupts_tail_ == vm->interrupts_head_) {
149 WARN("interrupt underflow");
150 return 0;
151 }
152
153 message = vm->interrupts_[vm->interrupts_head_];
154 vm->interrupts_head_ += 1;
155 vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
156
157 return message;
158 }
159
160 inline
161 void dcpu16_cycle_inc(struct dcpu16 *vm, unsigned int n) {
162 size_t i;
163
164 while (n--) {
165 /* new cycle */
166 vm->cycle_ += 1;
167 TRACE("%s>> starting cycle %llu", vm->cycle_);
168
169 /* signal interested cycle hooks */
170 acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]);
171
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]);
176 }
177 }
178 }
179
180 /* value_decode_
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
189 *
190 * zero all adjustables before decoding first operand, and pass in these values when
191 * decoding next operand..
192 *
193 */
194 static inline
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);
199
200 DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
201 sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_SP] + *sp_adjust);
202
203 TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
204 __func__,
205 value_is_a,
206 pc,
207 sp,
208 value_data);
209
210 if (value <= 0x07) { /* register */
211 *v = vm->reg + value;
212 TRACE("%s>> %s (0x%04x)",
213 __func__,
214 dcpu16_reg_names[value],
215 **v);
216 return;
217 }
218
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)",
223 __func__,
224 dcpu16_reg_names[value & 0x07],
225 vm->reg[value & 0x07],
226 **v);
227 return;
228 }
229
230 if (value <= 0x17) { /* [next word + register] */
231 *pc_adjust += 1; /* consume next word */
232 *cycle_adjust += 1;
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)",
236 __func__,
237 dcpu16_reg_names[value & 0x07],
238 value_data,
239 vm->reg[value & 0x07],
240 **v);
241 return;
242 }
243
244 switch (value) {
245 case 0x18: /* PUSH/[--SP] or POP/[SP++] */
246 if (value_is_a == 0) { /* b */
247 *v = &(vm->ram[sp - 1]);
248 *sp_adjust -= 1;
249 *e_addr = sp - 1;
250 TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v);
251 } else { /* a */
252 *v = &(vm->ram[sp]);
253 *sp_adjust += 1;
254 *e_addr = sp;
255 TRACE("%s>> POP [0x%04x] (0x%04x)", __func__, sp, **v);
256 }
257 break;
258
259 case 0x19: /* PEEK/[SP] */
260 *v = &(vm->ram[sp]);
261 *e_addr = sp;
262 TRACE("%s>> PEEK [0x%04x] (0x%04x)",
263 __func__,
264 sp,
265 **v);
266 break;
267
268 case 0x1a: /* PICK n */
269 *pc_adjust += 1;
270 *cycle_adjust += 1;
271 *e_addr = sp + value_data;
272 *v = vm->ram + *e_addr;
273 TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
274 __func__,
275 value_data,
276 sp + value_data,
277 **v);
278 break;
279
280 case 0x1b: /* SP */
281 *v = &(vm->reg[DCPU16_REG_SP]);
282 TRACE("%s>> %s (0x%04x)",
283 __func__,
284 dcpu16_reg_names[DCPU16_REG_SP],
285 **v);
286 break;
287
288 case 0x1c: /* PC */
289 *v = &(vm->reg[DCPU16_REG_PC]);
290 TRACE("%s>> %s (0x%04x)",
291 __func__,
292 dcpu16_reg_names[DCPU16_REG_PC],
293 **v);
294 break;
295
296 case 0x1d: /* EX */
297 *v = &(vm->reg[DCPU16_REG_EX]);
298 TRACE("%s>> %s (0x%04x)",
299 __func__,
300 dcpu16_reg_names[DCPU16_REG_EX],
301 **v);
302 break;
303
304 case 0x1e: /* [next word] / [[pc++]] */
305 *pc_adjust += 1;
306 *cycle_adjust += 1;
307 *e_addr = value_data;
308 *v = vm->ram + *e_addr;
309 TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
310 __func__,
311 value_data,
312 **v);
313 break;
314
315 case 0x1f: /* next word (literal) / [pc++] */
316 *pc_adjust += 1;
317 *cycle_adjust += 1;
318 *work_v = value_data;
319 *v = work_v;
320 TRACE("%s>> nextword (0x%04x)",
321 __func__,
322 **v);
323 break;
324
325 default: /* 0x20-0x3f: literal values 0xffff-0x1e */
326 *work_v = (value & 0x1f) - 1;
327 *v = work_v;
328 TRACE("%s>> literal (0x%04x)",
329 __func__,
330 **v);
331 }
332 }
333
334
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);
340 };
341
342 /* messy boilerplate for opcode handlers */
343
344 /* opcode doesn't adjust its own PC, the step function which invoked it handles that */
345 /* opcode does adjust stack and cycle count */
346
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)
348
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;\
353 do {\
354 op_type;\
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);\
360 } while (0)
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_)
367
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 */
369
370 /*
371 accounting helpers, these fire off the related callbacks for memory reads,
372 memory writes, and execution of reserved instructions
373 */
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)
377
378 /* extended opcodes */
379
380 /*
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.
386 */
387 OP_IMPL(nbi__reserved_) {
388 OP_NBI(nbi__reserved_);
389 /* reserved for future expansion */
390
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);
394
395 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
396 }
397
398 OP_IMPL(nbi_jsr) {
399 OP_NBI(nbi_jsr);
400 /* pushes the address of the next instruction to the stack, then sets PC to a */
401
402 ACCT_R(ev_a_addr);
403
404 vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC];
405 vm->reg[DCPU16_REG_PC] = *a;
406
407 dcpu16_cycle_inc(vm, 2);
408
409 ACCT_W(vm->reg[DCPU16_REG_SP] + 1);
410 }
411
412 OP_IMPL(nbi__reserved2_) {
413 OP_NBI(nbi__reserved2_);
414 /* reserved */
415
416 WARN("reserved nbi opcode invoked");
417
418 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
419 }
420
421 OP_IMPL(nbi_int) {
422 OP_NBI(nbi_int);
423
424 ACCT_R(ev_a_addr);
425
426 if (vm->reg[DCPU16_REG_IA]) {
427 if ( interrupt_enqueue_(vm, *a) ) {
428 WARN("failed to queue interrupt");
429 return;
430 }
431
432 if (vm->interrupts_deferred_)
433 return;
434
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];
439 vm->reg[0] = *a;
440 }
441
442 dcpu16_cycle_inc(vm, 4);
443 }
444
445 OP_IMPL(nbi_iag) {
446 OP_NBI(nbi_iag);
447
448 *a = vm->reg[DCPU16_REG_IA];
449
450 dcpu16_cycle_inc(vm, 1);
451
452 ACCT_W(ev_a_addr);
453 }
454
455 OP_IMPL(nbi_ias) {
456 OP_NBI(nbi_ias);
457
458 vm->reg[DCPU16_REG_IA] = *a;
459
460 dcpu16_cycle_inc(vm, 1);
461
462 ACCT_R(ev_a_addr);
463 }
464
465 /* does this just ignore its operand? */
466 OP_IMPL(nbi_rfi) {
467 OP_NBI(nbi_rfi);
468
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]++];
472
473 dcpu16_cycle_inc(vm, 3);
474 }
475
476 OP_IMPL(nbi_iaq) {
477 OP_NBI(nbi_iaq);
478
479 if (*a) {
480 vm->interrupts_deferred_ = 1;
481 } else {
482 vm->interrupts_deferred_ = 0;
483 }
484
485 dcpu16_cycle_inc(vm, 2);
486
487 ACCT_R(ev_a_addr);
488 }
489
490 OP_IMPL(nbi_hwn) {
491 OP_NBI(nbi_hwn);
492
493 ACCT_W(ev_a_addr);
494
495 *a = vm->hw_table_entries_;
496
497 dcpu16_cycle_inc(vm, 2);
498 }
499
500 OP_IMPL(nbi_hwq) {
501 OP_NBI(nbi_hwq);
502
503 ACCT_R(ev_a_addr);
504
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;
512 return;
513 }
514
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;
520
521 dcpu16_cycle_inc(vm, 4);
522 }
523
524 OP_IMPL(nbi_hwi) {
525 OP_NBI(nbi_hwi);
526
527 ACCT_R(ev_a_addr);
528
529 if (*a > vm->hw_table_entries_) {
530 WARN("interrupt for non-extant device 0x%04x", *a);
531 return;
532 }
533
534 dcpu16_cycle_inc(vm, 4);
535 if (vm->hw_table_[*a].hwi)
536 vm->hw_table_[*a].hwi(vm, &vm->hw_table_[*a]);
537 else
538 WARN("hardware 0x%04x has no interrupt handler", *a);
539 }
540
541 OP_IMPL(nbi_hcf) {
542 OP_NBI(nbi_hcf);
543
544 ACCT_R(ev_a_addr);
545
546 vm->on_fire_ = 1;
547 WARN("system on fire");
548
549 dcpu16_cycle_inc(vm, 9);
550 }
551
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_},
573 {0x00, "", NULL}
574 };
575 #define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
576
577
578 /* basic opcodes */
579
580 /*
581 N.B. the following function does not decode values.
582 Decoding is handled by the secondary opcode functions it calls.
583 */
584 OP_IMPL(_nbi_) {
585 /* non-basic instruction */
586
587 /* don't do normal value decoding here */
588
589 DCPU16_WORD nbi_opcode = val_b;
590 const struct opcode_entry *e = opcode_nbi_entries;
591
592 (void)val_b_data;
593
594 e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
595
596 assert(e->impl != NULL);
597
598 TRACE(">> %s 0x%04x", e->name, val_b);
599 e->impl(vm, 0, 0, val_a, val_a_data);
600 }
601
602 OP_IMPL(set) {
603 OP_BASIC(set);
604 /* sets b to a */
605
606 ACCT_R(ev_a_addr);
607
608 /*
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.
611 */
612 *b = *a;
613
614 dcpu16_cycle_inc(vm, 1);
615
616 ACCT_W(ev_b_addr);
617 }
618
619 OP_IMPL(add) {
620 OP_BASIC(add);
621 /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
622 unsigned int acc = *b + *a;
623
624 ACCT_R(ev_b_addr);
625 ACCT_R(ev_a_addr);
626
627 *b = acc;
628 vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
629
630 dcpu16_cycle_inc(vm, 2);
631
632 ACCT_W(ev_b_addr);
633 }
634
635 OP_IMPL(sub) {
636 OP_BASIC(sub);
637 /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
638 unsigned int acc = *b - *a;
639
640 ACCT_R(ev_b_addr);
641 ACCT_R(ev_a_addr);
642
643 *b = acc;
644 vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
645
646 dcpu16_cycle_inc(vm, 2);
647
648 ACCT_W(ev_b_addr);
649 }
650
651 OP_IMPL(mul) {
652 OP_BASIC(mul);
653 /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
654 unsigned int acc = *b * *a;
655
656 ACCT_R(ev_b_addr);
657 ACCT_R(ev_a_addr);
658
659 *b = acc;
660 vm->reg[DCPU16_REG_EX] = acc >> 16;
661
662 dcpu16_cycle_inc(vm, 2);
663
664 ACCT_W(ev_b_addr);
665 }
666
667 OP_IMPL(mli) {
668 OP_BASIC(mli);
669 /* sets b to b*a, signed */
670 int acc = (short)*b * (short)*a;
671
672 ACCT_R(ev_b_addr);
673 ACCT_R(ev_a_addr);
674
675 *b = acc;
676 vm->reg[DCPU16_REG_EX] = acc >> 16;
677
678 dcpu16_cycle_inc(vm, 2);
679
680 ACCT_W(ev_b_addr);
681 }
682
683 OP_IMPL(div) {
684 OP_BASIC(div);
685 /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
686
687 ACCT_R(ev_b_addr);
688 ACCT_R(ev_a_addr);
689
690 if (*a == 0) {
691 *b = 0;
692 vm->reg[DCPU16_REG_EX] = 0;
693 } else {
694 *b = *b / *a;
695 vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
696 }
697
698 dcpu16_cycle_inc(vm, 3);
699
700 ACCT_W(ev_b_addr);
701 }
702
703 OP_IMPL(dvi) {
704 OP_BASIC(dvi);
705 /* sets b to b/a, signed, round towards 0 */
706
707 ACCT_R(ev_b_addr);
708 ACCT_R(ev_a_addr);
709
710 if (*a == 0) {
711 *b = 0;
712 vm->reg[DCPU16_REG_EX] = 0;
713 } else {
714 *b = (short)*b / (short)*a;
715 vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
716 }
717
718 dcpu16_cycle_inc(vm, 3);
719
720 ACCT_W(ev_b_addr);
721 }
722
723 OP_IMPL(mod) {
724 OP_BASIC(mod);
725 /* sets b to b%a. if a==0, sets b to 0 instead. */
726
727 ACCT_R(ev_b_addr);
728 ACCT_R(ev_a_addr);
729
730 if (*a == 0) {
731 *b = 0;
732 } else {
733 *b = *b % *a;
734 }
735
736 dcpu16_cycle_inc(vm, 3);
737
738 ACCT_W(ev_a_addr);
739 }
740
741 OP_IMPL(mdi) {
742 OP_BASIC(mdi);
743 /* sets b to b%a, signed */
744
745 ACCT_R(ev_b_addr);
746 ACCT_R(ev_a_addr);
747
748 if (*a == 0) {
749 *b = 0;
750 } else {
751 *b = (short)*b % (short)*a;
752 }
753
754 dcpu16_cycle_inc(vm, 3);
755
756 ACCT_W(ev_b_addr);
757 }
758
759 OP_IMPL(and) {
760 OP_BASIC(and);
761 /* sets b to b&a */
762
763 ACCT_R(ev_b_addr);
764 ACCT_R(ev_a_addr);
765
766 *b = *b & *a;
767
768 dcpu16_cycle_inc(vm, 1);
769
770 ACCT_W(ev_b_addr);
771 }
772
773 OP_IMPL(bor) {
774 OP_BASIC(bor);
775 /* sets b to b|a */
776
777 ACCT_R(ev_b_addr);
778 ACCT_R(ev_a_addr);
779
780 *b = *b | *a;
781
782 dcpu16_cycle_inc(vm, 1);
783
784 ACCT_W(ev_b_addr);
785 }
786
787 OP_IMPL(xor) {
788 OP_BASIC(xor);
789 /* sets b to b^a */
790
791 ACCT_R(ev_b_addr);
792 ACCT_R(ev_a_addr);
793
794 *b = *b ^ *a;
795
796 dcpu16_cycle_inc(vm, 1);
797
798 ACCT_W(ev_b_addr);
799 }
800
801 OP_IMPL(shr) {
802 OP_BASIC(shr);
803 /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
804 unsigned int acc = *b >> *a;
805
806 ACCT_R(ev_b_addr);
807 ACCT_R(ev_a_addr);
808
809 *b = acc & 0xffff;
810 vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
811
812 dcpu16_cycle_inc(vm, 2);
813
814 WARN("IMPLEMENT");
815
816 ACCT_W(ev_b_addr);
817 }
818
819 OP_IMPL(asr) {
820 OP_BASIC(asr);
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;
823
824 ACCT_R(ev_b_addr);
825 ACCT_R(ev_a_addr);
826
827 *b = acc & 0xffff;
828 vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
829
830 dcpu16_cycle_inc(vm, 2);
831
832 WARN("IMPLEMENT");
833
834 ACCT_W(ev_b_addr);
835 }
836
837 OP_IMPL(shl) {
838 OP_BASIC(shl);
839 /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
840 unsigned int acc = *b << *a;
841
842 ACCT_R(ev_b_addr);
843 ACCT_R(ev_a_addr);
844
845 *b = acc;
846
847 vm->reg[DCPU16_REG_EX] = acc >> 16;
848
849 dcpu16_cycle_inc(vm, 2);
850
851 ACCT_W(ev_b_addr);
852 }
853
854 OP_IMPL(ifb) {
855 OP_BASIC(ifb);
856 /* performs next instruction only if (b&a)!=0 */
857
858 ACCT_R(ev_b_addr);
859 ACCT_R(ev_a_addr);
860
861 if ((*b & *a) != 0) {
862 /* */
863 } else {
864 vm->skip_ = 1;
865 dcpu16_cycle_inc(vm, 1);
866 }
867
868 dcpu16_cycle_inc(vm, 2);
869 }
870
871 OP_IMPL(ifc) {
872 OP_BASIC(ifc);
873 /* performs next instruction only if (b&a)==0 */
874
875 ACCT_R(ev_b_addr);
876 ACCT_R(ev_a_addr);
877
878 if ((*b & *a) == 0) {
879 /* */
880 } else {
881 vm->skip_ = 1;
882 dcpu16_cycle_inc(vm, 1);
883 }
884
885 dcpu16_cycle_inc(vm, 2);
886 }
887
888 OP_IMPL(ife) {
889 OP_BASIC(ife);
890 /* performs next instruction only if b==a */
891
892 ACCT_R(ev_b_addr);
893 ACCT_R(ev_a_addr);
894
895 if (*b == *a) {
896 /* */
897 } else {
898 vm->skip_ = 1;
899 dcpu16_cycle_inc(vm, 1);
900 }
901
902 dcpu16_cycle_inc(vm, 2);
903 }
904
905 OP_IMPL(ifn) {
906 OP_BASIC(ifn);
907 /* performs next instruction only if b!=a */
908
909 ACCT_R(ev_b_addr);
910 ACCT_R(ev_a_addr);
911
912 if (*b != *a) {
913 /* */
914 } else {
915 vm->skip_ = 1;
916 dcpu16_cycle_inc(vm, 1);
917 }
918
919 dcpu16_cycle_inc(vm, 2);
920 }
921
922 OP_IMPL(ifg) {
923 OP_BASIC(ifg);
924 /* performs next instruction only if b>a */
925
926 ACCT_R(ev_b_addr);
927 ACCT_R(ev_a_addr);
928
929 if (*b > *a) {
930 /* */
931 } else {
932 vm->skip_ = 1;
933 dcpu16_cycle_inc(vm, 1);
934 }
935
936 dcpu16_cycle_inc(vm, 2);
937 }
938
939 OP_IMPL(ifa) {
940 OP_BASIC(ifa);
941 /* performs next instruction only if b>a (signed) */
942
943 ACCT_R(ev_b_addr);
944 ACCT_R(ev_a_addr);
945
946 if (*b > *a) {
947 /* */
948 } else {
949 vm->skip_ = 1;
950 dcpu16_cycle_inc(vm, 1);
951 }
952
953 dcpu16_cycle_inc(vm, 2);
954 }
955
956 OP_IMPL(ifl) {
957 OP_BASIC(ifl);
958 /* performs next instruction only if b<a */
959
960 ACCT_R(ev_b_addr);
961 ACCT_R(ev_a_addr);
962
963 if (*b < *a) {
964 /* */
965 } else {
966 vm->skip_ = 1;
967 dcpu16_cycle_inc(vm, 1);
968 }
969
970 dcpu16_cycle_inc(vm, 2);
971 }
972
973 OP_IMPL(ifu) {
974 OP_BASIC(ifu);
975 /* performs next instruction only if b<a (signed) */
976
977 ACCT_R(ev_b_addr);
978 ACCT_R(ev_a_addr);
979
980 if (*b < *a) {
981 /* */
982 } else {
983 vm->skip_ = 1;
984 dcpu16_cycle_inc(vm, 1);
985 }
986
987 dcpu16_cycle_inc(vm, 2);
988 }
989
990 OP_IMPL(adx) {
991 OP_BASIC(adx);
992 /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
993 unsigned int acc;
994
995 ACCT_R(ev_b_addr);
996 ACCT_R(ev_a_addr);
997
998 acc = *b + *a + vm->reg[DCPU16_REG_EX];
999 *b = acc & 0xffff;
1000 if (acc > 0xffff)
1001 vm->reg[DCPU16_REG_EX] = 0x0001;
1002 else
1003 vm->reg[DCPU16_REG_EX] = 0x0000;
1004
1005 dcpu16_cycle_inc(vm, 3);
1006
1007 ACCT_W(ev_b_addr);
1008 }
1009
1010 OP_IMPL(sbx) {
1011 OP_BASIC(sbx);
1012 /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
1013 unsigned int acc;
1014
1015 ACCT_R(ev_b_addr);
1016 ACCT_R(ev_a_addr);
1017
1018 acc = *b - *a + vm->reg[DCPU16_REG_EX];
1019 *b = acc & 0xffff;
1020 if (acc > 0xffff)
1021 vm->reg[DCPU16_REG_EX] = 0xffff;
1022 else
1023 vm->reg[DCPU16_REG_EX] = 0;
1024
1025 dcpu16_cycle_inc(vm, 3);
1026
1027 ACCT_W(ev_b_addr);
1028 }
1029
1030 OP_IMPL(sti) {
1031 OP_BASIC(sti);
1032 /* sets b to a, then increases I and J by 1 */
1033
1034 ACCT_R(ev_b_addr);
1035 ACCT_R(ev_a_addr);
1036
1037 *b = *a;
1038 vm->reg[DCPU16_REG_I] += 1;
1039 vm->reg[DCPU16_REG_J] += 1;
1040
1041 dcpu16_cycle_inc(vm, 2);
1042
1043 ACCT_W(ev_b_addr);
1044 }
1045
1046 OP_IMPL(std) {
1047 OP_BASIC(std);
1048 /* sets b to a, then decreases I and J by 1 */
1049
1050 ACCT_R(ev_b_addr);
1051 ACCT_R(ev_a_addr);
1052
1053 *b = *a;
1054 vm->reg[DCPU16_REG_I] -= 1;
1055 vm->reg[DCPU16_REG_J] -= 1;
1056
1057 dcpu16_cycle_inc(vm, 2);
1058
1059 ACCT_W(ev_b_addr);
1060 }
1061
1062 OP_IMPL(_reserved_) {
1063 OP_BASIC(_reserved_);
1064
1065 WARN("reserved opcode invoked");
1066
1067 ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
1068 }
1069
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 },
1103 {0x00, "", NULL }
1104 };
1105
1106 static inline
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) {
1115 case 0x18:
1116 if (value_position == 0) { /* b */
1117 printf(" PUSH");
1118 } else {
1119 printf(" POP");
1120 }
1121 break;
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);
1130 }
1131 }
1132
1133
1134 /* split a sequence of (one to three) words into the components of an instruction */
1135 static inline
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);
1143 *instr_len = 1;
1144
1145 if ((*opcode != 0x0000) &&
1146 ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) ) {
1147 *b_data = mem + (DCPU16_WORD)(addr + *instr_len);
1148 *instr_len += 1;
1149 } else {
1150 *b_data = NULL;
1151 }
1152
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);
1156 *instr_len += 1;
1157 } else {
1158 *a_data = NULL;
1159 }
1160
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",
1162 __func__,
1163 addr,
1164 mem[addr],
1165 *opcode,
1166 *b,
1167 *b_data ? **b_data : 0,
1168 *a,
1169 *a_data ? **a_data : 0,
1170 *instr_len);
1171 }
1172
1173 /* dcpu16_mnemonify_buf
1174 print words as words
1175 */
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;
1179
1180 instruction_decode_(buf, 0, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1181
1182 if (opcode == 0x0000)
1183 e = opcode_nbi_entries +
1184 ( (b < OPCODE_NBI_MAX) ? b : (OPCODE_NBI_MAX - 1) );
1185 else
1186 e = opcode_basic_entries + opcode;
1187 printf("%s", e->name);
1188
1189 if (opcode) {
1190 dump_operand_value_(b, b_data ? *b_data : 0, 0);
1191 printf(",");
1192 }
1193
1194 if (opcode || b) {
1195 dump_operand_value_(a, a_data ? *a_data : 0, 1);
1196 }
1197
1198 return instr_len;
1199 }
1200
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
1204 */
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;
1210
1211 if (!vm) return 0;
1212
1213 #if 0
1214 /*
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..
1218 */
1219 for (i = 3; i; i--) {
1220 instruction_decode_(vm->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
1221 if (instr_len > i)
1222 partial++;
1223 if (instr_len == i
1224 && (opcode >= 0x10 && opcode <= 0x17) ) {
1225 indent++;
1226 break;
1227 }
1228 }
1229 #endif
1230
1231 /* just need instr_len */
1232 instruction_decode_(vm->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
1233
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]);
1238 }
1239
1240 /* align things neatly, show the instruction */
1241 printf("%s%s ;%s%s",
1242 instr_len < 3 ? " " : "",
1243 instr_len < 2 ? " " : "",
1244 partial ? "*" : " ",
1245 indent ? " " : "");
1246
1247 dcpu16_mnemonify_buf(buf);
1248
1249 return instr_len;
1250 }
1251
1252 int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) {
1253 TRACE("%s>> message:0x%04x", __func__, message);
1254 return interrupt_enqueue_(vm, message);
1255 }
1256
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;
1261
1262 if (!vm)
1263 return;
1264
1265 instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
1266
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;
1270
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);
1276 break;
1277 }
1278 }
1279
1280 /* and jump over next instr(s) if needed */
1281 while (vm->skip_) {
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);
1288 } else {
1289 vm->skip_ = 0;
1290 }
1291 }
1292
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);
1299
1300 TRACE("%s>> %s interrupt IA:0x%04x message:0x%04x",
1301 __func__,
1302 vm->reg[DCPU16_REG_IA] ? "servicing" : "ignoring",
1303 vm->reg[DCPU16_REG_IA],
1304 message);
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;
1312 }
1313 }
1314 }
1315 }
1316
1317 /*
1318 print the current state of the machine
1319 shows current cycle count, registers, and next instruction
1320 */
1321 void dcpu16_state_print(struct dcpu16 *vm) {
1322 unsigned int i;
1323
1324 if (!vm) return;
1325
1326 printf(" ");
1327 for (i = 0; i < 8; i++)
1328 printf(" %s:0x%04x", dcpu16_reg_names[i], vm->reg[i]);
1329 printf("\n");
1330
1331 printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
1332 vm->cycle_,
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],
1337 "PC");
1338
1339 dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
1340 printf("\n");
1341 }
1342
1343 /* dcpu16_dump_ram
1344 * print raw ram contents from start to stop
1345 */
1346 void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) {
1347 unsigned int i, j;
1348 const unsigned int n = 8; /* words per line */
1349
1350 if (!vm) return;
1351
1352 for (i = start, j = 0; i <= end; i++, j++) {
1353 if (j % n == 0)
1354 printf("0x%04x:\t", i);
1355 printf(" %04x%s", vm->ram[i], (j % n) == (n - 1) ? "\n" : "");
1356 }
1357 if ((j % n) != (n - 1))
1358 printf("\n");
1359 }
1360
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;
1364
1365 hw = calloc(1, sizeof *hw);
1366 if (hw == NULL) {
1367 vm->warn_cb_("%s():%s", "calloc", strerror(errno));
1368 return NULL;
1369 }
1370 memcpy(hw, mod->template, sizeof *hw);
1371 hw->vm = vm;
1372 hw->mod = mod;
1373
1374 if (mod->data_init(hw, data)) {
1375 vm->warn_cb_("failed to init hw module data");
1376 free(hw);
1377 return NULL;
1378 }
1379
1380 return hw;
1381 }
1382
1383 /* destroy a 'hardware' device */
1384 void dcpu16_hw_del(struct dcpu16_hw **hw) {
1385 if (hw) {
1386 if (*hw) {
1387 if ((*hw)->mod->data_free) {
1388 (*hw)->mod->data_free(*hw);
1389 }
1390 free(*hw);
1391 *hw = NULL;
1392 }
1393 }
1394 }
1395
1396 /* dcpu16_hw_ctl
1397 * invokes per-module controls for hw device
1398 */
1399 int dcpu16_hw_ctl(struct dcpu16_hw *hw, const char *cmd, void *data_in, void *data_out) {
1400 if (hw) {
1401 if (hw->mod) {
1402 if (hw->mod->ctl) {
1403 if (cmd) {
1404 return hw->mod->ctl(hw, cmd, data_in, data_out);
1405 }
1406 }
1407 }
1408 }
1409 return 0;
1410 }
1411
1412
1413 /* dcpu16_hw_attach
1414 * registers new 'hardware' device with system
1415 */
1416 int dcpu16_hw_attach(struct dcpu16 *vm, struct dcpu16_hw *hw) {
1417 if (!vm || !hw)
1418 return -1;
1419
1420 TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
1421 __func__,
1422 hw->name_,
1423 hw->id_h, hw->id_l,
1424 hw->mfg_l, hw->mfg_h,
1425 hw->ver);
1426
1427 if (vm->hw_table_entries_ == 0xffff) {
1428 WARN("maximum hardware entries reached");
1429 return -1;
1430 }
1431
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));
1437 return -1;
1438 }
1439 vm->hw_table_ = tmp_ptr;
1440 vm->hw_table_allocated_ += 32;
1441 }
1442
1443 memcpy(vm->hw_table_ + vm->hw_table_entries_, hw, sizeof *hw);
1444 vm->hw_table_entries_++;
1445
1446 TRACE("%s>> added hw entry %zu", __func__, vm->hw_table_entries_);
1447
1448 return 0;
1449 }
1450
1451 /* dcpu16_acct_add
1452 * Register callback fn to be triggered whenever event matching any events
1453 * in bitwise mask occur.
1454 */
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;
1457
1458 if (!vm)
1459 return -1;
1460
1461 cb.mask = mask;
1462 cb.fn = fn;
1463 cb.data = data;
1464
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));
1470 return -1;
1471 }
1472 vm->cb_table_ = tmp_ptr;
1473 vm->cb_table_allocated_ += 32;
1474 }
1475
1476 memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
1477 vm->cb_table_entries_++;
1478
1479 TRACE("%s>> attached event callback %zu", __func__, vm->cb_table_entries_);
1480
1481 return 0;
1482 }
1483
1484 /* dcpu16_reset
1485 * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
1486 */
1487 void dcpu16_reset(struct dcpu16 *vm) {
1488 size_t i;
1489
1490 if (!vm)
1491 return;
1492
1493 TRACE("%s>> reset", __func__);
1494
1495 vm->skip_ = 0;
1496 vm->interrupts_deferred_ = 0;
1497 vm->on_fire_ = 0;
1498 memset(vm->interrupts_, 0, sizeof vm->interrupts_);
1499 vm->interrupts_head_ = 0;
1500 vm->interrupts_tail_ = 0;
1501
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]);
1506 }
1507
1508 memset(vm->reg, 0, sizeof vm->reg);
1509 memset(vm->ram, 0, sizeof vm->ram);
1510 vm->cycle_ = 0;
1511
1512 acct_event_(vm, DCPU16_ACCT_EV_RESET, 0);
1513 }
1514
1515 /* dcpu16_new
1516 * allocate a new dcpu16 instance
1517 */
1518 struct dcpu16 *dcpu16_new(void) {
1519 struct dcpu16 *vm;
1520
1521 vm = calloc(1, sizeof *vm);
1522 if (vm == NULL)
1523 WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
1524
1525 vm->warn_cb_ = warn_cb_;
1526 vm->trace_cb_ = trace_cb_;
1527
1528 return vm;
1529 }
1530
1531 /* dcpu16_delete
1532 * release a dcpu16 instance
1533 */
1534 void dcpu16_delete(struct dcpu16 **vm) {
1535 if (!vm || !*vm)
1536 return;
1537
1538 free(*vm);
1539 *vm = NULL;
1540 }