BizHawk/quicknes/nes_emu/Nes_Cpu.cpp

1269 lines
27 KiB
C++

// Nes_Emu 0.7.0. http://www.slack.net/~ant/nes-emu/
// TODO: remove
#if !defined (NDEBUG) && 0
#pragma peephole on
#pragma global_optimizer on
#pragma optimization_level 4
#pragma scheduling 604
#undef BLARGG_ENABLE_OPTIMIZER
#endif
#include "Nes_Cpu.h"
#include <string.h>
#include <limits.h>
#include "blargg_endian.h"
#include "nes_cpu_io.h"
/* Copyright (C) 2003-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "blargg_source.h"
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
inline void Nes_Cpu::set_code_page( int i, uint8_t const* p )
{
code_map [i] = p - (unsigned) i * page_size;
}
void Nes_Cpu::reset( void const* unmapped_page )
{
r.status = 0;
r.sp = 0;
r.pc = 0;
r.a = 0;
r.x = 0;
r.y = 0;
error_count_ = 0;
clock_count = 0;
clock_limit = 0;
irq_time_ = LONG_MAX / 2 + 1;
end_time_ = LONG_MAX / 2 + 1;
assert( page_size == 0x800 ); // assumes this
set_code_page( 0, low_mem );
set_code_page( 1, low_mem );
set_code_page( 2, low_mem );
set_code_page( 3, low_mem );
for ( int i = 4; i < page_count + 1; i++ )
set_code_page( i, (uint8_t*) unmapped_page );
#ifndef NDEBUG
blargg_verify_byte_order();
#endif
}
void Nes_Cpu::map_code( nes_addr_t start, unsigned size, const void* data )
{
// address range must begin and end on page boundaries
require( start % page_size == 0 );
require( size % page_size == 0 );
require( start + size <= 0x10000 );
unsigned first_page = start / page_size;
for ( unsigned i = size / page_size; i--; )
set_code_page( first_page + i, (uint8_t*) data + i * page_size );
}
// Note: 'addr' is evaulated more than once in the following macros, so it
// must not contain side-effects.
//static void log_read( int opcode ) { LOG_FREQ( "read", 256, opcode ); }
#define READ_LIKELY_PPU( addr ) (NES_CPU_READ_PPU( this, (addr), (clock_count) ))
#define READ( addr ) (NES_CPU_READ( this, (addr), (clock_count) ))
#define WRITE( addr, data ) {NES_CPU_WRITE( this, (addr), (data), (clock_count) );}
#define READ_LOW( addr ) (low_mem [int (addr)])
#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [addr])
#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) )
#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
#define GET_SP() ((sp - 1) & 0xFF)
#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
int Nes_Cpu::read( nes_addr_t addr )
{
return READ( addr );
}
void Nes_Cpu::write( nes_addr_t addr, int value )
{
WRITE( addr, value );
}
void Nes_Cpu::set_tracecb(void (*cb)(unsigned int *data))
{
tracecb = cb;
}
#ifndef NES_CPU_GLUE_ONLY
static const unsigned char clock_table [256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
};
Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end )
{
set_end_time_( end );
clock_count = 0;
volatile result_t result = result_cycles;
#if !BLARGG_CPU_CISC
long clock_count = this->clock_count;
uint8_t* const low_mem = this->low_mem;
#endif
// registers
unsigned pc = r.pc;
int sp;
SET_SP( r.sp );
int a = r.a;
int x = r.x;
int y = r.y;
// status flags
int const st_n = 0x80;
int const st_v = 0x40;
int const st_r = 0x20;
int const st_b = 0x10;
int const st_d = 0x08;
int const st_i = 0x04;
int const st_z = 0x02;
int const st_c = 0x01;
#define IS_NEG (nz & 0x880)
#define CALC_STATUS( out ) do { \
out = status & (st_v | st_d | st_i); \
out |= (c >> 8) & st_c; \
if ( IS_NEG ) out |= st_n; \
if ( !(nz & 0xFF) ) out |= st_z; \
} while ( 0 )
#define SET_STATUS( in ) do { \
status = in & (st_v | st_d | st_i); \
c = in << 8; \
nz = (in << 4) & 0x800; \
nz |= ~in & st_z; \
} while ( 0 )
int status;
int c; // carry set if (c & 0x100) != 0
int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0
{
int temp = r.status;
SET_STATUS( temp );
}
goto loop;
dec_clock_loop:
clock_count--;
loop:
assert( (unsigned) GET_SP() < 0x100 );
assert( (unsigned) a < 0x100 );
assert( (unsigned) x < 0x100 );
assert( (unsigned) y < 0x100 );
uint8_t const* page = code_map [pc >> page_bits];
unsigned opcode = page [pc];
pc++;
if ( clock_count >= clock_limit )
goto stop;
if (tracecb)
{
unsigned int scratch[7];
scratch[0] = a;
scratch[1] = x;
scratch[2] = y;
scratch[3] = sp;
scratch[4] = pc - 1;
scratch[5] = status;
scratch[6] = opcode;
tracecb(scratch);
}
clock_count += clock_table [opcode];
unsigned data;
data = page [pc];
switch ( opcode )
{
// Macros
#define GET_OPERAND( addr ) page [addr]
#define GET_OPERAND16( addr ) GET_LE16( &page [addr] )
//#define GET_OPERAND( addr ) READ_PROG( addr )
//#define GET_OPERAND16( addr ) READ_PROG16( addr )
#define ADD_PAGE (pc++, data += 0x100 * GET_OPERAND( pc ));
#define GET_ADDR() GET_OPERAND16( pc )
#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8;
#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
#define IND_Y(r,c) { \
int temp = READ_LOW( data ) + y; \
data = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \
if (c) HANDLE_PAGE_CROSSING( temp ); \
if (!(r) || (temp & 0x100)) \
READ( data - ( temp & 0x100 ) ); \
}
#define IND_X { \
int temp = data + x; \
data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \
}
#define ARITH_ADDR_MODES( op ) \
case op - 0x04: /* (ind,x) */ \
IND_X \
goto ptr##op; \
case op + 0x0C: /* (ind),y */ \
IND_Y(true,true) \
goto ptr##op; \
case op + 0x10: /* zp,X */ \
data = uint8_t (data + x); \
case op + 0x00: /* zp */ \
data = READ_LOW( data ); \
goto imm##op; \
case op + 0x14: /* abs,Y */ \
data += y; \
goto ind##op; \
case op + 0x18: /* abs,X */ \
data += x; \
ind##op: { \
HANDLE_PAGE_CROSSING( data ); \
int temp = data; \
ADD_PAGE \
if ( temp & 0x100 ) \
READ( data - 0x100 ); \
goto ptr##op; \
} \
case op + 0x08: /* abs */ \
ADD_PAGE \
ptr##op: \
data = READ( data ); \
case op + 0x04: /* imm */ \
imm##op: \
#define ARITH_ADDR_MODES_PTR( op ) \
case op - 0x04: /* (ind,x) */ \
IND_X \
goto imm##op; \
case op + 0x0C: \
IND_Y(false,false) \
goto imm##op; \
case op + 0x10: /* zp,X */ \
data = uint8_t (data + x); \
goto imm##op; \
case op + 0x14: /* abs,Y */ \
data += y; \
goto ind##op; \
case op + 0x18: /* abs,X */ \
data += x; \
ind##op: { \
int temp = data; \
ADD_PAGE \
READ( data - ( temp & 0x100 ) ); \
goto imm##op; \
} \
case op + 0x08: /* abs */ \
ADD_PAGE \
case op + 0x00: /* zp */ \
imm##op: \
#define BRANCH( cond ) \
{ \
pc++; \
int offset = (BOOST::int8_t) data; \
int extra_clock = (pc & 0xFF) + offset; \
if ( !(cond) ) goto dec_clock_loop; \
pc += offset; \
pc = BOOST::uint16_t( pc ); \
clock_count += (extra_clock >> 8) & 1; \
goto loop; \
}
// Often-Used
case 0xB5: // LDA zp,x
data = uint8_t (data + x);
case 0xA5: // LDA zp
a = nz = READ_LOW( data );
pc++;
goto loop;
case 0xD0: // BNE
BRANCH( (uint8_t) nz );
case 0x20: { // JSR
int temp = pc + 1;
pc = GET_OPERAND16( pc );
WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
sp = (sp - 2) | 0x100;
WRITE_LOW( sp, temp );
goto loop;
}
case 0x4C: // JMP abs
pc = GET_OPERAND16( pc );
goto loop;
case 0xE8: INC_DEC_XY( x, 1 ) // INX
case 0x10: // BPL
BRANCH( !IS_NEG )
ARITH_ADDR_MODES( 0xC5 ) // CMP
nz = a - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
case 0x30: // BMI
BRANCH( IS_NEG )
case 0xF0: // BEQ
BRANCH( !(uint8_t) nz );
case 0x95: // STA zp,x
data = uint8_t (data + x);
case 0x85: // STA zp
pc++;
WRITE_LOW( data, a );
goto loop;
case 0xC8: INC_DEC_XY( y, 1 ) // INY
case 0xA8: // TAY
y = a;
case 0x98: // TYA
a = nz = y;
goto loop;
case 0xAD:{// LDA abs
unsigned addr = GET_ADDR();
pc += 2;
a = nz = READ_LIKELY_PPU( addr );
goto loop;
}
case 0x60: // RTS
pc = 1 + READ_LOW( sp );
pc += READ_LOW( 0x100 | (sp - 0xFF) ) * 0x100;
sp = (sp - 0xFE) | 0x100;
goto loop;
case 0x99: // STA abs,Y
data += y;
goto sta_ind_common;
case 0x9D: // STA abs,X
data += x;
sta_ind_common: {
int temp = data;
ADD_PAGE
READ( data - ( temp & 0x100 ) );
goto sta_ptr;
}
case 0x8D: // STA abs
ADD_PAGE
sta_ptr:
pc++;
WRITE( data, a );
goto loop;
case 0xA9: // LDA #imm
pc++;
a = data;
nz = data;
goto loop;
#if 0
case 0xA1: // LDA (ind,X)
IND_X
goto lda_ptr;
case 0xB1: // LDA (ind),Y
IND_Y(true,true)
goto lda_ptr;
case 0xB9: // LDA abs,Y
data += y;
goto lda_ind_common;
case 0xBD: // LDA abs,X
data += x;
lda_ind_common: {
HANDLE_PAGE_CROSSING( data );
int temp = data;
ADD_PAGE
if ( temp & 0x100 )
READ( data - 0x100 );
}
lda_ptr:
a = nz = READ( data );
pc++;
goto loop;
#else
// optimization of most commonly used memory read instructions
case 0xB9:// LDA abs,Y
data += y;
data -= x;
case 0xBD:{// LDA abs,X
pc++;
unsigned msb = GET_OPERAND( pc );
data += x;
// indexed common
pc++;
HANDLE_PAGE_CROSSING( data );
int temp = data;
data += msb * 0x100;
a = nz = READ_PROG( BOOST::uint16_t( data ) );
if ( (unsigned) (data - 0x2000) >= 0x6000 )
goto loop;
if ( temp & 0x100 )
READ( data - 0x100 );
a = nz = READ( data );
goto loop;
}
case 0xB1:{// LDA (ind),Y
unsigned msb = READ_LOW( (uint8_t) (data + 1) );
data = READ_LOW( data ) + y;
// indexed common
pc++;
HANDLE_PAGE_CROSSING( data );
int temp = data;
data += msb * 0x100;
a = nz = READ_PROG( BOOST::uint16_t( data ) );
if ( (unsigned) (data - 0x2000) >= 0x6000 )
goto loop;
if ( temp & 0x100 )
READ( data - 0x100 );
a = nz = READ( data );
goto loop;
}
case 0xA1: // LDA (ind,X)
IND_X
a = nz = READ( data );
pc++;
goto loop;
#endif
// Branch
case 0x50: // BVC
BRANCH( !(status & st_v) )
case 0x70: // BVS
BRANCH( status & st_v )
case 0xB0: // BCS
BRANCH( c & 0x100 )
case 0x90: // BCC
BRANCH( !(c & 0x100) )
// Load/store
case 0x94: // STY zp,x
data = uint8_t (data + x);
case 0x84: // STY zp
pc++;
WRITE_LOW( data, y );
goto loop;
case 0x96: // STX zp,y
data = uint8_t (data + y);
case 0x86: // STX zp
pc++;
WRITE_LOW( data, x );
goto loop;
case 0xB6: // LDX zp,y
data = uint8_t (data + y);
case 0xA6: // LDX zp
data = READ_LOW( data );
case 0xA2: // LDX #imm
pc++;
x = data;
nz = data;
goto loop;
case 0xB4: // LDY zp,x
data = uint8_t (data + x);
case 0xA4: // LDY zp
data = READ_LOW( data );
case 0xA0: // LDY #imm
pc++;
y = data;
nz = data;
goto loop;
case 0x91: // STA (ind),Y
IND_Y(false,false)
goto sta_ptr;
case 0x81: // STA (ind,X)
IND_X
goto sta_ptr;
case 0xBC: // LDY abs,X
data += x;
HANDLE_PAGE_CROSSING( data );
case 0xAC:{// LDY abs
pc++;
unsigned addr = data + 0x100 * GET_OPERAND( pc );
if ( data & 0x100 )
READ( addr - 0x100 );
pc++;
y = nz = READ( addr );
goto loop;
}
case 0xBE: // LDX abs,y
data += y;
HANDLE_PAGE_CROSSING( data );
case 0xAE:{// LDX abs
pc++;
unsigned addr = data + 0x100 * GET_OPERAND( pc );
pc++;
if ( data & 0x100 )
READ( addr - 0x100 );
x = nz = READ( addr );
goto loop;
}
{
int temp;
case 0x8C: // STY abs
temp = y;
goto store_abs;
case 0x8E: // STX abs
temp = x;
store_abs:
unsigned addr = GET_ADDR();
WRITE( addr, temp );
pc += 2;
goto loop;
}
// Compare
case 0xEC:{// CPX abs
unsigned addr = GET_ADDR();
pc++;
data = READ( addr );
goto cpx_data;
}
case 0xE4: // CPX zp
data = READ_LOW( data );
case 0xE0: // CPX #imm
cpx_data:
nz = x - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
case 0xCC:{// CPY abs
unsigned addr = GET_ADDR();
pc++;
data = READ( addr );
goto cpy_data;
}
case 0xC4: // CPY zp
data = READ_LOW( data );
case 0xC0: // CPY #imm
cpy_data:
nz = y - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
// Logical
ARITH_ADDR_MODES( 0x25 ) // AND
nz = (a &= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x45 ) // EOR
nz = (a ^= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x05 ) // ORA
nz = (a |= data);
pc++;
goto loop;
case 0x2C:{// BIT abs
unsigned addr = GET_ADDR();
pc += 2;
status &= ~st_v;
nz = READ_LIKELY_PPU( addr );
status |= nz & st_v;
if ( a & nz )
goto loop;
// result must be zero, even if N bit is set
nz = nz << 4 & 0x800;
goto loop;
}
case 0x24: // BIT zp
nz = READ_LOW( data );
pc++;
status &= ~st_v;
status |= nz & st_v;
if ( a & nz )
goto loop;
// result must be zero, even if N bit is set
nz = nz << 4 & 0x800;
goto loop;
// Add/subtract
ARITH_ADDR_MODES( 0xE5 ) // SBC
case 0xEB: // unofficial equivalent
data ^= 0xFF;
goto adc_imm;
ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: {
int carry = (c >> 8) & 1;
int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend
status &= ~st_v;
status |= (ov >> 2) & 0x40;
c = nz = a + data + carry;
pc++;
a = (uint8_t) nz;
goto loop;
}
// Shift/rotate
case 0x4A: // LSR A
lsr_a:
c = 0;
case 0x6A: // ROR A
nz = (c >> 1) & 0x80; // could use bit insert macro here
c = a << 8;
nz |= a >> 1;
a = nz;
goto loop;
case 0x0A: // ASL A
nz = a << 1;
c = nz;
a = (uint8_t) nz;
goto loop;
case 0x2A: { // ROL A
nz = a << 1;
int temp = (c >> 8) & 1;
c = nz;
nz |= temp;
a = (uint8_t) nz;
goto loop;
}
case 0x3E: // ROL abs,X
data += x;
goto rol_abs;
case 0x1E: // ASL abs,X
data += x;
case 0x0E: // ASL abs
c = 0;
case 0x2E: // ROL abs
rol_abs: {
int temp = data;
ADD_PAGE
if ( opcode == 0x1E || opcode == 0x3E ) READ( data - ( temp & 0x100 ) );
WRITE( data, temp = READ( data ) );
nz = (c >> 8) & 1;
nz |= (c = temp << 1);
}
rotate_common:
pc++;
WRITE( data, (uint8_t) nz );
goto loop;
case 0x7E: // ROR abs,X
data += x;
goto ror_abs;
case 0x5E: // LSR abs,X
data += x;
case 0x4E: // LSR abs
c = 0;
case 0x6E: // ROR abs
ror_abs: {
int temp = data;
ADD_PAGE
if ( opcode == 0x5E || opcode == 0x7E ) READ( data - ( temp & 0x100 ) );
WRITE( data, temp = READ( data ) );
nz = ((c >> 1) & 0x80) | (temp >> 1);
c = temp << 8;
goto rotate_common;
}
case 0x76: // ROR zp,x
data = uint8_t (data + x);
goto ror_zp;
case 0x56: // LSR zp,x
data = uint8_t (data + x);
case 0x46: // LSR zp
c = 0;
case 0x66: // ROR zp
ror_zp: {
int temp = READ_LOW( data );
nz = ((c >> 1) & 0x80) | (temp >> 1);
c = temp << 8;
goto write_nz_zp;
}
case 0x36: // ROL zp,x
data = uint8_t (data + x);
goto rol_zp;
case 0x16: // ASL zp,x
data = uint8_t (data + x);
case 0x06: // ASL zp
c = 0;
case 0x26: // ROL zp
rol_zp:
nz = (c >> 8) & 1;
nz |= (c = READ_LOW( data ) << 1);
goto write_nz_zp;
// Increment/decrement
case 0xCA: INC_DEC_XY( x, -1 ) // DEX
case 0x88: INC_DEC_XY( y, -1 ) // DEY
case 0xF6: // INC zp,x
data = uint8_t (data + x);
case 0xE6: // INC zp
nz = 1;
goto add_nz_zp;
case 0xD6: // DEC zp,x
data = uint8_t (data + x);
case 0xC6: // DEC zp
nz = -1;
add_nz_zp:
nz += READ_LOW( data );
write_nz_zp:
pc++;
WRITE_LOW( data, nz );
goto loop;
case 0xFE: { // INC abs,x
int temp = data + x;
data = x + GET_ADDR();
READ( data - ( temp & 0x100 ) );
goto inc_ptr;
}
case 0xEE: // INC abs
data = GET_ADDR();
inc_ptr:
nz = 1;
goto inc_common;
case 0xDE: { // DEC abs,x
int temp = data + x;
data = x + GET_ADDR();
READ( data - ( temp & 0x100 ) );
goto dec_ptr;
}
case 0xCE: // DEC abs
data = GET_ADDR();
dec_ptr:
nz = -1;
inc_common: {
int temp;
WRITE( data, temp = READ( data ) );
nz += temp;
pc += 2;
WRITE( data, (uint8_t) nz );
goto loop;
}
// Transfer
case 0xAA: // TAX
x = a;
case 0x8A: // TXA
a = nz = x;
goto loop;
case 0x9A: // TXS
SET_SP( x ); // verified (no flag change)
goto loop;
case 0xBA: // TSX
x = nz = GET_SP();
goto loop;
// Stack
case 0x48: // PHA
PUSH( a ); // verified
goto loop;
case 0x68: // PLA
a = nz = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
goto loop;
case 0x40: // RTI
{
int temp = READ_LOW( sp );
pc = READ_LOW( 0x100 | (sp - 0xFF) );
pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
sp = (sp - 0xFD) | 0x100;
data = status;
SET_STATUS( temp );
}
if ( !((data ^ status) & st_i) )
goto loop; // I flag didn't change
i_flag_changed:
//dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") );
this->r.status = status; // update externally-visible I flag
// update clock_limit based on modified I flag
clock_limit = end_time_;
if ( end_time_ <= irq_time_ )
goto loop;
if ( status & st_i )
goto loop;
clock_limit = irq_time_;
goto loop;
case 0x28:{// PLP
int temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
data = status;
SET_STATUS( temp );
if ( !((data ^ status) & st_i) )
goto loop; // I flag didn't change
if ( !(status & st_i) )
goto handle_cli;
goto handle_sei;
}
case 0x08: { // PHP
int temp;
CALC_STATUS( temp );
PUSH( temp | st_b | st_r );
goto loop;
}
case 0x6C: // JMP (ind)
data = GET_ADDR();
pc = READ( data );
pc |= READ( (data & 0xFF00) | ((data + 1) & 0xFF) ) << 8;
goto loop;
case 0x00: { // BRK
pc++;
WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
WRITE_LOW( 0x100 | (sp - 2), pc );
int temp;
CALC_STATUS( temp );
sp = (sp - 3) | 0x100;
WRITE_LOW( sp, temp | st_b | st_r );
pc = GET_LE16( &code_map [0xFFFE >> page_bits] [0xFFFE] );
status |= st_i;
goto i_flag_changed;
}
// Flags
case 0x38: // SEC
c = ~0;
goto loop;
case 0x18: // CLC
c = 0;
goto loop;
case 0xB8: // CLV
status &= ~st_v;
goto loop;
case 0xD8: // CLD
status &= ~st_d;
goto loop;
case 0xF8: // SED
status |= st_d;
goto loop;
case 0x58: // CLI
if ( !(status & st_i) )
goto loop;
status &= ~st_i;
handle_cli:
//dprintf( "%6d CLI\n", time() );
this->r.status = status; // update externally-visible I flag
if ( clock_count < end_time_ )
{
assert( clock_limit == end_time_ );
if ( end_time_ <= irq_time_ )
goto loop; // irq is later
if ( clock_count >= irq_time_ )
irq_time_ = clock_count + 1; // delay IRQ until after next instruction
clock_limit = irq_time_;
goto loop;
}
// execution is stopping now, so delayed CLI must be handled by caller
result = result_cli;
goto end;
case 0x78: // SEI
if ( status & st_i )
goto loop;
status |= st_i;
handle_sei:
//dprintf( "%6d SEI\n", time() );
this->r.status = status; // update externally-visible I flag
clock_limit = end_time_;
if ( clock_count < irq_time_ )
goto loop;
result = result_sei; // IRQ will occur now, even though I flag is set
goto end;
// Unofficial
case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: { // SKW
data += x;
HANDLE_PAGE_CROSSING( data );
int addr = GET_ADDR() + x;
if ( data & 0x100 )
READ( addr - 0x100 );
READ( addr );
}
case 0x0C: // SKW
pc++;
case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB
case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
pc++;
case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP
goto loop;
ARITH_ADDR_MODES_PTR( 0xC7 ) // DCP
WRITE( data, nz = READ( data ) );
nz = uint8_t( nz - 1 );
WRITE( data, nz );
pc++;
nz = a - nz;
c = ~nz;
nz &= 0xFF;
goto loop;
ARITH_ADDR_MODES_PTR( 0xE7 ) // ISC
WRITE( data, nz = READ( data ) );
nz = uint8_t( nz + 1 );
WRITE( data, nz );
data = nz ^ 0xFF;
goto adc_imm;
ARITH_ADDR_MODES_PTR( 0x27 ) { // RLA
WRITE( data, nz = READ( data ) );
int temp = c;
c = nz << 1;
nz = uint8_t( c ) | ( ( temp >> 8 ) & 0x01 );
WRITE( data, nz );
pc++;
nz = a &= nz;
goto loop;
}
ARITH_ADDR_MODES_PTR( 0x67 ) { // RRA
int temp;
WRITE( data, temp = READ( data ) );
nz = ((c >> 1) & 0x80) | (temp >> 1);
WRITE( data, nz );
data = nz;
c = temp << 8;
goto adc_imm;
}
ARITH_ADDR_MODES_PTR( 0x07 ) // SLO
WRITE( data, nz = READ( data ) );
c = nz << 1;
nz = uint8_t( c );
WRITE( data, nz );
nz = (a |= nz);
pc++;
goto loop;
ARITH_ADDR_MODES_PTR( 0x47 ) // SRE
WRITE( data, nz = READ( data ) );
c = nz << 8;
nz >>= 1;
WRITE( data, nz );
nz = a ^= nz;
pc++;
goto loop;
case 0x4B: // ALR
nz = (a &= data);
pc++;
goto lsr_a;
case 0x0B: // ANC
case 0x2B:
nz = a &= data;
c = a << 1;
pc++;
goto loop;
case 0x6B: // ARR
nz = a = uint8_t( ( ( data & a ) >> 1 ) | ( ( c >> 1 ) & 0x80 ) );
c = a << 2;
status = ( status & ~st_v ) | ( ( a ^ a << 1 ) & st_v );
pc++;
goto loop;
case 0xAB: // LXA
a = data;
x = data;
nz = data;
pc++;
goto loop;
case 0xA3: // LAX
IND_X
goto lax_ptr;
case 0xB3:
IND_Y(true,true)
goto lax_ptr;
case 0xB7:
data = uint8_t (data + y);
case 0xA7:
data = READ_LOW( data );
goto lax_imm;
case 0xBF: {
data += y;
HANDLE_PAGE_CROSSING( data );
int temp = data;
ADD_PAGE;
if ( temp & 0x100 )
READ( data - 0x100 );
goto lax_ptr;
}
case 0xAF:
ADD_PAGE
lax_ptr:
data = READ( data );
lax_imm:
nz = x = a = data;
pc++;
goto loop;
case 0x83: // SAX
IND_X
goto sax_imm;
case 0x97:
data = uint8_t (data + y);
goto sax_imm;
case 0x8F:
ADD_PAGE
case 0x87:
sax_imm:
WRITE( data, a & x );
pc++;
goto loop;
case 0xCB: // SBX
data = ( a & x ) - data;
c = ( data <= 0xFF ) ? 0x100 : 0;
nz = x = uint8_t( data );
pc++;
goto loop;
case 0x93: // SHA (ind),Y
IND_Y(false,false)
pc++;
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
goto loop;
case 0x9F: { // SHA abs,Y
data += y;
int temp = data;
ADD_PAGE
READ( data - ( temp & 0x100 ) );
pc++;
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
goto loop;
}
case 0x9E: { // SHX abs,Y
data += y;
int temp = data;
ADD_PAGE
READ( data - ( temp & 0x100 ) );
pc++;
if ( !( temp & 0x100 ) )
WRITE( data, uint8_t( x & ( ( data >> 8 ) + 1 ) ) );
goto loop;
}
case 0x9C: { // SHY abs,X
data += x;
int temp = data;
ADD_PAGE
READ( data - ( temp & 0x100 ) );
pc++;
if ( !( temp & 0x100) )
WRITE( data, uint8_t( y & ( ( data >> 8 ) + 1 ) ) );
goto loop;
}
case 0x9B: { // SHS abs,Y
data += y;
int temp = data;
ADD_PAGE
READ( data - ( temp & 0x100 ) );
pc++;
SET_SP( a & x );
WRITE( data, uint8_t( a & x & ( ( data >> 8 ) + 1 ) ) );
goto loop;
}
case 0xBB: { // LAS abs,Y
data += y;
HANDLE_PAGE_CROSSING( data );
int temp = data;
ADD_PAGE
if ( temp & 0x100 )
READ( data - 0x100 );
pc++;
a = GET_SP();
x = a &= READ( data );
SET_SP( a );
goto loop;
}
// Unimplemented
case page_wrap_opcode: // HLT
if ( pc > 0x10000 )
{
// handle wrap-around (assumes caller has put page of HLT at 0x10000)
pc = (pc - 1) & 0xFFFF;
clock_count -= 2;
goto loop;
}
// fall through
default:
// skip over proper number of bytes
static unsigned char const row [8] = { 0x95, 0x95, 0x95, 0xd5, 0x95, 0x95, 0xd5, 0xf5 };
int len = row [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
if ( opcode == 0x9C )
len = 3;
pc += len - 1;
error_count_++;
goto loop;
//result = result_badop; // TODO: re-enable
goto stop;
}
// If this fails then the case above is missing an opcode
assert( false );
stop:
pc--;
end:
{
int temp;
CALC_STATUS( temp );
r.status = temp;
}
this->clock_count = clock_count;
r.pc = pc;
r.sp = GET_SP();
r.a = a;
r.x = x;
r.y = y;
irq_time_ = LONG_MAX / 2 + 1;
return result;
}
#endif