2011-02-20 02:49:37 +00:00
using System ;
2011-03-02 06:18:26 +00:00
using System.Linq ;
2011-02-28 10:16:07 +00:00
using System.Diagnostics ;
2011-02-20 02:49:37 +00:00
using System.Globalization ;
using System.IO ;
using System.Collections.Generic ;
using BizHawk.Emulation.CPUs.M6502 ;
2011-03-13 08:13:32 +00:00
//TODO - redo all timekeeping in terms of master clock
2011-02-20 02:49:37 +00:00
namespace BizHawk.Emulation.Consoles.Nintendo
{
2011-03-08 07:25:35 +00:00
2011-02-27 09:45:50 +00:00
public partial class NES : IEmulator
2011-02-20 02:49:37 +00:00
{
2011-09-03 17:13:42 +00:00
//Broken
2011-06-13 08:38:10 +00:00
//Bill and ted's excellent video game adventure (U) doesnt work until more detailed emulation exists (check 001.txt)
2011-09-03 17:13:42 +00:00
//AD&D Hillsfar (U).nes black screen
2011-06-07 20:41:49 +00:00
2011-09-03 17:13:42 +00:00
//As good as other Emus
//Knight Rider - very glitchy and seems to be a good timing case! (seems to run same as nintendulator and fceux now.. which may not be entirely accurate)
//Indiana Jones and the Temple of Doom
//Air Wolf - big graphical glitch. seems to be a real bug, but it should never have been released with this. need to verify for sure that it is a real bug?
2011-06-07 22:29:44 +00:00
2011-09-03 17:13:42 +00:00
//Needs Testing again
2011-06-07 20:52:30 +00:00
//JJ - Tobidase Daisakusen Part 2 (J) - same as 3-D World Runner
2011-06-08 01:03:32 +00:00
//------
2011-09-03 17:13:42 +00:00
static readonly bool USE_DATABASE = true ;
public RomStatus RomStatus ;
2011-06-08 01:03:32 +00:00
2011-08-04 03:20:54 +00:00
public NES ( GameInfo game , byte [ ] rom )
2011-02-28 06:16:20 +00:00
{
2011-06-11 22:15:08 +00:00
CoreOutputComm = new CoreOutputComm ( ) ;
2011-03-08 07:25:35 +00:00
BootGodDB . Initialize ( ) ;
2011-03-13 02:48:45 +00:00
SetPalette ( Palettes . FCEUX_Standard ) ;
2011-03-21 01:49:20 +00:00
videoProvider = new MyVideoProvider ( this ) ;
2011-09-24 17:07:48 +00:00
Init ( game , rom ) ;
2011-02-28 06:16:20 +00:00
}
2011-09-24 17:07:48 +00:00
private NES ( )
{
BootGodDB . Initialize ( ) ;
}
2011-08-04 03:20:54 +00:00
2011-06-07 01:05:57 +00:00
public void WriteLogTimestamp ( )
{
2011-06-09 19:45:07 +00:00
if ( ppu ! = null )
Console . Write ( "[{0:d5}:{1:d3}:{2:d3}]" , Frame , ppu . ppur . status . sl , ppu . ppur . status . cycle ) ;
2011-06-07 01:05:57 +00:00
}
public void LogLine ( string format , params object [ ] args )
{
2011-07-30 20:49:36 +00:00
if ( ppu ! = null )
2011-06-09 19:45:07 +00:00
Console . WriteLine ( "[{0:d5}:{1:d3}:{2:d3}] {3}" , Frame , ppu . ppur . status . sl , ppu . ppur . status . cycle , string . Format ( format , args ) ) ;
2011-06-07 01:05:57 +00:00
}
2011-03-17 03:51:31 +00:00
NESWatch GetWatch ( NESWatch . EDomain domain , int address )
{
if ( domain = = NESWatch . EDomain . Sysbus )
{
2011-07-30 20:49:36 +00:00
NESWatch ret = sysbus_watch [ address ] ? ? new NESWatch ( this , domain , address ) ;
2011-03-17 03:51:31 +00:00
sysbus_watch [ address ] = ret ;
return ret ;
}
return null ;
}
2011-03-16 05:06:21 +00:00
class NESWatch
{
2011-03-17 03:51:31 +00:00
public enum EDomain
{
Sysbus
}
public NESWatch ( NES nes , EDomain domain , int address )
{
Address = address ;
Domain = domain ;
if ( domain = = EDomain . Sysbus )
{
watches = nes . sysbus_watch ;
}
}
public int Address ;
public EDomain Domain ;
public enum EFlags
{
None = 0 ,
GameGenie = 1 ,
ReadPrint = 2
}
EFlags flags ;
2011-07-30 20:49:36 +00:00
2011-03-17 03:51:31 +00:00
public void Sync ( )
{
if ( flags = = EFlags . None )
watches [ Address ] = null ;
else watches [ Address ] = this ;
}
public void SetGameGenie ( int check , int replace )
{
flags | = EFlags . GameGenie ;
gg_check = check ;
gg_replace = replace ;
Sync ( ) ;
}
public bool HasGameGenie { get { return ( flags & EFlags . GameGenie ) ! = 0 ; } }
public byte ApplyGameGenie ( byte curr )
{
if ( ! HasGameGenie ) return curr ;
if ( curr = = gg_check | | gg_check = = - 1 ) { Console . WriteLine ( "applied game genie" ) ; return ( byte ) gg_replace ; }
else return curr ;
}
2011-03-16 05:06:21 +00:00
2011-03-17 03:51:31 +00:00
public void RemoveGameGenie ( )
2011-03-16 05:06:21 +00:00
{
2011-03-17 03:51:31 +00:00
flags & = ~ EFlags . GameGenie ;
Sync ( ) ;
2011-03-16 05:06:21 +00:00
}
2011-03-17 03:51:31 +00:00
int gg_check , gg_replace ;
NESWatch [ ] watches ;
2011-03-16 05:06:21 +00:00
}
2011-06-11 22:15:08 +00:00
public CoreInputComm CoreInputComm { get ; set ; }
public CoreOutputComm CoreOutputComm { get ; private set ; }
2011-02-20 02:49:37 +00:00
class MyVideoProvider : IVideoProvider
{
2011-09-04 01:12:12 +00:00
public int top = 8 ;
public int bottom = 231 ;
2011-09-04 01:58:16 +00:00
public int left = 0 ;
2012-03-04 01:41:14 +00:00
public int right = 255 ;
2011-09-04 01:12:12 +00:00
2011-02-20 02:49:37 +00:00
NES emu ;
public MyVideoProvider ( NES emu )
{
this . emu = emu ;
}
2011-03-21 01:49:20 +00:00
int [ ] pixels = new int [ 256 * 240 ] ;
2011-02-20 02:49:37 +00:00
public int [ ] GetVideoBuffer ( )
{
2011-06-11 22:15:08 +00:00
int backdrop = emu . CoreInputComm . NES_BackdropColor ;
bool useBackdrop = ( backdrop & 0xFF000000 ) ! = 0 ;
2011-09-24 17:07:48 +00:00
2011-03-21 01:49:20 +00:00
//TODO - we could recalculate this on the fly (and invalidate/recalculate it when the palette is changed)
2012-03-04 01:41:14 +00:00
int width = BufferWidth ;
for ( int x = left ; x < = right ; x + + )
2011-03-21 01:49:20 +00:00
{
2012-03-04 01:41:14 +00:00
for ( int y = top ; y < = bottom ; y + + )
2011-06-11 22:15:08 +00:00
{
2011-09-24 17:07:48 +00:00
short pixel = emu . ppu . xbuf [ ( y * 256 ) + x ] ;
2011-09-04 01:12:12 +00:00
if ( ( pixel & 0x8000 ) ! = 0 & & useBackdrop )
{
2011-09-24 17:07:48 +00:00
pixels [ ( ( y - top ) * width ) + ( x - left ) ] = backdrop ;
2011-09-04 01:12:12 +00:00
}
2011-09-24 17:07:48 +00:00
else pixels [ ( ( y - top ) * width ) + ( x - left ) ] = emu . palette_compiled [ pixel & 0x7FFF ] ;
2011-06-11 22:15:08 +00:00
}
2011-03-21 01:49:20 +00:00
}
2011-02-20 02:49:37 +00:00
return pixels ;
}
2012-03-04 01:41:14 +00:00
public int BufferWidth { get { return right - left + 1 ; } }
public int BufferHeight { get { return bottom - top + 1 ; } }
2011-02-20 02:49:37 +00:00
public int BackgroundColor { get { return 0 ; } }
}
2011-09-04 01:12:12 +00:00
public int FirstDrawLine { get { return videoProvider . top ; } set { videoProvider . top = value ; } }
public int LastDrawLine { get { return videoProvider . bottom ; } set { videoProvider . bottom = value ; } }
2011-09-04 01:58:16 +00:00
public void SetClipLeftAndRight ( bool clip )
{
if ( clip )
{
videoProvider . left = 8 ;
videoProvider . right = 248 ;
}
else
{
videoProvider . left = 0 ;
2012-03-04 01:41:14 +00:00
videoProvider . right = 255 ;
2011-09-04 01:58:16 +00:00
}
}
2011-09-24 17:07:48 +00:00
2011-03-21 01:51:06 +00:00
MyVideoProvider videoProvider ;
public IVideoProvider VideoProvider { get { return videoProvider ; } }
2011-03-13 00:34:24 +00:00
public ISoundProvider SoundProvider { get { return apu ; } }
2011-02-20 02:49:37 +00:00
public static readonly ControllerDefinition NESController =
new ControllerDefinition
{
2012-02-19 07:09:24 +00:00
Name = "NES Controller" ,
BoolButtons = {
"P1 Up" , "P1 Down" , "P1 Left" , "P1 Right" , "P1 Select" , "P1 Start" , "P1 B" , "P1 A" , "Reset" ,
"P2 Up" , "P2 Down" , "P2 Left" , "P2 Right" , "P2 Select" , "P2 Start" , "P2 B" , "P2 A"
}
2011-02-20 02:49:37 +00:00
} ;
public ControllerDefinition ControllerDefinition { get { return NESController ; } }
IController controller ;
public IController Controller
{
get { return controller ; }
set { controller = value ; }
}
2011-02-27 11:40:08 +00:00
interface IPortDevice
{
void Write ( int value ) ;
byte Read ( ) ;
void Update ( ) ;
}
//static INPUTC GPC = { ReadGP, 0, StrobeGP, UpdateGP, 0, 0, LogGP, LoadGP };
class JoypadPortDevice : NullPortDevice
{
int state ;
NES nes ;
2011-06-18 21:47:20 +00:00
int player ;
public JoypadPortDevice ( NES nes , int player )
2011-02-27 11:40:08 +00:00
{
this . nes = nes ;
2011-06-18 21:47:20 +00:00
this . player = player ;
2011-02-27 11:40:08 +00:00
}
void Strobe ( )
{
value = 0 ;
2012-02-19 07:09:24 +00:00
foreach (
string str in new string [ ] {
"P" + ( player + 1 ) . ToString ( ) + " Right" , "P" + ( player + 1 ) . ToString ( ) + " Left" ,
"P" + ( player + 1 ) . ToString ( ) + " Down" , "P" + ( player + 1 ) . ToString ( ) + " Up" ,
"P" + ( player + 1 ) . ToString ( ) + " Start" , "P" + ( player + 1 ) . ToString ( ) + " Select" ,
"P" + ( player + 1 ) . ToString ( ) + " B" , "P" + ( player + 1 ) . ToString ( ) + " A"
}
)
2011-02-27 11:40:08 +00:00
{
value < < = 1 ;
value | = nes . Controller . IsPressed ( str ) ? 1 : 0 ;
}
}
public override void Write ( int value )
{
if ( state = = 1 & & value = = 0 )
Strobe ( ) ;
state = value ;
}
public override byte Read ( )
{
2011-07-30 20:49:36 +00:00
int ret = value & 1 ;
2011-02-27 11:40:08 +00:00
value > > = 1 ;
return ( byte ) ret ;
}
public override void Update ( )
{
}
int value ;
}
class NullPortDevice : IPortDevice
{
public virtual void Write ( int value )
{
}
public virtual byte Read ( )
{
return 0xFF ;
}
public virtual void Update ( )
{
}
}
2011-04-17 22:51:53 +00:00
int _frame ;
2011-07-30 20:49:36 +00:00
int _lagcount ;
bool lagged = true ;
bool islag = false ;
2011-04-17 22:51:53 +00:00
public int Frame { get { return _frame ; } set { _frame = value ; } }
2011-07-30 20:49:36 +00:00
public void ResetFrameCounter ( )
{
_frame = 0 ;
}
2011-06-07 07:14:34 +00:00
public long Timestamp { get ; private set ; }
2011-07-30 20:49:36 +00:00
public int LagCount { get { return _lagcount ; } set { _lagcount = value ; } }
public bool IsLagFrame { get { return islag ; } }
2011-03-01 14:38:52 +00:00
2011-02-20 02:49:37 +00:00
public bool DeterministicEmulation { get { return true ; } set { } }
2011-03-01 07:25:14 +00:00
public byte [ ] SaveRam
{
get
{
2011-07-30 20:49:36 +00:00
if ( board = = null ) return null ;
2011-03-01 07:25:14 +00:00
return board . SaveRam ;
}
}
2011-02-20 02:49:37 +00:00
public bool SaveRamModified
{
2011-03-01 07:25:14 +00:00
get { if ( board = = null ) return false ; if ( board . SaveRam = = null ) return false ; return true ; }
2011-02-20 02:49:37 +00:00
set { }
}
2011-07-30 20:49:36 +00:00
private IList < MemoryDomain > memoryDomains ;
2011-03-05 03:03:47 +00:00
2011-07-30 20:49:36 +00:00
private void SetupMemoryDomains ( )
{
var domains = new List < MemoryDomain > ( ) ;
2011-03-08 07:25:35 +00:00
var RAM = new MemoryDomain ( "RAM" , 0x800 , Endian . Little ,
2011-07-30 20:49:36 +00:00
addr = > ram [ addr & 0x07FF ] , ( addr , value ) = > ram [ addr & 0x07FF ] = value ) ;
var SystemBus = new MemoryDomain ( "System Bus" , 0x10000 , Endian . Little ,
addr = > ReadMemory ( ( ushort ) addr ) , ( addr , value ) = > WriteMemory ( ( ushort ) addr , value ) ) ;
var PPUBus = new MemoryDomain ( "PPU Bus" , 0x4000 , Endian . Little ,
addr = > ppu . ppubus_peek ( addr ) , ( addr , value ) = > ppu . ppubus_write ( addr , value ) ) ;
2011-03-16 03:13:51 +00:00
var CIRAMdomain = new MemoryDomain ( "CIRAM (nametables)" , 0x800 , Endian . Little ,
2011-03-08 07:25:35 +00:00
addr = > CIRAM [ addr & 0x07FF ] , ( addr , value ) = > CIRAM [ addr & 0x07FF ] = value ) ;
2011-08-28 19:07:33 +00:00
var OAMdoman = new MemoryDomain ( "OAM" , 64 * 4 , Endian . Unknown ,
addr = > ppu . OAM [ addr & ( 64 * 4 - 1 ) ] , ( addr , value ) = > ppu . OAM [ addr & ( 64 * 4 - 1 ) ] = value ) ;
2011-03-16 03:13:51 +00:00
2011-03-17 03:51:31 +00:00
//demo a game genie code
GetWatch ( NESWatch . EDomain . Sysbus , 0xB424 ) . SetGameGenie ( - 1 , 0x10 ) ;
GetWatch ( NESWatch . EDomain . Sysbus , 0xB424 ) . RemoveGameGenie ( ) ;
2011-03-16 05:06:21 +00:00
2011-07-30 20:49:36 +00:00
domains . Add ( RAM ) ;
2011-03-08 07:25:35 +00:00
domains . Add ( SystemBus ) ;
2011-07-30 20:49:36 +00:00
domains . Add ( PPUBus ) ;
2011-03-16 03:13:51 +00:00
domains . Add ( CIRAMdomain ) ;
2011-08-28 19:07:33 +00:00
domains . Add ( OAMdoman ) ;
2011-03-06 02:36:49 +00:00
2011-07-30 20:49:36 +00:00
if ( board . SaveRam ! = null )
{
var BatteryRam = new MemoryDomain ( "Battery RAM" , board . SaveRam . Length , Endian . Little ,
addr = > board . SaveRam [ addr ] , ( addr , value ) = > board . SaveRam [ addr ] = value ) ;
domains . Add ( BatteryRam ) ;
}
2011-03-06 03:07:25 +00:00
2011-07-30 20:49:36 +00:00
var PRGROM = new MemoryDomain ( "PRG ROM" , cart . prg_size * 1024 , Endian . Little ,
addr = > board . ROM [ addr ] , ( addr , value ) = > board . ROM [ addr ] = value ) ;
domains . Add ( PRGROM ) ;
2011-03-06 03:34:13 +00:00
2011-03-08 07:25:35 +00:00
if ( board . VROM ! = null )
2011-07-30 20:49:36 +00:00
{
var CHRROM = new MemoryDomain ( "CHR VROM" , cart . chr_size * 1024 , Endian . Little ,
2011-03-07 10:41:46 +00:00
addr = > board . VROM [ addr ] , ( addr , value ) = > board . VROM [ addr ] = value ) ;
2011-07-30 20:49:36 +00:00
domains . Add ( CHRROM ) ;
}
2011-03-06 03:34:13 +00:00
2011-07-30 20:49:36 +00:00
if ( board . VRAM ! = null )
{
2011-03-08 07:25:35 +00:00
var VRAM = new MemoryDomain ( "VRAM" , board . VRAM . Length , Endian . Little ,
2011-07-30 20:49:36 +00:00
addr = > board . VRAM [ addr ] , ( addr , value ) = > board . VRAM [ addr ] = value ) ;
2011-03-08 07:25:35 +00:00
domains . Add ( VRAM ) ;
2011-07-30 20:49:36 +00:00
}
2011-03-06 04:40:56 +00:00
2011-07-30 20:49:36 +00:00
if ( board . WRAM ! = null )
{
2011-03-08 07:25:35 +00:00
var WRAM = new MemoryDomain ( "WRAM" , board . WRAM . Length , Endian . Little ,
2011-07-30 20:49:36 +00:00
addr = > board . WRAM [ addr ] , ( addr , value ) = > board . WRAM [ addr ] = value ) ;
2011-03-08 07:25:35 +00:00
domains . Add ( WRAM ) ;
2011-07-30 20:49:36 +00:00
}
2011-03-06 04:40:56 +00:00
2011-07-30 20:49:36 +00:00
memoryDomains = domains . AsReadOnly ( ) ;
}
2011-03-05 03:03:47 +00:00
2011-02-20 02:49:37 +00:00
public string SystemId { get { return "NES" ; } }
2011-03-05 03:03:47 +00:00
public IList < MemoryDomain > MemoryDomains { get { return memoryDomains ; } }
public MemoryDomain MainMemory { get { return memoryDomains [ 0 ] ; } }
2011-02-21 09:48:53 +00:00
2011-03-07 10:41:46 +00:00
public string GameName { get { return game_name ; } }
2011-03-20 02:12:10 +00:00
public enum EDetectionOrigin
2011-03-19 20:12:06 +00:00
{
None , BootGodDB , GameDB , INES
}
2011-07-10 21:00:28 +00:00
StringWriter LoadReport ;
void LoadWriteLine ( string format , params object [ ] arg )
{
Console . WriteLine ( format , arg ) ;
LoadReport . WriteLine ( format , arg ) ;
}
2011-07-30 20:49:36 +00:00
void LoadWriteLine ( object arg ) { LoadWriteLine ( "{0}" , arg ) ; }
2011-09-24 17:07:48 +00:00
2012-03-06 08:01:48 +00:00
class MyWriter : StringWriter
{
public MyWriter ( TextWriter _loadReport )
{
loadReport = _loadReport ;
}
TextWriter loadReport ;
public override void WriteLine ( string format , params object [ ] arg )
{
Console . WriteLine ( format , arg ) ;
loadReport . WriteLine ( format , arg ) ;
}
}
2011-08-04 03:20:54 +00:00
public unsafe void Init ( GameInfo gameInfo , byte [ ] rom )
2011-02-27 09:45:50 +00:00
{
2011-07-10 21:00:28 +00:00
LoadReport = new StringWriter ( ) ;
LoadWriteLine ( "------" ) ;
LoadWriteLine ( "BEGIN NES rom analysis:" ) ;
2011-08-04 03:20:54 +00:00
byte [ ] file = rom ;
2011-03-02 06:18:26 +00:00
if ( file . Length < 16 ) throw new Exception ( "Alleged NES rom too small to be anything useful" ) ;
if ( file . Take ( 4 ) . SequenceEqual ( System . Text . Encoding . ASCII . GetBytes ( "UNIF" ) ) )
throw new Exception ( "You've tried to open a UNIF rom. We don't have any UNIF roms to test with. Please consult the developers." ) ;
2011-02-27 09:45:50 +00:00
fixed ( byte * bfile = & file [ 0 ] )
{
2011-03-19 20:12:06 +00:00
var origin = EDetectionOrigin . None ;
2011-02-27 09:45:50 +00:00
var header = ( iNES_HEADER * ) bfile ;
if ( ! header - > CheckID ( ) ) throw new InvalidOperationException ( "iNES header not found" ) ;
header - > Cleanup ( ) ;
2011-02-28 06:16:20 +00:00
//now that we know we have an iNES header, we can try to ignore it.
2012-03-06 07:51:41 +00:00
List < string > hash_sha1_several = new List < string > ( ) ;
string hash_sha1 = "sha1:" + Util . Hash_SHA1 ( file , 16 , file . Length - 16 ) ;
hash_sha1_several . Add ( hash_sha1 ) ;
string hash_md5 = "md5:" + Util . Hash_MD5 ( file , 16 , file . Length - 16 ) ;
2011-06-08 06:17:41 +00:00
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Found iNES header:" ) ;
2012-03-06 08:01:48 +00:00
CartInfo iNesHeaderInfo = header - > Analyze ( new MyWriter ( LoadReport ) ) ;
2012-03-06 07:51:41 +00:00
LoadWriteLine ( "Since this is iNES we can (somewhat) confidently parse PRG/CHR banks to hash." ) ;
2011-06-08 06:17:41 +00:00
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "headerless rom hash: {0}" , hash_sha1 ) ;
2012-03-07 19:14:15 +00:00
LoadWriteLine ( "headerless rom hash: {0}" , hash_md5 ) ;
2011-03-07 10:41:46 +00:00
2012-03-06 07:51:41 +00:00
if ( iNesHeaderInfo . prg_size = = 16 )
{
//8KB prg can't be stored in iNES format, which counts 16KB prg banks.
//so a correct hash will include only 8KB.
LoadWriteLine ( "Since this rom has a 16 KB PRG, we'll hash it as 8KB too for bootgod's DB:" ) ;
var msTemp = new MemoryStream ( ) ;
msTemp . Write ( file , 16 , 8 * 1024 ) ; //add prg
msTemp . Write ( file , 16 + 16 * 1024 , iNesHeaderInfo . chr_size * 1024 ) ; //add chr
msTemp . Flush ( ) ;
var bytes = msTemp . ToArray ( ) ;
var hash = "sha1:" + Util . Hash_SHA1 ( bytes , 0 , bytes . Length ) ;
2012-03-06 08:01:48 +00:00
LoadWriteLine ( " PRG (8KB) + CHR hash: {0}" , hash ) ;
2012-03-06 07:51:41 +00:00
hash_sha1_several . Add ( hash ) ;
2012-03-07 19:14:15 +00:00
hash = "md5:" + Util . Hash_MD5 ( bytes , 0 , bytes . Length ) ;
LoadWriteLine ( " PRG (8KB) + CHR hash: {0}" , hash ) ;
2012-03-06 07:51:41 +00:00
}
2011-06-13 08:38:10 +00:00
Type boardType = null ;
2011-03-19 20:12:06 +00:00
CartInfo choice = null ;
2011-07-30 20:49:36 +00:00
if ( USE_DATABASE )
2012-03-06 07:51:41 +00:00
choice = IdentifyFromBootGodDB ( hash_sha1_several ) ;
2011-03-20 20:42:12 +00:00
if ( choice = = null )
2011-02-28 06:16:20 +00:00
{
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Could not locate game in nescartdb" ) ;
2011-03-19 20:12:06 +00:00
if ( USE_DATABASE )
{
choice = IdentifyFromGameDB ( hash_md5 ) ;
if ( choice = = null )
2011-06-08 01:03:32 +00:00
{
2011-03-19 20:12:06 +00:00
choice = IdentifyFromGameDB ( hash_sha1 ) ;
2011-06-08 01:03:32 +00:00
}
2011-03-19 20:12:06 +00:00
}
if ( choice = = null )
{
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Could not locate game in bizhawk gamedb" ) ;
LoadWriteLine ( "Attempting inference from iNES header" ) ;
2011-06-08 06:17:41 +00:00
choice = iNesHeaderInfo ;
2011-03-19 20:12:06 +00:00
string iNES_board = iNESBoardDetector . Detect ( choice ) ;
if ( iNES_board = = null )
throw new Exception ( "couldnt identify NES rom" ) ;
choice . board_type = iNES_board ;
2011-06-13 08:38:10 +00:00
//try spinning up a board with 8K wram and with 0K wram to see if one answers
try
{
boardType = FindBoard ( choice , origin ) ;
}
catch { }
if ( boardType = = null )
{
if ( choice . wram_size = = 8 ) choice . wram_size = 0 ;
else if ( choice . wram_size = = 0 ) choice . wram_size = 8 ;
try
{
boardType = FindBoard ( choice , origin ) ;
}
catch { }
if ( boardType ! = null )
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Ambiguous iNES wram size resolved as {0}k" , choice . wram_size ) ;
2011-06-13 08:38:10 +00:00
}
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Chose board from iNES heuristics: " + iNES_board ) ;
2011-08-04 03:20:54 +00:00
choice . game . name = gameInfo . Name ;
2011-03-19 20:12:06 +00:00
origin = EDetectionOrigin . INES ;
}
2011-03-07 10:41:46 +00:00
else
2011-03-19 20:12:06 +00:00
{
origin = EDetectionOrigin . GameDB ;
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Chose board from bizhawk gamedb: " + choice . board_type ) ;
2011-03-19 20:12:06 +00:00
}
2011-02-28 06:16:20 +00:00
}
2011-03-20 20:42:12 +00:00
else
{
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "Chose board from nescartdb:" ) ;
LoadWriteLine ( choice ) ;
2011-03-20 20:42:12 +00:00
origin = EDetectionOrigin . BootGodDB ;
}
2011-02-28 06:16:20 +00:00
2011-06-08 06:17:41 +00:00
//TODO - generate better name with region and system
2011-03-08 07:25:35 +00:00
game_name = choice . game . name ;
2011-03-07 10:41:46 +00:00
//find a INESBoard to handle this
2011-04-18 01:57:22 +00:00
boardType = FindBoard ( choice , origin ) ;
2011-03-19 20:12:06 +00:00
if ( boardType = = null )
throw new Exception ( "No class implements the necessary board type: " + choice . board_type ) ;
2011-07-30 20:49:36 +00:00
if ( choice . DB_GameInfo ! = null )
if ( choice . DB_GameInfo . Status = = RomStatus . BadDump )
2011-07-10 21:00:28 +00:00
choice . bad = true ;
LoadWriteLine ( "Final game detection results:" ) ;
LoadWriteLine ( choice ) ;
LoadWriteLine ( "\"" + game_name + "\"" ) ;
LoadWriteLine ( "Implemented by: class " + boardType . Name ) ;
2011-06-08 06:53:11 +00:00
if ( choice . bad )
{
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "~~ ONE WAY OR ANOTHER, THIS DUMP IS KNOWN TO BE *BAD* ~~" ) ;
LoadWriteLine ( "~~ YOU SHOULD FIND A BETTER FILE ~~" ) ;
2011-06-08 06:53:11 +00:00
}
2011-06-08 06:17:41 +00:00
2011-07-10 21:00:28 +00:00
LoadWriteLine ( "END NES rom analysis" ) ;
LoadWriteLine ( "------" ) ;
2011-06-08 06:17:41 +00:00
2011-03-07 10:41:46 +00:00
board = ( INESBoard ) Activator . CreateInstance ( boardType ) ;
2011-02-28 06:16:20 +00:00
2011-03-08 07:25:35 +00:00
cart = choice ;
2011-03-07 10:41:46 +00:00
board . Create ( this ) ;
2011-03-20 02:12:10 +00:00
board . Configure ( origin ) ;
2011-03-03 19:56:16 +00:00
2011-07-10 21:00:28 +00:00
if ( origin = = EDetectionOrigin . BootGodDB )
{
2011-08-04 03:20:54 +00:00
RomStatus = RomStatus . GoodDump ;
2011-07-10 21:00:28 +00:00
CoreOutputComm . RomStatusAnnotation = "Identified from BootGod's database" ;
}
if ( origin = = EDetectionOrigin . INES )
{
2011-08-04 03:20:54 +00:00
RomStatus = RomStatus . NotInDatabase ;
2011-07-10 21:00:28 +00:00
CoreOutputComm . RomStatusAnnotation = "Inferred from iNES header; potentially wrong" ;
}
if ( origin = = EDetectionOrigin . GameDB )
{
if ( choice . bad )
{
2011-08-04 03:20:54 +00:00
RomStatus = RomStatus . BadDump ;
2011-07-10 21:00:28 +00:00
}
else
{
2011-08-04 03:20:54 +00:00
RomStatus = choice . DB_GameInfo . Status ;
2011-07-10 21:00:28 +00:00
}
}
LoadReport . Flush ( ) ;
CoreOutputComm . RomStatusDetails = LoadReport . ToString ( ) ;
2011-03-08 07:25:35 +00:00
//create the board's rom and vrom
board . ROM = new byte [ choice . prg_size * 1024 ] ;
Array . Copy ( file , 16 , board . ROM , 0 , board . ROM . Length ) ;
if ( choice . chr_size > 0 )
2011-03-01 07:25:14 +00:00
{
2011-03-08 07:25:35 +00:00
board . VROM = new byte [ choice . chr_size * 1024 ] ;
2012-03-06 07:51:41 +00:00
int vrom_offset = iNesHeaderInfo . prg_size * 1024 ;
Array . Copy ( file , 16 + vrom_offset , board . VROM , 0 , board . VROM . Length ) ;
2011-03-01 07:25:14 +00:00
}
2011-03-08 07:25:35 +00:00
//create the vram and wram if necessary
if ( cart . wram_size ! = 0 )
board . WRAM = new byte [ cart . wram_size * 1024 ] ;
if ( cart . vram_size ! = 0 )
board . VRAM = new byte [ cart . vram_size * 1024 ] ;
2011-03-07 10:41:46 +00:00
HardReset ( ) ;
SetupMemoryDomains ( ) ;
2011-02-27 09:45:50 +00:00
}
}
2011-03-01 09:32:12 +00:00
2011-04-17 22:51:53 +00:00
void SyncState ( Serializer ser )
2011-03-01 09:32:12 +00:00
{
2011-04-17 22:51:53 +00:00
ser . BeginSection ( "NES" ) ;
ser . Sync ( "Frame" , ref _frame ) ;
2011-07-30 20:49:36 +00:00
ser . Sync ( "Lag" , ref _lagcount ) ;
2011-04-17 22:51:53 +00:00
cpu . SyncState ( ser ) ;
ser . Sync ( "ram" , ref ram , false ) ;
ser . Sync ( "CIRAM" , ref CIRAM , false ) ;
ser . Sync ( "cpu_accumulate" , ref cpu_accumulate ) ;
2011-05-20 18:55:01 +00:00
ser . Sync ( "_irq_apu" , ref _irq_apu ) ;
ser . Sync ( "_irq_cart" , ref _irq_cart ) ;
sync_irq ( ) ;
2011-07-30 20:49:36 +00:00
//string inp = GetControllersAsMnemonic(); TODO sorry bout that
//ser.SyncFixedString("input", ref inp, 32);
2011-04-17 22:51:53 +00:00
board . SyncState ( ser ) ;
ppu . SyncState ( ser ) ;
2011-06-10 05:02:06 +00:00
apu . SyncState ( ser ) ;
2011-04-17 22:51:53 +00:00
ser . EndSection ( ) ;
2011-03-01 09:32:12 +00:00
}
2011-04-17 22:51:53 +00:00
public void SaveStateText ( TextWriter writer ) { SyncState ( Serializer . CreateTextWriter ( writer ) ) ; }
public void LoadStateText ( TextReader reader ) { SyncState ( Serializer . CreateTextReader ( reader ) ) ; }
public void SaveStateBinary ( BinaryWriter bw ) { SyncState ( Serializer . CreateBinaryWriter ( bw ) ) ; }
public void LoadStateBinary ( BinaryReader br ) { SyncState ( Serializer . CreateBinaryReader ( br ) ) ; }
2011-03-01 09:32:12 +00:00
public byte [ ] SaveStateBinary ( )
{
MemoryStream ms = new MemoryStream ( ) ;
BinaryWriter bw = new BinaryWriter ( ms ) ;
SaveStateBinary ( bw ) ;
bw . Flush ( ) ;
return ms . ToArray ( ) ;
}
2011-06-02 02:59:18 +00:00
2011-07-30 20:49:36 +00:00
public void Dispose ( ) { }
}
2011-02-28 06:16:20 +00:00
}
//todo
//http://blog.ntrq.net/?p=428
2011-03-01 07:25:14 +00:00
//cpu bus junk bits
//UBER DOC
//http://nocash.emubase.de/everynes.htm
//A VERY NICE board assignments list
//http://personales.epsg.upv.es/~jogilmo1/nes/TEXTOS/ARXIUS/BOARDTABLE.TXT
//why not make boards communicate over the actual board pinouts
//http://wiki.nesdev.com/w/index.php/Cartridge_connector
//a mappers list
//http://tuxnes.sourceforge.net/nesmapper.txt
2011-03-07 10:41:46 +00:00
//some ppu tests
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb