mirror of https://github.com/stella-emu/stella.git
Reworked 'read from write port' emulation for carts with extended RAM.
The values written and returned in such a case are now more accurate, and are a combination of the previous databus value and randomization (the latter emulating the randomness of Z-state bits). This provides more accurate emulation than before, where zeros were used instead. In particular, types 3E and E7 are now working correctly for the first time. Thanks to Batari for suggestions in this area. Moved random number generation from Cartridge to System class. The Subversion build number is now shown in the AboutDialog box. Still TODO is add architecture information (i386, x86_64, etc). git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1895 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
25f483dc27
commit
3df721e0be
|
@ -50,7 +50,7 @@ void Cartridge3E::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 32768; ++i)
|
||||
myRam[i] = myRandGenerator.next();
|
||||
myRam[i] = mySystem->randGenerator().next();
|
||||
|
||||
// We'll map bank 0 into the first segment upon reset
|
||||
bank(0);
|
||||
|
@ -101,10 +101,19 @@ uInt8 Cartridge3E::peek(uInt16 address)
|
|||
{
|
||||
if(myCurrentBank < 256)
|
||||
return myImage[(address & 0x07FF) + (myCurrentBank << 11)];
|
||||
else if(address < 0x400) // Read from write port gives undefined values
|
||||
return myRandGenerator.next();
|
||||
else
|
||||
return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)];
|
||||
{
|
||||
if(address < 0x0400)
|
||||
return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)];
|
||||
else
|
||||
{
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -46,7 +46,7 @@ void Cartridge4A50::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 32768; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
mySliceLow = mySliceMiddle = mySliceHigh = 0;
|
||||
myIsRomLow = myIsRomMiddle = myIsRomHigh = true;
|
||||
|
|
|
@ -53,7 +53,7 @@ void CartridgeAR::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 6 * 1024; ++i)
|
||||
myImage[i] = myRandGenerator.next();
|
||||
myImage[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Initialize SC BIOS ROM
|
||||
initializeROM();
|
||||
|
@ -300,7 +300,7 @@ void CartridgeAR::initializeROM()
|
|||
|
||||
// The accumulator should contain a random value after exiting the
|
||||
// SC BIOS code - a value placed in offset 281 will be stored in A
|
||||
ourDummyROMCode[281] = myRandGenerator.next();
|
||||
ourDummyROMCode[281] = mySystem->randGenerator().next();
|
||||
|
||||
// Initialize ROM with illegal 6502 opcode that causes a real 6502 to jam
|
||||
for(uInt32 i = 0; i < 2048; ++i)
|
||||
|
|
|
@ -30,8 +30,6 @@ CartridgeCV::CartridgeCV(const uInt8* image, uInt32 size)
|
|||
myROM = new uInt8[mySize];
|
||||
memcpy(myROM, image, mySize);
|
||||
|
||||
reset();
|
||||
|
||||
// This cart contains 1024 bytes extended RAM @ 0x1000
|
||||
registerRamArea(0x1000, 1024, 0x00, 0x400);
|
||||
}
|
||||
|
@ -52,7 +50,7 @@ void CartridgeCV::reset()
|
|||
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 1024; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
}
|
||||
else if(mySize == 4096)
|
||||
{
|
||||
|
@ -110,13 +108,13 @@ void CartridgeCV::install(System& system)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 CartridgeCV::peek(uInt16 address)
|
||||
{
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if((address & 0x0FFF) < 0x0800) // Write port is at 0xF400 - 0xF800 (1024 bytes)
|
||||
{ // Read port is handled in ::install()
|
||||
if(myBankLocked) return 0;
|
||||
else return myRAM[address & 0x03FF] = 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address & 0x03FF] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -46,7 +46,7 @@ void CartridgeE7::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 2048; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Install some default banks for the RAM and first segment
|
||||
bankRAM(0);
|
||||
|
@ -104,11 +104,14 @@ uInt8 CartridgeE7::peek(uInt16 address)
|
|||
bankRAM(address & 0x0003);
|
||||
}
|
||||
|
||||
// NOTE: The following does not handle reading from RAM, however,
|
||||
// this function should never be called for RAM because of the
|
||||
// way page accessing has been setup
|
||||
if((bank() == 7) && (address < 0x400))
|
||||
return myRandGenerator.next(); // Read from write port gives undefined values
|
||||
if((address < 0x0400) && (bank() == 7))
|
||||
{
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myImage[(myCurrentSlice[address >> 11] << 11) + (address & 0x07FF)] = value;
|
||||
}
|
||||
else
|
||||
return myImage[(myCurrentSlice[address >> 11] << 11) + (address & 0x07FF)];
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ void CartridgeEFSC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 128; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Upon reset we switch to bank 1
|
||||
bank(1);
|
||||
|
@ -99,13 +99,13 @@ uInt8 CartridgeEFSC::peek(uInt16 address)
|
|||
if((address >= 0x0FE0) && (address <= 0x0FEF))
|
||||
bank(address - 0x0FE0);
|
||||
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
|
||||
{
|
||||
if(myBankLocked) return 0;
|
||||
else return myRAM[address] = 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address] = value;
|
||||
}
|
||||
else
|
||||
return myImage[(myCurrentBank << 12) + address];
|
||||
|
|
|
@ -42,7 +42,7 @@ void CartridgeF4SC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 128; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Upon reset we switch to bank 0
|
||||
bank(0);
|
||||
|
@ -99,13 +99,13 @@ uInt8 CartridgeF4SC::peek(uInt16 address)
|
|||
if((address >= 0x0FF4) && (address <= 0x0FFB))
|
||||
bank(address - 0x0FF4);
|
||||
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
|
||||
{
|
||||
if(myBankLocked) return 0;
|
||||
else return myRAM[address] = 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address] = value;
|
||||
}
|
||||
else
|
||||
return myImage[(myCurrentBank << 12) + address];
|
||||
|
|
|
@ -42,7 +42,7 @@ void CartridgeF6SC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 128; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Upon reset we switch to bank 0
|
||||
bank(0);
|
||||
|
@ -122,13 +122,13 @@ uInt8 CartridgeF6SC::peek(uInt16 address)
|
|||
break;
|
||||
}
|
||||
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
|
||||
{
|
||||
if(myBankLocked) return 0;
|
||||
else return myRAM[address] = 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address] = value;
|
||||
}
|
||||
else
|
||||
return myImage[(myCurrentBank << 12) + address];
|
||||
|
|
|
@ -42,7 +42,7 @@ void CartridgeF8SC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 128; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Upon reset we switch to bank 1
|
||||
bank(1);
|
||||
|
@ -112,13 +112,13 @@ uInt8 CartridgeF8SC::peek(uInt16 address)
|
|||
break;
|
||||
}
|
||||
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
|
||||
{
|
||||
if(myBankLocked) return 0;
|
||||
else return myRAM[address] = 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address] = value;
|
||||
}
|
||||
else
|
||||
return myImage[(myCurrentBank << 12) + address];
|
||||
|
|
|
@ -42,7 +42,7 @@ void CartridgeFASC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 256; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
|
||||
// Upon reset we switch to bank 2
|
||||
bank(2);
|
||||
|
@ -117,22 +117,16 @@ uInt8 CartridgeFASC::peek(uInt16 address)
|
|||
break;
|
||||
}
|
||||
|
||||
// Reading from the write port triggers an unwanted write
|
||||
// The value written to RAM is somewhat undefined, so we use 0
|
||||
// Thanks to Kroko of AtariAge for this advice and code idea
|
||||
if(address < 0x0100) // Write port is at 0xF000 - 0xF100 (256 bytes)
|
||||
{
|
||||
if(!myBankLocked)
|
||||
{
|
||||
return myRAM[address] = 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(myBankLocked) return value;
|
||||
else return myRAM[address] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return myImage[(myCurrentBank << 12) + address];
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "System.hxx"
|
||||
#include "CartMC.hxx"
|
||||
|
||||
// TODO - properly handle read from write port functionality
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeMC::CartridgeMC(const uInt8* image, uInt32 size)
|
||||
: mySlot3Locked(false)
|
||||
|
@ -46,7 +48,7 @@ void CartridgeMC::reset()
|
|||
{
|
||||
// Initialize RAM with random values
|
||||
for(uInt32 i = 0; i < 32768; ++i)
|
||||
myRAM[i] = myRandGenerator.next();
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
M6532::M6532(const Console& console)
|
||||
: myConsole(console)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -42,11 +41,11 @@ void M6532::reset()
|
|||
{
|
||||
// Randomize the 128 bytes of memory
|
||||
for(uInt32 t = 0; t < 128; ++t)
|
||||
myRAM[t] = myRandGenerator.next();
|
||||
myRAM[t] = mySystem->randGenerator().next();
|
||||
|
||||
// The timer absolutely cannot be initialized to zero; some games will
|
||||
// loop or hang (notably Solaris and H.E.R.O.)
|
||||
myTimer = (0xff - (myRandGenerator.next() % 0xfe)) << 10;
|
||||
myTimer = (0xff - (mySystem->randGenerator().next() % 0xfe)) << 10;
|
||||
myIntervalShift = 10;
|
||||
myCyclesWhenTimerSet = 0;
|
||||
myInterruptEnabled = false;
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "Console.hxx"
|
||||
#include "Random.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "Version.hxx"
|
||||
|
||||
#include "OSystem.hxx"
|
||||
|
||||
|
@ -114,7 +115,7 @@ OSystem::OSystem()
|
|||
ostringstream info;
|
||||
const SDL_version* ver = SDL_Linked_Version();
|
||||
|
||||
info << "Build " << "TODO" << ", using ";
|
||||
info << "Build " << STELLA_BUILD << ", using ";
|
||||
info << "SDL " << (int)ver->major << "." << (int)ver->minor << "." << (int)ver->patch << " ";
|
||||
info << "[" << " " << "]";
|
||||
myBuildInfo = info.str();
|
||||
|
|
|
@ -66,7 +66,6 @@ class Random
|
|||
// Indicates the next random number
|
||||
uInt32 myValue;
|
||||
|
||||
private:
|
||||
// Set the OSystem we're using
|
||||
static const OSystem* ourSystem;
|
||||
};
|
||||
|
|
|
@ -169,7 +169,9 @@ void TIA::reset()
|
|||
myDumpEnabled = false;
|
||||
myDumpDisabledCycle = 0;
|
||||
|
||||
myFloatTIAOutputPins = mySettings.getBool("tiafloat");
|
||||
// The mask indicates which pins should be driven high
|
||||
// If a pin is floating (the default), then its mask value is 0
|
||||
myOutputPinsMask = mySettings.getBool("tiafloat") ? 0x00 : 0x3F;
|
||||
|
||||
myFrameCounter = 0;
|
||||
myScanlineCountForLastFrame = 0;
|
||||
|
@ -1233,9 +1235,9 @@ uInt8 TIA::peek(uInt16 addr)
|
|||
}
|
||||
|
||||
// On certain CMOS EPROM chips the unused TIA pins on a read are not
|
||||
// floating but pulled high. Programmers might want to check their
|
||||
// games for compatibility, so we make this optional.
|
||||
value |= myFloatTIAOutputPins ? (mySystem->getDataBusState() & 0x3F) : 0x3F;
|
||||
// floating but pulled high. Programmers might want to check their
|
||||
// games for compatibility, so we make this optional.
|
||||
value |= ((mySystem->getDataBusState() | myOutputPinsMask) & 0x3F);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -563,7 +563,8 @@ class TIA : public Device
|
|||
bool myAllowHMOVEBlanks;
|
||||
|
||||
// Indicates if unused TIA pins are floating on a peek
|
||||
bool myFloatTIAOutputPins;
|
||||
// Otherwise, they're forced high
|
||||
uInt8 myOutputPinsMask;
|
||||
|
||||
// Bitmap of the objects that should be considered while drawing
|
||||
uInt8 myEnabledObjects;
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
Device::Device()
|
||||
: mySystem(0)
|
||||
{
|
||||
myRandGenerator.initSeed();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -35,6 +34,3 @@ void Device::systemCyclesReset()
|
|||
{
|
||||
// By default I do nothing when my system resets its cycle counter
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Random Device::myRandGenerator;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
class System;
|
||||
|
||||
#include "Random.hxx"
|
||||
#include "Serializable.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
|
@ -47,7 +46,11 @@ class Device : public Serializable
|
|||
|
||||
public:
|
||||
/**
|
||||
Reset device to its power-on state
|
||||
Reset device to its power-on state.
|
||||
|
||||
*DO NOT* call this method until the device has been attached to
|
||||
the System. In fact, it should never be necessary to call this
|
||||
method directly at all.
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
|
@ -108,10 +111,6 @@ class Device : public Serializable
|
|||
protected:
|
||||
/// Pointer to the system the device is installed in or the null pointer
|
||||
System* mySystem;
|
||||
|
||||
/// Many devices need a source of random numbers, usually for emulating
|
||||
/// unknown/undefined behaviour
|
||||
static class Random myRandGenerator;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,9 @@ System::System(uInt16 n, uInt16 m)
|
|||
// Make sure the arguments are reasonable
|
||||
assert((1 <= m) && (m <= n) && (n <= 16));
|
||||
|
||||
// Create a new random number generator
|
||||
myRandom = new Random();
|
||||
|
||||
// Allocate page table
|
||||
myPageAccessTable = new PageAccess[myNumberOfPages];
|
||||
|
||||
|
@ -72,6 +75,9 @@ System::~System()
|
|||
|
||||
// Free my page access table
|
||||
delete[] myPageAccessTable;
|
||||
|
||||
// Free the random number generator
|
||||
delete myRandom;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -28,6 +28,7 @@ class NullDevice;
|
|||
#include "bspf.hxx"
|
||||
#include "Device.hxx"
|
||||
#include "NullDev.hxx"
|
||||
#include "Random.hxx"
|
||||
#include "Serializable.hxx"
|
||||
|
||||
/**
|
||||
|
@ -138,6 +139,16 @@ class System : public Serializable
|
|||
return *myTIA;
|
||||
}
|
||||
|
||||
/**
|
||||
Answer the random generator attached to the system.
|
||||
|
||||
@return The random generator
|
||||
*/
|
||||
Random& randGenerator()
|
||||
{
|
||||
return *myRandom;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the null device associated with the system. Every system
|
||||
has a null device associated with it that's used by pages which
|
||||
|
@ -215,13 +226,35 @@ class System : public Serializable
|
|||
Get the current state of the data bus in the system. The current
|
||||
state is the last data that was accessed by the system.
|
||||
|
||||
@return the data bus state
|
||||
@return The data bus state
|
||||
*/
|
||||
inline uInt8 getDataBusState() const
|
||||
{
|
||||
return myDataBusState;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the current state of the data bus in the system, taking into
|
||||
account that certain bits are in Z-state (undriven). In those
|
||||
cases, the bits are floating, but will usually be the same as the
|
||||
last data bus value (the 'usually' is emulated by randomly driving
|
||||
certain bits high).
|
||||
|
||||
However, some CMOS EPROM chips always drive Z-state bits high.
|
||||
This is emulated by hmask, which specifies to push a specific
|
||||
Z-state bit high.
|
||||
|
||||
@param zmask The bits which are in Z-state
|
||||
@param hmask The bits which should always be driven high
|
||||
@return The data bus state
|
||||
*/
|
||||
inline uInt8 getDataBusState(uInt8 zmask, uInt8 hmask = 0x00)
|
||||
{
|
||||
// For the pins that are floating, randomly decide which are high or low
|
||||
// Otherwise, they're specifically driven high
|
||||
return (myDataBusState | (myRandom->next() | hmask)) & zmask;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the byte at the specified address. No masking of the
|
||||
address occurs before it's sent to the device mapped at
|
||||
|
@ -352,6 +385,10 @@ class System : public Serializable
|
|||
// TIA device attached to the system or the null pointer
|
||||
TIA* myTIA;
|
||||
|
||||
// Many devices need a source of random numbers, usually for emulating
|
||||
// unknown/undefined behaviour
|
||||
Random* myRandom;
|
||||
|
||||
// Number of system cycles executed since the last reset
|
||||
uInt32 myCycles;
|
||||
|
||||
|
|
Loading…
Reference in New Issue