2011-03-08 07:25:35 +00:00
using System ;
using System.Collections.Generic ;
2013-04-11 02:04:13 +00:00
using System.Runtime.CompilerServices ;
2013-11-04 00:36:15 +00:00
using BizHawk.Common ;
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2011-03-08 07:25:35 +00:00
using BizHawk.Emulation.CPUs.M6502 ;
2013-02-24 20:17:12 +00:00
#pragma warning disable 162
2011-03-08 07:25:35 +00:00
namespace BizHawk.Emulation.Consoles.Nintendo
{
public partial class NES : IEmulator
{
//hardware/state
2012-10-30 23:01:54 +00:00
// any of the 3 cpus are drop in replacements
2012-10-31 01:55:26 +00:00
public MOS6502X cpu ;
//public MOS6502X_CPP cpu;
2012-10-30 23:01:54 +00:00
//public MOS6502XDouble cpu;
// dispose list as the native core can't keep track of its own stuff
List < System . Runtime . InteropServices . GCHandle > DisposeList = new List < System . Runtime . InteropServices . GCHandle > ( ) ;
2011-03-08 07:25:35 +00:00
int cpu_accumulate ; //cpu timekeeper
public PPU ppu ;
2011-03-13 00:34:24 +00:00
public APU apu ;
2011-03-08 07:25:35 +00:00
byte [ ] ram ;
2011-03-17 03:51:31 +00:00
NESWatch [ ] sysbus_watch = new NESWatch [ 65536 ] ;
2011-06-09 19:45:07 +00:00
public byte [ ] CIRAM ; //AKA nametables
2011-03-08 07:25:35 +00:00
string game_name ; //friendly name exposed to user and used as filename base
CartInfo cart ; //the current cart prototype. should be moved into the board, perhaps
INESBoard board ; //the board hardware that is currently driving things
2012-10-16 22:27:48 +00:00
EDetectionOrigin origin = EDetectionOrigin . None ;
2011-08-27 15:49:16 +00:00
public bool SoundOn = true ;
2012-09-29 08:39:59 +00:00
int sprdma_countdown ;
2012-06-25 06:32:54 +00:00
bool _irq_apu ; //various irq signals that get merged to the cpu irq pin
2012-12-09 17:07:34 +00:00
/// <summary>clock speed of the main cpu in hz</summary>
public int cpuclockrate { get ; private set ; }
2012-03-25 09:25:27 +00:00
//irq state management
2012-06-25 06:32:54 +00:00
public bool irq_apu { get { return _irq_apu ; } set { _irq_apu = value ; } }
2011-03-19 09:12:56 +00:00
2011-03-08 07:25:35 +00:00
//user configuration
2011-03-13 02:48:45 +00:00
int [ , ] palette = new int [ 64 , 3 ] ;
2011-03-21 01:49:20 +00:00
int [ ] palette_compiled = new int [ 64 * 8 ] ;
2011-03-08 07:25:35 +00:00
IPortDevice [ ] ports ;
2013-03-25 01:59:34 +00:00
private DisplayType _display_type = DisplayType . NTSC ;
2012-07-12 23:13:22 +00:00
//Sound config
2013-05-10 18:38:58 +00:00
public void SetSquare1 ( int v ) { apu . Square1V = v ; }
public void SetSquare2 ( int v ) { apu . Square2V = v ; }
public void SetTriangle ( int v ) { apu . TriangleV = v ; }
public void SetNoise ( int v ) { apu . NoiseV = v ; }
public void SetDMC ( int v ) { apu . DMCV = v ; }
2011-03-08 07:25:35 +00:00
2012-09-29 22:19:49 +00:00
public void Dispose ( )
{
if ( magicSoundProvider ! = null ) magicSoundProvider . Dispose ( ) ;
magicSoundProvider = null ;
2012-10-30 23:01:54 +00:00
if ( DisposeList ! = null )
{
foreach ( var h in DisposeList )
h . Free ( ) ;
DisposeList = null ;
}
2012-09-29 22:19:49 +00:00
}
2012-12-09 03:13:47 +00:00
class MagicSoundProvider : ISoundProvider , ISyncSoundProvider , IDisposable
2012-09-29 22:19:49 +00:00
{
2012-12-09 03:13:47 +00:00
Sound . Utilities . BlipBuffer blip ;
2012-09-29 22:19:49 +00:00
NES nes ;
2012-12-09 03:13:47 +00:00
const int blipbuffsize = 4096 ;
2012-10-31 18:35:34 +00:00
public MagicSoundProvider ( NES nes , uint infreq )
2012-09-29 22:19:49 +00:00
{
this . nes = nes ;
2012-12-09 03:13:47 +00:00
blip = new Sound . Utilities . BlipBuffer ( blipbuffsize ) ;
blip . SetRates ( infreq , 44100 ) ;
//var actualMetaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
2012-09-29 22:19:49 +00:00
//1.789773mhz NTSC
2012-12-09 03:13:47 +00:00
//resampler = new Sound.Utilities.SpeexResampler(2, infreq, 44100 * APU.DECIMATIONFACTOR, infreq, 44100, actualMetaspu.buffer.enqueue_samples);
//output = new Sound.Utilities.DCFilter(actualMetaspu);
2012-09-29 22:19:49 +00:00
}
public void GetSamples ( short [ ] samples )
{
2012-12-09 17:07:34 +00:00
//Console.WriteLine("Sync: {0}", nes.apu.dlist.Count);
2012-12-09 03:13:47 +00:00
int nsamp = samples . Length / 2 ;
if ( nsamp > blipbuffsize ) // oh well.
nsamp = blipbuffsize ;
uint targetclock = ( uint ) blip . ClocksNeeded ( nsamp ) ;
uint actualclock = nes . apu . sampleclock ;
foreach ( var d in nes . apu . dlist )
blip . AddDelta ( d . time * targetclock / actualclock , d . value ) ;
nes . apu . dlist . Clear ( ) ;
blip . EndFrame ( targetclock ) ;
nes . apu . sampleclock = 0 ;
blip . ReadSamples ( samples , nsamp , true ) ;
// duplicate to stereo
for ( int i = 0 ; i < nsamp * 2 ; i + = 2 )
samples [ i + 1 ] = samples [ i ] ;
2012-09-29 22:19:49 +00:00
//mix in the cart's extra sound circuit
nes . board . ApplyCustomAudio ( samples ) ;
}
2012-12-09 03:13:47 +00:00
public void GetSamples ( out short [ ] samples , out int nsamp )
{
2012-12-09 17:07:34 +00:00
//Console.WriteLine("ASync: {0}", nes.apu.dlist.Count);
2012-12-09 03:13:47 +00:00
foreach ( var d in nes . apu . dlist )
blip . AddDelta ( d . time , d . value ) ;
nes . apu . dlist . Clear ( ) ;
blip . EndFrame ( nes . apu . sampleclock ) ;
nes . apu . sampleclock = 0 ;
nsamp = blip . SamplesAvailable ( ) ;
samples = new short [ nsamp * 2 ] ;
blip . ReadSamples ( samples , nsamp , true ) ;
// duplicate to stereo
for ( int i = 0 ; i < nsamp * 2 ; i + = 2 )
samples [ i + 1 ] = samples [ i ] ;
nes . board . ApplyCustomAudio ( samples ) ;
}
2012-09-29 22:19:49 +00:00
public void DiscardSamples ( )
{
2012-12-09 03:13:47 +00:00
nes . apu . dlist . Clear ( ) ;
nes . apu . sampleclock = 0 ;
2012-09-29 22:19:49 +00:00
}
public int MaxVolume { get ; set ; }
public void Dispose ( )
{
2012-12-09 03:13:47 +00:00
if ( blip ! = null )
{
blip . Dispose ( ) ;
blip = null ;
}
2012-09-29 22:19:49 +00:00
}
}
MagicSoundProvider magicSoundProvider ;
2011-03-08 07:25:35 +00:00
public void HardReset ( )
{
2012-10-31 18:48:06 +00:00
cpu = new MOS6502X ( ( h ) = > DisposeList . Add ( h ) ) ;
2012-10-31 01:55:26 +00:00
//cpu = new MOS6502X_CPP((h) => DisposeList.Add(h));
2012-10-30 23:01:54 +00:00
//cpu = new MOS6502XDouble((h) => DisposeList.Add(h));
2012-11-02 19:28:00 +00:00
cpu . SetCallbacks ( ReadMemory , ReadMemory , PeekMemory , WriteMemory , ( h ) = > DisposeList . Add ( h ) ) ;
2012-11-17 01:31:34 +00:00
cpu . FetchCallback = ( ) = >
{
2012-12-10 00:43:43 +00:00
if ( CoreComm . Tracer . Enabled )
2012-11-17 01:31:34 +00:00
{
2012-12-10 00:43:43 +00:00
CoreComm . Tracer . Put ( cpu . TraceState ( ) ) ;
2012-11-17 01:31:34 +00:00
}
} ;
2012-03-24 03:45:47 +00:00
cpu . BCD_Enabled = false ;
2011-03-08 07:25:35 +00:00
ppu = new PPU ( this ) ;
ram = new byte [ 0x800 ] ;
CIRAM = new byte [ 0x800 ] ;
ports = new IPortDevice [ 2 ] ;
2012-10-31 18:48:06 +00:00
ports [ 0 ] = new JoypadPortDevice ( this , 0 ) ;
ports [ 1 ] = new JoypadPortDevice ( this , 1 ) ;
2011-03-08 07:25:35 +00:00
2012-11-06 01:41:22 +00:00
// don't replace the magicSoundProvider on reset, as it's not needed
// if (magicSoundProvider != null) magicSoundProvider.Dispose();
2012-10-31 18:48:06 +00:00
2012-10-31 18:25:46 +00:00
// set up region
2012-10-31 18:48:06 +00:00
switch ( cart . system )
2012-10-31 18:25:46 +00:00
{
2012-10-31 18:48:06 +00:00
case "NES-PAL" :
case "NES-PAL-A" :
case "NES-PAL-B" :
2012-12-03 15:01:04 +00:00
apu = new APU ( this , apu , true ) ;
2012-10-31 18:48:06 +00:00
ppu . region = PPU . Region . PAL ;
2012-12-10 00:43:43 +00:00
CoreComm . VsyncNum = 50 ;
CoreComm . VsyncDen = 1 ;
2012-12-09 17:07:34 +00:00
cpuclockrate = 1662607 ;
2012-10-31 18:48:06 +00:00
cpu_sequence = cpu_sequence_PAL ;
2013-03-25 01:59:34 +00:00
_display_type = DisplayType . PAL ;
2012-10-31 18:48:06 +00:00
break ;
case "NES-NTSC" :
case "Famicom" :
2012-12-03 15:01:04 +00:00
apu = new APU ( this , apu , false ) ;
2012-10-31 18:48:06 +00:00
ppu . region = PPU . Region . NTSC ;
2012-12-09 17:07:34 +00:00
cpuclockrate = 1789773 ;
2012-10-31 18:48:06 +00:00
cpu_sequence = cpu_sequence_NTSC ;
break ;
// this is in bootgod, but not used at all
case "Dendy" :
2012-12-03 15:01:04 +00:00
apu = new APU ( this , apu , false ) ;
2012-10-31 18:48:06 +00:00
ppu . region = PPU . Region . Dendy ;
2012-12-10 00:43:43 +00:00
CoreComm . VsyncNum = 50 ;
CoreComm . VsyncDen = 1 ;
2012-12-09 17:07:34 +00:00
cpuclockrate = 1773448 ;
2012-10-31 18:48:06 +00:00
cpu_sequence = cpu_sequence_NTSC ;
2013-03-25 01:59:34 +00:00
_display_type = DisplayType . DENDY ;
2012-10-31 18:48:06 +00:00
break ;
case null :
Console . WriteLine ( "Unknown NES system! Defaulting to NTSC." ) ;
goto case "NES-NTSC" ;
default :
Console . WriteLine ( "Unrecognized NES system \"{0}\"! Defaulting to NTSC." , cart . system ) ;
goto case "NES-NTSC" ;
2012-10-31 18:25:46 +00:00
}
2012-12-09 17:07:34 +00:00
if ( magicSoundProvider = = null )
magicSoundProvider = new MagicSoundProvider ( this , ( uint ) cpuclockrate ) ;
BoardSystemHardReset ( ) ;
2012-09-29 22:19:49 +00:00
2013-02-11 09:08:54 +00:00
//check fceux's PowerNES and FCEU_MemoryRand function for more information:
2012-11-17 08:16:02 +00:00
//relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
2013-10-27 16:23:48 +00:00
for ( int i = 0 ; i < 0x800 ; i + + ) if ( ( i & 4 ) ! = 0 ) ram [ i ] = 0xFF ; else ram [ i ] = 0x00 ;
2011-03-08 07:25:35 +00:00
2012-11-06 03:05:43 +00:00
SetupMemoryDomains ( ) ;
2011-03-08 07:25:35 +00:00
//in this emulator, reset takes place instantaneously
cpu . PC = ( ushort ) ( ReadMemory ( 0xFFFC ) | ( ReadMemory ( 0xFFFD ) < < 8 ) ) ;
2011-03-20 02:12:10 +00:00
cpu . P = 0x34 ;
cpu . S = 0xFD ;
2011-03-08 07:25:35 +00:00
}
2011-03-20 02:12:10 +00:00
bool resetSignal ;
2012-11-06 00:40:51 +00:00
bool hardResetSignal ;
2012-09-20 19:52:47 +00:00
public void FrameAdvance ( bool render , bool rendersound )
2011-03-08 07:25:35 +00:00
{
2011-06-18 21:47:20 +00:00
lagged = true ;
if ( resetSignal )
2011-03-20 20:42:12 +00:00
{
2012-07-17 23:05:58 +00:00
board . NESSoftReset ( ) ;
2012-03-18 03:46:06 +00:00
cpu . NESSoftReset ( ) ;
2012-04-14 13:51:26 +00:00
apu . NESSoftReset ( ) ;
2012-03-18 03:46:06 +00:00
//need to study what happens to ppu and apu and stuff..
2011-03-20 20:42:12 +00:00
}
2012-11-06 00:40:51 +00:00
else if ( hardResetSignal )
{
HardReset ( ) ;
}
2011-03-20 20:42:12 +00:00
2011-03-08 07:25:35 +00:00
Controller . UpdateControls ( Frame + + ) ;
2011-05-08 00:06:43 +00:00
//if (resetSignal)
//Controller.UnpressButton("Reset"); TODO fix this
2011-03-20 02:12:10 +00:00
resetSignal = Controller [ "Reset" ] ;
2012-11-06 00:40:51 +00:00
hardResetSignal = Controller [ "Power" ] ;
2012-10-26 18:51:08 +00:00
if ( board is FDS )
{
var b = board as FDS ;
if ( Controller [ "FDS Eject" ] )
b . Eject ( ) ;
for ( int i = 0 ; i < b . NumSides ; i + + )
if ( Controller [ "FDS Insert " + i ] )
b . InsertSide ( i ) ;
}
2011-03-08 07:25:35 +00:00
ppu . FrameAdvance ( ) ;
2011-06-18 21:47:20 +00:00
if ( lagged )
{
_lagcount + + ;
islag = true ;
}
else
islag = false ;
2012-10-04 21:51:34 +00:00
videoProvider . FillFrameBuffer ( ) ;
2011-03-08 07:25:35 +00:00
}
2012-03-15 21:28:37 +00:00
//PAL:
//0 15 30 45 60 -> 12 27 42 57 -> 9 24 39 54 -> 6 21 36 51 -> 3 18 33 48 -> 0
//sequence of ppu clocks per cpu clock: 4,3,3,3,3
//NTSC:
//sequence of ppu clocks per cpu clock: 3
2012-10-31 18:25:46 +00:00
ByteBuffer cpu_sequence ;
2012-03-15 21:28:37 +00:00
static ByteBuffer cpu_sequence_NTSC = new ByteBuffer ( new byte [ ] { 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 } ) ;
static ByteBuffer cpu_sequence_PAL = new ByteBuffer ( new byte [ ] { 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 } ) ;
public int cpu_step , cpu_stepcounter , cpu_deadcounter ;
2013-04-11 02:04:13 +00:00
2013-04-14 20:04:38 +00:00
#if VS2012
2013-04-11 02:04:13 +00:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2013-04-14 20:04:38 +00:00
#endif
2013-09-14 19:34:14 +00:00
private void RunCpuOne ( )
2012-03-15 21:28:37 +00:00
{
cpu_stepcounter + + ;
2012-10-31 18:25:46 +00:00
if ( cpu_stepcounter = = cpu_sequence [ cpu_step ] )
2011-03-13 00:34:24 +00:00
{
2012-03-15 21:28:37 +00:00
cpu_step + + ;
cpu_step & = 31 ;
cpu_stepcounter = 0 ;
2012-03-25 09:25:27 +00:00
if ( sprdma_countdown > 0 )
{
sprdma_countdown - - ;
if ( sprdma_countdown = = 0 )
{
//its weird that this is 514.. normally itd be 512 (and people would say its wrong) or 513 (and people would say its right)
//but 514 passes test 4-irq_and_dma
2012-09-29 08:39:59 +00:00
cpu_deadcounter + = 514 ;
2012-03-25 09:25:27 +00:00
}
}
2012-06-25 06:32:54 +00:00
if ( cpu_deadcounter > 0 )
2012-03-25 09:25:27 +00:00
cpu_deadcounter - - ;
else
2012-06-25 06:32:54 +00:00
{
cpu . IRQ = _irq_apu | | board . IRQSignal ;
2012-03-15 21:28:37 +00:00
cpu . ExecuteOne ( ) ;
2012-06-25 06:32:54 +00:00
}
2012-03-25 09:25:27 +00:00
2012-08-04 01:47:54 +00:00
apu . RunOne ( ) ;
2012-10-31 14:36:43 +00:00
board . ClockCPU ( ) ;
2012-03-15 21:28:37 +00:00
ppu . PostCpuInstructionOne ( ) ;
2011-03-13 00:34:24 +00:00
}
2011-03-08 07:25:35 +00:00
}
2013-04-14 20:04:38 +00:00
#if VS2012
2013-04-11 02:04:13 +00:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2013-04-14 20:04:38 +00:00
#endif
2011-03-08 07:25:35 +00:00
public byte ReadReg ( int addr )
{
switch ( addr )
{
2011-03-13 00:34:24 +00:00
case 0x4000 : case 0x4001 : case 0x4002 : case 0x4003 :
case 0x4004 : case 0x4005 : case 0x4006 : case 0x4007 :
case 0x4008 : case 0x4009 : case 0x400A : case 0x400B :
case 0x400C : case 0x400D : case 0x400E : case 0x400F :
case 0x4010 : case 0x4011 : case 0x4012 : case 0x4013 :
return apu . ReadReg ( addr ) ;
case 0x4014 : /*OAM DMA*/ break ;
2011-06-20 09:07:38 +00:00
case 0x4015 : return apu . ReadReg ( addr ) ;
2011-03-08 07:25:35 +00:00
case 0x4016 :
2011-06-18 21:47:20 +00:00
case 0x4017 :
2011-03-08 07:25:35 +00:00
return read_joyport ( addr ) ;
default :
//Console.WriteLine("read register: {0:x4}", addr);
break ;
}
return 0xFF ;
}
2012-11-02 19:28:00 +00:00
public byte PeekReg ( int addr )
{
switch ( addr )
{
case 0x4000 : case 0x4001 : case 0x4002 : case 0x4003 :
case 0x4004 : case 0x4005 : case 0x4006 : case 0x4007 :
case 0x4008 : case 0x4009 : case 0x400A : case 0x400B :
case 0x400C : case 0x400D : case 0x400E : case 0x400F :
case 0x4010 : case 0x4011 : case 0x4012 : case 0x4013 :
return apu . PeekReg ( addr ) ;
case 0x4014 : /*OAM DMA*/ break ;
case 0x4015 : return apu . PeekReg ( addr ) ;
case 0x4016 :
case 0x4017 :
return peek_joyport ( addr ) ;
default :
//Console.WriteLine("read register: {0:x4}", addr);
break ;
}
return 0xFF ;
}
2011-03-08 07:25:35 +00:00
void WriteReg ( int addr , byte val )
{
switch ( addr )
{
2011-03-13 00:34:24 +00:00
case 0x4000 : case 0x4001 : case 0x4002 : case 0x4003 :
case 0x4004 : case 0x4005 : case 0x4006 : case 0x4007 :
case 0x4008 : case 0x4009 : case 0x400A : case 0x400B :
case 0x400C : case 0x400D : case 0x400E : case 0x400F :
case 0x4010 : case 0x4011 : case 0x4012 : case 0x4013 :
apu . WriteReg ( addr , val ) ;
break ;
2011-03-08 07:25:35 +00:00
case 0x4014 : Exec_OAMDma ( val ) ; break ;
2011-03-13 00:34:24 +00:00
case 0x4015 : apu . WriteReg ( addr , val ) ; break ;
2011-03-08 07:25:35 +00:00
case 0x4016 :
ports [ 0 ] . Write ( val & 1 ) ;
ports [ 1 ] . Write ( val & 1 ) ;
break ;
2011-03-13 00:34:24 +00:00
case 0x4017 : apu . WriteReg ( addr , val ) ; break ;
2011-03-08 07:25:35 +00:00
default :
//Console.WriteLine("wrote register: {0:x4} = {1:x2}", addr, val);
break ;
}
}
byte read_joyport ( int addr )
{
2013-11-10 18:15:32 +00:00
CoreComm . InputCallback . Call ( ) ;
2012-11-02 19:28:00 +00:00
return handle_read_joyport ( addr , false ) ;
}
byte peek_joyport ( int addr )
{
return handle_read_joyport ( addr , true ) ;
}
byte handle_read_joyport ( int addr , bool peek )
{
2011-03-08 07:25:35 +00:00
//read joystick port
//many todos here
2012-03-25 09:25:27 +00:00
lagged = false ;
2011-06-18 21:47:20 +00:00
byte ret ;
2012-10-31 21:15:44 +00:00
if ( addr = = 0x4016 )
2013-07-18 14:15:46 +00:00
ret = ports [ 0 ] . Read ( peek ) ;
2012-10-31 21:15:44 +00:00
else
2013-07-18 14:15:46 +00:00
ret = ports [ 1 ] . Read ( peek ) ;
2011-06-18 21:47:20 +00:00
return ret ;
2011-03-08 07:25:35 +00:00
}
void Exec_OAMDma ( byte val )
{
ushort addr = ( ushort ) ( val < < 8 ) ;
for ( int i = 0 ; i < 256 ; i + + )
{
byte db = ReadMemory ( ( ushort ) addr ) ;
WriteMemory ( 0x2004 , db ) ;
addr + + ;
}
2012-03-25 09:25:27 +00:00
//schedule a sprite dma event for beginning 1 cycle in the future.
//this receives 2 because thats just the way it works out.
sprdma_countdown = 2 ;
2011-03-08 07:25:35 +00:00
}
2011-03-13 02:48:45 +00:00
/// <summary>
/// sets the provided palette as current
/// </summary>
2011-06-10 05:31:46 +00:00
public void SetPalette ( int [ , ] pal )
2011-03-13 02:48:45 +00:00
{
Array . Copy ( pal , palette , 64 * 3 ) ;
2011-03-21 01:49:20 +00:00
for ( int i = 0 ; i < 64 * 8 ; i + + )
2011-03-13 02:48:45 +00:00
{
2011-03-21 01:49:20 +00:00
int d = i > > 6 ;
int c = i & 63 ;
int r = palette [ c , 0 ] ;
int g = palette [ c , 1 ] ;
int b = palette [ c , 2 ] ;
Palettes . ApplyDeemphasis ( ref r , ref g , ref b , d ) ;
2011-03-13 02:48:45 +00:00
palette_compiled [ i ] = ( int ) unchecked ( ( int ) 0xFF000000 | ( r < < 16 ) | ( g < < 8 ) | b ) ;
}
}
/// <summary>
2011-03-21 01:49:20 +00:00
/// looks up an internal NES pixel value to an rgb int (applying the core's current palette and assuming no deemph)
2011-03-13 02:48:45 +00:00
/// </summary>
public int LookupColor ( int pixel )
{
return palette_compiled [ pixel ] ;
}
2012-03-15 21:28:37 +00:00
public byte DummyReadMemory ( ushort addr ) { return 0 ; }
2012-09-01 18:34:31 +00:00
private void ApplySystemBusPoke ( int addr , byte value )
{
if ( addr < 0x2000 )
{
ram [ ( addr & 0x7FF ) ] = value ;
}
else if ( addr < 0x4000 )
{
ppu . WriteReg ( ( addr & 0x07 ) , value ) ;
}
else if ( addr < 0x4020 )
{
WriteReg ( addr , value ) ;
}
else
{
ApplyGameGenie ( addr , value , null ) ; //Apply a cheat to the remaining regions since they have no direct access, this may not be the best way to handle this situation
}
}
2012-08-19 20:38:20 +00:00
2012-11-02 19:28:00 +00:00
public byte PeekMemory ( ushort addr )
{
byte ret ;
if ( addr > = 0x4020 )
{
ret = board . PeekCart ( addr ) ; //easy optimization, since rom reads are so common, move this up (reordering the rest of these elseifs is not easy)
}
else if ( addr < 0x0800 )
{
ret = ram [ addr ] ;
}
2012-11-05 06:26:52 +00:00
else if ( addr < 0x2000 )
2012-11-02 19:28:00 +00:00
{
ret = ram [ addr & 0x7FF ] ;
}
else if ( addr < 0x4000 )
{
ret = ppu . ReadReg ( addr & 7 ) ;
}
else if ( addr < 0x4020 )
{
ret = ReadReg ( addr ) ; //we're not rebasing the register just to keep register names canonical
}
else
{
throw new Exception ( "Woopsie-doodle!" ) ;
ret = 0xFF ;
}
return ret ;
}
2012-09-29 08:39:59 +00:00
//old data bus values from previous reads
public byte DB ;
2011-03-08 07:25:35 +00:00
public byte ReadMemory ( ushort addr )
{
2011-03-16 03:13:51 +00:00
byte ret ;
2012-08-19 20:38:20 +00:00
if ( addr > = 0x8000 )
{
ret = board . ReadPRG ( addr - 0x8000 ) ; //easy optimization, since rom reads are so common, move this up (reordering the rest of these elseifs is not easy)
}
else if ( addr < 0x0800 )
{
ret = ram [ addr ] ;
}
2012-11-05 06:26:52 +00:00
else if ( addr < 0x2000 )
2012-08-19 20:38:20 +00:00
{
ret = ram [ addr & 0x7FF ] ;
}
else if ( addr < 0x4000 )
{
ret = ppu . ReadReg ( addr & 7 ) ;
}
else if ( addr < 0x4020 )
{
ret = ReadReg ( addr ) ; //we're not rebasing the register just to keep register names canonical
}
else if ( addr < 0x6000 )
{
ret = board . ReadEXP ( addr - 0x4000 ) ;
}
else
{
ret = board . ReadWRAM ( addr - 0x6000 ) ;
}
2011-03-16 03:13:51 +00:00
2011-03-16 05:06:21 +00:00
//handle breakpoints and stuff.
//the idea is that each core can implement its own watch class on an address which will track all the different kinds of monitors and breakpoints and etc.
//but since freeze is a common case, it was implemented through its own mechanisms
2012-09-01 14:13:36 +00:00
if ( sysbus_watch [ addr ] ! = null )
{
sysbus_watch [ addr ] . Sync ( ) ;
ret = sysbus_watch [ addr ] . ApplyGameGenie ( ret ) ;
}
2011-03-16 05:06:21 +00:00
2013-11-10 21:20:55 +00:00
CoreComm . MemoryCallbackSystem . CallRead ( addr ) ;
2012-10-13 18:59:09 +00:00
2012-09-29 08:39:59 +00:00
DB = ret ;
2011-03-16 03:13:51 +00:00
return ret ;
2011-03-08 07:25:35 +00:00
}
2012-09-01 14:13:36 +00:00
public void ApplyGameGenie ( int addr , byte value , byte? compare )
{
if ( addr < sysbus_watch . Length )
{
GetWatch ( NESWatch . EDomain . Sysbus , addr ) . SetGameGenie ( compare , value ) ;
}
}
public void RemoveGameGenie ( int addr )
{
if ( addr < sysbus_watch . Length )
{
GetWatch ( NESWatch . EDomain . Sysbus , addr ) . RemoveGameGenie ( ) ;
}
}
2011-03-08 07:25:35 +00:00
public void WriteMemory ( ushort addr , byte value )
{
2012-08-19 21:09:48 +00:00
if ( addr < 0x0800 )
{
ram [ addr ] = value ;
}
else if ( addr < 0x2000 )
{
ram [ addr & 0x7FF ] = value ;
}
else if ( addr < 0x4000 )
{
ppu . WriteReg ( addr & 7 , value ) ;
}
else if ( addr < 0x4020 )
{
WriteReg ( addr , value ) ; //we're not rebasing the register just to keep register names canonical
}
else if ( addr < 0x6000 )
{
board . WriteEXP ( addr - 0x4000 , value ) ;
}
else if ( addr < 0x8000 )
{
board . WriteWRAM ( addr - 0x6000 , value ) ;
}
else
{
board . WritePRG ( addr - 0x8000 , value ) ;
}
2012-10-13 18:59:09 +00:00
2013-11-10 21:20:55 +00:00
CoreComm . MemoryCallbackSystem . CallWrite ( addr ) ;
2011-03-08 07:25:35 +00:00
}
}
}