X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=dcpu16.c;h=616237de3d99d7bc2628cd405ea6f8ee65c2fad1;hb=e1a0aecf189adf6befb6ad708685813191af48bc;hp=75c6cf0df39c3d0d33ce0bd06cf6bad984dd17b2;hpb=b819e4d8f696703ad42c97b357672fd9325bdac6;p=dcpu16 diff --git a/dcpu16.c b/dcpu16.c index 75c6cf0..616237d 100644 --- a/dcpu16.c +++ b/dcpu16.c @@ -18,10 +18,6 @@ * * Justin Wind * 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 * @@ -42,7 +38,23 @@ static const char * const src_id_ = "$Id$"; #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) @@ -60,7 +72,10 @@ void warn_(char *fmt, ...) { } 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 @@ -88,7 +103,10 @@ static void (*trace_cb_)(char *fmt, ...) = #endif ; void dcpu16_trace_cb_set(void (*fn)(char *fmt, ...)) { - trace_cb_ = fn; + if (fn) + trace_cb_ = fn; + else + trace_cb_ = printf_null_; } @@ -106,21 +124,16 @@ void acct_event_(struct dcpu16 *vm, dcpu16_acct_event ev, DCPU16_WORD addr) { } } -/* 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; } @@ -129,17 +142,17 @@ int interrupt_enqueue_(struct dcpu16 *d, DCPU16_WORD message) { } 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; } @@ -152,43 +165,44 @@ DCPU16_WORD interrupt_dequeue_(struct dcpu16 *d) { * 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_SP] + *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; } @@ -196,13 +210,13 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, 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; } @@ -210,12 +224,12 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, 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); @@ -223,7 +237,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, break; case 0x19: /* PEEK/[SP] */ - *v = &(d->ram[sp]); + *v = &(vm->ram[sp]); *e_addr = sp; TRACE("%s>> PEEK [0x%04x] (0x%04x)", __func__, @@ -235,7 +249,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, *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, @@ -244,26 +258,26 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, 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; @@ -271,7 +285,7 @@ void value_decode_(struct dcpu16 *d, DCPU16_WORD value, unsigned int value_is_a, *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, @@ -310,7 +324,7 @@ struct opcode_entry { /* 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;\ @@ -318,15 +332,15 @@ struct opcode_entry { 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_) @@ -337,9 +351,9 @@ struct opcode_entry { 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 */ @@ -355,10 +369,10 @@ OP_IMPL(nbi__reserved_) { /* 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) { @@ -367,12 +381,12 @@ 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_) { @@ -381,7 +395,7 @@ 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) { @@ -389,29 +403,29 @@ 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); } @@ -419,7 +433,7 @@ OP_IMPL(nbi_iag) { OP_IMPL(nbi_ias) { OP_NBI(nbi_ias); - d->ia = *a; + vm->reg[DCPU16_REG_IA] = *a; ACCT_R(ev_a_addr); } @@ -428,18 +442,18 @@ OP_IMPL(nbi_ias) { 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); @@ -450,9 +464,9 @@ OP_IMPL(nbi_hwn) { 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) { @@ -460,23 +474,23 @@ 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) { @@ -484,13 +498,16 @@ 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) { @@ -498,10 +515,10 @@ 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[] = { @@ -533,7 +550,7 @@ 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_) { @@ -551,7 +568,7 @@ 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) { @@ -566,7 +583,7 @@ OP_IMPL(set) { */ *b = *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -580,9 +597,9 @@ OP_IMPL(add) { 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); } @@ -596,9 +613,9 @@ OP_IMPL(sub) { 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); } @@ -612,9 +629,9 @@ OP_IMPL(mul) { 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); } @@ -628,9 +645,9 @@ OP_IMPL(mli) { 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); } @@ -644,13 +661,13 @@ OP_IMPL(div) { 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); } @@ -664,13 +681,13 @@ OP_IMPL(dvi) { 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); } @@ -688,7 +705,7 @@ OP_IMPL(mod) { *b = *b % *a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_a_addr); } @@ -706,7 +723,7 @@ OP_IMPL(mdi) { *b = (short)*b % (short)*a; } - d->cycle += 3; + vm->cycle += 3; ACCT_W(ev_b_addr); } @@ -720,7 +737,7 @@ OP_IMPL(and) { *b = *b & *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -734,7 +751,7 @@ OP_IMPL(bor) { *b = *b | *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -748,7 +765,7 @@ OP_IMPL(xor) { *b = *b ^ *a; - d->cycle += 1; + vm->cycle += 1; ACCT_W(ev_b_addr); } @@ -762,9 +779,9 @@ OP_IMPL(shr) { 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"); @@ -780,9 +797,9 @@ OP_IMPL(asr) { 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"); @@ -799,9 +816,9 @@ OP_IMPL(shl) { *b = acc; - d->ex = acc >> 16; + vm->reg[DCPU16_REG_EX] = acc >> 16; - d->cycle += 2; + vm->cycle += 2; ACCT_W(ev_b_addr); } @@ -816,11 +833,11 @@ OP_IMPL(ifb) { 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) { @@ -831,13 +848,13 @@ 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) { @@ -850,11 +867,11 @@ 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) { @@ -867,11 +884,11 @@ 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) { @@ -884,11 +901,11 @@ 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) { @@ -901,11 +918,11 @@ 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) { @@ -918,11 +935,11 @@ 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) { @@ -935,11 +952,11 @@ 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) { @@ -950,14 +967,14 @@ 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); } @@ -970,14 +987,14 @@ OP_IMPL(sbx) { 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); } @@ -990,10 +1007,10 @@ OP_IMPL(sti) { 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); } @@ -1006,10 +1023,10 @@ OP_IMPL(std) { 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); } @@ -1019,7 +1036,7 @@ OP_IMPL(_reserved_) { 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[] = { @@ -1061,11 +1078,11 @@ 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 */ @@ -1097,9 +1114,9 @@ void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr, *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; @@ -1108,13 +1125,11 @@ void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr, 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, @@ -1125,7 +1140,6 @@ void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr, *a, *a_data ? **a_data : 0, *instr_len); -#endif } /* dcpu16_mnemonify_buf @@ -1160,13 +1174,13 @@ DCPU16_WORD dcpu16_mnemonify_buf(DCPU16_WORD *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 /* @@ -1175,7 +1189,7 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { 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 @@ -1187,12 +1201,12 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { #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 */ @@ -1207,64 +1221,79 @@ DCPU16_WORD dcpu16_disassemble_print(struct dcpu16 *d, DCPU16_WORD addr) { 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; } } @@ -1272,41 +1301,41 @@ void dcpu16_step(struct dcpu16 *d) { 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"); @@ -1319,6 +1348,18 @@ int dcpu16_hw_add(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_)); @@ -1333,6 +1374,8 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) { 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; } @@ -1343,6 +1386,9 @@ int dcpu16_hw_add(struct dcpu16 *vm, struct dcpu16_hw *hw) { 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; @@ -1361,25 +1407,40 @@ int dcpu16_acct_add(struct dcpu16 *vm, dcpu16_acct_event mask, dcpu16_ev_cb_t *f 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 @@ -1392,6 +1453,9 @@ struct dcpu16 *dcpu16_new(void) { if (vm == NULL) WARN("%s: %s(%zu): %s", __func__, "calloc", strerror(errno)); + vm->warn_cb_ = warn_cb_; + vm->trace_cb_ = trace_cb_; + return vm; } @@ -1399,7 +1463,8 @@ struct dcpu16 *dcpu16_new(void) { * release a dcpu16 instance */ void dcpu16_delete(struct dcpu16 **vm) { - if (!vm || !*vm) return; + if (!vm || !*vm) + return; free(*vm); *vm = NULL;