* 2012 04 12 - added basic callback support for address accesses
*
* TODO
+ * change api to print into buffers rather than stdio
* drop checks for assigning to literals -- it won't affect anything anyhow
- * debug short literal decoding
+ * refactor opcode functiontables into switch statements
*/
static const char * const src_id_ = "$Id$";
-#define WORD DCPU16_WORD
+#define OPCODE_BASIC_BITS 4
+#define OPCODE_OPERAND_BITS 6
static const char regnames_[] = "ABCXYZIJ";
}
-/* dcpu16_acct_add
- * Register callback fn to be triggered whenever event matching exactly mask_ev
- * and additionally matching any of mask events occur.
- */
-int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event_ match_all, dcpu16_acct_event_ match_any, void (*fn)(dcpu16_acct_event_, DCPU16_WORD)) {
- struct dcpu16_acct_cb cb;
-
- cb.match_all = match_all;
- cb.match_any = match_any;
- cb.fn = fn;
-
- /* add to vm->cb_table_, vm->cb_table_entries_, vm->cb_table_allocated_ */
- if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
- size_t new_entries = vm->cb_table_allocated_ + 32;
- void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
- if (tmp_ptr == NULL) {
- fprintf(stderr, "%s():%s", "realloc", strerror(errno));
- return -1;
- }
- vm->cb_table_ = tmp_ptr;
- vm->cb_table_allocated_ += 32;
- }
-
- memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
- vm->cb_table_entries_++;
-
- return 0;
-}
-
/* acct_event_
* invokes callbacks for specified event
*/
size_t i;
for (i = 0; i < vm->cb_table_entries_; i++) {
- if ( (cb[i].match_all & ev) == cb[i].match_all /* exact match on event flags */
- && (cb[i].match_any & ev) ) { /* any match on rest */
+ if ( (cb[i].mask & ev) )
cb[i].fn(ev, addr);
- }
}
}
+
/* 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)
*/
static
-unsigned int value_decode(struct dcpu16 *d, WORD value, WORD *work_v, WORD **v, dcpu16_acct_event_ *e, WORD *e_addr) {
- WORD nextword;
+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;
assert(value <= 0x3f);
- *e = 0;
-
/* does this value indicate a literal */
if (value >= 0x1f)
retval = 1;
regnames_[value&0x07],
d->reg[value&0x07],
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = d->reg[(value & 0x07)];
} else if (value <= 0x17) { /* [next word + register] */
nextword,
d->reg[(value & 0x07)],
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = nextword + d->reg[(value & 0x07)];
} else switch (value) {
TRACE(">> POP [0x%04x] (0x%04x)",
d->sp - 1,
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = d->sp - 1;
break;
TRACE(">> PEEK [0x%04x] (0x%04x)",
d->sp,
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = d->sp;
break;
TRACE(">> PUSH [0x%04x] (0x%04x)",
d->sp + 1,
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = d->sp + 1;
break;
TRACE(">> [nextword] [0x%04x] (0x%04x)",
nextword,
**v);
- *e |= DCPU16_ACCT_RAM;
*e_addr = nextword;
break;
return retval;
}
-#define OPCODE_BASIC_BITS (4)
-#define OPCODE_BASIC_SHIFT (0)
-
-#define OPCODE_NBI_BITS (6)
-#define OPCODE_NBI_SHIFT (4)
-
-#define OPCODE_FUTURE_BITS (16)
-#define OPCODE_FUTURE_SHIFT (10)
#define OPCODE_NAME_LEN 16
struct opcode_entry {
unsigned short value;
char name[OPCODE_NAME_LEN];
- void (*impl)(struct dcpu16 *, WORD, WORD);
+ void (*impl)(struct dcpu16 *, DCPU16_WORD, DCPU16_WORD);
};
/* messy boilerplate for opcode handlers */
-#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, WORD val_a, WORD val_b)
+#define OP_IMPL(x) static void op_##x(struct dcpu16 *d, DCPU16_WORD val_a, DCPU16_WORD val_b)
-#define OP_NBI_ (void)val_b, (void)b, (void)ev_b, (void)ev_b_addr
-#define OP_BASIC_ (void)value_decode(d, val_b, &d->reg_work_[0], &b, &ev_b, &ev_b_addr)
-#define OP_TYPE(op_type) WORD *a, *b;\
+#define OP_TYPE(op_type) DCPU16_WORD *a, *b;\
unsigned int lit_a;\
- dcpu16_acct_event_ ev_a = 0, ev_b = 0;\
- WORD ev_a_addr = 0, ev_b_addr = 0;\
+ DCPU16_WORD ev_a_addr = 0, ev_b_addr = 0;\
do {\
- lit_a = value_decode(d, val_a, &d->reg_work_[0], &a, &ev_a, &ev_a_addr);\
+ lit_a = value_decode_(d, val_a, &d->reg_work_[0], &a, &ev_a_addr);\
op_type;\
- if (d->skip_) {\
- TRACE("++ SKIPPED");\
- d->skip_ = 0;\
- return;\
- }\
} 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_BASIC(x) OP_TYPE(OP_BASIC_)
#define OP_NBI(x) OP_TYPE(OP_NBI_)
-/* accounting helpers */
-#define ACCT_R(ev) do { if (ev) { acct_event_(d, ev | DCPU16_ACCT_EV_READ, ev##_addr); } } while (0)
-#define ACCT_W(ev) do { if (ev) { acct_event_(d, ev | DCPU16_ACCT_EV_WRITE, ev##_addr); } } while (0)
+/*
+ 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)
/* extended opcodes */
OP_NBI(nbi__reserved_);
/* reserved for future expansion */
- WORD future_opcode = (d->ram[d->pc] >> OPCODE_FUTURE_SHIFT);
+ DCPU16_WORD future_opcode = (d->ram[d->pc] >> (OPCODE_BASIC_BITS + OPCODE_OPERAND_BITS));
WARN("reserved future opcode 0x%04x invoked", future_opcode);
+
+ ACCT_ILL(d->pc);
}
OP_IMPL(nbi_jsr) {
OP_NBI(nbi_jsr);
/* pushes the address of the next instruction to the stack, then sets PC to a */
- ACCT_R(ev_a);
+ ACCT_R(ev_a_addr);
d->ram[ --d->sp ] = d->pc;
d->pc = *a;
d->cycle += 2;
+
+ ACCT_W(d->sp + 1);
}
OP_IMPL(nbi__reserved2_) {
/* reserved */
WARN("reserved nbi opcode invoked");
+
+ ACCT_ILL(d->pc);
}
static const struct opcode_entry opcode_nbi_entries[] = {
/* don't do normal value decoding here */
- WORD nbi_opcode = val_a;
+ DCPU16_WORD nbi_opcode = val_a;
const struct opcode_entry *e = opcode_nbi_entries;
e = opcode_nbi_entries + ( (nbi_opcode < OPCODE_NBI_MAX) ? nbi_opcode : (OPCODE_NBI_MAX - 1) );
OP_BASIC(set);
/* sets a to b */
- ACCT_R(ev_b);
- ACCT_W(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_W(ev_a_addr);
/* only set non-literal target */
if (!lit_a) {
/* sets a to a+b, sets O to 0x0001 if there's an overflow, 0x0 otherwise */
unsigned int acc = *a + *b;
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = acc;
d->cycle += 2;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(sub) {
/* sets a to a-b, sets O to 0xffff if there's an underflow, 0x0 otherwise */
unsigned int acc = *a - *b;
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = acc;
d->o = (acc > 0xffff);
d->cycle += 2;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(mul) {
/* sets a to a*b, sets O to ((a*b)>>16)&0xffff */
unsigned int acc = *a * *b;
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = acc;
d->o = acc >> 16;
d->cycle += 2;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_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. */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (*b == 0) {
if (!lit_a) {
d->cycle += 3;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(mod) {
OP_BASIC(mod);
/* sets a to a%b. if b==0, sets a to 0 instead. */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (*b == 0) {
if (!lit_a) {
d->cycle += 3;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(shl) {
/* sets a to a<<b, sets O to ((a<<b)>>16)&0xffff */
unsigned int acc = *a << *b;
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = acc;
d->cycle += 2;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(shr) {
/* sets a to a>>b, sets O to ((a<<16)>>b)&0xffff */
unsigned int acc = *a >> *b;
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = acc;
d->cycle += 2;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(and) {
OP_BASIC(and);
/* sets a to a&b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = *a & *b;
d->cycle += 1;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(bor) {
OP_BASIC(bor);
/* sets a to a|b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = *a | *b;
d->cycle += 1;
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
}
OP_IMPL(xor) {
OP_BASIC(xor);
/* sets a to a^b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (!lit_a) {
*a = *a ^ *b;
}
- ACCT_W(ev_a);
+ ACCT_W(ev_a_addr);
d->cycle += 1;
}
OP_BASIC(ife);
/* performs next instruction only if a==b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (*a == *b) {
/* */
OP_BASIC(ifn);
/* performs next instruction only if a!=b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (*a != *b) {
/* */
OP_BASIC(ifg);
/* performs next instruction only if a>b */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if (*a > *b) {
/* */
OP_BASIC(ifb);
/* performs next instruction only if (a&b)!=0 */
- ACCT_R(ev_b);
- ACCT_R(ev_a);
+ ACCT_R(ev_b_addr);
+ ACCT_R(ev_a_addr);
if ((*a & *b) != 0) {
/* */
};
static inline
-void dump_operand_value_(WORD value, WORD nextword) {
+void dump_operand_value_(DCPU16_WORD value, DCPU16_WORD nextword) {
if (value <= 0x07) {
printf(" %c", regnames_[value]);
} else if (value <= 0x0f) {
}
}
+
/* split a word into the parts of an instruction, and determine how many words it takes up in total */
static inline
-void instruction_decode_(struct dcpu16 *d, WORD addr, WORD *opcode, WORD *a, WORD *b, unsigned int *instr_len) {
+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 << 6) - 1);
- *b = (d->ram[addr] >> (OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1);
- *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) )
+ *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;
+ }
}
/* dcpu16_disassemble_print
- print the words of the instruction at addr, followed by its assembly
- 7c01 0030 ; SET A,
- 7de1 1000 0020 ; SET [0x1000], 0x0020
- 7803 1000 ; SUB A,
- c00d ; IFN A,
+ print the words of the instruction at addr, followed by its assembly representation
+ returns the length of the instruction in words
*/
-WORD dcpu16_disassemble_print(struct dcpu16 *d, WORD addr) {
- WORD opcode, a, b;
- unsigned int instr_len, i;
+DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) {
+ DCPU16_WORD opcode, a, b, instr_len, i;
const struct opcode_entry *e;
unsigned int indent = 0;
unsigned int partial = 0;
+ if (!d) return 0;
+
/*
Check the previous instruction, to see if this one should be
indented. This check isn't foolproof, as preceeding addresses
return instr_len;
}
+/* execute the next instruction */
void dcpu16_step(struct dcpu16 *d) {
- WORD opcode;
- WORD val_a, val_b;
+ DCPU16_WORD opcode, a, b, instr_len;
const struct opcode_entry *e;
- /* decode opcode and invoke */
+ if (!d) return;
- opcode = (d->ram[ d->pc ] >> OPCODE_BASIC_SHIFT) & ((1 << OPCODE_BASIC_BITS) - 1);
- val_a = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS)) & ((1 << 6) - 1);
- val_b = (d->ram[d->pc] >> (OPCODE_BASIC_SHIFT + OPCODE_BASIC_BITS + 6)) & ((1 << 6) - 1);
+ /*
+ 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);
- d->pc++;
+ d->pc++; /* all instructions take at least one word */
for (e = opcode_basic_entries; e->impl; e++) {
if (e->value == opcode) {
- TRACE(">> %s 0x%04x, 0x%04x", e->name, val_a, val_b);
- e->impl(d, val_a, val_b);
+ TRACE(">> %s 0x%04x, 0x%04x", e->name, a, b);
+ e->impl(d, a, b);
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;
+ TRACE("++ SKIPPED %x words", instr_len);
+ }
}
+/*
+ print the current state of the machine
+ shows current cycle count, registers, and next instruction
+*/
void dcpu16_state_print(struct dcpu16 *d) {
unsigned int i;
+ if (!d) return;
+
+ printf(" ");
+ for (i = 0; i < 8; i++)
+ printf(" %c:0x%04x", regnames_[i], d->reg[i]);
+ printf("\n");
+
printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:",
d->cycle,
"O", d->o,
"PC");
dcpu16_disassemble_print(d, d->pc);
- printf("\n ");
-
- for (i = 0; i < 8; i++)
- printf(" %c:0x%04x", regnames_[i], d->reg[i]);
printf("\n");
}
/* dcpu16_dump_ram
* print raw ram contents from start to stop
*/
-void dcpu16_dump_ram(struct dcpu16 *d, WORD start, WORD end) {
+void dcpu16_dump_ram(struct dcpu16 *d, DCPU16_WORD start, DCPU16_WORD end) {
unsigned int i, j;
const unsigned int n = 8; /* words per line */
+ if (!d) return;
+
for (i = start, j = 0; i <= end; i++, j++) {
if (j % n == 0)
printf("0x%04x:\t", i);
printf("\n");
}
+/* 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)) {
+ struct dcpu16_acct_cb cb;
+
+ cb.mask = mask;
+ cb.fn = fn;
+
+ if (vm->cb_table_entries_ == vm->cb_table_allocated_) {
+ size_t new_entries = vm->cb_table_allocated_ + 32;
+ void *tmp_ptr = realloc(vm->cb_table_, new_entries * sizeof *(vm->cb_table_));
+ if (tmp_ptr == NULL) {
+ fprintf(stderr, "%s():%s", "realloc", strerror(errno));
+ return -1;
+ }
+ vm->cb_table_ = tmp_ptr;
+ vm->cb_table_allocated_ += 32;
+ }
+
+ memcpy(vm->cb_table_ + vm->cb_table_entries_, &cb, sizeof cb);
+ vm->cb_table_entries_++;
+
+ return 0;
+}
+
/* dcpu16_reset
* resets a dcpu16 instance to initial state
*/
void dcpu16_reset(struct dcpu16 *d) {
+ if (!d) return;
+
memset(d, 0, sizeof *d);
}
* release a dcpu16 instance
*/
void dcpu16_delete(struct dcpu16 **vm) {
+ if (!vm || !*vm) return;
+
free(*vm);
*vm = NULL;
}