// snes_spc 0.9.0. http://www.slack.net/~ant/ /* Copyright (C) 2004-2007 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //// Memory access // TODO: remove non-wrapping versions? #define SPC_NO_SP_WRAPAROUND 0 unsigned SNES_SPC::CPU_mem_bit(const uint8_t *pc, rel_time_t rel_time) { unsigned addr = get_le16(pc); unsigned t = this->cpu_read(addr & 0x1FFF, rel_time) >> (addr >> 13); return (t << 8) & 0x100; } //// Status flag handling // Hex value in name to clarify code and bit shifting. // Flag stored in indicated variable during emulation const int n80 = 0x80; // nz const int v40 = 0x40; // psw const int p20 = 0x20; // dp const int b10 = 0x10; // psw const int h08 = 0x08; // psw const int i04 = 0x04; // psw const int z02 = 0x02; // nz const int c01 = 0x01; // c const int nz_neg_mask = 0x880; // either bit set indicates N flag set uint8_t *SNES_SPC::run_until_(time_t end_time) { rel_time_t rel_time = this->m.spc_time - end_time; /*assert( rel_time <= 0 );*/ this->m.spc_time = end_time; this->m.dsp_time += rel_time; this->m.timers[0].next_time += rel_time; this->m.timers[1].next_time += rel_time; this->m.timers[2].next_time += rel_time; auto ram = this->m.ram.ram; int a = this->m.cpu_regs.a; int x = this->m.cpu_regs.x; int y = this->m.cpu_regs.y; const uint8_t *pc; uint8_t *sp; int psw; int c; int nz; int dp; // timers are by far the most common thing read from dp auto CPU_READ_TIMER = [&](rel_time_t offset, int addr_) -> int { int out; rel_time_t adj_time = rel_time + offset; int dp_addr = addr_; int ti = dp_addr - (r_t0out + 0xF0); if (static_cast(ti) < timer_count) { auto t = &this->m.timers[ti]; if (adj_time >= t->next_time) t = this->run_timer_(t, adj_time); out = t->counter; t->counter = 0; } else { out = ram[dp_addr]; int i = dp_addr - 0xF0; if (static_cast(i) < 0x10) out = this->cpu_read_smp_reg(i, adj_time); } return out; }; auto DP_ADDR = [&](int addr) { return dp + addr; }; auto READ_PROG16 = [&](int addr) { return get_le16(ram + addr); }; auto SET_PC = [&](int n) { pc = ram + n; }; auto GET_PC = [&]() { return pc - ram; }; auto SET_SP = [&](int v) { sp = ram + 0x101 + v; }; auto GET_SP = [&]() { return sp - 0x101 - ram; }; auto PUSH16 = [&](int data) { sp -= 2; #if !SPC_NO_SP_WRAPAROUND int addr = sp - ram; if (addr > 0x100) #endif set_le16(sp, data); #if !SPC_NO_SP_WRAPAROUND else { ram[static_cast(addr) + 0x100] = static_cast(data); sp[1] = static_cast(data >> 8); sp += 0x100; } #endif }; auto PUSH = [&](int data) { *--sp = static_cast(data); #if !SPC_NO_SP_WRAPAROUND if (sp - ram == 0x100) sp += 0x100; #endif }; auto POP = [&](int &out) { out = *sp++; #if !SPC_NO_SP_WRAPAROUND if (sp - ram == 0x201) { out = sp[-0x101]; sp -= 0x100; } #endif }; auto MEM_BIT = [&](rel_time_t rel) { return this->CPU_mem_bit(pc, rel_time + rel); }; auto GET_PSW = [&](int &out) { out = psw & ~(n80 | p20 | z02 | c01); out |= (c >> 8) & c01; out |= (dp >> 3) & p20; out |= ((nz >> 4) | nz) & n80; if (!static_cast(nz)) out |= z02; }; auto SET_PSW = [&](int in) { psw = in; c = in << 8; dp = (in << 3) & 0x100; nz = ((in << 4) & 0x800) | (~in & z02); }; SET_PC(this->m.cpu_regs.pc); SET_SP(this->m.cpu_regs.sp); SET_PSW(this->m.cpu_regs.psw); goto loop; // Main loop cbranch_taken_loop: pc += *reinterpret_cast(pc); inc_pc_loop: ++pc; loop: unsigned data; unsigned opcode = *pc; if (this->allow_time_overflow && rel_time >= 0) goto stop; if ((rel_time += this->m.cycle_table[opcode]) > 0 && !this->allow_time_overflow) goto out_of_time; #ifdef SPC_CPU_OPCODE_HOOK SPC_CPU_OPCODE_HOOK(GET_PC(), opcode); #endif // TODO: if PC is at end of memory, this will get wrong operand (very obscure) data = *++pc; switch (opcode) { // Common instructions #define BRANCH(cond) \ { \ ++pc; \ pc += static_cast(data); \ if (cond) \ goto loop; \ pc -= static_cast(data); \ rel_time -= 2; \ goto loop; \ } case 0xF0: // BEQ BRANCH(!static_cast(nz)) // 89% taken case 0xD0: // BNE BRANCH(static_cast(nz)) case 0x3F: // CALL { int old_addr = GET_PC() + 2; SET_PC(get_le16(pc)); PUSH16(old_addr); goto loop; } case 0x6F:// RET #if SPC_NO_SP_WRAPAROUND SET_PC(get_le16(sp)); sp += 2; #else { int addr = sp - ram; SET_PC(get_le16(sp)); sp += 2; if (addr < 0x1FF) goto loop; SET_PC(sp[-0x101] * 0x100 + ram[static_cast(addr) + 0x100]); sp -= 0x100; } #endif goto loop; case 0xE4: // MOV a,dp ++pc; // 80% from timer a = nz = CPU_READ_TIMER(0, DP_ADDR(data)); goto loop; case 0xFA: // MOV dp,dp { int temp = CPU_READ_TIMER(-2, DP_ADDR(data)); data = temp + no_read_before_write; } // fall through case 0x8F: // MOV dp,#imm { int temp = *(pc + 1); pc += 2; { int i = dp + temp; ram[i] = static_cast(data); i -= 0xF0; if (static_cast(i) < 0x10) // 76% { this->m.smp_regs[0][i] = static_cast(data); // Registers other than $F2 and $F4-$F7 //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) if (((~0x2F00 << (bits_in_int - 16)) << i) < 0) // 12% this->cpu_write_smp_reg(data, rel_time, i); } } goto loop; } case 0xC4: // MOV dp,a ++pc; { int i = dp + data; ram[i] = static_cast(a); i -= 0xF0; if (static_cast(i) < 0x10) // 39% { unsigned sel = i - 2; this->m.smp_regs[0][i] = static_cast(a); if (sel == 1) // 51% $F3 this->dsp_write(a, rel_time); else if (sel > 1) // 1% not $F2 or $F3 this->cpu_write_smp_reg_(a, rel_time, i); } } goto loop; #define CASE(n) case n: // Define common address modes based on opcode for immediate mode. Execution // ends with data set to the address of the operand. #define ADDR_MODES_(op) \ CASE(op - 0x02) /* (X) */ \ data = x + dp; \ --pc; \ goto end_##op; \ CASE(op + 0x0F) /* (dp)+Y */ \ data = READ_PROG16(data + dp) + y; \ goto end_##op; \ CASE(op - 0x01) /* (dp+X) */ \ data = READ_PROG16(static_cast(data + x) + dp); \ goto end_##op; \ CASE(op + 0x0E) /* abs+Y */ \ data += y; \ goto abs_##op; \ CASE(op + 0x0D) /* abs+X */ \ data += x; \ CASE(op - 0x03) /* abs */ \ abs_##op: \ data += 0x100 * *(++pc); \ goto end_##op; \ CASE(op + 0x0C) /* dp+X */ \ data = static_cast(data + x); #define ADDR_MODES_NO_DP(op) \ ADDR_MODES_(op) \ data += dp; \ end_##op: #define ADDR_MODES(op) \ ADDR_MODES_(op) \ CASE(op - 0x04) /* dp */ \ data += dp; \ end_##op: // 1. 8-bit Data Transmission Commands. Group I ADDR_MODES_NO_DP(0xE8) // MOV A,addr a = nz = this->cpu_read(data, rel_time); goto inc_pc_loop; case 0xBF: // MOV A,(X)+ { int temp = x + dp; x = static_cast(x + 1); a = nz = this->cpu_read(temp, rel_time - 1); goto loop; } case 0xE8: // MOV A,imm a = data; nz = data; goto inc_pc_loop; case 0xF9: // MOV X,dp+Y data = static_cast(data + y); case 0xF8: // MOV X,dp x = nz = CPU_READ_TIMER(0, DP_ADDR(data)); goto inc_pc_loop; case 0xE9: // MOV X,abs data = get_le16(pc); ++pc; data = this->cpu_read(data, rel_time); case 0xCD: // MOV X,imm x = data; nz = data; goto inc_pc_loop; case 0xFB: // MOV Y,dp+X data = static_cast(data + x); case 0xEB: // MOV Y,dp // 70% from timer ++pc; y = nz = CPU_READ_TIMER(0, DP_ADDR(data)); goto loop; case 0xEC: // MOV Y,abs { int temp = get_le16(pc); pc += 2; y = nz = CPU_READ_TIMER(0, temp); //y = nz = this->cpu_read(temp, rel_time); goto loop; } case 0x8D: // MOV Y,imm y = data; nz = data; goto inc_pc_loop; // 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 ADDR_MODES_NO_DP(0xC8) // MOV addr,A this->cpu_write(a, data, rel_time); goto inc_pc_loop; { int temp; case 0xCC: // MOV abs,Y temp = y; goto mov_abs_temp; case 0xC9: // MOV abs,X temp = x; mov_abs_temp: this->cpu_write(temp, get_le16(pc), rel_time); pc += 2; goto loop; } case 0xD9: // MOV dp+Y,X data = static_cast(data + y); case 0xD8: // MOV dp,X this->cpu_write(x, data + dp, rel_time); goto inc_pc_loop; case 0xDB: // MOV dp+X,Y data = static_cast(data + x); case 0xCB: // MOV dp,Y this->cpu_write(y, data + dp, rel_time); goto inc_pc_loop; // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. case 0x7D: // MOV A,X a = nz = x; goto loop; case 0xDD: // MOV A,Y a = nz = y; goto loop; case 0x5D: // MOV X,A x = nz = a; goto loop; case 0xFD: // MOV Y,A y = nz = a; goto loop; case 0x9D: // MOV X,SP x = nz = GET_SP(); goto loop; case 0xBD: // MOV SP,X SET_SP(x); goto loop; //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) case 0xAF: // MOV (X)+,A this->cpu_write(a + no_read_before_write, DP_ADDR(x), rel_time); ++x; goto loop; // 5. 8-BIT LOGIC OPERATION COMMANDS #define LOGICAL_OP(op, func) \ ADDR_MODES(op) /* addr */ \ data = this->cpu_read(data, rel_time); \ case op: /* imm */ \ nz = a func##= data; \ goto inc_pc_loop; \ { \ unsigned addr; \ case op + 0x11: /* X,Y */ \ data = this->cpu_read(DP_ADDR(y), rel_time - 2); \ addr = x + dp; \ goto addr_##op; \ case op + 0x01: /* dp,dp */ \ data = this->cpu_read(DP_ADDR(data), rel_time - 3); \ case op + 0x10: /*dp,imm*/ \ { \ auto addr2 = pc + 1; \ pc += 2; \ addr = *addr2 + dp; \ } \ addr_##op: \ nz = data func this->cpu_read(addr, rel_time - 1); \ this->cpu_write(nz, addr, rel_time); \ goto loop; \ } LOGICAL_OP(0x28, &); // AND LOGICAL_OP(0x08, |); // OR LOGICAL_OP(0x48, ^); // EOR // 4. 8-BIT ARITHMETIC OPERATION COMMANDS ADDR_MODES(0x68) // CMP addr data = this->cpu_read(data, rel_time); case 0x68: // CMP imm nz = a - data; c = ~nz; nz &= 0xFF; goto inc_pc_loop; case 0x79: // CMP (X),(Y) data = this->cpu_read(DP_ADDR(y), rel_time - 2); nz = this->cpu_read(DP_ADDR(x), rel_time - 1) - data; c = ~nz; nz &= 0xFF; goto loop; case 0x69: // CMP dp,dp data = this->cpu_read(DP_ADDR(data), rel_time - 3); case 0x78: // CMP dp,imm nz = this->cpu_read(DP_ADDR(*(++pc)), rel_time - 1) - data; c = ~nz; nz &= 0xFF; goto inc_pc_loop; case 0x3E: // CMP X,dp data += dp; goto cmp_x_addr; case 0x1E: // CMP X,abs data = get_le16(pc); ++pc; cmp_x_addr: data = this->cpu_read(data, rel_time); case 0xC8: // CMP X,imm nz = x - data; c = ~nz; nz &= 0xFF; goto inc_pc_loop; case 0x7E: // CMP Y,dp data += dp; goto cmp_y_addr; case 0x5E: // CMP Y,abs data = get_le16(pc); ++pc; cmp_y_addr: data = this->cpu_read(data, rel_time); case 0xAD: // CMP Y,imm nz = y - data; c = ~nz; nz &= 0xFF; goto inc_pc_loop; { int addr; case 0xB9: // SBC (x),(y) case 0x99: // ADC (x),(y) --pc; // compensate for inc later data = this->cpu_read(DP_ADDR(y), rel_time - 2); addr = x + dp; goto adc_addr; case 0xA9: // SBC dp,dp case 0x89: // ADC dp,dp data = this->cpu_read(DP_ADDR(data), rel_time - 3); case 0xB8: // SBC dp,imm case 0x98: // ADC dp,imm addr = *(++pc) + dp; adc_addr: nz = this->cpu_read(addr, rel_time - 1); goto adc_data; // catch ADC and SBC together, then decode later based on operand #undef CASE #define CASE(n) case n: case (n) + 0x20: ADDR_MODES(0x88) // ADC/SBC addr data = this->cpu_read(data, rel_time); case 0xA8: // SBC imm case 0x88: // ADC imm addr = -1; // A nz = a; adc_data: { int flags; if (opcode >= 0xA0) // SBC data ^= 0xFF; flags = data ^ nz; nz += data + (c >> 8 & 1); flags ^= nz; psw = (psw & ~(v40 | h08)) | ((flags >> 1) & h08) | (((flags + 0x80) >> 2) & v40); c = nz; if (addr < 0) { a = static_cast(nz); goto inc_pc_loop; } this->cpu_write(/*(uint8_t)*/nz, addr, rel_time); goto inc_pc_loop; } } // 6. ADDITION & SUBTRACTION COMMANDS #define INC_DEC_REG(reg, op) \ nz = reg op; \ reg = static_cast(nz); \ goto loop; case 0xBC: INC_DEC_REG(a, + 1) // INC A case 0x3D: INC_DEC_REG(x, + 1) // INC X case 0xFC: INC_DEC_REG(y, + 1) // INC Y case 0x9C: INC_DEC_REG(a, - 1) // DEC A case 0x1D: INC_DEC_REG(x, - 1) // DEC X case 0xDC: INC_DEC_REG(y, - 1) // DEC Y case 0x9B: // DEC dp+X case 0xBB: // INC dp+X data = static_cast(data + x); case 0x8B: // DEC dp case 0xAB: // INC dp data += dp; goto inc_abs; case 0x8C: // DEC abs case 0xAC: // INC abs data = get_le16(pc); ++pc; inc_abs: nz = ((opcode >> 4) & 2) - 1; nz += this->cpu_read(data, rel_time - 1); this->cpu_write(/*(uint8_t)*/ nz, data, rel_time); goto inc_pc_loop; // 7. SHIFT, ROTATION COMMANDS case 0x5C: // LSR A c = 0; case 0x7C: // ROR A { nz = ((c >> 1) & 0x80) | (a >> 1); c = a << 8; a = nz; goto loop; } case 0x1C: // ASL A c = 0; case 0x3C: // ROL A { int temp = c >> 8 & 1; c = a << 1; nz = c | temp; a = static_cast(nz); goto loop; } case 0x0B: // ASL dp c = 0; data += dp; goto rol_mem; case 0x1B: // ASL dp+X c = 0; case 0x3B: // ROL dp+X data = static_cast(data + x); case 0x2B: // ROL dp data += dp; goto rol_mem; case 0x0C: // ASL abs c = 0; case 0x2C: // ROL abs data = get_le16(pc); ++pc; rol_mem: nz = c >> 8 & 1; nz |= (c = this->cpu_read(data, rel_time - 1) << 1); this->cpu_write(/*(uint8_t)*/ nz, data, rel_time); goto inc_pc_loop; case 0x4B: // LSR dp c = 0; data += dp; goto ror_mem; case 0x5B: // LSR dp+X c = 0; case 0x7B: // ROR dp+X data = static_cast(data + x); case 0x6B: // ROR dp data += dp; goto ror_mem; case 0x4C: // LSR abs c = 0; case 0x6C: // ROR abs data = get_le16(pc); ++pc; ror_mem: { int temp = this->cpu_read(data, rel_time - 1); nz = (c >> 1 & 0x80) | (temp >> 1); c = temp << 8; this->cpu_write(nz, data, rel_time); goto inc_pc_loop; } case 0x9F: // XCN nz = a = (a >> 4) | static_cast(a << 4); goto loop; // 8. 16-BIT TRANSMISION COMMANDS case 0xBA: // MOVW YA,dp a = this->cpu_read(DP_ADDR(data), rel_time - 2); nz = (a & 0x7F) | (a >> 1); y = this->cpu_read(DP_ADDR(static_cast(data + 1)), rel_time); nz |= y; goto inc_pc_loop; case 0xDA: // MOVW dp,YA this->cpu_write(a, DP_ADDR(data), rel_time - 1); this->cpu_write(y + no_read_before_write, DP_ADDR(static_cast(data + 1)), rel_time); goto inc_pc_loop; // 9. 16-BIT OPERATION COMMANDS case 0x3A: // INCW dp case 0x1A: // DECW dp { int temp; // low byte data += dp; temp = this->cpu_read(data, rel_time - 3); temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW nz = ((temp >> 1) | temp) & 0x7F; this->cpu_write(/*(uint8_t)*/ temp, data, rel_time - 2); // high byte data = static_cast(data + 1) + dp; temp = static_cast((temp >> 8) + this->cpu_read(data, rel_time - 1)); nz |= temp; this->cpu_write(temp, data, rel_time); goto inc_pc_loop; } case 0x7A: // ADDW YA,dp case 0x9A: // SUBW YA,dp { int lo = this->cpu_read(DP_ADDR(data), rel_time - 2); int hi = this->cpu_read(DP_ADDR(static_cast(data + 1)), rel_time); if (opcode == 0x9A) // SUBW { lo = (lo ^ 0xFF) + 1; hi ^= 0xFF; } lo += a; int result = y + hi + (lo >> 8); int flags = hi ^ y ^ result; psw = (psw & ~(v40 | h08)) | (flags >> 1 & h08) | ((flags + 0x80) >> 2 & v40); c = result; a = static_cast(lo); result = static_cast(result); y = result; nz = (((lo >> 1) | lo) & 0x7F) | result; goto inc_pc_loop; } case 0x5A: // CMPW YA,dp { int temp = a - this->cpu_read(DP_ADDR(data), rel_time - 1); nz = ((temp >> 1) | temp) & 0x7F; temp = y + (temp >> 8); temp -= this->cpu_read(DP_ADDR(static_cast(data + 1)), rel_time); nz |= temp; c = ~temp; nz &= 0xFF; goto inc_pc_loop; } // 10. MULTIPLICATION & DIVISON COMMANDS case 0xCF: // MUL YA { unsigned temp = y * a; a = static_cast(temp); nz = ((temp >> 1) | temp) & 0x7F; y = temp >> 8; nz |= y; goto loop; } case 0x9E: // DIV YA,X { unsigned ya = y * 0x100 + a; psw &= ~(h08 | v40); if (y >= x) psw |= v40; if ((y & 15) >= (x & 15)) psw |= h08; if (y < x * 2) { a = ya / x; y = ya - a * x; } else { a = 255 - (ya - x * 0x200) / (256 - x); y = x + (ya - x * 0x200) % (256 - x); } nz = static_cast(a); a = static_cast(a); goto loop; } // 11. DECIMAL COMPENSATION COMMANDS case 0xDF: // DAA if (a > 0x99 || c & 0x100) { a += 0x60; c = 0x100; } if ((a & 0x0F) > 9 || psw & h08) a += 0x06; nz = a; a = static_cast(a); goto loop; case 0xBE: // DAS if (a > 0x99 || !(c & 0x100)) { a -= 0x60; c = 0; } if ((a & 0x0F) > 9 || !(psw & h08)) a -= 0x06; nz = a; a = static_cast(a); goto loop; // 12. BRANCHING COMMANDS case 0x2F: // BRA rel pc += static_cast(data); goto inc_pc_loop; case 0x30: // BMI BRANCH(nz & nz_neg_mask) case 0x10: // BPL BRANCH(!(nz & nz_neg_mask)) case 0xB0: // BCS BRANCH(c & 0x100) case 0x90: // BCC BRANCH(!(c & 0x100)) case 0x70: // BVS BRANCH(psw & v40) case 0x50: // BVC BRANCH(!(psw & v40)) #define CBRANCH(cond) \ { \ ++pc; \ if (cond) \ goto cbranch_taken_loop; \ rel_time -= 2; \ goto inc_pc_loop; \ } case 0x03: // BBS dp.bit,rel case 0x23: case 0x43: case 0x63: case 0x83: case 0xA3: case 0xC3: case 0xE3: CBRANCH((this->cpu_read(DP_ADDR(data), rel_time - 4) >> (opcode >> 5)) & 1) case 0x13: // BBC dp.bit,rel case 0x33: case 0x53: case 0x73: case 0x93: case 0xB3: case 0xD3: case 0xF3: CBRANCH(!((this->cpu_read(DP_ADDR(data), rel_time - 4) >> (opcode >> 5)) & 1)) case 0xDE: // CBNE dp+X,rel data = static_cast(data + x); // fall through case 0x2E: // CBNE dp,rel { // 61% from timer int temp = CPU_READ_TIMER(-4, DP_ADDR(data)); CBRANCH(temp != a) } case 0x6E: // DBNZ dp,rel { unsigned temp = this->cpu_read(DP_ADDR(data), rel_time - 4) - 1; this->cpu_write(/*(uint8_t)*/ temp + no_read_before_write, DP_ADDR(static_cast(data)), rel_time - 3); CBRANCH(temp) } case 0xFE: // DBNZ Y,rel y = static_cast(y - 1); BRANCH(y) case 0x1F: // JMP [abs+X] SET_PC(get_le16(pc) + x); // fall through case 0x5F: // JMP abs SET_PC(get_le16(pc)); goto loop; // 13. SUB-ROUTINE CALL RETURN COMMANDS case 0x0F: // BRK { int temp; int ret_addr = GET_PC(); SET_PC(READ_PROG16(0xFFDE)); // vector address verified PUSH16(ret_addr); GET_PSW(temp); psw = (psw | b10) & ~i04; PUSH(temp); goto loop; } case 0x4F: // PCALL offset { int ret_addr = GET_PC() + 1; SET_PC(0xFF00 | data); PUSH16(ret_addr); goto loop; } case 0x01: // TCALL n case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71: case 0x81: case 0x91: case 0xA1: case 0xB1: case 0xC1: case 0xD1: case 0xE1: case 0xF1: { int ret_addr = GET_PC(); SET_PC(READ_PROG16(0xFFDE - (opcode >> 3))); PUSH16(ret_addr); goto loop; } // 14. STACK OPERATION COMMANDS { int temp; case 0x7F: // RET1 temp = *sp; SET_PC(get_le16(sp + 1)); sp += 3; goto set_psw; case 0x8E: // POP PSW POP(temp); set_psw: SET_PSW(temp); goto loop; } case 0x0D: // PUSH PSW { int temp; GET_PSW(temp); PUSH(temp); goto loop; } case 0x2D: // PUSH A PUSH(a); goto loop; case 0x4D: // PUSH X PUSH(x); goto loop; case 0x6D: // PUSH Y PUSH(y); goto loop; case 0xAE: // POP A POP(a); goto loop; case 0xCE: // POP X POP(x); goto loop; case 0xEE: // POP Y POP(y); goto loop; // 15. BIT OPERATION COMMANDS case 0x02: // SET1 case 0x22: case 0x42: case 0x62: case 0x82: case 0xA2: case 0xC2: case 0xE2: case 0x12: // CLR1 case 0x32: case 0x52: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: { int bit = 1 << (opcode >> 5); int mask = ~bit; if (opcode & 0x10) bit = 0; data += dp; this->cpu_write((this->cpu_read(data, rel_time - 1) & mask) | bit, data, rel_time); goto inc_pc_loop; } case 0x0E: // TSET1 abs case 0x4E: // TCLR1 abs data = get_le16(pc); pc += 2; { unsigned temp = this->cpu_read(data, rel_time - 2); nz = static_cast(a - temp); temp &= ~a; if (opcode == 0x0E) temp |= a; this->cpu_write(temp, data, rel_time); } goto loop; case 0x4A: // AND1 C,mem.bit c &= MEM_BIT(0); pc += 2; goto loop; case 0x6A: // AND1 C,/mem.bit c &= ~MEM_BIT(0); pc += 2; goto loop; case 0x0A: // OR1 C,mem.bit c |= MEM_BIT(-1); pc += 2; goto loop; case 0x2A: // OR1 C,/mem.bit c |= ~MEM_BIT(-1); pc += 2; goto loop; case 0x8A: // EOR1 C,mem.bit c ^= MEM_BIT(-1); pc += 2; goto loop; case 0xEA: // NOT1 mem.bit data = get_le16(pc); pc += 2; { unsigned temp = this->cpu_read(data & 0x1FFF, rel_time - 1); temp ^= 1 << (data >> 13); this->cpu_write(temp, data & 0x1FFF, rel_time); } goto loop; case 0xCA: // MOV1 mem.bit,C data = get_le16(pc); pc += 2; { unsigned temp = this->cpu_read(data & 0x1FFF, rel_time - 2); unsigned bit = data >> 13; temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); this->cpu_write(temp + no_read_before_write, data & 0x1FFF, rel_time); } goto loop; case 0xAA: // MOV1 C,mem.bit c = MEM_BIT(0); pc += 2; goto loop; // 16. PROGRAM PSW FLAG OPERATION COMMANDS case 0x60: // CLRC c = 0; goto loop; case 0x80: // SETC c = ~0; goto loop; case 0xED: // NOTC c ^= 0x100; goto loop; case 0xE0: // CLRV psw &= ~(v40 | h08); goto loop; case 0x20: // CLRP dp = 0; goto loop; case 0x40: // SETP dp = 0x100; goto loop; case 0xA0: // EI psw |= i04; goto loop; case 0xC0: // DI psw &= ~i04; goto loop; // 17. OTHER COMMANDS case 0x00: // NOP goto loop; case 0xFF: // STOP { // handle PC wrap-around unsigned addr = GET_PC() - 1; if (addr >= 0x10000) { addr &= 0xFFFF; SET_PC(addr); goto loop; } } // fall through case 0xEF: // SLEEP --pc; rel_time = 0; goto stop; } // switch assert(0); // catch any unhandled instructions out_of_time: rel_time -= this->m.cycle_table[*pc]; // undo partial execution of opcode stop: // Uncache registers m.cpu_regs.pc = static_cast(GET_PC()); m.cpu_regs.sp = static_cast(GET_SP()); m.cpu_regs.a = static_cast(a); m.cpu_regs.x = static_cast(x); m.cpu_regs.y = static_cast(y); int temp; GET_PSW(temp); m.cpu_regs.psw = static_cast(temp); this->m.spc_time += rel_time; this->m.dsp_time -= rel_time; this->m.timers[0].next_time -= rel_time; this->m.timers[1].next_time -= rel_time; this->m.timers[2].next_time -= rel_time; /*assert( m.spc_time >= end_time );*/ return &this->m.smp_regs[0][r_cpuio0]; }