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