2013-11-04 00:36:15 +00:00
using BizHawk.Common ;
namespace BizHawk
2012-10-30 22:14:31 +00:00
{
partial class Atari2600
{
class mDPC : MapperBase
{
2013-04-20 22:09:19 +00:00
private ulong totalCycles = 0 ;
private ulong elapsedCycles = 0 ;
private double FractionalClocks ;
private int bank_4k = 0 ;
private IntBuffer Counters = new IntBuffer ( 8 ) ;
private ByteBuffer Flags = new ByteBuffer ( 8 ) ;
private IntBuffer Tops = new IntBuffer ( 8 ) ;
private IntBuffer Bottoms = new IntBuffer ( 8 ) ;
private ByteBuffer DisplayBank_2k = new ByteBuffer ( 2048 ) ;
private byte RandomNumber = 0 ;
2012-10-30 23:13:49 +00:00
2013-04-20 22:09:19 +00:00
private bool [ ] MusicMode = new bool [ 3 ] ; //TOOD: savestates
2012-10-30 23:13:49 +00:00
2013-03-11 01:46:12 +00:00
public override byte PeekMemory ( ushort addr )
{
return base . PeekMemory ( addr ) ; //TODO
}
2013-04-20 22:09:19 +00:00
public override void ClockCpu ( )
{
totalCycles + + ;
}
2012-10-30 22:14:31 +00:00
public override byte ReadMemory ( ushort addr )
{
2013-04-20 22:09:19 +00:00
ClockRandomNumberGenerator ( ) ;
addr & = 0x0FFF ;
if ( addr < 0x0040 )
2012-10-30 23:13:49 +00:00
{
2013-04-20 22:09:19 +00:00
byte result = 0 ;
int index = addr & 0x07 ;
int function = ( addr > > 3 ) & 0x07 ;
// Update flag register for selected data fetcher
if ( ( Counters [ index ] & 0x00ff ) = = Tops [ index ] )
{
Flags [ index ] = 0xff ;
}
else if ( ( Counters [ index ] & 0x00ff ) = = Bottoms [ index ] )
2012-10-30 23:13:49 +00:00
{
2013-04-20 22:09:19 +00:00
Flags [ index ] = 0x00 ;
}
2012-10-30 23:13:49 +00:00
2013-04-20 22:09:19 +00:00
switch ( function )
{
default :
result = 0 ;
break ;
case 0x00 :
if ( index < 4 )
{
result = RandomNumber ;
}
else //it's a music read
{
byte [ ] MusicAmplitudes = {
0x00 , 0x04 , 0x05 , 0x09 , 0x06 , 0x0a , 0x0b , 0x0f
} ;
2012-10-30 23:13:49 +00:00
2013-04-20 22:09:19 +00:00
//// Update the music data fetchers (counter & flag)
UpdateMusicModeDataFetchers ( ) ;
byte i = 0 ;
if ( MusicMode [ 0 ] & & Flags [ 5 ] > 0 )
2012-10-30 23:13:49 +00:00
{
2013-04-20 22:09:19 +00:00
i | = 0x01 ;
2012-10-30 23:13:49 +00:00
}
2013-04-20 22:09:19 +00:00
if ( MusicMode [ 1 ] & & Flags [ 6 ] > 0 )
2012-10-30 23:13:49 +00:00
{
2013-04-20 22:09:19 +00:00
i | = 0x02 ;
}
if ( MusicMode [ 2 ] & & Flags [ 7 ] > 0 )
{
i | = 0x04 ;
2012-10-30 23:13:49 +00:00
}
2012-11-01 01:54:33 +00:00
2013-04-20 22:09:19 +00:00
result = MusicAmplitudes [ i ] ;
}
break ;
case 0x01 :
result = DisplayBank_2k [ 2047 - Counters [ index ] ] ;
break ;
case 0x02 :
result = DisplayBank_2k [ 2047 - ( Counters [ index ] & Flags [ index ] ) ] ;
break ;
case 0x07 :
result = Flags [ index ] ;
break ;
2012-10-30 23:13:49 +00:00
}
2013-04-20 22:09:19 +00:00
// Clock the selected data fetcher's counter if needed
if ( ( index < 5 ) | | ( ( index > = 5 ) & & ( ! MusicMode [ index - 5 ] ) ) )
2012-10-30 23:13:49 +00:00
{
2013-04-20 22:09:19 +00:00
Counters [ index ] = ( Counters [ index ] - 1 ) & 0x07ff ;
2012-10-30 23:13:49 +00:00
}
2013-04-20 22:09:19 +00:00
return result ;
2012-10-30 23:13:49 +00:00
}
else
{
Address ( addr ) ;
2013-04-20 22:09:19 +00:00
return core . rom [ ( bank_4k < < 12 ) + addr ] ;
2012-10-30 23:13:49 +00:00
}
}
public override void WriteMemory ( ushort addr , byte value )
{
2012-11-01 01:54:33 +00:00
addr & = 0x0FFF ;
// Clock the random number generator. This should be done for every
// cartridge access, however, we're only doing it for the DPC and
// hot-spot accesses to save time.
ClockRandomNumberGenerator ( ) ;
if ( ( addr > = 0x0040 ) & & ( addr < 0x0080 ) )
{
// Get the index of the data fetcher that's being accessed
int index = addr & 0x07 ;
int function = ( addr > > 3 ) & 0x07 ;
switch ( function )
{
case 0x00 : // DFx top count
Tops [ index ] = value ;
Flags [ index ] = 0x00 ;
break ;
case 0x01 : // DFx bottom count
Bottoms [ index ] = value ;
break ;
case 0x02 : // DFx counter low
if ( ( index > = 5 ) & & MusicMode [ index - 5 ] )
{
Counters [ index ] = ( Counters [ index ] & 0x0700 ) | Tops [ index ] ; // Data fetcher is in music mode so its low counter value should be loaded from the top register not the poked value
}
else
{
// Data fetcher is either not a music mode data fetcher or it
// isn't in music mode so it's low counter value should be loaded
// with the poked value
Counters [ index ] = ( Counters [ index ] & 0x0700 ) | value ;
}
break ;
case 0x03 : // DFx counter high
Counters [ index ] = ( ( value & 0x07 ) < < 8 ) | ( Counters [ index ] & 0x00ff ) ;
// Execute special code for music mode data fetchers
if ( index > = 5 )
{
MusicMode [ index - 5 ] = ( value & 0x10 ) > 0 ? true : false ;
// NOTE: We are not handling the clock source input for
// the music mode data fetchers. We're going to assume
// they always use the OSC input.
}
break ;
case 0x06 : // Random Number Generator Reset
RandomNumber = 1 ;
break ;
}
}
else
{
Address ( addr ) ;
}
return ;
2012-10-30 23:13:49 +00:00
}
private void Address ( ushort addr )
{
2013-04-20 22:09:19 +00:00
if ( addr = = 0x0FF8 )
2012-10-30 23:13:49 +00:00
{
bank_4k = 0 ;
}
2013-04-20 22:09:19 +00:00
else if ( addr = = 0x0FF9 )
2012-10-30 23:13:49 +00:00
{
bank_4k = 1 ;
}
}
public override void Dispose ( )
{
DisplayBank_2k . Dispose ( ) ;
Counters . Dispose ( ) ;
Flags . Dispose ( ) ;
base . Dispose ( ) ;
}
public override void SyncState ( Serializer ser )
{
2012-11-01 01:54:33 +00:00
//TODO
2012-10-30 23:13:49 +00:00
base . SyncState ( ser ) ;
ser . Sync ( "bank_4k" , ref bank_4k ) ;
ser . Sync ( "DisplayBank_2k" , ref DisplayBank_2k ) ;
ser . Sync ( "Flags" , ref Flags ) ;
ser . Sync ( "Counters" , ref Counters ) ;
ser . Sync ( "RandomNumber" , ref RandomNumber ) ;
}
2013-04-20 22:09:19 +00:00
2012-10-30 23:13:49 +00:00
private void UpdateMusicModeDataFetchers ( )
{
2012-11-01 01:54:33 +00:00
// Calculate the number of cycles since the last update
//int cycles = mySystem->cycles() - mySystemCycles;
//mySystemCycles = mySystem->cycles();
2013-04-20 22:09:19 +00:00
ulong cycles = totalCycles - elapsedCycles ;
elapsedCycles = totalCycles ;
2012-11-01 01:54:33 +00:00
// Calculate the number of DPC OSC clocks since the last update
double clocks = ( ( 20000.0 * cycles ) / 1193191.66666667 ) + FractionalClocks ;
int wholeClocks = ( int ) clocks ;
FractionalClocks = clocks - ( double ) wholeClocks ;
if ( wholeClocks < = 0 )
{
return ;
}
// Let's update counters and flags of the music mode data fetchers
for ( int x = 5 ; x < = 7 ; + + x )
{
// Update only if the data fetcher is in music mode
if ( MusicMode [ x - 5 ] )
{
int top = Tops [ x ] + 1 ;
int newLow = Counters [ x ] & 0x00ff ;
if ( Tops [ x ] ! = 0 )
{
newLow - = ( wholeClocks % top ) ;
if ( newLow < 0 )
{
newLow + = top ;
}
}
else
{
newLow = 0 ;
}
// Update flag register for this data fetcher
if ( newLow < = Bottoms [ x ] )
{
Flags [ x ] = 0x00 ;
}
else if ( newLow < = Tops [ x ] )
{
Flags [ x ] = 0xff ;
}
Counters [ x ] = ( Counters [ x ] & 0x0700 ) | newLow ;
}
}
}
private void ClockRandomNumberGenerator ( )
{
// Table for computing the input bit of the random number generator's
// shift register (it's the NOT of the EOR of four bits)
byte [ ] f = {
1 , 0 , 0 , 1 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 1 , 0 , 0 , 1
} ;
// Using bits 7, 5, 4, & 3 of the shift register compute the input
// bit for the shift register
byte bit = f [ ( ( RandomNumber > > 3 ) & 0x07 ) |
( ( RandomNumber & 0x80 ) > 0 ? 0x08 : 0x00 ) ] ;
// Update the shift register
RandomNumber = ( byte ) ( RandomNumber < < 1 | bit ) ;
2012-10-30 22:14:31 +00:00
}
}
}
}