Path: blob/main/misc/emulator/xnes/snes9x/apu/SPC_CPU.h
28798 views
// snes_spc 0.9.0. http://www.slack.net/~ant/12/* Copyright (C) 2004-2007 Shay Green. This module is free software; you3can redistribute it and/or modify it under the terms of the GNU Lesser4General Public License as published by the Free Software Foundation; either5version 2.1 of the License, or (at your option) any later version. This6module is distributed in the hope that it will be useful, but WITHOUT ANY7WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS8FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more9details. You should have received a copy of the GNU Lesser General Public10License along with this module; if not, write to the Free Software Foundation,11Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */1213//// Memory access1415#if SPC_MORE_ACCURACY16#define SUSPICIOUS_OPCODE( name ) ((void) 0)17#else18#define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" )19#endif2021#define CPU_READ( time, offset, addr )\22cpu_read( addr, time + offset )2324#define CPU_WRITE( time, offset, addr, data )\25cpu_write( data, addr, time + offset )2627#if SPC_MORE_ACCURACY28#define CPU_READ_TIMER( time, offset, addr, out )\29{ out = CPU_READ( time, offset, addr ); }3031#else32// timers are by far the most common thing read from dp33#define CPU_READ_TIMER( time, offset, addr_, out )\34{\35rel_time_t adj_time = time + offset;\36int dp_addr = addr_;\37int ti = dp_addr - (r_t0out + 0xF0);\38if ( (unsigned) ti < timer_count )\39{\40Timer* t = &m.timers [ti];\41if ( adj_time >= t->next_time )\42t = run_timer_( t, adj_time );\43out = t->counter;\44t->counter = 0;\45}\46else\47{\48out = ram [dp_addr];\49int i = dp_addr - 0xF0;\50if ( (unsigned) i < 0x10 )\51out = cpu_read_smp_reg( i, adj_time );\52}\53}54#endif5556#define TIME_ADJ( n ) (n)5758#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out )59#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) )60#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) )6162#define DP_ADDR( addr ) (dp + (addr))6364#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out )65#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )66#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data )6768#define READ_PROG16( addr ) GET_LE16( ram + (addr) )6970#define SET_PC( n ) (pc = ram + (n))71#define GET_PC() (pc - ram)72#define READ_PC( pc ) (*(pc))73#define READ_PC16( pc ) GET_LE16( pc )7475// TODO: remove non-wrapping versions?76#define SPC_NO_SP_WRAPAROUND 07778#define SET_SP( v ) (sp = ram + 0x101 + (v))79#define GET_SP() (sp - 0x101 - ram)8081#if SPC_NO_SP_WRAPAROUND82#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))83#define PUSH( v ) (void) (*--sp = (uint8_t) (v))84#define POP( out ) (void) ((out) = *sp++)8586#else87#define PUSH16( data )\88{\89int addr = (sp -= 2) - ram;\90if ( addr > 0x100 )\91{\92SET_LE16( sp, data );\93}\94else\95{\96ram [(uint8_t) addr + 0x100] = (uint8_t) data;\97sp [1] = (uint8_t) (data >> 8);\98sp += 0x100;\99}\100}101102#define PUSH( data )\103{\104*--sp = (uint8_t) (data);\105if ( sp - ram == 0x100 )\106sp += 0x100;\107}108109#define POP( out )\110{\111out = *sp++;\112if ( sp - ram == 0x201 )\113{\114out = sp [-0x101];\115sp -= 0x100;\116}\117}118119#endif120121#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel )122123unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time )124{125unsigned addr = READ_PC16( pc );126unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13);127return t << 8 & 0x100;128}129130//// Status flag handling131132// Hex value in name to clarify code and bit shifting.133// Flag stored in indicated variable during emulation134int const n80 = 0x80; // nz135int const v40 = 0x40; // psw136int const p20 = 0x20; // dp137int const b10 = 0x10; // psw138int const h08 = 0x08; // psw139int const i04 = 0x04; // psw140int const z02 = 0x02; // nz141int const c01 = 0x01; // c142143int const nz_neg_mask = 0x880; // either bit set indicates N flag set144145#define GET_PSW( out )\146{\147out = psw & ~(n80 | p20 | z02 | c01);\148out |= c >> 8 & c01;\149out |= dp >> 3 & p20;\150out |= ((nz >> 4) | nz) & n80;\151if ( !(uint8_t) nz ) out |= z02;\152}153154#define SET_PSW( in )\155{\156psw = in;\157c = in << 8;\158dp = in << 3 & 0x100;\159nz = (in << 4 & 0x800) | (~in & z02);\160}161162SPC_CPU_RUN_FUNC163{164uint8_t* const ram = RAM;165int a = m.cpu_regs.a;166int x = m.cpu_regs.x;167int y = m.cpu_regs.y;168uint8_t const* pc;169uint8_t* sp;170int psw;171int c;172int nz;173int dp;174175SET_PC( m.cpu_regs.pc );176SET_SP( m.cpu_regs.sp );177SET_PSW( m.cpu_regs.psw );178179goto loop;180181182// Main loop183184cbranch_taken_loop:185pc += *(BOOST::int8_t const*) pc;186inc_pc_loop:187pc++;188loop:189{190unsigned opcode;191unsigned data;192193check( (unsigned) a < 0x100 );194check( (unsigned) x < 0x100 );195check( (unsigned) y < 0x100 );196197opcode = *pc;198if (allow_time_overflow && rel_time >= 0 )199goto stop;200if ( (rel_time += m.cycle_table [opcode]) > 0 && !allow_time_overflow)201goto out_of_time;202203#ifdef SPC_CPU_OPCODE_HOOK204SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );205#endif206/*207//SUB_CASE_COUNTER( 1 );208#define PROFILE_TIMER_LOOP( op, addr, len )\209if ( opcode == op )\210{\211int cond = (unsigned) ((addr) - 0xFD) < 3 &&\212pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\213SUB_CASE_COUNTER( op && cond );\214}215216PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );217PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );218PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 );219*/220221#ifdef DEBUGGER222if (debug_trace)223debug_do_trace(a, x, y, pc, sp, psw, c, nz, dp);224#endif225226227// TODO: if PC is at end of memory, this will get wrong operand (very obscure)228data = *++pc;229switch ( opcode )230{231232// Common instructions233234#define BRANCH( cond )\235{\236pc++;\237pc += (BOOST::int8_t) data;\238if ( cond )\239goto loop;\240pc -= (BOOST::int8_t) data;\241rel_time -= 2;\242goto loop;\243}244245case 0xF0: // BEQ246BRANCH( !(uint8_t) nz ) // 89% taken247248case 0xD0: // BNE249BRANCH( (uint8_t) nz )250251case 0x3F:{// CALL252int old_addr = GET_PC() + 2;253SET_PC( READ_PC16( pc ) );254PUSH16( old_addr );255goto loop;256}257258case 0x6F:// RET259#if SPC_NO_SP_WRAPAROUND260{261SET_PC( GET_LE16( sp ) );262sp += 2;263}264#else265{266int addr = sp - ram;267SET_PC( GET_LE16( sp ) );268sp += 2;269if ( addr < 0x1FF )270goto loop;271272SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] );273sp -= 0x100;274}275#endif276goto loop;277278case 0xE4: // MOV a,dp279++pc;280// 80% from timer281READ_DP_TIMER( 0, data, a = nz );282goto loop;283284case 0xFA:{// MOV dp,dp285int temp;286READ_DP_TIMER( -2, data, temp );287data = temp + no_read_before_write ;288}289// fall through290case 0x8F:{// MOV dp,#imm291int temp = READ_PC( pc + 1 );292pc += 2;293294#if !SPC_MORE_ACCURACY295{296int i = dp + temp;297ram [i] = (uint8_t) data;298i -= 0xF0;299if ( (unsigned) i < 0x10 ) // 76%300{301REGS [i] = (uint8_t) data;302303// Registers other than $F2 and $F4-$F7304//if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 )305if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%306cpu_write_smp_reg( data, rel_time, i );307}308}309#else310WRITE_DP( 0, temp, data );311#endif312goto loop;313}314315case 0xC4: // MOV dp,a316++pc;317#if !SPC_MORE_ACCURACY318{319int i = dp + data;320ram [i] = (uint8_t) a;321i -= 0xF0;322if ( (unsigned) i < 0x10 ) // 39%323{324unsigned sel = i - 2;325REGS [i] = (uint8_t) a;326327if ( sel == 1 ) // 51% $F3328dsp_write( a, rel_time );329else if ( sel > 1 ) // 1% not $F2 or $F3330cpu_write_smp_reg_( a, rel_time, i );331}332}333#else334WRITE_DP( 0, data, a );335#endif336goto loop;337338#define CASE( n ) case n:339340// Define common address modes based on opcode for immediate mode. Execution341// ends with data set to the address of the operand.342#define ADDR_MODES_( op )\343CASE( op - 0x02 ) /* (X) */\344data = x + dp;\345pc--;\346goto end_##op;\347CASE( op + 0x0F ) /* (dp)+Y */\348data = READ_PROG16( data + dp ) + y;\349goto end_##op;\350CASE( op - 0x01 ) /* (dp+X) */\351data = READ_PROG16( ((uint8_t) (data + x)) + dp );\352goto end_##op;\353CASE( op + 0x0E ) /* abs+Y */\354data += y;\355goto abs_##op;\356CASE( op + 0x0D ) /* abs+X */\357data += x;\358CASE( op - 0x03 ) /* abs */\359abs_##op:\360data += 0x100 * READ_PC( ++pc );\361goto end_##op;\362CASE( op + 0x0C ) /* dp+X */\363data = (uint8_t) (data + x);364365#define ADDR_MODES_NO_DP( op )\366ADDR_MODES_( op )\367data += dp;\368end_##op:369370#define ADDR_MODES( op )\371ADDR_MODES_( op )\372CASE( op - 0x04 ) /* dp */\373data += dp;\374end_##op:375376// 1. 8-bit Data Transmission Commands. Group I377378ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr379a = nz = READ( 0, data );380goto inc_pc_loop;381382case 0xBF:{// MOV A,(X)+383int temp = x + dp;384x = (uint8_t) (x + 1);385a = nz = READ( -1, temp );386goto loop;387}388389case 0xE8: // MOV A,imm390a = data;391nz = data;392goto inc_pc_loop;393394case 0xF9: // MOV X,dp+Y395data = (uint8_t) (data + y);396case 0xF8: // MOV X,dp397READ_DP_TIMER( 0, data, x = nz );398goto inc_pc_loop;399400case 0xE9: // MOV X,abs401data = READ_PC16( pc );402++pc;403data = READ( 0, data );404case 0xCD: // MOV X,imm405x = data;406nz = data;407goto inc_pc_loop;408409case 0xFB: // MOV Y,dp+X410data = (uint8_t) (data + x);411case 0xEB: // MOV Y,dp412// 70% from timer413pc++;414READ_DP_TIMER( 0, data, y = nz );415goto loop;416417case 0xEC:{// MOV Y,abs418int temp = READ_PC16( pc );419pc += 2;420READ_TIMER( 0, temp, y = nz );421//y = nz = READ( 0, temp );422goto loop;423}424425case 0x8D: // MOV Y,imm426y = data;427nz = data;428goto inc_pc_loop;429430// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2431432ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A433WRITE( 0, data, a );434goto inc_pc_loop;435436{437int temp;438case 0xCC: // MOV abs,Y439temp = y;440goto mov_abs_temp;441case 0xC9: // MOV abs,X442temp = x;443mov_abs_temp:444WRITE( 0, READ_PC16( pc ), temp );445pc += 2;446goto loop;447}448449case 0xD9: // MOV dp+Y,X450data = (uint8_t) (data + y);451case 0xD8: // MOV dp,X452WRITE( 0, data + dp, x );453goto inc_pc_loop;454455case 0xDB: // MOV dp+X,Y456data = (uint8_t) (data + x);457case 0xCB: // MOV dp,Y458WRITE( 0, data + dp, y );459goto inc_pc_loop;460461// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.462463case 0x7D: // MOV A,X464a = x;465nz = x;466goto loop;467468case 0xDD: // MOV A,Y469a = y;470nz = y;471goto loop;472473case 0x5D: // MOV X,A474x = a;475nz = a;476goto loop;477478case 0xFD: // MOV Y,A479y = a;480nz = a;481goto loop;482483case 0x9D: // MOV X,SP484x = nz = GET_SP();485goto loop;486487case 0xBD: // MOV SP,X488SET_SP( x );489goto loop;490491//case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)492493case 0xAF: // MOV (X)+,A494WRITE_DP( 0, x, a + no_read_before_write );495x++;496goto loop;497498// 5. 8-BIT LOGIC OPERATION COMMANDS499500#define LOGICAL_OP( op, func )\501ADDR_MODES( op ) /* addr */\502data = READ( 0, data );\503case op: /* imm */\504nz = a func##= data;\505goto inc_pc_loop;\506{ unsigned addr;\507case op + 0x11: /* X,Y */\508data = READ_DP( -2, y );\509addr = x + dp;\510goto addr_##op;\511case op + 0x01: /* dp,dp */\512data = READ_DP( -3, data );\513case op + 0x10:{/*dp,imm*/\514uint8_t const* addr2 = pc + 1;\515pc += 2;\516addr = READ_PC( addr2 ) + dp;\517}\518addr_##op:\519nz = data func READ( -1, addr );\520WRITE( 0, addr, nz );\521goto loop;\522}523524LOGICAL_OP( 0x28, & ); // AND525526LOGICAL_OP( 0x08, | ); // OR527528LOGICAL_OP( 0x48, ^ ); // EOR529530// 4. 8-BIT ARITHMETIC OPERATION COMMANDS531532ADDR_MODES( 0x68 ) // CMP addr533data = READ( 0, data );534case 0x68: // CMP imm535nz = a - data;536c = ~nz;537nz &= 0xFF;538goto inc_pc_loop;539540case 0x79: // CMP (X),(Y)541data = READ_DP( -2, y );542nz = READ_DP( -1, x ) - data;543c = ~nz;544nz &= 0xFF;545goto loop;546547case 0x69: // CMP dp,dp548data = READ_DP( -3, data );549case 0x78: // CMP dp,imm550nz = READ_DP( -1, READ_PC( ++pc ) ) - data;551c = ~nz;552nz &= 0xFF;553goto inc_pc_loop;554555case 0x3E: // CMP X,dp556data += dp;557goto cmp_x_addr;558case 0x1E: // CMP X,abs559data = READ_PC16( pc );560pc++;561cmp_x_addr:562data = READ( 0, data );563case 0xC8: // CMP X,imm564nz = x - data;565c = ~nz;566nz &= 0xFF;567goto inc_pc_loop;568569case 0x7E: // CMP Y,dp570data += dp;571goto cmp_y_addr;572case 0x5E: // CMP Y,abs573data = READ_PC16( pc );574pc++;575cmp_y_addr:576data = READ( 0, data );577case 0xAD: // CMP Y,imm578nz = y - data;579c = ~nz;580nz &= 0xFF;581goto inc_pc_loop;582583{584int addr;585case 0xB9: // SBC (x),(y)586case 0x99: // ADC (x),(y)587pc--; // compensate for inc later588data = READ_DP( -2, y );589addr = x + dp;590goto adc_addr;591case 0xA9: // SBC dp,dp592case 0x89: // ADC dp,dp593data = READ_DP( -3, data );594case 0xB8: // SBC dp,imm595case 0x98: // ADC dp,imm596addr = READ_PC( ++pc ) + dp;597adc_addr:598nz = READ( -1, addr );599goto adc_data;600601// catch ADC and SBC together, then decode later based on operand602#undef CASE603#define CASE( n ) case n: case (n) + 0x20:604ADDR_MODES( 0x88 ) // ADC/SBC addr605data = READ( 0, data );606case 0xA8: // SBC imm607case 0x88: // ADC imm608addr = -1; // A609nz = a;610adc_data: {611int flags;612if ( opcode >= 0xA0 ) // SBC613data ^= 0xFF;614615flags = data ^ nz;616nz += data + (c >> 8 & 1);617flags ^= nz;618619psw = (psw & ~(v40 | h08)) |620(flags >> 1 & h08) |621((flags + 0x80) >> 2 & v40);622c = nz;623if ( addr < 0 )624{625a = (uint8_t) nz;626goto inc_pc_loop;627}628WRITE( 0, addr, /*(uint8_t)*/ nz );629goto inc_pc_loop;630}631632}633634// 6. ADDITION & SUBTRACTION COMMANDS635636#define INC_DEC_REG( reg, op )\637nz = reg op;\638reg = (uint8_t) nz;\639goto loop;640641case 0xBC: INC_DEC_REG( a, + 1 ) // INC A642case 0x3D: INC_DEC_REG( x, + 1 ) // INC X643case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y644645case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A646case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X647case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y648649case 0x9B: // DEC dp+X650case 0xBB: // INC dp+X651data = (uint8_t) (data + x);652case 0x8B: // DEC dp653case 0xAB: // INC dp654data += dp;655goto inc_abs;656case 0x8C: // DEC abs657case 0xAC: // INC abs658data = READ_PC16( pc );659pc++;660inc_abs:661nz = (opcode >> 4 & 2) - 1;662nz += READ( -1, data );663WRITE( 0, data, /*(uint8_t)*/ nz );664goto inc_pc_loop;665666// 7. SHIFT, ROTATION COMMANDS667668case 0x5C: // LSR A669c = 0;670case 0x7C:{// ROR A671nz = (c >> 1 & 0x80) | (a >> 1);672c = a << 8;673a = nz;674goto loop;675}676677case 0x1C: // ASL A678c = 0;679case 0x3C:{// ROL A680int temp = c >> 8 & 1;681c = a << 1;682nz = c | temp;683a = (uint8_t) nz;684goto loop;685}686687case 0x0B: // ASL dp688c = 0;689data += dp;690goto rol_mem;691case 0x1B: // ASL dp+X692c = 0;693case 0x3B: // ROL dp+X694data = (uint8_t) (data + x);695case 0x2B: // ROL dp696data += dp;697goto rol_mem;698case 0x0C: // ASL abs699c = 0;700case 0x2C: // ROL abs701data = READ_PC16( pc );702pc++;703rol_mem:704nz = c >> 8 & 1;705nz |= (c = READ( -1, data ) << 1);706WRITE( 0, data, /*(uint8_t)*/ nz );707goto inc_pc_loop;708709case 0x4B: // LSR dp710c = 0;711data += dp;712goto ror_mem;713case 0x5B: // LSR dp+X714c = 0;715case 0x7B: // ROR dp+X716data = (uint8_t) (data + x);717case 0x6B: // ROR dp718data += dp;719goto ror_mem;720case 0x4C: // LSR abs721c = 0;722case 0x6C: // ROR abs723data = READ_PC16( pc );724pc++;725ror_mem: {726int temp = READ( -1, data );727nz = (c >> 1 & 0x80) | (temp >> 1);728c = temp << 8;729WRITE( 0, data, nz );730goto inc_pc_loop;731}732733case 0x9F: // XCN734nz = a = (a >> 4) | (uint8_t) (a << 4);735goto loop;736737// 8. 16-BIT TRANSMISION COMMANDS738739case 0xBA: // MOVW YA,dp740a = READ_DP( -2, data );741nz = (a & 0x7F) | (a >> 1);742y = READ_DP( 0, (uint8_t) (data + 1) );743nz |= y;744goto inc_pc_loop;745746case 0xDA: // MOVW dp,YA747WRITE_DP( -1, data, a );748WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );749goto inc_pc_loop;750751// 9. 16-BIT OPERATION COMMANDS752753case 0x3A: // INCW dp754case 0x1A:{// DECW dp755int temp;756// low byte757data += dp;758temp = READ( -3, data );759temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW760nz = ((temp >> 1) | temp) & 0x7F;761WRITE( -2, data, /*(uint8_t)*/ temp );762763// high byte764data = (uint8_t) (data + 1) + dp;765temp = (uint8_t) ((temp >> 8) + READ( -1, data ));766nz |= temp;767WRITE( 0, data, temp );768769goto inc_pc_loop;770}771772case 0x7A: // ADDW YA,dp773case 0x9A:{// SUBW YA,dp774int lo = READ_DP( -2, data );775int hi = READ_DP( 0, (uint8_t) (data + 1) );776int result;777int flags;778779if ( opcode == 0x9A ) // SUBW780{781lo = (lo ^ 0xFF) + 1;782hi ^= 0xFF;783}784785lo += a;786result = y + hi + (lo >> 8);787flags = hi ^ y ^ result;788789psw = (psw & ~(v40 | h08)) |790(flags >> 1 & h08) |791((flags + 0x80) >> 2 & v40);792c = result;793a = (uint8_t) lo;794result = (uint8_t) result;795y = result;796nz = (((lo >> 1) | lo) & 0x7F) | result;797798goto inc_pc_loop;799}800801case 0x5A: { // CMPW YA,dp802int temp = a - READ_DP( -1, data );803nz = ((temp >> 1) | temp) & 0x7F;804temp = y + (temp >> 8);805temp -= READ_DP( 0, (uint8_t) (data + 1) );806nz |= temp;807c = ~temp;808nz &= 0xFF;809goto inc_pc_loop;810}811812// 10. MULTIPLICATION & DIVISON COMMANDS813814case 0xCF: { // MUL YA815unsigned temp = y * a;816a = (uint8_t) temp;817nz = ((temp >> 1) | temp) & 0x7F;818y = temp >> 8;819nz |= y;820goto loop;821}822823case 0x9E: // DIV YA,X824{825unsigned ya = y * 0x100 + a;826827psw &= ~(h08 | v40);828829if ( y >= x )830psw |= v40;831832if ( (y & 15) >= (x & 15) )833psw |= h08;834835if ( y < x * 2 )836{837a = ya / x;838y = ya - a * x;839}840else841{842a = 255 - (ya - x * 0x200) / (256 - x);843y = x + (ya - x * 0x200) % (256 - x);844}845846nz = (uint8_t) a;847a = (uint8_t) a;848849goto loop;850}851852// 11. DECIMAL COMPENSATION COMMANDS853854case 0xDF: // DAA855SUSPICIOUS_OPCODE( "DAA" );856if ( a > 0x99 || c & 0x100 )857{858a += 0x60;859c = 0x100;860}861862if ( (a & 0x0F) > 9 || psw & h08 )863a += 0x06;864865nz = a;866a = (uint8_t) a;867goto loop;868869case 0xBE: // DAS870SUSPICIOUS_OPCODE( "DAS" );871if ( a > 0x99 || !(c & 0x100) )872{873a -= 0x60;874c = 0;875}876877if ( (a & 0x0F) > 9 || !(psw & h08) )878a -= 0x06;879880nz = a;881a = (uint8_t) a;882goto loop;883884// 12. BRANCHING COMMANDS885886case 0x2F: // BRA rel887pc += (BOOST::int8_t) data;888goto inc_pc_loop;889890case 0x30: // BMI891BRANCH( (nz & nz_neg_mask) )892893case 0x10: // BPL894BRANCH( !(nz & nz_neg_mask) )895896case 0xB0: // BCS897BRANCH( c & 0x100 )898899case 0x90: // BCC900BRANCH( !(c & 0x100) )901902case 0x70: // BVS903BRANCH( psw & v40 )904905case 0x50: // BVC906BRANCH( !(psw & v40) )907908#define CBRANCH( cond )\909{\910pc++;\911if ( cond )\912goto cbranch_taken_loop;\913rel_time -= 2;\914goto inc_pc_loop;\915}916917case 0x03: // BBS dp.bit,rel918case 0x23:919case 0x43:920case 0x63:921case 0x83:922case 0xA3:923case 0xC3:924case 0xE3:925CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )926927case 0x13: // BBC dp.bit,rel928case 0x33:929case 0x53:930case 0x73:931case 0x93:932case 0xB3:933case 0xD3:934case 0xF3:935CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )936937case 0xDE: // CBNE dp+X,rel938data = (uint8_t) (data + x);939// fall through940case 0x2E:{// CBNE dp,rel941int temp;942// 61% from timer943READ_DP_TIMER( -4, data, temp );944CBRANCH( temp != a )945}946947case 0x6E: { // DBNZ dp,rel948unsigned temp = READ_DP( -4, data ) - 1;949WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );950CBRANCH( temp )951}952953case 0xFE: // DBNZ Y,rel954y = (uint8_t) (y - 1);955BRANCH( y )956957case 0x1F: // JMP [abs+X]958SET_PC( READ_PC16( pc ) + x );959// fall through960case 0x5F: // JMP abs961SET_PC( READ_PC16( pc ) );962goto loop;963964// 13. SUB-ROUTINE CALL RETURN COMMANDS965966case 0x0F:{// BRK967int temp;968int ret_addr = GET_PC();969SUSPICIOUS_OPCODE( "BRK" );970SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified971PUSH16( ret_addr );972GET_PSW( temp );973psw = (psw | b10) & ~i04;974PUSH( temp );975goto loop;976}977978case 0x4F:{// PCALL offset979int ret_addr = GET_PC() + 1;980SET_PC( 0xFF00 | data );981PUSH16( ret_addr );982goto loop;983}984985case 0x01: // TCALL n986case 0x11:987case 0x21:988case 0x31:989case 0x41:990case 0x51:991case 0x61:992case 0x71:993case 0x81:994case 0x91:995case 0xA1:996case 0xB1:997case 0xC1:998case 0xD1:999case 0xE1:1000case 0xF1: {1001int ret_addr = GET_PC();1002SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );1003PUSH16( ret_addr );1004goto loop;1005}10061007// 14. STACK OPERATION COMMANDS10081009{1010int temp;1011case 0x7F: // RET11012temp = *sp;1013SET_PC( GET_LE16( sp + 1 ) );1014sp += 3;1015goto set_psw;1016case 0x8E: // POP PSW1017POP( temp );1018set_psw:1019SET_PSW( temp );1020goto loop;1021}10221023case 0x0D: { // PUSH PSW1024int temp;1025GET_PSW( temp );1026PUSH( temp );1027goto loop;1028}10291030case 0x2D: // PUSH A1031PUSH( a );1032goto loop;10331034case 0x4D: // PUSH X1035PUSH( x );1036goto loop;10371038case 0x6D: // PUSH Y1039PUSH( y );1040goto loop;10411042case 0xAE: // POP A1043POP( a );1044goto loop;10451046case 0xCE: // POP X1047POP( x );1048goto loop;10491050case 0xEE: // POP Y1051POP( y );1052goto loop;10531054// 15. BIT OPERATION COMMANDS10551056case 0x02: // SET11057case 0x22:1058case 0x42:1059case 0x62:1060case 0x82:1061case 0xA2:1062case 0xC2:1063case 0xE2:1064case 0x12: // CLR11065case 0x32:1066case 0x52:1067case 0x72:1068case 0x92:1069case 0xB2:1070case 0xD2:1071case 0xF2: {1072int bit = 1 << (opcode >> 5);1073int mask = ~bit;1074if ( opcode & 0x10 )1075bit = 0;1076data += dp;1077WRITE( 0, data, (READ( -1, data ) & mask) | bit );1078goto inc_pc_loop;1079}10801081case 0x0E: // TSET1 abs1082case 0x4E: // TCLR1 abs1083data = READ_PC16( pc );1084pc += 2;1085{1086unsigned temp = READ( -2, data );1087nz = (uint8_t) (a - temp);1088temp &= ~a;1089if ( opcode == 0x0E )1090temp |= a;1091WRITE( 0, data, temp );1092}1093goto loop;10941095case 0x4A: // AND1 C,mem.bit1096c &= MEM_BIT( 0 );1097pc += 2;1098goto loop;10991100case 0x6A: // AND1 C,/mem.bit1101c &= ~MEM_BIT( 0 );1102pc += 2;1103goto loop;11041105case 0x0A: // OR1 C,mem.bit1106c |= MEM_BIT( -1 );1107pc += 2;1108goto loop;11091110case 0x2A: // OR1 C,/mem.bit1111c |= ~MEM_BIT( -1 );1112pc += 2;1113goto loop;11141115case 0x8A: // EOR1 C,mem.bit1116c ^= MEM_BIT( -1 );1117pc += 2;1118goto loop;11191120case 0xEA: // NOT1 mem.bit1121data = READ_PC16( pc );1122pc += 2;1123{1124unsigned temp = READ( -1, data & 0x1FFF );1125temp ^= 1 << (data >> 13);1126WRITE( 0, data & 0x1FFF, temp );1127}1128goto loop;11291130case 0xCA: // MOV1 mem.bit,C1131data = READ_PC16( pc );1132pc += 2;1133{1134unsigned temp = READ( -2, data & 0x1FFF );1135unsigned bit = data >> 13;1136temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit);1137WRITE( 0, data & 0x1FFF, temp + no_read_before_write );1138}1139goto loop;11401141case 0xAA: // MOV1 C,mem.bit1142c = MEM_BIT( 0 );1143pc += 2;1144goto loop;11451146// 16. PROGRAM PSW FLAG OPERATION COMMANDS11471148case 0x60: // CLRC1149c = 0;1150goto loop;11511152case 0x80: // SETC1153c = ~0;1154goto loop;11551156case 0xED: // NOTC1157c ^= 0x100;1158goto loop;11591160case 0xE0: // CLRV1161psw &= ~(v40 | h08);1162goto loop;11631164case 0x20: // CLRP1165dp = 0;1166goto loop;11671168case 0x40: // SETP1169dp = 0x100;1170goto loop;11711172case 0xA0: // EI1173SUSPICIOUS_OPCODE( "EI" );1174psw |= i04;1175goto loop;11761177case 0xC0: // DI1178SUSPICIOUS_OPCODE( "DI" );1179psw &= ~i04;1180goto loop;11811182// 17. OTHER COMMANDS11831184case 0x00: // NOP1185goto loop;11861187case 0xFF:{// STOP1188// handle PC wrap-around1189unsigned addr = GET_PC() - 1;1190if ( addr >= 0x10000 )1191{1192addr &= 0xFFFF;1193SET_PC( addr );1194dprintf( "SPC: PC wrapped around\n" );1195goto loop;1196}1197}1198// fall through1199case 0xEF: // SLEEP1200SUSPICIOUS_OPCODE( "STOP/SLEEP" );1201--pc;1202rel_time = 0;1203m.cpu_error = "SPC emulation error";1204goto stop;1205} // switch12061207assert( 0 ); // catch any unhandled instructions1208}1209out_of_time:1210rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode1211stop:12121213// Uncache registers1214if ( GET_PC() >= 0x10000 )1215dprintf( "SPC: PC wrapped around\n" );1216m.cpu_regs.pc = (uint16_t) GET_PC();1217m.cpu_regs.sp = ( uint8_t) GET_SP();1218m.cpu_regs.a = ( uint8_t) a;1219m.cpu_regs.x = ( uint8_t) x;1220m.cpu_regs.y = ( uint8_t) y;1221{1222int temp;1223GET_PSW( temp );1224m.cpu_regs.psw = (uint8_t) temp;1225}1226}1227SPC_CPU_RUN_FUNC_END122812291230