X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=dcpu16.c;h=6ab689888d70a28587f07a47560adf1130d254cd;hb=57241bb9e6f6b1acb019efe4f32eb758cf9e93d7;hp=d7b3d4822eb25cf61edf0cd2c4b9fd1f5f6dbd48;hpb=0a1b4588f79e3719af9431a98da44350030df754;p=dcpu16 diff --git a/dcpu16.c b/dcpu16.c index d7b3d48..6ab6898 100644 --- a/dcpu16.c +++ b/dcpu16.c @@ -11,6 +11,7 @@ /* * 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. @@ -26,7 +27,6 @@ * !! 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 @@ -157,6 +157,26 @@ DCPU16_WORD interrupt_dequeue_(struct dcpu16 *vm) { 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].data); + } + } +} + /* 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 @@ -178,10 +198,11 @@ void value_decode_(struct dcpu16 *vm, DCPU16_WORD value, unsigned int value_is_a assert(value <= 0x3f); DCPU16_WORD pc = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *pc_adjust), - sp = (DCPU16_WORD)(vm->reg[DCPU16_REG_PC] + *sp_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); @@ -335,7 +356,7 @@ struct opcode_entry { &vm->reg_work_[1], &a, &ev_a_addr,\ &pc_adjust, &sp_adjust, &cycle_adjust);\ vm->reg[DCPU16_REG_SP] += sp_adjust;\ - vm->cycle += cycle_adjust;\ + dcpu16_cycle_inc(vm, 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_(vm, val_b, 0, val_b_data,\ @@ -383,7 +404,7 @@ OP_IMPL(nbi_jsr) { vm->ram[ --vm->reg[DCPU16_REG_SP] ] = vm->reg[DCPU16_REG_PC]; vm->reg[DCPU16_REG_PC] = *a; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(vm->reg[DCPU16_REG_SP] + 1); } @@ -418,7 +439,7 @@ OP_IMPL(nbi_int) { vm->reg[0] = *a; } - vm->cycle += 4; + dcpu16_cycle_inc(vm, 4); } OP_IMPL(nbi_iag) { @@ -426,6 +447,8 @@ OP_IMPL(nbi_iag) { *a = vm->reg[DCPU16_REG_IA]; + dcpu16_cycle_inc(vm, 1); + ACCT_W(ev_a_addr); } @@ -434,6 +457,8 @@ OP_IMPL(nbi_ias) { vm->reg[DCPU16_REG_IA] = *a; + dcpu16_cycle_inc(vm, 1); + ACCT_R(ev_a_addr); } @@ -444,6 +469,8 @@ OP_IMPL(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) { @@ -455,6 +482,8 @@ OP_IMPL(nbi_iaq) { vm->interrupts_deferred_ = 0; } + dcpu16_cycle_inc(vm, 2); + ACCT_R(ev_a_addr); } @@ -465,7 +494,7 @@ OP_IMPL(nbi_hwn) { *a = vm->hw_table_entries_; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(nbi_hwq) { @@ -489,7 +518,7 @@ OP_IMPL(nbi_hwq) { vm->reg[DCPU16_REG_X] = vm->hw_table_[*a].mfg_l; vm->reg[DCPU16_REG_Y] = vm->hw_table_[*a].mfg_h; - vm->cycle += 4; + dcpu16_cycle_inc(vm, 4); } OP_IMPL(nbi_hwi) { @@ -502,7 +531,7 @@ OP_IMPL(nbi_hwi) { return; } - vm->cycle += 4; + dcpu16_cycle_inc(vm, 4); if (vm->hw_table_[*a].hwi) vm->hw_table_[*a].hwi(vm, vm->hw_table_[*a].data); else @@ -517,7 +546,7 @@ OP_IMPL(nbi_hcf) { vm->on_fire_ = 1; WARN("system on fire"); - vm->cycle += 9; + dcpu16_cycle_inc(vm, 9); } static const struct opcode_entry opcode_nbi_entries[] = { @@ -582,7 +611,7 @@ OP_IMPL(set) { */ *b = *a; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); ACCT_W(ev_b_addr); } @@ -598,7 +627,7 @@ OP_IMPL(add) { *b = acc; vm->reg[DCPU16_REG_EX] = (acc > 0xffff); - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -614,7 +643,7 @@ OP_IMPL(sub) { *b = acc; vm->reg[DCPU16_REG_EX] = (acc > 0xffff); - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -630,7 +659,7 @@ OP_IMPL(mul) { *b = acc; vm->reg[DCPU16_REG_EX] = acc >> 16; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -646,7 +675,7 @@ OP_IMPL(mli) { *b = acc; vm->reg[DCPU16_REG_EX] = acc >> 16; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -666,7 +695,7 @@ OP_IMPL(div) { vm->reg[DCPU16_REG_EX] = (*b << 16) / *a; } - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_b_addr); } @@ -686,7 +715,7 @@ OP_IMPL(dvi) { vm->reg[DCPU16_REG_EX] = (short)(*b << 16) / (short)*a; } - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_b_addr); } @@ -704,7 +733,7 @@ OP_IMPL(mod) { *b = *b % *a; } - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_a_addr); } @@ -722,7 +751,7 @@ OP_IMPL(mdi) { *b = (short)*b % (short)*a; } - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_b_addr); } @@ -736,7 +765,7 @@ OP_IMPL(and) { *b = *b & *a; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); ACCT_W(ev_b_addr); } @@ -750,7 +779,7 @@ OP_IMPL(bor) { *b = *b | *a; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); ACCT_W(ev_b_addr); } @@ -764,7 +793,7 @@ OP_IMPL(xor) { *b = *b ^ *a; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); ACCT_W(ev_b_addr); } @@ -780,7 +809,7 @@ OP_IMPL(shr) { *b = acc & 0xffff; vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); WARN("IMPLEMENT"); @@ -798,7 +827,7 @@ OP_IMPL(asr) { *b = acc & 0xffff; vm->reg[DCPU16_REG_EX] = (*b << 16) >> *a; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); WARN("IMPLEMENT"); @@ -817,7 +846,7 @@ OP_IMPL(shl) { vm->reg[DCPU16_REG_EX] = acc >> 16; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -833,10 +862,10 @@ OP_IMPL(ifb) { /* */ } else { vm->skip_ = 1; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifc) { @@ -850,10 +879,10 @@ OP_IMPL(ifc) { /* */ } else { vm->skip_ = 1; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ife) { @@ -867,10 +896,10 @@ OP_IMPL(ife) { /* */ } else { vm->skip_ = 1; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifn) { @@ -884,10 +913,10 @@ OP_IMPL(ifn) { /* */ } else { vm->skip_ = 1; - vm->cycle++; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifg) { @@ -901,10 +930,10 @@ OP_IMPL(ifg) { /* */ } else { vm->skip_ = 1; - vm->cycle++; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifa) { @@ -918,10 +947,10 @@ OP_IMPL(ifa) { /* */ } else { vm->skip_ = 1; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifl) { @@ -935,10 +964,10 @@ OP_IMPL(ifl) { /* */ } else { vm->skip_ = 1; - vm->cycle++; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(ifu) { @@ -952,10 +981,10 @@ OP_IMPL(ifu) { /* */ } else { vm->skip_ = 1; - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); } OP_IMPL(adx) { @@ -973,7 +1002,7 @@ OP_IMPL(adx) { else vm->reg[DCPU16_REG_EX] = 0x0000; - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_b_addr); } @@ -993,7 +1022,7 @@ OP_IMPL(sbx) { else vm->reg[DCPU16_REG_EX] = 0; - vm->cycle += 3; + dcpu16_cycle_inc(vm, 3); ACCT_W(ev_b_addr); } @@ -1009,7 +1038,7 @@ OP_IMPL(sti) { vm->reg[DCPU16_REG_I] += 1; vm->reg[DCPU16_REG_J] += 1; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -1025,7 +1054,7 @@ OP_IMPL(std) { vm->reg[DCPU16_REG_I] -= 1; vm->reg[DCPU16_REG_J] -= 1; - vm->cycle += 2; + dcpu16_cycle_inc(vm, 2); ACCT_W(ev_b_addr); } @@ -1113,9 +1142,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; @@ -1124,13 +1153,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, @@ -1141,7 +1168,6 @@ void instruction_decode_(DCPU16_WORD *mem, DCPU16_WORD addr, *a, *a_data ? **a_data : 0, *instr_len); -#endif } /* dcpu16_mnemonify_buf @@ -1231,22 +1257,10 @@ int dcpu16_interrupt(struct dcpu16 *vm, DCPU16_WORD message) { /* execute the next instruction */ 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 (!vm) return; - - /* 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]); - - /* 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); - } - } + if (!vm) + return; instruction_decode_(vm->ram, vm->reg[DCPU16_REG_PC], &opcode, &b, &b_data, &a, &a_data, &instr_len); @@ -1270,7 +1284,7 @@ void dcpu16_step(struct dcpu16 *vm) { TRACE("++ SKIPPED %x words", instr_len); if (opcode >= 0x10 && opcode <= 0x17) { /* skipping a branch instruction? skip branch's skippable instruction as well */ - vm->cycle += 1; + dcpu16_cycle_inc(vm, 1); } else { vm->skip_ = 0; } @@ -1283,16 +1297,18 @@ void dcpu16_step(struct dcpu16 *vm) { 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]) { - TRACE("servicing interrupt IA:0x%04x message:0x%04x \n", 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("ignoring interrupt IA:0"); } } } @@ -1313,7 +1329,7 @@ void dcpu16_state_print(struct dcpu16 *vm) { printf("\n"); printf("(0x%08llx) %2s:0x%04x %2s:0x%04x %2s:0x%04x %2s:0x%04x [%2s]:", - vm->cycle, + 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], @@ -1342,6 +1358,41 @@ void dcpu16_dump_ram(struct dcpu16 *vm, DCPU16_WORD start, DCPU16_WORD end) { 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 */ @@ -1349,6 +1400,13 @@ 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; @@ -1368,6 +1426,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; } @@ -1399,6 +1459,8 @@ 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; } @@ -1411,6 +1473,8 @@ void dcpu16_reset(struct dcpu16 *vm) { if (!vm) return; + TRACE("%s>> reset", __func__); + vm->skip_ = 0; vm->interrupts_deferred_ = 0; vm->on_fire_ = 0; @@ -1426,7 +1490,7 @@ void dcpu16_reset(struct dcpu16 *vm) { memset(vm->reg, 0, sizeof vm->reg); memset(vm->ram, 0, sizeof vm->ram); - vm->cycle = 0; + vm->cycle_ = 0; acct_event_(vm, DCPU16_ACCT_EV_RESET, 0); }