2016-01-23 20:02:20 -05:00
# pragma once
# include "stdafx.h"
# include "BaseMapper.h"
# include "CPU.h"
2016-08-25 23:29:23 -04:00
# include "A12Watcher.h"
2016-01-23 20:02:20 -05:00
class Rambo1 : public BaseMapper
{
2017-05-08 22:47:59 -04:00
protected :
2016-05-31 23:17:22 -04:00
const uint8_t PpuIrqDelay = 2 ;
2016-01-23 20:02:20 -05:00
const uint8_t CpuIrqDelay = 1 ;
bool _irqEnabled = false ;
bool _irqCycleMode = false ;
bool _needReload = false ;
uint8_t _irqCounter = 0 ;
uint8_t _irqReloadValue = 0 ;
uint8_t _cpuClockCounter = 0 ;
2016-08-25 23:29:23 -04:00
A12Watcher _a12Watcher ;
2016-01-23 20:02:20 -05:00
uint8_t _currentRegister = 0 ;
uint8_t _registers [ 16 ] ;
uint8_t _needIrqDelay = 0 ;
2016-05-31 23:17:22 -04:00
bool _forceClock = false ;
2016-01-23 20:02:20 -05:00
protected :
2016-12-17 23:14:47 -05:00
virtual uint16_t GetPRGPageSize ( ) override { return 0x2000 ; }
virtual uint16_t GetCHRPageSize ( ) override { return 0x400 ; }
2016-01-23 20:02:20 -05:00
2016-12-17 23:14:47 -05:00
void InitMapper ( ) override
2016-01-23 20:02:20 -05:00
{
memset ( _registers , 0 , sizeof ( _registers ) ) ;
SelectPRGPage ( 3 , - 1 ) ;
}
2016-12-17 23:14:47 -05:00
void StreamState ( bool saving ) override
2016-01-23 20:02:20 -05:00
{
BaseMapper : : StreamState ( saving ) ;
2016-06-02 23:56:11 -04:00
ArrayInfo < uint8_t > registers = { _registers , 16 } ;
2016-08-25 23:29:23 -04:00
SnapshotInfo a12Watcher { & _a12Watcher } ;
Stream ( _irqEnabled , _irqCycleMode , _needReload , _needIrqDelay , _irqCounter , _irqReloadValue ,
a12Watcher , _cpuClockCounter , _currentRegister , registers , _forceClock ) ;
2016-01-23 20:02:20 -05:00
}
2016-12-17 23:14:47 -05:00
virtual void ProcessCpuClock ( ) override
2016-01-23 20:02:20 -05:00
{
if ( _needIrqDelay ) {
_needIrqDelay - - ;
if ( _needIrqDelay = = 0 ) {
CPU : : SetIRQSource ( IRQSource : : External ) ;
}
}
2016-05-31 23:17:22 -04:00
if ( _irqCycleMode | | _forceClock ) {
2016-01-23 20:02:20 -05:00
_cpuClockCounter = ( _cpuClockCounter + 1 ) & 0x03 ;
if ( _cpuClockCounter = = 0 ) {
ClockIrqCounter ( Rambo1 : : CpuIrqDelay ) ;
2016-05-31 23:17:22 -04:00
_forceClock = false ;
2016-01-23 20:02:20 -05:00
}
}
}
void ClockIrqCounter ( const uint8_t delay )
{
if ( _needReload ) {
2016-05-31 23:17:22 -04:00
//Fixes Hard Drivin'
if ( _irqReloadValue < = 1 ) {
_irqCounter = _irqReloadValue + 1 ;
} else {
_irqCounter = _irqReloadValue + 2 ;
}
2016-01-23 20:02:20 -05:00
_needReload = false ;
} else if ( _irqCounter = = 0 ) {
2016-05-31 23:17:22 -04:00
_irqCounter = _irqReloadValue + 1 ;
}
_irqCounter - - ;
if ( _irqCounter = = 0 & & _irqEnabled ) {
_needIrqDelay = delay ;
2016-01-23 20:02:20 -05:00
}
}
void UpdateState ( )
{
if ( _currentRegister & 0x40 ) {
SelectPRGPage ( 0 , _registers [ 15 ] ) ;
SelectPRGPage ( 1 , _registers [ 6 ] ) ;
SelectPRGPage ( 2 , _registers [ 7 ] ) ;
} else {
SelectPRGPage ( 0 , _registers [ 6 ] ) ;
SelectPRGPage ( 1 , _registers [ 7 ] ) ;
SelectPRGPage ( 2 , _registers [ 15 ] ) ;
}
uint8_t a12Inversion = _currentRegister & 0x80 ? 0x04 : 0x00 ;
SelectCHRPage ( 0 ^ a12Inversion , _registers [ 0 ] ) ;
SelectCHRPage ( 2 ^ a12Inversion , _registers [ 1 ] ) ;
SelectCHRPage ( 4 ^ a12Inversion , _registers [ 2 ] ) ;
SelectCHRPage ( 5 ^ a12Inversion , _registers [ 3 ] ) ;
SelectCHRPage ( 6 ^ a12Inversion , _registers [ 4 ] ) ;
SelectCHRPage ( 7 ^ a12Inversion , _registers [ 5 ] ) ;
if ( _currentRegister & 0x20 ) {
SelectCHRPage ( 1 ^ a12Inversion , _registers [ 8 ] ) ;
SelectCHRPage ( 3 ^ a12Inversion , _registers [ 9 ] ) ;
} else {
SelectCHRPage ( 1 ^ a12Inversion , _registers [ 0 ] + 1 ) ;
SelectCHRPage ( 3 ^ a12Inversion , _registers [ 1 ] + 1 ) ;
}
}
2016-12-17 23:14:47 -05:00
void WriteRegister ( uint16_t addr , uint8_t value ) override
2016-01-23 20:02:20 -05:00
{
switch ( addr & 0xE001 ) {
case 0x8000 :
_currentRegister = value ;
break ;
case 0x8001 :
_registers [ _currentRegister & 0x0F ] = value ;
UpdateState ( ) ;
break ;
case 0xA000 :
SetMirroringType ( value & 0x01 ? MirroringType : : Horizontal : MirroringType : : Vertical ) ;
break ;
case 0xC000 :
_irqReloadValue = value ;
break ;
case 0xC001 :
2016-05-31 23:17:22 -04:00
if ( _irqCycleMode & & ( ( value & 0x01 ) = = 0x00 ) ) {
//"To be clear, after the write in the reg $C001, are needed more than four CPU clock cycles before the switch takes place, allowing another clock of irq running the reload." -FHorse
//Fixes Skull & Crossbones
_forceClock = true ;
}
2016-01-23 20:02:20 -05:00
_irqCycleMode = ( value & 0x01 ) = = 0x01 ;
2016-08-25 23:40:45 -04:00
if ( _irqCycleMode ) {
_cpuClockCounter = 0 ;
}
2016-01-23 20:02:20 -05:00
_needReload = true ;
break ;
case 0xE000 :
_irqEnabled = false ;
CPU : : ClearIRQSource ( IRQSource : : External ) ;
break ;
case 0xE001 :
_irqEnabled = true ;
break ;
}
}
public :
2016-12-17 23:14:47 -05:00
virtual void NotifyVRAMAddressChange ( uint16_t addr ) override
2016-01-23 20:02:20 -05:00
{
2016-05-31 23:17:22 -04:00
if ( ! _irqCycleMode ) {
2016-08-25 23:29:23 -04:00
if ( _a12Watcher . UpdateVramAddress ( addr ) = = A12StateChange : : Rise ) {
2016-08-25 23:40:45 -04:00
ClockIrqCounter ( Rambo1 : : PpuIrqDelay ) ;
2016-01-23 20:02:20 -05:00
}
}
}
} ;