// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Spc_Cpu.h" #include "blargg_endian.h" #include "Snes_Spc.h" /* Copyright (C) 2004-2006 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 */ #include "blargg_source.h" // Several instructions are commented out (or not even implemented). These aren't // used by the SPC files tested. // Optimize performance for the most common instructions, and size for the rest: // // 15% 0xF0 BEQ rel // 8% 0xE4 MOV A,dp // 4% 0xF5 MOV A,abs+X // 4% 0xD0 BNE rel // 4% 0x6F RET // 4% 0x3F CALL addr // 4% 0xF4 MOV A,dp+X // 3% 0xC4 MOV dp,A // 2% 0xEB MOV Y,dp // 2% 0x3D INC X // 2% 0xF6 MOV A,abs+Y // (1% and below not shown) Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) { remain_ = 0; assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int blargg_verify_byte_order(); } #define READ( addr ) (emu.read( addr )) #define WRITE( addr, value ) (emu.write( addr, value )) #define READ_DP( addr ) READ( (addr) + dp ) #define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) #define READ_PROG( addr ) (ram [addr]) #define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) int Spc_Cpu::read( spc_addr_t addr ) { return READ( addr ); } void Spc_Cpu::write( spc_addr_t addr, int data ) { WRITE( addr, data ); } // Cycle table derived from text copy of SPC-700 manual (using regular expressions) static unsigned char const cycle_table [0x100] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F }; // The C,mem instructions are hardly used, so a non-inline function is used for // the common access code. unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) { unsigned addr = READ_PROG16( pc ); unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); return (t << 8) & 0x100; } spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) { remain_ = cycle_count; uint8_t* const ram = this->ram; // cache // Stack pointer is kept one greater than usual SPC stack pointer to allow // common pre-decrement and post-increment memory instructions that some // processors have. Address wrap-around isn't supported. #define PUSH( v ) (*--sp = uint8_t (v)) #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) #define POP() (*sp++) #define SET_SP( v ) (sp = ram + 0x101 + (v)) #define GET_SP() (sp - 0x101 - ram) uint8_t* sp; SET_SP( r.sp ); // registers unsigned pc = (unsigned) r.pc; int a = r.a; int x = r.x; int y = r.y; // status flags const int st_n = 0x80; const int st_v = 0x40; const int st_p = 0x20; const int st_b = 0x10; const int st_h = 0x08; const int st_i = 0x04; const int st_z = 0x02; const int st_c = 0x01; #define IS_NEG (nz & 0x880) #define CALC_STATUS( out ) do {\ out = status & ~(st_n | st_z | st_c);\ out |= (c >> 8) & st_c;\ out |= (dp >> 3) & st_p;\ if ( IS_NEG ) out |= st_n;\ if ( !(nz & 0xFF) ) out |= st_z;\ } while ( 0 ) #define SET_STATUS( in ) do {\ status = in & ~(st_n | st_z | st_c | st_p);\ c = in << 8;\ nz = (in << 4) & 0x800;\ nz |= ~in & st_z;\ dp = (in << 3) & 0x100;\ } while ( 0 ) int status; int c; // store C as 'c' & 0x100. int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 unsigned dp; // direct page base { int temp = r.status; SET_STATUS( temp ); } goto loop; unsigned data; // first operand of instruction and temporary across function calls // Common endings for instructions cbranch_taken_loop: // compare and branch pc += (BOOST::int8_t) READ_PROG( pc ); remain_ -= 2; inc_pc_loop: // end of instruction with an operand pc++; loop: check( (unsigned) pc < 0x10000 ); check( (unsigned) GET_SP() < 0x100 ); check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); unsigned opcode = READ_PROG( pc ); pc++; // to do: if pc is at end of memory, this will get wrong byte data = READ_PROG( pc ); if ( remain_ <= 0 ) goto stop; remain_ -= cycle_table [opcode]; // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise // use a local temporary. switch ( opcode ) { #define BRANCH( cond ) {\ pc++;\ int offset = (BOOST::int8_t) data;\ if ( cond ) {\ pc += offset;\ remain_ -= 2;\ }\ goto loop;\ } // Most-Common case 0xF0: // BEQ (most common) BRANCH( !(uint8_t) nz ) case 0xD0: // BNE BRANCH( (uint8_t) nz ) case 0x3F: // CALL PUSH16( pc + 2 ); pc = READ_PROG16( pc ); goto loop; case 0x6F: // RET pc = POP(); pc += POP() * 0x100; 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( uint8_t (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:\ pc++;\ data += 0x100 * READ_PROG( pc );\ goto end_##op;\ CASE( op + 0x0C ) /* dp+X */\ data = uint8_t (data + x);\ CASE( op - 0x04 ) /* dp */\ data += dp;\ end_##op: // 1. 8-bit Data Transmission Commands. Group I ADDR_MODES( 0xE8 ) // MOV A,addr // case 0xE4: // MOV a,dp (most common) mov_a_addr: a = nz = READ( data ); goto inc_pc_loop; case 0xBF: // MOV A,(X)+ data = x + dp; x = uint8_t (x + 1); pc--; goto mov_a_addr; case 0xE8: // MOV A,imm a = data; nz = data; goto inc_pc_loop; case 0xF9: // MOV X,dp+Y data = uint8_t (data + y); case 0xF8: // MOV X,dp data += dp; goto mov_x_addr; case 0xE9: // MOV X,abs data = READ_PROG16( pc ); pc++; mov_x_addr: data = READ( data ); case 0xCD: // MOV X,imm x = data; nz = data; goto inc_pc_loop; case 0xFB: // MOV Y,dp+X data = uint8_t (data + x); case 0xEB: // MOV Y,dp data += dp; goto mov_y_addr; case 0xEC: // MOV Y,abs data = READ_PROG16( pc ); pc++; mov_y_addr: data = READ( data ); case 0x8D: // MOV Y,imm y = data; nz = data; goto inc_pc_loop; // 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 ADDR_MODES( 0xC8 ) // MOV addr,A WRITE( data, a ); 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: WRITE( READ_PROG16( pc ), temp ); pc += 2; goto loop; } case 0xD9: // MOV dp+Y,X data = uint8_t (data + y); case 0xD8: // MOV dp,X WRITE( data + dp, x ); goto inc_pc_loop; case 0xDB: // MOV dp+X,Y data = uint8_t (data + x); case 0xCB: // MOV dp,Y WRITE( data + dp, y ); goto inc_pc_loop; case 0xFA: // MOV dp,dp data = READ( data + dp ); case 0x8F: // MOV dp,#imm pc++; WRITE_DP( READ_PROG( pc ), data ); goto inc_pc_loop; // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. case 0x7D: // MOV A,X a = x; nz = x; goto loop; case 0xDD: // MOV A,Y a = y; nz = y; goto loop; case 0x5D: // MOV X,A x = a; nz = a; goto loop; case 0xFD: // MOV Y,A y = a; 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 WRITE_DP( x, a ); x++; goto loop; // 5. 8-BIT LOGIC OPERATION COMMANDS #define LOGICAL_OP( op, func )\ ADDR_MODES( op ) /* addr */\ data = READ( data );\ case op: /* imm */\ nz = a func##= data;\ goto inc_pc_loop;\ { unsigned addr;\ case op + 0x11: /* X,Y */\ data = READ_DP( y );\ addr = x + dp;\ pc--;\ goto addr_##op;\ case op + 0x01: /* dp,dp */\ data = READ_DP( data );\ case op + 0x10: /*dp,imm*/\ pc++;\ addr = READ_PROG( pc ) + dp;\ addr_##op:\ nz = data func READ( addr );\ WRITE( addr, nz );\ goto inc_pc_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 = READ( data ); case 0x68: // CMP imm nz = a - data; c = ~nz; nz &= 0xFF; goto inc_pc_loop; case 0x79: // CMP (X),(Y) data = READ_DP( x ); nz = data - READ_DP( y ); c = ~nz; nz &= 0xFF; goto loop; case 0x69: // CMP (dp),(dp) data = READ_DP( data ); case 0x78: // CMP dp,imm pc++; nz = READ_DP( READ_PROG( pc ) ) - 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 = READ_PROG16( pc ); pc++; cmp_x_addr: data = READ( data ); 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 = READ_PROG16( pc ); pc++; cmp_y_addr: data = READ( data ); 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 = READ_DP( x ); addr = y + dp; goto adc_addr; case 0xA9: // SBC dp,dp case 0x89: // ADC dp,dp data = READ_DP( data ); case 0xB8: // SBC dp,imm case 0x98: // ADC dp,imm pc++; addr = READ_PROG( pc ) + dp; adc_addr: nz = READ( addr ); 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 = READ( data ); case 0xA8: // SBC imm case 0x88: // ADC imm addr = -1; // A nz = a; adc_data: { if ( opcode & 0x20 ) data ^= 0xFF; // SBC int carry = (c >> 8) & 1; int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend int hc = (nz & 15) + carry; c = nz += data + carry; hc = (nz & 15) - hc; status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); if ( addr < 0 ) { a = (uint8_t) nz; goto inc_pc_loop; } WRITE( addr, (uint8_t) nz ); goto inc_pc_loop; } } // 6. ADDITION & SUBTRACTION COMMANDS #define INC_DEC_REG( reg, n )\ nz = reg + n;\ reg = (uint8_t) 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 = uint8_t (data + x); case 0x8B: // DEC dp case 0xAB: // INC dp data += dp; goto inc_abs; case 0x8C: // DEC abs case 0xAC: // INC abs data = READ_PROG16( pc ); pc++; inc_abs: nz = ((opcode >> 4) & 2) - 1; nz += READ( data ); WRITE( data, (uint8_t) nz ); 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 = (uint8_t) 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 = uint8_t (data + x); case 0x2B: // ROL dp data += dp; goto rol_mem; case 0x0C: // ASL abs c = 0; case 0x2C: // ROL abs data = READ_PROG16( pc ); pc++; rol_mem: nz = (c >> 8) & 1; nz |= (c = READ( data ) << 1); WRITE( data, (uint8_t) nz ); 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 = uint8_t (data + x); case 0x6B: // ROR dp data += dp; goto ror_mem; case 0x4C: // LSR abs c = 0; case 0x6C: // ROR abs data = READ_PROG16( pc ); pc++; ror_mem: { int temp = READ( data ); nz = ((c >> 1) & 0x80) | (temp >> 1); c = temp << 8; WRITE( data, nz ); goto inc_pc_loop; } case 0x9F: // XCN nz = a = (a >> 4) | uint8_t (a << 4); goto loop; // 8. 16-BIT TRANSMISION COMMANDS case 0xBA: // MOVW YA,dp a = READ_DP( data ); nz = (a & 0x7F) | (a >> 1); y = READ_DP( uint8_t (data + 1) ); nz |= y; goto inc_pc_loop; case 0xDA: // MOVW dp,YA WRITE_DP( data, a ); WRITE_DP( uint8_t (data + 1), y ); goto inc_pc_loop; // 9. 16-BIT OPERATION COMMANDS case 0x3A: // INCW dp case 0x1A:{// DECW dp data += dp; // low byte int temp = READ( data ); temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW nz = ((temp >> 1) | temp) & 0x7F; WRITE( data, (uint8_t) temp ); // high byte data = uint8_t (data + 1) + dp; temp >>= 8; temp = uint8_t (temp + READ( data )); nz |= temp; WRITE( data, temp ); goto inc_pc_loop; } case 0x9A: // SUBW YA,dp case 0x7A: // ADDW YA,dp { // read 16-bit addend int temp = READ_DP( data ); int sign = READ_DP( uint8_t (data + 1) ); temp += 0x100 * sign; status &= ~(st_v | st_h); // to do: fix half-carry for SUBW (it's probably wrong) // for SUBW, negate and truncate to 16 bits if ( opcode & 0x80 ) { temp = (temp ^ 0xFFFF) + 1; sign = temp >> 8; } // add low byte (A) temp += a; a = (uint8_t) temp; nz = (temp | (temp >> 1)) & 0x7F; // add high byte (Y) temp >>= 8; c = y + temp; nz = (nz | c) & 0xFF; // half-carry (temporary avoids CodeWarrior optimizer bug) unsigned hc = (c & 15) - (y & 15); status |= (hc >> 4) & st_h; // overflow if sign of YA changed when previous sign and addend sign were same status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; y = (uint8_t) c; goto inc_pc_loop; } case 0x5A: { // CMPW YA,dp int temp = a - READ_DP( data ); nz = ((temp >> 1) | temp) & 0x7F; temp = y + (temp >> 8); temp -= READ_DP( uint8_t (data + 1) ); nz |= temp; c = ~temp; nz &= 0xFF; goto inc_pc_loop; } // 10. MULTIPLICATION & DIVISON COMMANDS case 0xCF: { // MUL YA unsigned temp = y * a; a = (uint8_t) temp; nz = ((temp >> 1) | temp) & 0x7F; y = temp >> 8; nz |= y; goto loop; } case 0x9E: // DIV YA,X { // behavior based on SPC CPU tests status &= ~(st_h | st_v); if ( (y & 15) >= (x & 15) ) status |= st_h; if ( y >= x ) status |= st_v; unsigned ya = y * 0x100 + a; 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 = (uint8_t) a; a = (uint8_t) a; goto loop; } // 11. DECIMAL COMPENSATION COMMANDS // seem unused // case 0xDF: // DAA // case 0xBE: // DAS // 12. BRANCHING COMMANDS case 0x2F: // BRA rel pc += (BOOST::int8_t) data; goto inc_pc_loop; case 0x30: // BMI BRANCH( IS_NEG ) case 0x10: // BPL BRANCH( !IS_NEG ) case 0xB0: // BCS BRANCH( c & 0x100 ) case 0x90: // BCC BRANCH( !(c & 0x100) ) case 0x70: // BVS BRANCH( status & st_v ) case 0x50: // BVC BRANCH( !(status & st_v) ) case 0x03: // BBS dp.bit,rel case 0x23: case 0x43: case 0x63: case 0x83: case 0xA3: case 0xC3: case 0xE3: pc++; if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) goto cbranch_taken_loop; goto inc_pc_loop; case 0x13: // BBC dp.bit,rel case 0x33: case 0x53: case 0x73: case 0x93: case 0xB3: case 0xD3: case 0xF3: pc++; if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) goto cbranch_taken_loop; goto inc_pc_loop; case 0xDE: // CBNE dp+X,rel data = uint8_t (data + x); // fall through case 0x2E: // CBNE dp,rel pc++; if ( READ_DP( data ) != a ) goto cbranch_taken_loop; goto inc_pc_loop; case 0xFE: // DBNZ Y,rel y = uint8_t (y - 1); BRANCH( y ) case 0x6E: { // DBNZ dp,rel pc++; unsigned temp = READ_DP( data ) - 1; WRITE_DP( (uint8_t) data, (uint8_t) temp ); if ( temp ) goto cbranch_taken_loop; goto inc_pc_loop; } case 0x1F: // JMP (abs+X) pc = READ_PROG16( pc ) + x; // fall through case 0x5F: // JMP abs pc = READ_PROG16( pc ); goto loop; // 13. SUB-ROUTINE CALL RETURN COMMANDS case 0x0F:{// BRK check( false ); // untested PUSH16( pc + 1 ); pc = READ_PROG16( 0xFFDE ); // vector address verified int temp; CALC_STATUS( temp ); PUSH( temp ); status = (status | st_b) & ~st_i; goto loop; } case 0x4F: // PCALL offset pc++; PUSH16( pc ); pc = 0xFF00 + data; 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: PUSH16( pc ); pc = READ_PROG16( 0xFFDE - (opcode >> 3) ); goto loop; // 14. STACK OPERATION COMMANDS { int temp; case 0x7F: // RET1 temp = POP(); pc = POP(); pc |= POP() << 8; goto set_status; case 0x8E: // POP PSW temp = POP(); set_status: SET_STATUS( temp ); goto loop; } case 0x0D: { // PUSH PSW int temp; CALC_STATUS( 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 a = POP(); goto loop; case 0xCE: // POP X x = POP(); goto loop; case 0xEE: // POP Y y = POP(); 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: { data += dp; int bit = 1 << (opcode >> 5); int mask = ~bit; if ( opcode & 0x10 ) bit = 0; WRITE( data, (READ( data ) & mask) | bit ); goto inc_pc_loop; } case 0x0E: // TSET1 abs case 0x4E:{// TCLR1 abs data = READ_PROG16( pc ); pc += 2; unsigned temp = READ( data ); nz = temp & a; temp &= ~a; if ( !(opcode & 0x40) ) temp |= a; WRITE( data, temp ); goto loop; } case 0x4A: // AND1 C,mem.bit c &= mem_bit( pc ); pc += 2; goto loop; case 0x6A: // AND1 C,/mem.bit check( false ); // untested c &= ~mem_bit( pc ); pc += 2; goto loop; case 0x0A: // OR1 C,mem.bit check( false ); // untested c |= mem_bit( pc ); pc += 2; goto loop; case 0x2A: // OR1 C,/mem.bit check( false ); // untested c |= ~mem_bit( pc ); pc += 2; goto loop; case 0x8A: // EOR1 C,mem.bit c ^= mem_bit( pc ); pc += 2; goto loop; case 0xEA: { // NOT1 mem.bit data = READ_PROG16( pc ); pc += 2; unsigned temp = READ( data & 0x1FFF ); temp ^= 1 << (data >> 13); WRITE( data & 0x1FFF, temp ); goto loop; } case 0xCA: { // MOV1 mem.bit,C data = READ_PROG16( pc ); pc += 2; unsigned temp = READ( data & 0x1FFF ); unsigned bit = data >> 13; temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); WRITE( data & 0x1FFF, temp ); goto loop; } case 0xAA: // MOV1 C,mem.bit c = mem_bit( pc ); pc += 2; goto loop; // 16. PROGRAM STATUS 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 status &= ~(st_v | st_h); goto loop; case 0x20: // CLRP dp = 0; goto loop; case 0x40: // SETP dp = 0x100; goto loop; case 0xA0: // EI check( false ); // untested status |= st_i; goto loop; case 0xC0: // DI check( false ); // untested status &= ~st_i; goto loop; // 17. OTHER COMMANDS case 0x00: // NOP goto loop; //case 0xEF: // SLEEP //case 0xFF: // STOP } // switch // unhandled instructions fall out of switch so emulator can catch them stop: pc--; { int temp; CALC_STATUS( temp ); r.status = (uint8_t) temp; } r.pc = pc; r.sp = (uint8_t) GET_SP(); r.a = (uint8_t) a; r.x = (uint8_t) x; r.y = (uint8_t) y; return remain_; }