*
* 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
*
#define OPCODE_OPERAND_B_BITS 5
#define OPCODE_OPERAND_A_BITS 6
-static const char * const regnames_ = "ABCXYZIJ";
+const char * const dcpu16_reg_names[] = {
+ "A",
+ "B",
+ "C",
+ "X",
+ "Y",
+ "Z",
+ "I",
+ "J",
+ "PC",
+ "SP",
+ "EX",
+ "IA",
+ NULL
+};
+
+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_;
}
}
}
-/* convert register name to index into register array */
-static inline
-off_t reg_index_(int reg) {
- return strchr(regnames_, reg) - regnames_;
-}
/* add an entry to the interrupt queue */
static
-int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) {
- d->interrupts_[d->interrupts_tail_] = message;
- d->interrupts_tail_ += 1;
- d->interrupts_tail_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+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 (d->interrupts_tail_ == d->interrupts_head_) {
- d->on_fire_ = 1;
+ if (vm->interrupts_tail_ == vm->interrupts_head_) {
+ vm->on_fire_ = 1;
WARN("interrupt queue overflow (system is now on fire)");
return -1;
}
}
static
-DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) {
+DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) {
DCPU16_WORD message;
- if (d->interrupts_tail_ == d->interrupts_head_) {
+ if (vm->interrupts_tail_ == vm->interrupts_head_) {
WARN("interrupt underflow");
return 0;
}
- message = d->interrupts_[d->interrupts_head_];
- d->interrupts_head_ += 1;
- d->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
+ message = vm->interrupts_[vm->interrupts_head_];
+ vm->interrupts_head_ += 1;
+ vm->interrupts_head_ %= DCPU16_INTERRUPT_QUEUE_SIZE;
return message;
}
* 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
- * cycles is set to number of cycles spent looking up operand
+ * 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 *d, DCPU16_WORD value, unsigned int value_is_a, DCPU16_WORD value_data,
+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);
- DCPU16_WORD pc = (DCPU16_WORD)(d->pc + *pc_adjust),
- sp = (DCPU16_WORD)(d->sp + *sp_adjust);
+ DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust),
+ sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *sp_adjust);
- TRACE("%s: pc:0x%04x sp:0x%04x value_data:0x%04x\n",
+ 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("%s>> %c (0x%04x)",
+ *v = vm->reg + value;
+ TRACE("%s>> %s (0x%04x)",
__func__,
- regnames_[value],
+ dcpu16_reg_names[value],
**v);
return;
}
if (value <= 0x0f) { /* [register] */
- *v = &(d->ram[ d->reg[value & 0x07] ]);
- *e_addr = d->reg[value & 0x07];
- TRACE("%s>> [%c] [0x%04x] (0x%04x)",
+ *v = &(vm->ram[ vm->reg[value & 0x07] ]);
+ *e_addr = vm->reg[value & 0x07];
+ TRACE("%s>> [%s] [0x%04x] (0x%04x)",
__func__,
- regnames_[value & 0x07],
- d->reg[value & 0x07],
+ dcpu16_reg_names[value & 0x07],
+ vm->reg[value & 0x07],
**v);
return;
}
if (value <= 0x17) { /* [next word + register] */
*pc_adjust += 1; /* consume next word */
*cycle_adjust += 1;
- *e_addr = value_data + d->reg[value & 0x07];
- *v = d->ram + *e_addr;
- TRACE("%s>> [nextword + %c] [0x%04x + 0x%04x] (0x%04x)",
+ *e_addr = value_data + vm->reg[value & 0x07];
+ *v = vm->ram + *e_addr;
+ TRACE("%s>> [nextword + %s] [0x%04x + 0x%04x] (0x%04x)",
__func__,
- regnames_[value & 0x07],
+ dcpu16_reg_names[value & 0x07],
value_data,
- d->reg[value & 0x07],
+ vm->reg[value & 0x07],
**v);
return;
}
switch (value) {
case 0x18: /* PUSH/[--SP] or POP/[SP++] */
if (value_is_a == 0) { /* b */
- *v = &(d->ram[sp - 1]);
+ *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 = &(d->ram[sp]);
+ *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[sp]);
+ *v = &(vm->ram[sp]);
*e_addr = sp;
TRACE("%s>> PEEK [0x%04x] (0x%04x)",
__func__,
*pc_adjust += 1;
*cycle_adjust += 1;
*e_addr = sp + value_data;
- *v = d->ram + *e_addr;
+ *v = vm->ram + *e_addr;
TRACE("%s>> PICK 0x%04x [0x%04x] (0x%04x)",
__func__,
value_data,
break;
case 0x1b: /* SP */
- *v = &(d->sp);
+ *v = &(vm->reg[DCPU16_REG_SP]);
TRACE("%s>> %s (0x%04x)",
__func__,
- "SP",
+ dcpu16_reg_names[DCPU16_REG_SP],
**v);
break;
case 0x1c: /* PC */
- *v = &(d->pc);
+ *v = &(vm->reg[DCPU16_REG_PC]);
TRACE("%s>> %s (0x%04x)",
__func__,
- "PC",
+ dcpu16_reg_names[DCPU16_REG_PC],
**v);
break;
case 0x1d: /* EX */
- *v = &(d->ex);
+ *v = &(vm->reg[DCPU16_REG_EX]);
TRACE("%s>> %s (0x%04x)",
__func__,
- "EX",
+ dcpu16_reg_names[DCPU16_REG_EX],
**v);
break;
*pc_adjust += 1;
*cycle_adjust += 1;
*e_addr = value_data;
- *v = d->ram + *e_addr;
+ *v = vm->ram + *e_addr;
TRACE("%s>> [nextword] [0x%04x] (0x%04x)",
__func__,
value_data,
/* 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 *d, DCPU16_WORD val_b, DCPU16_WORD val_b_data, DCPU16_WORD val_a, DCPU16_WORD val_a_data)
+#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;\
DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
unsigned int cycle_adjust = 0;\
do {\
op_type;\
- value_decode_(d, val_a, 1, val_a_data,\
- &d->reg_work_[1], &a, &ev_a_addr,\
+ value_decode_(vm, val_a, 1, val_a_data,\
+ &vm->reg_work_[1], &a, &ev_a_addr,\
&pc_adjust, &sp_adjust, &cycle_adjust);\
- d->sp += sp_adjust;\
- d->cycle += cycle_adjust;\
+ vm->reg[DCPU16_REG_SP] += sp_adjust;\
+ vm->cycle += cycle_adjust;\
} while (0)
#define OP_NBI_ (void)val_b, (void)b, (void)ev_b_addr, (void)val_b_data
-#define OP_BASIC_ value_decode_(d, val_b, 0, val_b_data,\
- &d->reg_work_[0], &b, &ev_b_addr,\
+#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_)
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 */
/* reserved for future expansion */
/* fire an illegal instruction event for current instruction */
- DCPU16_WORD future_opcode = (d->ram[d->pc - pc_adjust] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS));
+ 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 - pc_adjust);
+ 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;
+ vm->cycle += 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 - pc_adjust);
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
}
OP_IMPL(nbi_int) {
ACCT_R(ev_a_addr);
- if (d->ia) {
- if ( interrupt_enqueue_(d, *a) ) {
+ if (vm->reg[DCPU16_REG_IA]) {
+ if ( interrupt_enqueue_(vm, *a) ) {
WARN("failed to queue interrupt");
return;
}
- if (d->interrupts_deferred_)
+ if (vm->interrupts_deferred_)
return;
- d->interrupts_deferred_ = 1;
- d->ram[--d->sp] = d->pc;
- d->ram[--d->sp] = d->reg[reg_index_('A')];
- d->pc = d->ia;
- d->reg[0] = *a;
+ 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;
}
- d->cycle += 4;
+ vm->cycle += 4;
}
OP_IMPL(nbi_iag) {
OP_NBI(nbi_iag);
- *a = d->ia;
+ *a = vm->reg[DCPU16_REG_IA];
ACCT_W(ev_a_addr);
}
OP_IMPL(nbi_ias) {
OP_NBI(nbi_ias);
- d->ia = *a;
+ vm->reg[DCPU16_REG_IA] = *a;
ACCT_R(ev_a_addr);
}
OP_IMPL(nbi_rfi) {
OP_NBI(nbi_rfi);
- d->interrupts_deferred_ = 0;
- d->reg[reg_index_('A')] = d->ram[d->sp++];
- d->pc = d->ram[d->sp++];
+ 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]++];
}
OP_IMPL(nbi_iaq) {
OP_NBI(nbi_iaq);
if (*a) {
- d->interrupts_deferred_ = 1;
+ vm->interrupts_deferred_ = 1;
} else {
- d->interrupts_deferred_ = 0;
+ vm->interrupts_deferred_ = 0;
}
ACCT_R(ev_a_addr);
ACCT_W(ev_a_addr);
- *a = d->hw_table_entries_;
+ *a = vm->hw_table_entries_;
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(nbi_hwq) {
ACCT_R(ev_a_addr);
- if (*a >= d->hw_table_entries_) {
+ if (*a >= vm->hw_table_entries_) {
WARN("hardware query for non-extant device 0x%04x", *a);
- d->reg[reg_index_('A')] = 0;
- d->reg[reg_index_('B')] = 0;
- d->reg[reg_index_('C')] = 0;
- d->reg[reg_index_('X')] = 0;
- d->reg[reg_index_('Y')] = 0;
+ 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;
}
- d->reg[reg_index_('A')] = d->hw_table_[*a].id_l;
- d->reg[reg_index_('B')] = d->hw_table_[*a].id_h;
- d->reg[reg_index_('C')] = d->hw_table_[*a].ver;
- d->reg[reg_index_('X')] = d->hw_table_[*a].mfg_l;
- d->reg[reg_index_('Y')] = d->hw_table_[*a].mfg_h;
+ 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;
- d->cycle += 4;
+ vm->cycle += 4;
}
OP_IMPL(nbi_hwi) {
ACCT_R(ev_a_addr);
- if (*a > d->hw_table_entries_) {
+ if (*a > vm->hw_table_entries_) {
WARN("interrupt for non-extant device 0x%04x", *a);
return;
}
- d->cycle += 4;
- d->hw_table_[*a].int_fn(d, d->hw_table_[*a].data);
+ vm->cycle += 4;
+ if (vm->hw_table_[*a].hwi)
+ vm->hw_table_[*a].hwi(vm, vm->hw_table_[*a].data);
+ else
+ WARN("hardware 0x%04x has no interrupt handler", *a);
}
OP_IMPL(nbi_hcf) {
ACCT_R(ev_a_addr);
- d->on_fire_ = 1;
+ vm->on_fire_ = 1;
WARN("system on fire");
- d->cycle += 9;
+ vm->cycle += 9;
}
static const struct opcode_entry opcode_nbi_entries[] = {
/* basic opcodes */
/*
- N.B. the following function does not decode values, (thus does not advance sp)
+ N.B. the following function does not decode values.
Decoding is handled by the secondary opcode functions it calls.
*/
OP_IMPL(_nbi_) {
assert(e->impl != NULL);
TRACE(">> %s 0x%04x", e->name, val_b);
- e->impl(d, 0, 0, val_a, val_a_data);
+ e->impl(vm, 0, 0, val_a, val_a_data);
}
OP_IMPL(set) {
*/
*b = *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = acc;
- d->ex = (acc > 0xffff);
+ vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = acc;
- d->ex = (acc > 0xffff);
+ vm->reg[DCPU16_REG_EX] = (acc > 0xffff);
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = acc;
- d->ex = acc >> 16;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = acc;
- d->ex = acc >> 16;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
if (*a == 0) {
*b = 0;
- d->ex = 0;
+ vm->reg[DCPU16_REG_EX] = 0;
} else {
*b = *b / *a;
- d->ex = (*b << 16) / *a;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) / *a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
if (*a == 0) {
*b = 0;
- d->ex = 0;
+ vm->reg[DCPU16_REG_EX] = 0;
} else {
*b = (short)*b / (short)*a;
- d->ex = (short)(*b << 16) / (short)*a;
+ vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
*b = *b % *a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_a_addr);
}
*b = (short)*b % (short)*a;
}
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
*b = *b & *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
*b = *b | *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
*b = *b ^ *a;
- d->cycle += 1;
+ vm->cycle += 1;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = acc & 0xffff;
- d->ex = (*b << 16) >> *a;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
- d->cycle += 2;
+ vm->cycle += 2;
WARN("IMPLEMENT");
ACCT_R(ev_a_addr);
*b = acc & 0xffff;
- d->ex = (*b << 16) >> *a;
+ vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a;
- d->cycle += 2;
+ vm->cycle += 2;
WARN("IMPLEMENT");
*b = acc;
- d->ex = acc >> 16;
+ vm->reg[DCPU16_REG_EX] = acc >> 16;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
if ((*b & *a) != 0) {
/* */
} else {
- d->skip_ = 1;
- d->cycle += 1;
+ vm->skip_ = 1;
+ vm->cycle += 1;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifc) {
ACCT_R(ev_a_addr);
if ((*b & *a) == 0) {
-
+ /* */
} else {
- d->skip_ = 1;
- d->cycle += 1;
+ vm->skip_ = 1;
+ vm->cycle += 1;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ife) {
if (*b == *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle += 1;
+ vm->skip_ = 1;
+ vm->cycle += 1;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifn) {
if (*b != *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ vm->cycle++;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifg) {
if (*b > *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ vm->cycle++;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifa) {
if (*b > *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle += 1;
+ vm->skip_ = 1;
+ vm->cycle += 1;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifl) {
if (*b < *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle++;
+ vm->skip_ = 1;
+ vm->cycle++;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(ifu) {
if (*b < *a) {
/* */
} else {
- d->skip_ = 1;
- d->cycle += 1;
+ vm->skip_ = 1;
+ vm->cycle += 1;
}
- d->cycle += 2;
+ vm->cycle += 2;
}
OP_IMPL(adx) {
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- acc = *b + *a + d->ex;
+ acc = *b + *a + vm->reg[DCPU16_REG_EX];
*b = acc & 0xffff;
if (acc > 0xffff)
- d->ex = 0x0001;
+ vm->reg[DCPU16_REG_EX] = 0x0001;
else
- d->ex = 0x0000;
+ vm->reg[DCPU16_REG_EX] = 0x0000;
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_b_addr);
ACCT_R(ev_a_addr);
- acc = *b - *a + d->ex;
+ acc = *b - *a + vm->reg[DCPU16_REG_EX];
*b = acc & 0xffff;
if (acc > 0xffff)
- d->ex = 0xffff;
+ vm->reg[DCPU16_REG_EX] = 0xffff;
else
- d->ex = 0;
+ vm->reg[DCPU16_REG_EX] = 0;
- d->cycle += 3;
+ vm->cycle += 3;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = *a;
- d->reg[reg_index_('I')] += 1;
- d->reg[reg_index_('J')] += 1;
+ vm->reg[DCPU16_REG_I] += 1;
+ vm->reg[DCPU16_REG_J] += 1;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
ACCT_R(ev_a_addr);
*b = *a;
- d->reg[reg_index_('I')] -= 1;
- d->reg[reg_index_('J')] -= 1;
+ vm->reg[DCPU16_REG_I] -= 1;
+ vm->reg[DCPU16_REG_J] -= 1;
- d->cycle += 2;
+ vm->cycle += 2;
ACCT_W(ev_b_addr);
}
WARN("reserved opcode invoked");
- ACCT_ILL(d->pc - pc_adjust);
+ ACCT_ILL(vm->reg[DCPU16_REG_PC] - pc_adjust);
}
static const struct opcode_entry opcode_basic_entries[] = {
static inline
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:
if (value_position == 0) { /* b */
*a = (mem[addr] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_B_BITS)) & ((1 << OPCODE_OPERAND_A_BITS) - 1);
*instr_len = 1;
- if ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) {
+ if ((*opcode != 0x0000) &&
+ ( (*b >= 0x10 && *b <= 0x17) || *b == 0x1e || *b == 0x1f ) ) {
*b_data = mem + (DCPU16_WORD)(addr + *instr_len);
- TRACE("**b_data:%hu", **b_data);
*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);
- TRACE("**a_data:%hu", **a_data);
*instr_len += 1;
} else {
*a_data = NULL;
}
-#if 0
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,
*a,
*a_data ? **a_data : 0,
*instr_len);
-#endif
}
/* dcpu16_mnemonify_buf
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 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] = { d->ram[addr], d->ram[(DCPU16_WORD)(addr + 1)], d->ram[(DCPU16_WORD)(addr + 2)] };
+ 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
/*
could be data which happen to match instructions..
*/
for (i = 3; i; i--) {
- instruction_decode_(d->ram, (DCPU16_WORD)(addr - i), &opcode, &b, &b_data, &a, &a_data, &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
#endif
/* just need instr_len */
- instruction_decode_(d->ram, addr, &opcode, &b, &b_data, &a, &a_data, &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 */
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) {
+void dcpu16_step(struct dcpu16 *vm) {
DCPU16_WORD opcode, b, a, instr_len, *b_data, *a_data;
+ size_t i;
const struct opcode_entry *e;
- if (!d) return;
+ if (!vm)
+ return;
- acct_event_(d, DCPU16_ACCT_EV_CYCLE, d->pc);
+ /* signal interested parties that a new cycle has ticked */
+ TRACE("%s>> sending global cycle event", __func__);
+ acct_event_(vm, DCPU16_ACCT_EV_CYCLE, vm->reg[DCPU16_REG_PC]);
- /* if we're currently servicing interrupts */
- if (d->interrupts_deferred_ == 0) {
- /* and there are interrupts to be serviced */
- if (d->interrupts_head_ != d->interrupts_tail_) {
- DCPU16_WORD message;
- message = interrupt_dequeue_(d);
-
- if (d->ia) {
- TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", d->ia, message);
- /* then service the next interrupt */
- d->interrupts_deferred_ = 1;
- d->ram[--d->sp] = d->pc;
- d->ram[--d->sp] = d->reg[reg_index_('A')];
- d->pc = d->ia;
- d->reg[0] = message;
- } else {
- TRACE("ignoring interrupt IA:0");
- }
+ /* signal attached hardware */
+ for (i = 0; i < vm->hw_table_entries_; i++) {
+ if (vm->hw_table_[i].cycle) {
+ TRACE("%s>> sending cycle to %s", __func__, vm->hw_table_[i].name_);
+ vm->hw_table_[i].cycle(vm, vm->hw_table_[i].data);
}
}
- /* and make sure to execute an instruction after an interrupt */
+ instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len);
- instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
+ /* 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>> %s 0x%04x, 0x%04x", __func__, e->name, b, a);
- e->impl(d, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
+ e->impl(vm, b, b_data ? *b_data : 0, a, a_data ? *a_data : 0);
break;
}
}
- /* get ready for the next one */
- d->pc += instr_len;
-
/* and jump over next instr(s) if needed */
- if (d->skip_) {
- instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
- d->pc += instr_len;
+ 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 */
- d->cycle += 1;
- instruction_decode_(d->ram, d->pc, &opcode, &b, &b_data, &a, &a_data, &instr_len);
- d->pc += instr_len;
- TRACE("++ SKIPPED %x words", instr_len);
+ vm->cycle += 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);
+
+ if (vm->reg[DCPU16_REG_IA]) {
+ TRACE("%s>> servicing interrupt IA:0x%04x message:0x%04x \n", __func__, vm->reg[DCPU16_REG_IA], message);
+ /* 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;
+ } else {
+ TRACE("%s>> ignoring interrupt IA:0", __func__);
+ }
}
- d->skip_ = 0;
}
}
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:0x%04x [%2s]:",
- d->cycle,
- "EX", d->ex,
- "SP", d->sp,
- "PC", d->pc,
- "IA", d->ia,
+ 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");
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_));
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;
}
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;
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
* signals cpu to reset, clearing runstate and ram, then reload any init callbacks
*/
-void dcpu16_reset(struct dcpu16 *d) {
- if (!d) return;
-
- d->cycle = 0;
- memset(d->reg, 0, sizeof d->reg);
- d->pc = 0;
- d->sp = 0;
- d->ex = 0;
- d->ia = 0;
- d->skip_ = 0;
- memset(d->ram, 0, sizeof d->ram);
-
- acct_event_(d, DCPU16_ACCT_EV_RESET, 0);
+void dcpu16_reset(struct dcpu16 *vm) {
+ size_t i;
+
+ if (!vm)
+ return;
+
+ TRACE("%s>> reset", __func__);
+
+ 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].data);
+ }
+
+ 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;