2011-01-11 02:55:51 +00:00
using System ;
2011-01-21 03:59:50 +00:00
using System.Collections.Generic ;
2013-10-27 22:07:40 +00:00
2014-07-03 19:20:34 +00:00
using BizHawk.Common.BufferExtensions ;
2013-11-04 01:06:36 +00:00
using BizHawk.Emulation.Common ;
2016-03-04 13:37:09 +00:00
using BizHawk.Emulation.Cores.Components ;
2014-01-22 01:14:36 +00:00
using BizHawk.Emulation.Cores.Components.H6280 ;
2013-11-03 23:45:44 +00:00
using BizHawk.Emulation.DiscSystem ;
2011-01-11 02:55:51 +00:00
2013-11-13 23:36:21 +00:00
namespace BizHawk.Emulation.Cores.PCEngine
2011-01-11 02:55:51 +00:00
{
2011-09-24 17:05:34 +00:00
public enum NecSystemType { TurboGrafx , TurboCD , SuperGrafx }
2017-07-12 19:10:55 +00:00
[ Core (
2014-04-25 01:19:57 +00:00
"PCEHawk" ,
"Vecna" ,
isPorted : false ,
2017-04-25 17:57:42 +00:00
isReleased : true ) ]
2015-01-14 21:55:48 +00:00
public sealed partial class PCEngine : IEmulator , ISaveRam , IStatable , IInputPollable ,
2015-10-28 08:51:53 +00:00
IDebuggable , ISettable < PCEngine . PCESettings , PCEngine . PCESyncSettings > , IDriveLight , ICodeDataLogger
2011-09-24 17:05:34 +00:00
{
2014-08-23 19:06:37 +00:00
[CoreConstructor("PCE", "SGX")]
2017-05-06 00:05:36 +00:00
public PCEngine ( CoreComm comm , GameInfo game , byte [ ] rom , object settings , object syncSettings )
2011-09-24 17:05:34 +00:00
{
2014-12-05 01:56:45 +00:00
MemoryCallbacks = new MemoryCallbackSystem ( ) ;
2012-12-10 00:43:43 +00:00
CoreComm = comm ;
2011-09-24 17:05:34 +00:00
switch ( game . System )
{
2016-12-12 16:23:07 +00:00
default :
2011-09-24 17:05:34 +00:00
case "PCE" :
2016-12-12 16:23:07 +00:00
SystemId = "PCE" ;
2012-09-14 23:18:16 +00:00
Type = NecSystemType . TurboGrafx ;
2011-09-24 17:05:34 +00:00
break ;
case "SGX" :
2016-12-12 16:23:07 +00:00
SystemId = "SGX" ;
2012-09-14 23:18:16 +00:00
Type = NecSystemType . SuperGrafx ;
2011-09-24 17:05:34 +00:00
break ;
}
2017-04-25 17:57:42 +00:00
2017-05-06 00:05:36 +00:00
Settings = ( PCESettings ) settings ? ? new PCESettings ( ) ;
2014-06-28 22:48:07 +00:00
_syncSettings = ( PCESyncSettings ) syncSettings ? ? new PCESyncSettings ( ) ;
2013-12-30 20:26:33 +00:00
Init ( game , rom ) ;
2017-07-18 15:37:17 +00:00
_controllerDeck = new PceControllerDeck (
_syncSettings . Port1 ,
_syncSettings . Port2 ,
_syncSettings . Port3 ,
_syncSettings . Port4 ,
_syncSettings . Port5 ) ;
2011-09-24 17:05:34 +00:00
}
2014-06-28 22:48:07 +00:00
public PCEngine ( CoreComm comm , GameInfo game , Disc disc , object Settings , object syncSettings )
2011-09-24 17:05:34 +00:00
{
2012-12-10 00:43:43 +00:00
CoreComm = comm ;
2014-12-05 01:56:45 +00:00
MemoryCallbacks = new MemoryCallbackSystem ( ) ;
2014-12-12 01:49:54 +00:00
DriveLightEnabled = true ;
2016-12-12 16:23:07 +00:00
SystemId = "PCECD" ;
2011-09-24 17:05:34 +00:00
Type = NecSystemType . TurboCD ;
this . disc = disc ;
2016-12-12 16:23:07 +00:00
this . Settings = ( PCESettings ) Settings ? ? new PCESettings ( ) ;
2014-06-28 22:48:07 +00:00
_syncSettings = ( PCESyncSettings ) syncSettings ? ? new PCESyncSettings ( ) ;
2014-02-10 15:26:18 +00:00
GameInfo biosInfo ;
byte [ ] rom = CoreComm . CoreFileProvider . GetFirmwareWithGameInfo ( "PCECD" , "Bios" , true , out biosInfo ,
"PCE-CD System Card not found. Please check the BIOS settings in Config->Firmwares." ) ;
if ( biosInfo . Status = = RomStatus . BadDump )
{
CoreComm . ShowMessage (
"The PCE-CD System Card you have selected is known to be a bad dump. This may cause problems playing PCE-CD games.\n\n"
+ "It is recommended that you find a good dump of the system card. Sorry to be the bearer of bad news!" ) ;
}
else if ( biosInfo . NotInDatabase )
{
CoreComm . ShowMessage (
"The PCE-CD System Card you have selected is not recognized in our database. That might mean it's a bad dump, or isn't the correct rom." ) ;
}
else if ( biosInfo [ "BIOS" ] = = false )
{
2017-04-25 17:57:42 +00:00
// zeromus says: someone please write a note about how this could possibly happen.
// it seems like this is a relic of using gameDB for storing whether something is a bios? firmwareDB should be handling it now.
2014-02-10 15:26:18 +00:00
CoreComm . ShowMessage (
2014-05-08 03:18:00 +00:00
"The PCE-CD System Card you have selected is not a BIOS image. You may have selected the wrong rom. FYI-Please report this to developers, I don't think this error message should happen." ) ;
2014-02-10 15:26:18 +00:00
}
if ( biosInfo [ "SuperSysCard" ] )
{
game . AddOption ( "SuperSysCard" ) ;
}
if ( game [ "NeedSuperSysCard" ] & & game [ "SuperSysCard" ] = = false )
{
CoreComm . ShowMessage (
"This game requires a version 3.0 System card and won't run with the system card you've selected. Try selecting a 3.0 System Card in the firmware configuration." ) ;
throw new Exception ( ) ;
}
2014-07-03 19:20:34 +00:00
game . FirmwareHash = rom . HashSHA1 ( ) ;
2014-02-10 15:26:18 +00:00
2013-12-30 20:26:33 +00:00
Init ( game , rom ) ;
2017-04-25 17:57:42 +00:00
2012-10-08 20:37:41 +00:00
// the default RomStatusDetails don't do anything with Disc
2015-07-03 09:11:07 +00:00
CoreComm . RomStatusDetails = string . Format ( "{0}\r\nDisk partial hash:{1}" , game . Name , new DiscSystem . DiscHasher ( disc ) . OldHash ( ) ) ;
2017-07-18 15:37:17 +00:00
_controllerDeck = new PceControllerDeck (
_syncSettings . Port1 ,
_syncSettings . Port2 ,
_syncSettings . Port3 ,
_syncSettings . Port4 ,
_syncSettings . Port5 ) ;
2011-09-24 17:05:34 +00:00
}
2016-12-12 16:23:07 +00:00
// ROM
2016-12-12 16:31:38 +00:00
private byte [ ] RomData ;
private int RomLength ;
private Disc disc ;
2014-12-12 01:49:54 +00:00
2016-12-12 16:23:07 +00:00
// Machine
public NecSystemType Type ;
2016-12-12 16:31:38 +00:00
internal HuC6280 Cpu ;
2016-12-12 16:23:07 +00:00
public VDC VDC1 , VDC2 ;
public VCE VCE ;
2016-12-12 16:31:38 +00:00
private VPC VPC ;
private ScsiCDBus SCSI ;
private ADPCM ADPCM ;
2017-05-02 01:09:11 +00:00
private IController _controller ;
2016-12-12 16:23:07 +00:00
public HuC6280PSG PSG ;
2016-12-12 16:31:38 +00:00
internal CDAudio CDAudio ;
private SoundMixer SoundMixer ;
2016-12-12 16:23:07 +00:00
2017-04-25 17:57:42 +00:00
private bool TurboGrafx = > Type = = NecSystemType . TurboGrafx ;
private bool SuperGrafx = > Type = = NecSystemType . SuperGrafx ;
private bool TurboCD = > Type = = NecSystemType . TurboCD ;
2016-12-12 16:23:07 +00:00
// BRAM
2016-12-12 16:31:38 +00:00
private bool BramEnabled = false ;
private bool BramLocked = true ;
private byte [ ] BRAM ;
2016-12-12 16:23:07 +00:00
// Memory system
2016-12-12 16:31:38 +00:00
private byte [ ] Ram ; // PCE= 8K base ram, SGX= 64k base ram
private byte [ ] CDRam ; // TurboCD extra 64k of ram
private byte [ ] SuperRam ; // Super System Card 192K of additional RAM
private byte [ ] ArcadeRam ; // Arcade Card 2048K of additional RAM
2016-12-12 16:23:07 +00:00
2016-12-12 16:31:38 +00:00
private bool ForceSpriteLimit ;
2016-12-12 16:23:07 +00:00
// 21,477,270 Machine clocks / sec
// 7,159,090 Cpu cycles / sec
private ITraceable Tracer { get ; set ; }
private void Init ( GameInfo game , byte [ ] rom )
2011-09-24 17:05:34 +00:00
{
2015-01-24 15:05:03 +00:00
Cpu = new HuC6280 ( MemoryCallbacks ) ;
2011-09-24 17:05:34 +00:00
VCE = new VCE ( ) ;
2012-03-11 06:50:46 +00:00
VDC1 = new VDC ( this , Cpu , VCE ) ;
2011-09-24 17:05:34 +00:00
PSG = new HuC6280PSG ( ) ;
SCSI = new ScsiCDBus ( this , disc ) ;
2017-04-25 17:57:42 +00:00
Cpu . Logger = s = > Tracer . Put ( s ) ;
2012-10-01 00:21:25 +00:00
2011-09-24 17:05:34 +00:00
if ( TurboGrafx )
{
Ram = new byte [ 0x2000 ] ;
Cpu . ReadMemory21 = ReadMemory ;
Cpu . WriteMemory21 = WriteMemory ;
Cpu . WriteVDC = VDC1 . WriteVDC ;
2017-04-25 17:57:42 +00:00
_soundProvider = new FakeSyncSound ( PSG , 735 ) ;
2011-09-24 17:05:34 +00:00
CDAudio = new CDAudio ( null , 0 ) ;
}
else if ( SuperGrafx )
{
2012-03-11 06:50:46 +00:00
VDC2 = new VDC ( this , Cpu , VCE ) ;
2012-03-11 09:51:23 +00:00
VPC = new VPC ( this , VDC1 , VDC2 , VCE , Cpu ) ;
2011-09-24 17:05:34 +00:00
Ram = new byte [ 0x8000 ] ;
Cpu . ReadMemory21 = ReadMemorySGX ;
Cpu . WriteMemory21 = WriteMemorySGX ;
Cpu . WriteVDC = VDC1 . WriteVDC ;
2017-04-25 17:57:42 +00:00
_soundProvider = new FakeSyncSound ( PSG , 735 ) ;
2011-09-24 17:05:34 +00:00
CDAudio = new CDAudio ( null , 0 ) ;
}
else if ( TurboCD )
{
Ram = new byte [ 0x2000 ] ;
CDRam = new byte [ 0x10000 ] ;
ADPCM = new ADPCM ( this , SCSI ) ;
Cpu . ReadMemory21 = ReadMemoryCD ;
Cpu . WriteMemory21 = WriteMemoryCD ;
Cpu . WriteVDC = VDC1 . WriteVDC ;
CDAudio = new CDAudio ( disc ) ;
SetCDAudioCallback ( ) ;
PSG . MaxVolume = short . MaxValue * 3 / 4 ;
SoundMixer = new SoundMixer ( PSG , CDAudio , ADPCM ) ;
2017-04-25 17:57:42 +00:00
_soundProvider = new FakeSyncSound ( SoundMixer , 735 ) ;
2011-09-24 17:05:34 +00:00
Cpu . ThinkAction = ( cycles ) = > { SCSI . Think ( ) ; ADPCM . Think ( cycles ) ; } ;
}
if ( rom . Length = = 0x60000 )
{
// 384k roms require special loading code. Why ;_;
// In memory, 384k roms look like [1st 256k][Then full 384k]
RomData = new byte [ 0xA0000 ] ;
var origRom = rom ;
for ( int i = 0 ; i < 0x40000 ; i + + )
RomData [ i ] = origRom [ i ] ;
for ( int i = 0 ; i < 0x60000 ; i + + )
RomData [ i + 0x40000 ] = origRom [ i ] ;
RomLength = RomData . Length ;
}
else if ( rom . Length > 1024 * 1024 )
{
// If the rom is bigger than 1 megabyte, switch to Street Fighter 2 mapper
Cpu . ReadMemory21 = ReadMemorySF2 ;
Cpu . WriteMemory21 = WriteMemorySF2 ;
RomData = rom ;
RomLength = RomData . Length ;
2017-04-25 17:57:42 +00:00
2014-02-09 05:49:05 +00:00
// user request: current value of the SF2MapperLatch on the tracelogger
2016-02-21 22:34:14 +00:00
Cpu . Logger = ( s ) = > Tracer . Put ( new TraceInfo
{
2017-04-25 17:57:42 +00:00
Disassembly = $"{SF2MapperLatch:X1}:{s}" ,
2017-05-10 11:45:23 +00:00
RegisterInfo = ""
2016-02-21 22:34:14 +00:00
} ) ;
2011-09-24 17:05:34 +00:00
}
else
{
// normal rom.
RomData = rom ;
RomLength = RomData . Length ;
}
if ( game [ "BRAM" ] | | Type = = NecSystemType . TurboCD )
{
BramEnabled = true ;
BRAM = new byte [ 2048 ] ;
// pre-format BRAM. damn are we helpful.
BRAM [ 0 ] = 0x48 ; BRAM [ 1 ] = 0x55 ; BRAM [ 2 ] = 0x42 ; BRAM [ 3 ] = 0x4D ;
BRAM [ 4 ] = 0x00 ; BRAM [ 5 ] = 0x88 ; BRAM [ 6 ] = 0x10 ; BRAM [ 7 ] = 0x80 ;
}
if ( game [ "SuperSysCard" ] )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
SuperRam = new byte [ 0x30000 ] ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
if ( game [ "ArcadeCard" ] )
{
ArcadeRam = new byte [ 0x200000 ] ;
ArcadeCard = true ;
2016-12-12 16:23:07 +00:00
ArcadeCardRewindHack = Settings . ArcadeCardRewindHack ;
2011-09-24 17:05:34 +00:00
for ( int i = 0 ; i < 4 ; i + + )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
ArcadePage [ i ] = new ArcadeCardPage ( ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
}
if ( game [ "PopulousSRAM" ] )
{
PopulousRAM = new byte [ 0x8000 ] ;
Cpu . ReadMemory21 = ReadMemoryPopulous ;
Cpu . WriteMemory21 = WriteMemoryPopulous ;
}
2013-12-23 21:54:10 +00:00
// the gamedb can force sprite limit on, ignoring settings
2014-02-27 01:00:21 +00:00
if ( game [ "ForceSpriteLimit" ] | | game . NotInDatabase )
2017-04-25 17:57:42 +00:00
{
2014-02-27 01:00:21 +00:00
ForceSpriteLimit = true ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
if ( game [ "CdVol" ] )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
CDAudio . MaxVolume = int . Parse ( game . OptionValue ( "CdVol" ) ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
if ( game [ "PsgVol" ] )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
PSG . MaxVolume = int . Parse ( game . OptionValue ( "PsgVol" ) ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
if ( game [ "AdpcmVol" ] )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
ADPCM . MaxVolume = int . Parse ( game . OptionValue ( "AdpcmVol" ) ) ;
2017-04-25 17:57:42 +00:00
}
2013-12-23 21:54:10 +00:00
// the gamedb can also force equalizevolumes on
2016-12-12 16:23:07 +00:00
if ( TurboCD & & ( Settings . EqualizeVolume | | game [ "EqualizeVolumes" ] | | game . NotInDatabase ) )
2017-04-25 17:57:42 +00:00
{
2011-09-24 17:05:34 +00:00
SoundMixer . EqualizeVolumes ( ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
// Ok, yes, HBlankPeriod's only purpose is game-specific hax.
// 1) At least they're not coded directly into the emulator, but instead data-driven.
// 2) The games which have custom HBlankPeriods work without it, the override only
// serves to clean up minor gfx anomalies.
// 3) There's no point in haxing the timing with incorrect values in an attempt to avoid this.
// The proper fix is cycle-accurate/bus-accurate timing. That isn't coming to the C#
// version of this core. Let's just acknolwedge that the timing is imperfect and fix
// it in the least intrusive and most honest way we can.
if ( game [ "HBlankPeriod" ] )
2017-04-25 17:57:42 +00:00
{
2012-09-13 04:13:49 +00:00
VDC1 . HBlankCycles = game . GetIntValue ( "HBlankPeriod" ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
// This is also a hack. Proper multi-res/TV emulation will be a native-code core feature.
if ( game [ "MultiResHack" ] )
2017-04-25 17:57:42 +00:00
{
2012-09-13 04:13:49 +00:00
VDC1 . MultiResHack = game . GetIntValue ( "MultiResHack" ) ;
2017-04-25 17:57:42 +00:00
}
2011-09-24 17:05:34 +00:00
Cpu . ResetPC ( ) ;
2015-01-14 22:37:37 +00:00
2016-02-28 13:28:00 +00:00
Tracer = new TraceBuffer { Header = Cpu . TraceHeader } ;
2015-01-15 22:39:43 +00:00
var ser = new BasicServiceProvider ( this ) ;
ServiceProvider = ser ;
2015-01-15 15:56:41 +00:00
ser . Register < ITraceable > ( Tracer ) ;
ser . Register < IDisassemblable > ( Cpu ) ;
ser . Register < IVideoProvider > ( ( IVideoProvider ) VPC ? ? VDC1 ) ;
2017-04-25 17:57:42 +00:00
ser . Register < ISoundProvider > ( _soundProvider ) ;
2015-01-15 22:39:43 +00:00
SetupMemoryDomains ( ) ;
2011-09-24 17:05:34 +00:00
}
2017-04-25 17:57:42 +00:00
private int _frame ;
2011-09-24 17:05:34 +00:00
2016-12-12 16:23:07 +00:00
private static Dictionary < string , int > SizesFromHuMap ( IEnumerable < HuC6280 . MemMapping > mm )
2015-10-28 08:51:53 +00:00
{
2016-12-12 16:23:07 +00:00
Dictionary < string , int > sizes = new Dictionary < string , int > ( ) ;
foreach ( var m in mm )
2015-10-28 08:51:53 +00:00
{
2017-04-25 17:57:42 +00:00
if ( ! sizes . ContainsKey ( m . Name ) | | m . MaxOffs > = sizes [ m . Name ] )
{
sizes [ m . Name ] = m . MaxOffs ;
}
2015-10-28 08:51:53 +00:00
}
2016-12-12 16:23:07 +00:00
var keys = new List < string > ( sizes . Keys ) ;
foreach ( var key in keys )
2011-09-24 17:05:34 +00:00
{
2016-12-12 16:23:07 +00:00
// becase we were looking at offsets, and each bank is 8192 big, we need to add that size
sizes [ key ] + = 8192 ;
2011-09-24 17:05:34 +00:00
}
2017-04-25 17:57:42 +00:00
2016-12-12 16:23:07 +00:00
return sizes ;
2011-09-24 17:05:34 +00:00
}
2011-01-11 02:55:51 +00:00
2016-12-12 16:23:07 +00:00
private void CheckSpriteLimit ( )
2014-02-27 01:00:21 +00:00
{
2016-12-12 16:23:07 +00:00
bool spriteLimit = ForceSpriteLimit | Settings . SpriteLimit ;
2014-02-27 01:00:21 +00:00
VDC1 . PerformSpriteLimit = spriteLimit ;
if ( VDC2 ! = null )
2017-04-25 17:57:42 +00:00
{
2014-02-27 01:00:21 +00:00
VDC2 . PerformSpriteLimit = spriteLimit ;
2017-04-25 17:57:42 +00:00
}
2014-02-27 01:00:21 +00:00
}
2017-04-25 17:57:42 +00:00
private ISoundProvider _soundProvider ;
2011-09-24 17:05:34 +00:00
2016-12-12 16:31:38 +00:00
private string Region { get ; set ; }
2011-09-24 17:05:34 +00:00
}
2011-01-11 02:55:51 +00:00
}