/*
* emulates the DCPU16 system from http://0x10c.com/doc/dcpu-16.txt
+ * currently emulates '1.7' spec from http://pastebin.com/Q4JvQvnM
*
* I couldn't remember ever implementing an emulator before, so this
* happened. As such, consider this a toy in progress.
*
* Justin Wind <justin.wind@gmail.com>
* 2012 04 05 - implementation started
- * 2012 04 06 - first functionality achieved
- * 2012 04 09 - minor cleanups
- * 2012 04 10 - moved cli to separate module
- * 2012 04 12 - added basic callback support for address accesses
+ * 2012 05 05 - start of v1.7 revisions
+ * 2012 05 08 - v1.7 revisions mostly complete
*
* TODO
+ * !! v1.7 bit-shift and signed opcodes need to be reviewed/finished
+ * !! v1.7 hardware interface needs to be finished
+ * !! v1.7 interrupts need to be finished
* change api to print into buffers rather than stdio
- * refactor opcode functiontables into switch statements
+ * let callbacks determine whether to override events, or just observe
+ * sort init callbacks by base addr, to call in-order
+ * make all callbacks register addr range of interest
*/
static const char * const src_id_ = "$Id$";
-#define OPCODE_BASIC_BITS 4
-#define OPCODE_OPERAND_BITS 6
+#define OPCODE_BASIC_BITS 5
+#define OPCODE_OPERAND_B_BITS 5
+#define OPCODE_OPERAND_A_BITS 6
+
+const char * const dcpu16_reg_names[] = {
+ "A",
+ "B",
+ "C",
+ "X",
+ "Y",
+ "Z",
+ "I",
+ "J",
+ "PC",
+ "SP",
+ "EX",
+ "IA",
+ NULL
+};
-static const char * const regnames_ = "ABCXYZIJ";
+static void printf_null_(char *fmt, ...) { (void)fmt; }
/* some default warning and debug reporting functions, which can be overridden by clients */
#define WARN(...) do { if (warn_cb_) warn_cb_(__VA_ARGS__); } while (0)
}
static void (*warn_cb_)(char *fmt, ...) = warn_;
void dcpu16_warn_cb_set(void (*fn)(char *fmt, ...)) {
- warn_cb_ = fn;
+ if (fn)
+ warn_cb_ = fn;
+ else
+ warn_cb_ = printf_null_;
}
#ifdef DEBUG
#endif
;
void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) {
- trace_cb_ = fn;
+ if (fn)
+ trace_cb_ = fn;
+ else
+ trace_cb_ = printf_null_;
}
* invokes callbacks for specified event
*/
static inline
-void acct_event_(struct dcpu16 *vm, dcpu16_acct_event_ ev, DCPU16_WORD addr) {
+void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) {
struct dcpu16_acct_cb *cb = vm->cb_table_;
size_t i;
for (i = 0; i < vm->cb_table_entries_; i++) {
if ( (cb[i].mask & ev) )
- cb[i].fn(ev, addr);
+ cb[i].fn(vm, ev, addr, cb[i].data);
}
}
-/* value_decode_
- * sets *v to be the destination of the value
- * advances d->pc if necessary
- * workv is buffer to use to accumulate literal value before use, one exists for either potential instruction operand
- * e_addr is for accounting callback
- * returns true if destination points to literal (id est *v should ignore writes)
- */
+/* add an entry to the interrupt queue */
static
-unsigned int value_decode_(struct dcpu16 *d, DCPU16_WORD value, DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr) {
- DCPU16_WORD nextword;
- unsigned int retval = 0;
+int interrupt_enqueue_(struct dcpu16 *vm, DCPU16_WORD message) {
+ vm->interrupts_[vm->interrupts_tail_] = message;
+ vm->interrupts_tail_ += 1;
+ vm->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+
+ if (vm->interrupts_tail_ == vm->interrupts_head_) {
+ vm->on_fire_ = 1;
+ WARN("interrupt queue overflow (system is now on fire)");
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) {
+ DCPU16_WORD message;
+
+ if (vm->interrupts_tail_ == vm->interrupts_head_) {
+ WARN("interrupt underflow");
+ return 0;
+ }
+
+ message = vm->interrupts_[vm->interrupts_head_];
+ vm->interrupts_head_ += 1;
+ vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+
+ return message;
+}
+
+inline
+void dcpu16_cycle_inc(struct dcpu16 *vm, unsigned int n) {
+ size_t i;
+
+ while (n--) {
+ /* new cycle */
+ vm->cycle_ += 1;
+ TRACE("%s>> starting cycle %llu", vm->cycle_);
+
+ /* signal interested cycle hooks */
+ acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]);
+ /* signal attached hardware */
+ for (i = 0; i < vm->hw_table_entries_; i++) {
+ TRACE("%s>> notifying %s", __func__, vm->hw_table_[i].name_);
+ vm->hw_table_[i].cycle(vm, &vm->hw_table_[i]);
+ }
+ }
+}
+
+/* value_decode_
+ * sets *v to be the address of the represented value
+ * value_is_a is 0 for b, 1 for a, alters behavior of some operands
+ * value_data is 'nextword' for this operand, ignored if unused
+ * workv is buffer to use to accumulate literal value into, before use. one exists for either potential instruction operand
+ * e_addr is set to a referenced address, for accounting callback
+ * pc_adjust is set to how to change the program counter
+ * stack_adjust is set to how to change the stack pointer
+ * cycle_adjust is set to number of cycles spent looking up operand
+ *
+ * zero all adjustables before decoding first operand, and pass in these values when
+ * decoding next operand..
+ *
+ */
+static inline
+void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
+ DCPU16_WORD *work_v, DCPU16_WORD **v, DCPU16_WORD *e_addr,
+ short *pc_adjust, short *sp_adjust, unsigned int *cycle_adjust) {
assert(value <= 0x3f);
- /* does this value indicate a literal */
- if (value >= 0x1f)
- retval = 1;
+ DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
+ sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_SP] + *sp_adjust);
+
+ TRACE("%s>> is_a:%u pc:0x%04x sp:0x%04x value_data:0x%04x\n",
+ __func__,
+ value_is_a,
+ pc,
+ sp,
+ value_data);
if (value <= 0x07) { /* register */
- *v = d->reg + value;
- TRACE(">> %c (0x%04x)",
- regnames_[value],
+ *v = vm->reg + value;
+ TRACE("%s>> %s (0x%04x)",
+ __func__,
+ dcpu16_reg_names[value],
**v);
+ return;
+ }
- } else if (value <= 0x0f) { /* [register] */
- *v = &(d->ram[ d->reg[(value & 0x07)] ]);
- TRACE(">> [%c] [0x%04x] (0x%04x)",
- regnames_[value&0x07],
- d->reg[value&0x07],
- **v);
- *e_addr = d->reg[(value & 0x07)];
-
- } else if (value <= 0x17) { /* [next word + register] */
- nextword = d->ram[ d->pc++ ];
- d->cycle++;
- *v = &(d->ram[ nextword + d->reg[(value & 0x07)] ]);
- TRACE(">> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
- regnames_[(value & 0x07)],
- nextword,
- d->reg[(value & 0x07)],
+ if (value <= 0x0f) { /* [register] */
+ *v = &(vm->ram[ vm->reg[value & 0x07] ]);
+ *e_addr = vm->reg[value & 0x07];
+ TRACE("%s>> [%s] [0x%04x] (0x%04x)",
+ __func__,
+ dcpu16_reg_names[value & 0x07],
+ vm->reg[value & 0x07],
**v);
- *e_addr = nextword + d->reg[(value & 0x07)];
+ return;
+ }
- } else switch (value) {
- case 0x18: /* POP / [sp++] */
- *v = &(d->ram[ d->sp++ ]);
- TRACE(">> POP [0x%04x] (0x%04x)",
- d->sp - 1,
+ if (value <= 0x17) { /* [next word + register] */
+ *pc_adjust += 1; /* consume next word */
+ *cycle_adjust += 1;
+ *e_addr = value_data + vm->reg[value & 0x07];
+ *v = vm->ram + *e_addr;
+ TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
+ __func__,
+ dcpu16_reg_names[value & 0x07],
+ value_data,
+ vm->reg[value & 0x07],
**v);
- *e_addr = d->sp - 1;
+ return;
+ }
+
+ switch (value) {
+ case 0x18: /* PUSH/[--SP] or POP/[SP++] */
+ if (value_is_a == 0) { /* b */
+ *v = &(vm->ram[sp - 1]);
+ *sp_adjust -= 1;
+ *e_addr = sp - 1;
+ TRACE("%s>> PUSH [0x%04x] (0x%04x)", __func__, sp - 1, **v);
+ } else { /* a */
+ *v = &(vm->ram[sp]);
+ *sp_adjust += 1;
+ *e_addr = sp;
+ TRACE("%s>> POP [0x%04x] (0x%04x)", __func__, sp, **v);
+ }
break;
- case 0x19: /* PEEK / [sp] */
- *v = &(d->ram[ d->sp ]);
- TRACE(">> PEEK [0x%04x] (0x%04x)",
- d->sp,
+ case 0x19: /* PEEK/[SP] */
+ *v = &(vm->ram[sp]);
+ *e_addr = sp;
+ TRACE("%s>> PEEK [0x%04x] (0x%04x)",
+ __func__,
+ sp,
**v);
- *e_addr = d->sp;
break;
- case 0x1a: /* PUSH / [--sp] */
- *v = &(d->ram[ --d->sp ]);
- TRACE(">> PUSH [0x%04x] (0x%04x)",
- d->sp + 1,
+ case 0x1a: /* PICK n */
+ *pc_adjust += 1;
+ *cycle_adjust += 1;
+ *e_addr = sp + value_data;
+ *v = vm->ram + *e_addr;
+ TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
+ __func__,
+ value_data,
+ sp + value_data,
**v);
- *e_addr = d->sp + 1;
break;
case 0x1b: /* SP */
- *v = &(d->sp);
- TRACE(">> SP (0x%04x)",
+ *v = &(vm->reg[DCPU16_REG_SP]);
+ TRACE("%s>> %s (0x%04x)",
+ __func__,
+ dcpu16_reg_names[DCPU16_REG_SP],
**v);
break;
case 0x1c: /* PC */
- *v = &(d->pc);
- TRACE(">> PC (0x%04x)", **v);
+ *v = &(vm->reg[DCPU16_REG_PC]);
+ TRACE("%s>> %s (0x%04x)",
+ __func__,
+ dcpu16_reg_names[DCPU16_REG_PC],
+ **v);
break;
- case 0x1d: /* O */
- *v = &(d->o);
- TRACE(">> O (0x%04x)", **v);
+ case 0x1d: /* EX */
+ *v = &(vm->reg[DCPU16_REG_EX]);
+ TRACE("%s>> %s (0x%04x)",
+ __func__,
+ dcpu16_reg_names[DCPU16_REG_EX],
+ **v);
break;
case 0x1e: /* [next word] / [[pc++]] */
- nextword = d->ram[ d->pc++ ];
- d->cycle++;
- *v = &(d->ram[ nextword ]);
- TRACE(">> [nextword] [0x%04x] (0x%04x)",
- nextword,
+ *pc_adjust += 1;
+ *cycle_adjust += 1;
+ *e_addr = value_data;
+ *v = vm->ram + *e_addr;
+ TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
+ __func__,
+ value_data,
**v);
- *e_addr = nextword;
break;
case 0x1f: /* next word (literal) / [pc++] */
- nextword = d->ram[ d->pc++ ];
- d->cycle++;
- *work_v = nextword;
+ *pc_adjust += 1;
+ *cycle_adjust += 1;
+ *work_v = value_data;
*v = work_v;
- TRACE(">> nextword (0x%04x)", **v);
+ TRACE("%s>> nextword (0x%04x)",
+ __func__,
+ **v);
break;
- default: /* 0x20-0x3f: literal values 0x00-0x1f */
- *work_v = value & 0x1f;
+ default: /* 0x20-0x3f: literal values 0xffff-0x1e */
+ *work_v = (value & 0x1f) - 1;
*v = work_v;
- TRACE(">> literal (0x%04x)", **v);
+ TRACE("%s>> literal (0x%04x)",
+ __func__,
+ **v);
}
-
- return retval;
}
struct opcode_entry {
unsigned short value;
char name[OPCODE_NAME_LEN];
- void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD);
+ void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD, DCPU16_WORD);
};
/* messy boilerplate for opcode handlers */
-#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_a, DCPU16_WORD val_b)
+/* opcode doesn't adjust its own PC, the step function which invoked it handles that */
+/* opcode does adjust stack and cycle count */
+
+#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)
#define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
- unsigned int lit_a;\
DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
+ short pc_adjust = 0, sp_adjust = 0;\
+ unsigned int cycle_adjust = 0;\
do {\
- lit_a = value_decode_(d, val_a, &d->reg_work_[0], &a, &ev_a_addr);\
op_type;\
+ value_decode_(vm, val_a, 1, val_a_data,\
+ &vm->reg_work_[1], &a, &ev_a_addr,\
+ &pc_adjust, &sp_adjust, &cycle_adjust);\
+ vm->reg[DCPU16_REG_SP] += sp_adjust;\
+ dcpu16_cycle_inc(vm, cycle_adjust);\
} while (0)
-#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr
-#define OP_BASIC_ (void)value_decode_(d, val_b, &d->reg_work_[0], &b, &ev_b_addr)
+#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
+#define OP_BASIC_ value_decode_(vm, val_b, 0, val_b_data,\
+ &vm->reg_work_[0], &b, &ev_b_addr,\
+ &pc_adjust, &sp_adjust, &cycle_adjust)
#define OP_BASIC(x) OP_TYPE(OP_BASIC_)
#define OP_NBI(x) OP_TYPE(OP_NBI_)
+/* 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 */
+
/*
accounting helpers, these fire off the related callbacks for memory reads,
memory writes, and execution of reserved instructions
*/
-#define ACCT_R(addr) do { acct_event_(d, DCPU16_ACCT_EV_READ, addr); } while (0)
-#define ACCT_W(addr) do { acct_event_(d, DCPU16_ACCT_EV_WRITE, addr); } while (0)
-#define ACCT_ILL(addr) do { acct_event_(d, DCPU16_ACCT_EV_NOP, addr); } while (0)
+#define ACCT_R(addr) do { acct_event_(vm, DCPU16_ACCT_EV_READ, addr); } while (0)
+#define ACCT_W(addr) do { acct_event_(vm, DCPU16_ACCT_EV_WRITE, addr); } while (0)
+#define ACCT_ILL(addr) do { acct_event_(vm, DCPU16_ACCT_EV_NOP, addr); } while (0)
/* extended opcodes */
OP_NBI(nbi__reserved_);
/* reserved for future expansion */
- DCPU16_WORD future_opcode = (d->ram[d->pc] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS));
+ /* fire an illegal instruction event for current instruction */
+ DCPU16_WORD future_opcode = (vm->ram[vm->reg[DCPU16_REG_PC] - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
WARN("reserved future opcode 0x%04x invoked", future_opcode);
- ACCT_ILL(d->pc);
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
}
OP_IMPL(nbi_jsr) {
ACCT_R(ev_a_addr);
- d->ram[ --d->sp ] = d->pc;
- d->pc = *a;
+ vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC];
+ vm->reg[DCPU16_REG_PC] = *a;
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
- ACCT_W(d->sp + 1);
+ ACCT_W(vm->reg[DCPU16_REG_SP] + 1);
}
OP_IMPL(nbi__reserved2_) {
WARN("reserved nbi opcode invoked");
- ACCT_ILL(d->pc);
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
+}
+
+OP_IMPL(nbi_int) {
+ OP_NBI(nbi_int);
+
+ ACCT_R(ev_a_addr);
+
+ if (vm->reg[DCPU16_REG_IA]) {
+ if ( interrupt_enqueue_(vm, *a) ) {
+ WARN("failed to queue interrupt");
+ return;
+ }
+
+ if (vm->interrupts_deferred_)
+ return;
+
+ vm->interrupts_deferred_ = 1;
+ vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
+ vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
+ vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
+ vm->reg[0] = *a;
+ }
+
+ dcpu16_cycle_inc(vm, 4);
+}
+
+OP_IMPL(nbi_iag) {
+ OP_NBI(nbi_iag);
+
+ *a = vm->reg[DCPU16_REG_IA];
+
+ dcpu16_cycle_inc(vm, 1);
+
+ ACCT_W(ev_a_addr);
+}
+
+OP_IMPL(nbi_ias) {
+ OP_NBI(nbi_ias);
+
+ vm->reg[DCPU16_REG_IA] = *a;
+
+ dcpu16_cycle_inc(vm, 1);
+
+ ACCT_R(ev_a_addr);
+}
+
+/* does this just ignore its operand? */
+OP_IMPL(nbi_rfi) {
+ OP_NBI(nbi_rfi);
+
+ vm->interrupts_deferred_ = 0;
+ vm->reg[DCPU16_REG_A] = vm->ram[vm->reg[DCPU16_REG_SP]++];
+ vm->reg[DCPU16_REG_PC] = vm->ram[vm->reg[DCPU16_REG_SP]++];
+
+ dcpu16_cycle_inc(vm, 3);
+}
+
+OP_IMPL(nbi_iaq) {
+ OP_NBI(nbi_iaq);
+
+ if (*a) {
+ vm->interrupts_deferred_ = 1;
+ } else {
+ vm->interrupts_deferred_ = 0;
+ }
+
+ dcpu16_cycle_inc(vm, 2);
+
+ ACCT_R(ev_a_addr);
+}
+
+OP_IMPL(nbi_hwn) {
+ OP_NBI(nbi_hwn);
+
+ ACCT_W(ev_a_addr);
+
+ *a = vm->hw_table_entries_;
+
+ dcpu16_cycle_inc(vm, 2);
+}
+
+OP_IMPL(nbi_hwq) {
+ OP_NBI(nbi_hwq);
+
+ ACCT_R(ev_a_addr);
+
+ if (*a >= vm->hw_table_entries_) {
+ WARN("hardware query for non-extant device 0x%04x", *a);
+ vm->reg[DCPU16_REG_A] = 0;
+ vm->reg[DCPU16_REG_B] = 0;
+ vm->reg[DCPU16_REG_C] = 0;
+ vm->reg[DCPU16_REG_X] = 0;
+ vm->reg[DCPU16_REG_Y] = 0;
+ return;
+ }
+
+ vm->reg[DCPU16_REG_A] = vm->hw_table_[*a].id_l;
+ vm->reg[DCPU16_REG_B] = vm->hw_table_[*a].id_h;
+ vm->reg[DCPU16_REG_C] = vm->hw_table_[*a].ver;
+ vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mfg_l;
+ vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mfg_h;
+
+ dcpu16_cycle_inc(vm, 4);
+}
+
+OP_IMPL(nbi_hwi) {
+ OP_NBI(nbi_hwi);
+
+ ACCT_R(ev_a_addr);
+
+ if (*a > vm->hw_table_entries_) {
+ WARN("interrupt for non-extant device 0x%04x", *a);
+ return;
+ }
+
+ dcpu16_cycle_inc(vm, 4);
+ if (vm->hw_table_[*a].hwi)
+ vm->hw_table_[*a].hwi(vm, &vm->hw_table_[*a]);
+ else
+ WARN("hardware 0x%04x has no interrupt handler", *a);
+}
+
+OP_IMPL(nbi_hcf) {
+ OP_NBI(nbi_hcf);
+
+ ACCT_R(ev_a_addr);
+
+ vm->on_fire_ = 1;
+ WARN("system on fire");
+
+ dcpu16_cycle_inc(vm, 9);
}
static const struct opcode_entry opcode_nbi_entries[] = {
- {0x0, "(reserved)", op_nbi__reserved_},
- {0x1, "JSR", op_nbi_jsr},
- {0x2, "(reserved)", op_nbi__reserved2_},
- {0x0, "", NULL}
+ {0x00, "(reserved)", op_nbi__reserved_},
+ {0x01, "JSR", op_nbi_jsr},
+ {0x02, "(reserved)", op_nbi__reserved2_},
+ {0x03, "(reserved)", op_nbi__reserved2_},
+ {0x04, "(reserved)", op_nbi__reserved2_},
+ {0x05, "(reserved)", op_nbi__reserved2_},
+ {0x06, "(reserved)", op_nbi__reserved2_},
+ {0x07, "HCF", op_nbi_hcf}, /* undocumented */
+ {0x08, "INT", op_nbi_int},
+ {0x09, "IAG", op_nbi_iag},
+ {0x0a, "IAS", op_nbi_ias},
+ {0x0b, "RFI", op_nbi_rfi},
+ {0x0c, "IAQ", op_nbi_iaq},
+ {0x0d, "(reserved)", op_nbi__reserved2_},
+ {0x0e, "(reserved)", op_nbi__reserved2_},
+ {0x0f, "(reserved)", op_nbi__reserved2_},
+ {0x10, "HWN", op_nbi_hwn},
+ {0x11, "HWQ", op_nbi_hwq},
+ {0x12, "HWI", op_nbi_hwi},
+ {0x13, "(reserved)", op_nbi__reserved2_},
+ {0x00, "", NULL}
};
#define OPCODE_NBI_MAX (((sizeof(opcode_nbi_entries)) / (sizeof(struct opcode_entry))) - 1)
/* basic opcodes */
/*
- N.B. the following function does not decode values, (thus does not advance pc &c)
- Decoding is handled by the opcode functions it calls.
+ N.B. the following function does not decode values.
+ Decoding is handled by the secondary opcode functions it calls.
*/
OP_IMPL(_nbi_) {
/* non-basic instruction */
/* don't do normal value decoding here */
- DCPU16_WORD nbi_opcode = val_a;
+ DCPU16_WORD nbi_opcode = val_b;
const struct opcode_entry *e = opcode_nbi_entries;
+ (void)val_b_data;
+
e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
assert(e->impl != NULL);
TRACE(">> %s 0x%04x", e->name, val_b);
- e->impl(d, val_b, 0);
+ e->impl(vm, 0, 0, val_a, val_a_data);
}
OP_IMPL(set) {
OP_BASIC(set);
- /* sets a to b */
+ /* sets b to a */
- ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
/*
- if a is a literal, it's aimed at a scratch register,
+ if b is a literal, it's aimed at a scratch register,
so it's fine to update, as it won't have any effect.
*/
- *a = *b;
+ *b = *a;
- d->cycle += 1;
+ dcpu16_cycle_inc(vm, 1);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(add) {
OP_BASIC(add);
- /* sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise */
- unsigned int acc = *a + *b;
+ /* sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise */
+ unsigned int acc = *b + *a;
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = acc;
- d->o = (acc > 0xffff);
+ *b = acc;
+ vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(sub) {
OP_BASIC(sub);
- /* sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise */
- unsigned int acc = *a - *b;
+ /* sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise */
+ unsigned int acc = *b - *a;
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = acc;
- d->o = (acc > 0xffff);
+ *b = acc;
+ vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(mul) {
OP_BASIC(mul);
- /* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
- unsigned int acc = *a * *b;
+ /* sets b to b*a, unsigned, sets EX to ((b*a)>>16)&0xffff */
+ unsigned int acc = *b * *a;
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = acc;
- d->o = acc >> 16;
+ *b = acc;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(mli) {
+ OP_BASIC(mli);
+ /* sets b to b*a, signed */
+ int acc = (short)*b * (short)*a;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = acc;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ ACCT_W(ev_b_addr);
}
OP_IMPL(div) {
OP_BASIC(div);
- /* sets a to a/b, sets O to ((a<<16)/b)&0xffff. if b==0, sets a and O to 0 instead. */
+ /* sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets a and EX to 0 instead. */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if (*b == 0) {
- *a = 0;
- d->o = 0;
+ if (*a == 0) {
+ *b = 0;
+ vm->reg[DCPU16_REG_EX] = 0;
} else {
- *a = *a / *b;
- d->o = (*a << 16) / *b;
+ *b = *b / *a;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
}
- d->cycle += 3;
+ dcpu16_cycle_inc(vm, 3);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
-OP_IMPL(mod) {
- OP_BASIC(mod);
- /* sets a to a%b. if b==0, sets a to 0 instead. */
+OP_IMPL(dvi) {
+ OP_BASIC(dvi);
+ /* sets b to b/a, signed, round towards 0 */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if (*b == 0) {
- *a = 0;
+ if (*a == 0) {
+ *b = 0;
+ vm->reg[DCPU16_REG_EX] = 0;
} else {
- *a = *a % *b;
+ *b = (short)*b / (short)*a;
+ vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
}
- d->cycle += 3;
+ dcpu16_cycle_inc(vm, 3);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
-OP_IMPL(shl) {
- OP_BASIC(shl);
- /* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
- unsigned int acc = *a << *b;
+OP_IMPL(mod) {
+ OP_BASIC(mod);
+ /* sets b to b%a. if a==0, sets b to 0 instead. */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = acc;
-
- d->o = acc >> 16;
+ if (*a == 0) {
+ *b = 0;
+ } else {
+ *b = *b % *a;
+ }
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 3);
ACCT_W(ev_a_addr);
}
-OP_IMPL(shr) {
- OP_BASIC(shr);
- /* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
- unsigned int acc = *a >> *b;
+OP_IMPL(mdi) {
+ OP_BASIC(mdi);
+ /* sets b to b%a, signed */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = acc;
- d->o = (*a << 16) >> *b;
+ if (*a == 0) {
+ *b = 0;
+ } else {
+ *b = (short)*b % (short)*a;
+ }
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 3);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(and) {
OP_BASIC(and);
- /* sets a to a&b */
+ /* sets b to b&a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = *a & *b;
+ *b = *b & *a;
- d->cycle += 1;
+ dcpu16_cycle_inc(vm, 1);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(bor) {
OP_BASIC(bor);
- /* sets a to a|b */
+ /* sets b to b|a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = *a | *b;
+ *b = *b | *a;
- d->cycle += 1;
+ dcpu16_cycle_inc(vm, 1);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
}
OP_IMPL(xor) {
OP_BASIC(xor);
- /* sets a to a^b */
+ /* sets b to b^a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- *a = *a ^ *b;
+ *b = *b ^ *a;
- d->cycle += 1;
+ dcpu16_cycle_inc(vm, 1);
- ACCT_W(ev_a_addr);
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(shr) {
+ OP_BASIC(shr);
+ /* sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff */
+ unsigned int acc = *b >> *a;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = acc & 0xffff;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ WARN("IMPLEMENT");
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(asr) {
+ OP_BASIC(asr);
+ /* sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed) */
+ unsigned int acc = *b << *a;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = acc & 0xffff;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ WARN("IMPLEMENT");
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(shl) {
+ OP_BASIC(shl);
+ /* sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff */
+ unsigned int acc = *b << *a;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = acc;
+
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(ifb) {
+ OP_BASIC(ifb);
+ /* performs next instruction only if (b&a)!=0 */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ if ((*b & *a) != 0) {
+ /* */
+ } else {
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
+ }
+
+ dcpu16_cycle_inc(vm, 2);
+}
+
+OP_IMPL(ifc) {
+ OP_BASIC(ifc);
+ /* performs next instruction only if (b&a)==0 */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ if ((*b & *a) == 0) {
+ /* */
+ } else {
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
+ }
+
+ dcpu16_cycle_inc(vm, 2);
}
OP_IMPL(ife) {
OP_BASIC(ife);
- /* performs next instruction only if a==b */
+ /* performs next instruction only if b==a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if (*a == *b) {
+ if (*b == *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
}
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
}
OP_IMPL(ifn) {
OP_BASIC(ifn);
- /* performs next instruction only if a!=b */
+ /* performs next instruction only if b!=a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if (*a != *b) {
+ if (*b != *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
}
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
}
OP_IMPL(ifg) {
OP_BASIC(ifg);
- /* performs next instruction only if a>b */
+ /* performs next instruction only if b>a */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if (*a > *b) {
+ if (*b > *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
}
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
}
-OP_IMPL(ifb) {
- OP_BASIC(ifb);
- /* performs next instruction only if (a&b)!=0 */
+OP_IMPL(ifa) {
+ OP_BASIC(ifa);
+ /* performs next instruction only if b>a (signed) */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ if (*b > *a) {
+ /* */
+ } else {
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
+ }
+
+ dcpu16_cycle_inc(vm, 2);
+}
+
+OP_IMPL(ifl) {
+ OP_BASIC(ifl);
+ /* performs next instruction only if b<a */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ if (*b < *a) {
+ /* */
+ } else {
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
+ }
+
+ dcpu16_cycle_inc(vm, 2);
+}
+
+OP_IMPL(ifu) {
+ OP_BASIC(ifu);
+ /* performs next instruction only if b<a (signed) */
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- if ((*a & *b) != 0) {
+ if (*b < *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ dcpu16_cycle_inc(vm, 1);
}
- d->cycle += 2;
+ dcpu16_cycle_inc(vm, 2);
+}
+
+OP_IMPL(adx) {
+ OP_BASIC(adx);
+ /* sets b to b+a+EX, sets EX to 0x0001 if overflow, 0x0 otherwise */
+ unsigned int acc;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ acc = *b + *a + vm->reg[DCPU16_REG_EX];
+ *b = acc & 0xffff;
+ if (acc > 0xffff)
+ vm->reg[DCPU16_REG_EX] = 0x0001;
+ else
+ vm->reg[DCPU16_REG_EX] = 0x0000;
+
+ dcpu16_cycle_inc(vm, 3);
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(sbx) {
+ OP_BASIC(sbx);
+ /* sets b to b-a+EX, sets EX to 0xffff if underflow, 0x0 otherwise */
+ unsigned int acc;
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ acc = *b - *a + vm->reg[DCPU16_REG_EX];
+ *b = acc & 0xffff;
+ if (acc > 0xffff)
+ vm->reg[DCPU16_REG_EX] = 0xffff;
+ else
+ vm->reg[DCPU16_REG_EX] = 0;
+
+ dcpu16_cycle_inc(vm, 3);
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(sti) {
+ OP_BASIC(sti);
+ /* sets b to a, then increases I and J by 1 */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = *a;
+ vm->reg[DCPU16_REG_I] += 1;
+ vm->reg[DCPU16_REG_J] += 1;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(std) {
+ OP_BASIC(std);
+ /* sets b to a, then decreases I and J by 1 */
+
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
+
+ *b = *a;
+ vm->reg[DCPU16_REG_I] -= 1;
+ vm->reg[DCPU16_REG_J] -= 1;
+
+ dcpu16_cycle_inc(vm, 2);
+
+ ACCT_W(ev_b_addr);
+}
+
+OP_IMPL(_reserved_) {
+ OP_BASIC(_reserved_);
+
+ WARN("reserved opcode invoked");
+
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
}
static const struct opcode_entry opcode_basic_entries[] = {
- {0x0, "(nbi)", op__nbi_},
- {0x1, "SET", op_set },
- {0x2, "ADD", op_add },
- {0x3, "SUB", op_sub },
- {0x4, "MUL", op_mul },
- {0x5, "DIV", op_div },
- {0x6, "MOD", op_mod },
- {0x7, "SHL", op_shl },
- {0x8, "SHR", op_shr },
- {0x9, "AND", op_and },
- {0xa, "BOR", op_bor },
- {0xb, "XOR", op_xor },
- {0xc, "IFE", op_ife },
- {0xd, "IFN", op_ifn },
- {0xe, "IFG", op_ifg },
- {0xf, "IFB", op_ifb },
- {0x0, "", NULL }
+ {0x00, "(nbi)", op__nbi_},
+ {0x01, "SET", op_set },
+ {0x02, "ADD", op_add },
+ {0x03, "SUB", op_sub },
+ {0x04, "MUL", op_mul },
+ {0x05, "MLI", op_mli },
+ {0x06, "DIV", op_div },
+ {0x07, "DVI", op_dvi },
+ {0x08, "MOD", op_mod },
+ {0x09, "MDI", op_mdi },
+ {0x0a, "AND", op_and },
+ {0x0b, "BOR", op_bor },
+ {0x0c, "XOR", op_xor },
+ {0x0d, "SHR", op_shr },
+ {0x0e, "ASR", op_asr },
+ {0x0f, "SHL", op_shl },
+ {0x10, "IFB", op_ifb },
+ {0x11, "IFC", op_ifc },
+ {0x12, "IFE", op_ife },
+ {0x13, "IFN", op_ifn },
+ {0x14, "IFG", op_ifg },
+ {0x15, "IFA", op_ifa },
+ {0x16, "IFL", op_ifl },
+ {0x17, "IFU", op_ifu },
+ {0x18, "(reserved)", op__reserved_ },
+ {0x19, "(reserved)", op__reserved_ },
+ {0x1a, "ADX", op_adx },
+ {0x1b, "SBX", op_sbx },
+ {0x1c, "(reserved)", op__reserved_ },
+ {0x1d, "(reserved)", op__reserved_ },
+ {0x1e, "STI", op_sti },
+ {0x1f, "STD", op_std },
+ {0x00, "", NULL }
};
static inline
-void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword) {
+void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword, unsigned int value_position) {
if (value <= 0x07) {
- printf(" %c", regnames_[value]);
+ printf(" %s", dcpu16_reg_names[value]);
} else if (value <= 0x0f) {
- printf(" [%c]", regnames_[value & 0x07]);
+ printf(" [%s]", dcpu16_reg_names[value & 0x07]);
} else if (value <= 0x17) {
- printf(" [0x%04x + %c]", nextword, regnames_[value & 0x07]);
+ printf(" [0x%04x + %s]", nextword, dcpu16_reg_names[value & 0x07]);
} else switch (value) {
- case 0x18: printf(" POP"); break;
+ case 0x18:
+ if (value_position == 0) { /* b */
+ printf(" PUSH");
+ } else {
+ printf(" POP");
+ }
+ break;
case 0x19: printf(" PEEK"); break;
- case 0x1a: printf(" PUSH"); break;
+ case 0x1a: printf(" PICK 0x%04x", nextword); break;
case 0x1b: printf(" SP"); break;
case 0x1c: printf(" PC"); break;
- case 0x1d: printf(" O"); break;
+ case 0x1d: printf(" EX"); break;
case 0x1e: printf(" [0x%04x]", nextword); break;
case 0x1f: printf(" 0x%04x", nextword); break;
- default: printf(" 0x%02x", value - 0x20);
+ default: printf(" 0x%02x", value - 0x21);
}
}
-/* split a word into the parts of an instruction, and determine how many words it takes up in total */
+/* split a sequence of (one to three) words into the components of an instruction */
static inline
-void instruction_decode_(struct dcpu16 *d, DCPU16_WORD addr, DCPU16_WORD *opcode, DCPU16_WORD *a, DCPU16_WORD *b, DCPU16_WORD *instr_len) {
- *opcode = d->ram[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
- *a = (d->ram[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_BITS) - 1);
- *b = (d->ram[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS)) & ((1 << OPCODE_OPERAND_BITS) - 1);
- if (instr_len) {
- *instr_len = 1;
- /* both basic and nbi opcodes use their b operand */
- if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f )
- *instr_len += 1;
- /* but only basic uses a */
- if (*opcode
- && ((*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) )
- *instr_len += 1;
+void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr,
+ DCPU16_WORD *opcode, DCPU16_WORD *b, DCPU16_WORD **b_data, DCPU16_WORD *a, DCPU16_WORD **a_data,
+ DCPU16_WORD *instr_len) {
+ *opcode = *a = *b = mem[addr];
+ *opcode = mem[addr] & ((1 << OPCODE_BASIC_BITS) - 1);
+ *b = (mem[addr] >> OPCODE_BASIC_BITS) & ((1 << OPCODE_OPERAND_B_BITS) - 1);
+ *a = (mem[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)) & ((1 << OPCODE_OPERAND_A_BITS) - 1);
+ *instr_len = 1;
+
+ if ((*opcode != 0x0000) &&
+ ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) ) {
+ *b_data = mem + (DCPU16_WORD)(addr + *instr_len);
+ *instr_len += 1;
+ } else {
+ *b_data = NULL;
}
+
+ if ( (*opcode != 0x0000 || (*opcode == 0 && *b != 0x0000) )
+ && ( (*a >= 0x10 && *a <= 0x17) || *a == 0x1e || *a == 0x1f) ) {
+ *a_data = mem + (DCPU16_WORD)(addr + *instr_len);
+ *instr_len += 1;
+ } else {
+ *a_data = NULL;
+ }
+
+ 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",
+ __func__,
+ addr,
+ mem[addr],
+ *opcode,
+ *b,
+ *b_data ? **b_data : 0,
+ *a,
+ *a_data ? **a_data : 0,
+ *instr_len);
+}
+
+/* dcpu16_mnemonify_buf
+ print words as words
+ */
+DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *buf) {
+ DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
+ const struct opcode_entry *e;
+
+ instruction_decode_(buf, 0, &opcode, &b, &b_data, &a, &a_data, &instr_len);
+
+ if (opcode == 0x0000)
+ e = opcode_nbi_entries +
+ ( (b < OPCODE_NBI_MAX) ? b : (OPCODE_NBI_MAX - 1) );
+ else
+ e = opcode_basic_entries + opcode;
+ printf("%s", e->name);
+
+ if (opcode) {
+ dump_operand_value_(b, b_data ? *b_data : 0, 0);
+ printf(",");
+ }
+
+ if (opcode || b) {
+ dump_operand_value_(a, a_data ? *a_data : 0, 1);
+ }
+
+ return instr_len;
}
/* dcpu16_disassemble_print
print the words of the instruction at addr, followed by its assembly representation
returns the length of the instruction in words
*/
-DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
- DCPU16_WORD opcode, a, b, instr_len, i;
- const struct opcode_entry *e;
+DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *vm, DCPU16_WORD addr) {
+ DCPU16_WORD opcode, b, a, instr_len, i, *b_data, *a_data;
+ DCPU16_WORD buf[3] = { vm->ram[addr], vm->ram[(DCPU16_WORD)(addr + 1)], vm->ram[(DCPU16_WORD)(addr + 2)] };
unsigned int indent = 0;
unsigned int partial = 0;
- if (!d) return 0;
+ if (!vm) return 0;
+#if 0
/*
Check the previous instruction, to see if this one should be
indented. This check isn't foolproof, as preceeding addresses
could be data which happen to match instructions..
*/
for (i = 3; i; i--) {
- instruction_decode_(d, addr - i, &opcode, &a, &b, &instr_len);
+ instruction_decode_(vm->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &instr_len);
if (instr_len > i)
partial++;
- if (instr_len == i && opcode >= 0xc) {
+ if (instr_len == i
+ && (opcode >= 0x10 && opcode <= 0x17) ) {
indent++;
break;
}
}
+#endif
- /* now get what we're really interested in */
- instruction_decode_(d, addr, &opcode, &a, &b, &instr_len);
-
- if (opcode)
- e = opcode_basic_entries + opcode;
- else
- e = opcode_nbi_entries + ( (a < OPCODE_NBI_MAX) ? a : (OPCODE_NBI_MAX - 1) );
+ /* just need instr_len */
+ instruction_decode_(vm->ram, addr, &opcode, &b, &b_data, &a, &a_data, &instr_len);
/* show the raw words */
- printf("%04x", d->ram[addr]);
+ printf("%04x", vm->ram[addr]);
for (i = 1; i < instr_len; i++) {
- printf(" %04x", d->ram[addr + i]);
+ printf(" %04x", vm->ram[addr + i]);
}
/* align things neatly, show the instruction */
- printf("%s%s ;%s%s%s",
+ printf("%s%s ;%s%s",
instr_len < 3 ? " " : "",
instr_len < 2 ? " " : "",
partial ? "*" : " ",
- indent ? " " : "",
- e->name);
-
- /* show the operands */
- i = 0;
- if (opcode) {
- dump_operand_value_(a, d->ram[addr + 1]);
- if ((a >= 0x10 && a <= 0x17) || a == 0x1e || a == 0x1f)
- addr++;
- printf(",");
- }
+ indent ? " " : "");
- if (opcode || a)
- dump_operand_value_(b, d->ram[addr + 1]);
+ dcpu16_mnemonify_buf(buf);
return instr_len;
}
+int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) {
+ TRACE("%s>> message:0x%04x", __func__, message);
+ return interrupt_enqueue_(vm, message);
+}
+
/* execute the next instruction */
-void dcpu16_step(struct dcpu16 *d) {
- DCPU16_WORD opcode, a, b, instr_len;
+void dcpu16_step(struct dcpu16 *vm) {
+ DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
const struct opcode_entry *e;
- if (!d) return;
+ if (!vm)
+ return;
- /*
- PC is advanced while decoding the operands by the opcode functions.
- Things like this could be organized a little better..
- */
- instruction_decode_(d, d->pc, &opcode, &a, &b, NULL);
+ instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
- d->pc++; /* all instructions take at least one word */
+ /* consume what we decoded */
+ /* this happens immediately as PC might be re-set as an operation */
+ vm->reg[DCPU16_REG_PC] += instr_len;
+ /* run the operation */
for (e = opcode_basic_entries; e->impl; e++) {
if (e->value == opcode) {
- TRACE(">> %s 0x%04x, 0x%04x", e->name, a, b);
- e->impl(d, a, b);
+ TRACE("%s>> %s 0x%04x, 0x%04x", __func__, e->name, b, a);
+ e->impl(vm, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
break;
}
}
- /* and jump over next instr if needed */
- if (d->skip_) {
- instruction_decode_(d, d->pc, &opcode, &a, &b, &instr_len);
- d->pc += instr_len;
- d->skip_ = 0;
+ /* and jump over next instr(s) if needed */
+ while (vm->skip_) {
+ instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
+ vm->reg[DCPU16_REG_PC] += instr_len;
TRACE("++ SKIPPED %x words", instr_len);
+ if (opcode >= 0x10 && opcode <= 0x17) {
+ /* skipping a branch instruction? skip branch's skippable instruction as well */
+ dcpu16_cycle_inc(vm, 1);
+ } else {
+ vm->skip_ = 0;
+ }
+ }
+
+ /* if we're currently servicing interrupts */
+ if (vm->interrupts_deferred_ == 0) {
+ /* and there are interrupts to be serviced */
+ if (vm->interrupts_head_ != vm->interrupts_tail_) {
+ DCPU16_WORD message;
+ message = interrupt_dequeue_(vm);
+
+ TRACE("%s>> %s interrupt IA:0x%04x message:0x%04x",
+ __func__,
+ vm->reg[DCPU16_REG_IA] ? "servicing" : "ignoring",
+ vm->reg[DCPU16_REG_IA],
+ message);
+ if (vm->reg[DCPU16_REG_IA]) {
+ /* then service the next interrupt */
+ vm->interrupts_deferred_ = 1;
+ vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_PC];
+ vm->ram[--vm->reg[DCPU16_REG_SP]] = vm->reg[DCPU16_REG_A];
+ vm->reg[DCPU16_REG_PC] = vm->reg[DCPU16_REG_IA];
+ vm->reg[DCPU16_REG_A] = message;
+ }
+ }
}
}
print the current state of the machine
shows current cycle count, registers, and next instruction
*/
-void dcpu16_state_print(struct dcpu16 *d) {
+void dcpu16_state_print(struct dcpu16 *vm) {
unsigned int i;
- if (!d) return;
+ if (!vm) return;
printf(" ");
for (i = 0; i < 8; i++)
- printf(" %c:0x%04x", regnames_[i], d->reg[i]);
+ printf(" %s:0x%04x", dcpu16_reg_names[i], vm->reg[i]);
printf("\n");
- printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
- d->cycle,
- "O", d->o,
- "SP", d->sp,
- "PC", d->pc,
+ printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
+ vm->cycle_,
+ dcpu16_reg_names[DCPU16_REG_EX], vm->reg[DCPU16_REG_EX],
+ dcpu16_reg_names[DCPU16_REG_SP], vm->reg[DCPU16_REG_SP],
+ dcpu16_reg_names[DCPU16_REG_PC], vm->reg[DCPU16_REG_PC],
+ dcpu16_reg_names[DCPU16_REG_IA], vm->reg[DCPU16_REG_IA],
"PC");
- dcpu16_disassemble_print(d, d->pc);
+ dcpu16_disassemble_print(vm, vm->reg[DCPU16_REG_PC]);
printf("\n");
}
/* dcpu16_dump_ram
* print raw ram contents from start to stop
*/
-void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
+void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) {
unsigned int i, j;
const unsigned int n = 8; /* words per line */
- if (!d) return;
+ if (!vm) return;
for (i = start, j = 0; i <= end; i++, j++) {
if (j % n == 0)
printf("0x%04x:\t", i);
- printf(" %04x%s", d->ram[i], (j % n) == (n - 1) ? "\n" : "");
+ printf(" %04x%s", vm->ram[i], (j % n) == (n - 1) ? "\n" : "");
}
if ((j % n) != (n - 1))
printf("\n");
}
+/* instantiate a new 'hardware' device */
+struct dcpu16_hw *dcpu16_hw_new(struct dcpu16 *vm, struct dcpu16_hw_module *mod, void *data) {
+ struct dcpu16_hw *hw;
+
+ hw = calloc(1, sizeof *hw);
+ if (hw == NULL) {
+ vm->warn_cb_("%s():%s", "calloc", strerror(errno));
+ return NULL;
+ }
+ memcpy(hw, mod->template, sizeof *hw);
+ hw->vm = vm;
+
+ if (mod->data_init(hw, data)) {
+ vm->warn_cb_("failed to init hw module data");
+ free(hw);
+ return NULL;
+ }
+ hw->data_free = mod->data_free;
+
+ return hw;
+}
+
+/* destroy a 'hardware' device */
+void dcpu16_hw_del(struct dcpu16_hw **hw) {
+ if (hw) {
+ if (*hw) {
+ if ((*hw)->data_free) {
+ (*hw)->data_free(*hw);
+ }
+ free(*hw);
+ *hw = NULL;
+ }
+ }
+}
+
+/* dcpu16_hw_add
+ * registers new 'hardware' device with system
+ */
+int dcpu16_hw_attach(struct dcpu16 *vm, struct dcpu16_hw *hw) {
+ if (!vm || !hw)
+ return -1;
+
+ TRACE("%s>> name:%s ID:0x%04x%04x MFG:0x%04x%04x VER:0x%04x",
+ __func__,
+ hw->name_,
+ hw->id_h, hw->id_l,
+ hw->mfg_l, hw->mfg_h,
+ hw->ver);
+
+ if (vm->hw_table_entries_ == 0xffff) {
+ WARN("maximum hardware entries reached");
+ return -1;
+ }
+
+ if (vm->hw_table_entries_ == vm->hw_table_allocated_) {
+ size_t new_entries = vm->hw_table_allocated_ + 32;
+ void *tmp_ptr = realloc(vm->hw_table_, new_entries * sizeof * (vm->hw_table_));
+ if (tmp_ptr == NULL) {
+ fprintf(stderr, "%s():%s", "realloc", strerror(errno));
+ return -1;
+ }
+ vm->hw_table_ = tmp_ptr;
+ vm->hw_table_allocated_ += 32;
+ }
+
+ memcpy(vm->hw_table_ + vm->hw_table_entries_, hw, sizeof *hw);
+ vm->hw_table_entries_++;
+
+ TRACE("%s>> added hw entry %zu", __func__, vm->hw_table_entries_);
+
+ return 0;
+}
+
/* dcpu16_acct_add
* Register callback fn to be triggered whenever event matching any events
* in bitwise mask occur.
*/
-int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ mask, void (*fn)(dcpu16_acct_event_, DCPU16_WORD)) {
+int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *fn, void *data) {
struct dcpu16_acct_cb cb;
+ if (!vm)
+ return -1;
+
cb.mask = mask;
cb.fn = fn;
+ cb.data = data;
if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
size_t new_entries = vm->cb_table_allocated_ + 32;
memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
vm->cb_table_entries_++;
+ TRACE("%s>> attached event callback %zu", __func__, vm->cb_table_entries_);
+
return 0;
}
/* dcpu16_reset
- * resets a dcpu16 instance to initial state
+ * signals cpu to reset, clearing runstate and ram, then reload any init callbacks
*/
-void dcpu16_reset(struct dcpu16 *d) {
- if (!d) return;
+void dcpu16_reset(struct dcpu16 *vm) {
+ size_t i;
+
+ if (!vm)
+ return;
+
+ TRACE("%s>> reset", __func__);
- memset(d, 0, sizeof *d);
+ vm->skip_ = 0;
+ vm->interrupts_deferred_ = 0;
+ vm->on_fire_ = 0;
+ memset(vm->interrupts_, 0, sizeof vm->interrupts_);
+ vm->interrupts_head_ = 0;
+ vm->interrupts_tail_ = 0;
+
+ /* signal attached hardware */
+ for (i = 0; i < vm->hw_table_entries_; i++) {
+ if (vm->hw_table_[i].reset)
+ vm->hw_table_[i].reset(vm, &vm->hw_table_[i]);
+ }
+
+ memset(vm->reg, 0, sizeof vm->reg);
+ memset(vm->ram, 0, sizeof vm->ram);
+ vm->cycle_ = 0;
+
+ acct_event_(vm, DCPU16_ACCT_EV_RESET, 0);
}
/* dcpu16_new
if (vm == NULL)
WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno));
+ vm->warn_cb_ = warn_cb_;
+ vm->trace_cb_ = trace_cb_;
+
return vm;
}
* release a dcpu16 instance
*/
void dcpu16_delete(struct dcpu16 **vm) {
- if (!vm || !*vm) return;
+ if (!vm || !*vm)
+ return;
free(*vm);
*vm = NULL;