More work on Chetiry bankswitch scheme. Preliminary version can

now load and play test ROMs, complete with emulation of timing delays
due to slow accesses on real Harmony hardware.  Still TODO is the tunes,
DPC+ stuff, which is stubbed out at this point.

Fixed bug in EFSC bankswitching; state files didn't contain extended RAM
information.

Cleaned up the Serializer API, resulting in slightly faster operation
and smaller state files.  Because of this, the state file format
has changed for this release (old state files will no longer work).


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2487 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-05-20 14:23:48 +00:00
parent f14d47f042
commit 01c9bc30c3
41 changed files with 765 additions and 602 deletions

View File

@ -44,6 +44,13 @@
their high-byte truncated, and system equates (TIA and I/O registers) their high-byte truncated, and system equates (TIA and I/O registers)
are now properly marked as such. are now properly marked as such.
* Fixed bug in EFSC state saving; the Superchip RAM wasn't actually
being loaded and saved to state files.
* Improved speed of loading and saving state files, as well as slightly
reducing their size. Because of this, old state files will not work
with this release.
* Replaced commandline argument 'uselauncher' with 'exitlauncher'. The * Replaced commandline argument 'uselauncher' with 'exitlauncher'. The
new option specifies the behaviour of the ROM launcher when exiting new option specifies the behaviour of the ROM launcher when exiting
a ROM (always exit to launcher, or only when the launcher was actually a ROM (always exit to launcher, or only when the launcher was actually
@ -1948,7 +1955,7 @@
* A new Windows port has been created, with the GUI based on StellaX. * A new Windows port has been created, with the GUI based on StellaX.
This is the first new release for Windows since Stella 1.2. This is the first new release for Windows since Stella 1.2.
(software mode is not yet optimized; OpenGL mode works much better) (software mode is not yet optimized; OpenGL mode works much better)
* A new Mac OSX port has been created by Mark Grebe. This is the first new * A new Mac OSX port has been created by Mark Grebe. This is the first new
release for Mac OSX since Stella 1.2. release for Mac OSX since Stella 1.2.

View File

@ -397,12 +397,12 @@ bool SoundSDL::save(Serializer& out) const
reg6 = myTIASound.get(0x1a); reg6 = myTIASound.get(0x1a);
} }
out.putByte((char)reg1); out.putByte(reg1);
out.putByte((char)reg2); out.putByte(reg2);
out.putByte((char)reg3); out.putByte(reg3);
out.putByte((char)reg4); out.putByte(reg4);
out.putByte((char)reg5); out.putByte(reg5);
out.putByte((char)reg6); out.putByte(reg6);
out.putInt(myLastRegisterSetCycle); out.putInt(myLastRegisterSetCycle);
} }
@ -425,12 +425,12 @@ bool SoundSDL::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt8 reg1 = (uInt8) in.getByte(), uInt8 reg1 = in.getByte(),
reg2 = (uInt8) in.getByte(), reg2 = in.getByte(),
reg3 = (uInt8) in.getByte(), reg3 = in.getByte(),
reg4 = (uInt8) in.getByte(), reg4 = in.getByte(),
reg5 = (uInt8) in.getByte(), reg5 = in.getByte(),
reg6 = (uInt8) in.getByte(); reg6 = in.getByte();
myLastRegisterSetCycle = (Int32) in.getInt(); myLastRegisterSetCycle = (Int32) in.getInt();

View File

@ -156,7 +156,7 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
else if(type == "CM") else if(type == "CM")
cartridge = new CartridgeCM(image, size, settings); cartridge = new CartridgeCM(image, size, settings);
else if(type == "CTY") else if(type == "CTY")
cartridge = new CartridgeCTY(image, size, settings); cartridge = new CartridgeCTY(image, size, osystem);
else if(type == "CV") else if(type == "CV")
cartridge = new CartridgeCV(image, size, settings); cartridge = new CartridgeCV(image, size, settings);
else if(type == "DPC") else if(type == "DPC")

View File

@ -198,7 +198,7 @@ bool Cartridge0840::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -217,7 +217,7 @@ bool Cartridge0840::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16)in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -58,9 +58,9 @@ void Cartridge3E::reset()
// Initialize RAM // Initialize RAM
if(mySettings.getBool("ramrandom")) if(mySettings.getBool("ramrandom"))
for(uInt32 i = 0; i < 32768; ++i) for(uInt32 i = 0; i < 32768; ++i)
myRam[i] = mySystem->randGenerator().next(); myRAM[i] = mySystem->randGenerator().next();
else else
memset(myRam, 0, 32768); memset(myRAM, 0, 32768);
// We'll map the startup bank into the first segment upon reset // We'll map the startup bank into the first segment upon reset
bank(myStartBank); bank(myStartBank);
@ -111,7 +111,7 @@ uInt8 Cartridge3E::peek(uInt16 address)
else else
{ {
if(address < 0x0400) if(address < 0x0400)
return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)]; return myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)];
else else
{ {
// Reading from the write port triggers an unwanted write // Reading from the write port triggers an unwanted write
@ -122,7 +122,7 @@ uInt8 Cartridge3E::peek(uInt16 address)
else else
{ {
triggerReadFromWritePort(peekAddress); triggerReadFromWritePort(peekAddress);
return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value; return myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value;
} }
} }
} }
@ -207,7 +207,7 @@ bool Cartridge3E::bank(uInt16 bank)
// Map read-port RAM image into the system // Map read-port RAM image into the system
for(address = 0x1000; address < 0x1400; address += (1 << shift)) for(address = 0x1000; address < 0x1400; address += (1 << shift))
{ {
access.directPeekBase = &myRam[offset + (address & 0x03FF)]; access.directPeekBase = &myRAM[offset + (address & 0x03FF)];
access.codeAccessBase = &myCodeAccessBase[mySize + offset + (address & 0x03FF)]; access.codeAccessBase = &myCodeAccessBase[mySize + offset + (address & 0x03FF)];
mySystem->setPageAccess(address >> shift, access); mySystem->setPageAccess(address >> shift, access);
} }
@ -218,7 +218,7 @@ bool Cartridge3E::bank(uInt16 bank)
// Map write-port RAM image into the system // Map write-port RAM image into the system
for(address = 0x1400; address < 0x1800; address += (1 << shift)) for(address = 0x1400; address < 0x1800; address += (1 << shift))
{ {
access.directPokeBase = &myRam[offset + (address & 0x03FF)]; access.directPokeBase = &myRAM[offset + (address & 0x03FF)];
access.codeAccessBase = &myCodeAccessBase[mySize + offset + (address & 0x03FF)]; access.codeAccessBase = &myCodeAccessBase[mySize + offset + (address & 0x03FF)];
mySystem->setPageAccess(address >> shift, access); mySystem->setPageAccess(address >> shift, access);
} }
@ -253,7 +253,7 @@ bool Cartridge3E::patch(uInt16 address, uInt8 value)
if(myCurrentBank < 256) if(myCurrentBank < 256)
myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value; myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value;
else else
myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value; myRAM[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value;
} }
else else
myImage[(address & 0x07FF) + mySize - 2048] = value; myImage[(address & 0x07FF) + mySize - 2048] = value;
@ -274,12 +274,8 @@ bool Cartridge3E::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 32768);
// Output RAM
out.putInt(32768);
for(uInt32 addr = 0; addr < 32768; ++addr)
out.putByte((char)myRam[addr]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -298,12 +294,8 @@ bool Cartridge3E::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 32768);
// Input RAM
uInt32 limit = (uInt32) in.getInt();
for(uInt32 addr = 0; addr < limit; ++addr)
myRam[addr] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -174,7 +174,7 @@ class Cartridge3E : public Cartridge
uInt8* myImage; uInt8* myImage;
// RAM contents. For now every ROM gets all 32K of potential RAM // RAM contents. For now every ROM gets all 32K of potential RAM
uInt8 myRam[32 * 1024]; uInt8 myRAM[32 * 1024];
// Size of the ROM image // Size of the ROM image
uInt32 mySize; uInt32 mySize;

View File

@ -192,7 +192,7 @@ bool Cartridge3F::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -211,7 +211,7 @@ bool Cartridge3F::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -452,14 +452,12 @@ bool Cartridge4A50::save(Serializer& out) const
out.putString(name()); out.putString(name());
// The 32K bytes of RAM // The 32K bytes of RAM
out.putInt(32768); out.putByteArray(myRAM, 32768);
for(uInt32 i = 0; i < 32768; ++i)
out.putByte((char)myRAM[i]);
// Index pointers // Index pointers
out.putInt(mySliceLow); out.putShort(mySliceLow);
out.putInt(mySliceMiddle); out.putShort(mySliceMiddle);
out.putInt(mySliceHigh); out.putShort(mySliceHigh);
// Whether index pointers are for ROM or RAM // Whether index pointers are for ROM or RAM
out.putBool(myIsRomLow); out.putBool(myIsRomLow);
@ -468,7 +466,7 @@ bool Cartridge4A50::save(Serializer& out) const
// Last address and data values // Last address and data values
out.putByte(myLastData); out.putByte(myLastData);
out.putInt(myLastAddress); out.putShort(myLastAddress);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -487,14 +485,12 @@ bool Cartridge4A50::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 limit = (uInt32) in.getInt(); in.getByteArray(myRAM, 32768);
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
// Index pointers // Index pointers
mySliceLow = (uInt16) in.getInt(); mySliceLow = in.getShort();
mySliceMiddle = (uInt16) in.getInt(); mySliceMiddle = in.getShort();
mySliceHigh = (uInt16) in.getInt(); mySliceHigh = in.getShort();
// Whether index pointers are for ROM or RAM // Whether index pointers are for ROM or RAM
myIsRomLow = in.getBool(); myIsRomLow = in.getBool();
@ -502,8 +498,8 @@ bool Cartridge4A50::load(Serializer& in)
myIsRomHigh = in.getBool(); myIsRomHigh = in.getBool();
// Last address and data values // Last address and data values
myLastData = (uInt8) in.getByte(); myLastData = in.getByte();
myLastAddress = (uInt16) in.getInt(); myLastAddress = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -467,33 +467,23 @@ bool CartridgeAR::save(Serializer& out) const
{ {
try try
{ {
uInt32 i;
out.putString(name()); out.putString(name());
// Indicates the offest within the image for the corresponding bank // Indicates the offest within the image for the corresponding bank
out.putInt(2); out.putIntArray(myImageOffset, 2);
for(i = 0; i < 2; ++i)
out.putInt(myImageOffset[i]);
// The 6K of RAM and 2K of ROM contained in the Supercharger // The 6K of RAM and 2K of ROM contained in the Supercharger
out.putInt(8192); out.putByteArray(myImage, 8192);
for(i = 0; i < 8192; ++i)
out.putByte((char)myImage[i]);
// The 256 byte header for the current 8448 byte load // The 256 byte header for the current 8448 byte load
out.putInt(256); out.putByteArray(myHeader, 256);
for(i = 0; i < 256; ++i)
out.putByte((char)myHeader[i]);
// All of the 8448 byte loads associated with the game // All of the 8448 byte loads associated with the game
// Note that the size of this array is myNumberOfLoadImages * 8448 // Note that the size of this array is myNumberOfLoadImages * 8448
out.putInt(myNumberOfLoadImages * 8448); out.putByteArray(myLoadImages, myNumberOfLoadImages * 8448);
for(i = 0; i < (uInt32) myNumberOfLoadImages * 8448; ++i)
out.putInt(myLoadImages[i]);
// Indicates how many 8448 loads there are // Indicates how many 8448 loads there are
out.putByte((char)myNumberOfLoadImages); out.putByte(myNumberOfLoadImages);
// Indicates if the RAM is write enabled // Indicates if the RAM is write enabled
out.putBool(myWriteEnabled); out.putBool(myWriteEnabled);
@ -505,7 +495,7 @@ bool CartridgeAR::save(Serializer& out) const
out.putInt(myPowerRomCycle); out.putInt(myPowerRomCycle);
// Data hold register used for writing // Data hold register used for writing
out.putByte((char)myDataHoldRegister); out.putByte(myDataHoldRegister);
// Indicates number of distinct accesses when data hold register was set // Indicates number of distinct accesses when data hold register was set
out.putInt(myNumberOfDistinctAccesses); out.putInt(myNumberOfDistinctAccesses);
@ -530,31 +520,21 @@ bool CartridgeAR::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 i, limit;
// Indicates the offest within the image for the corresponding bank // Indicates the offest within the image for the corresponding bank
limit = (uInt32) in.getInt(); in.getIntArray(myImageOffset, 2);
for(i = 0; i < limit; ++i)
myImageOffset[i] = (uInt32) in.getInt();
// The 6K of RAM and 2K of ROM contained in the Supercharger // The 6K of RAM and 2K of ROM contained in the Supercharger
limit = (uInt32) in.getInt(); in.getByteArray(myImage, 8192);
for(i = 0; i < limit; ++i)
myImage[i] = (uInt8) in.getByte();
// The 256 byte header for the current 8448 byte load // The 256 byte header for the current 8448 byte load
limit = (uInt32) in.getInt(); in.getByteArray(myHeader, 256);
for(i = 0; i < limit; ++i)
myHeader[i] = (uInt8) in.getByte();
// All of the 8448 byte loads associated with the game // All of the 8448 byte loads associated with the game
// Note that the size of this array is myNumberOfLoadImages * 8448 // Note that the size of this array is myNumberOfLoadImages * 8448
limit = (uInt32) in.getInt(); in.getByteArray(myLoadImages, myNumberOfLoadImages * 8448);
for(i = 0; i < limit; ++i)
myLoadImages[i] = (uInt8) in.getInt();
// Indicates how many 8448 loads there are // Indicates how many 8448 loads there are
myNumberOfLoadImages = (uInt8) in.getByte(); myNumberOfLoadImages = in.getByte();
// Indicates if the RAM is write enabled // Indicates if the RAM is write enabled
myWriteEnabled = in.getBool(); myWriteEnabled = in.getBool();
@ -566,10 +546,10 @@ bool CartridgeAR::load(Serializer& in)
myPowerRomCycle = (Int32) in.getInt(); myPowerRomCycle = (Int32) in.getInt();
// Data hold register used for writing // Data hold register used for writing
myDataHoldRegister = (uInt8) in.getByte(); myDataHoldRegister = in.getByte();
// Indicates number of distinct accesses when data hold register was set // Indicates number of distinct accesses when data hold register was set
myNumberOfDistinctAccesses = (uInt32) in.getInt(); myNumberOfDistinctAccesses = in.getInt();
// Indicates if a write is pending or not // Indicates if a write is pending or not
myWritePending = in.getBool(); myWritePending = in.getBool();

View File

@ -202,15 +202,10 @@ bool CartridgeCM::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByte(myRamState); out.putByte(myRamState);
out.putByte(myColumn); out.putByte(myColumn);
out.putByteArray(myRAM, 2048);
// The 2048 bytes of RAM
out.putInt(2048);
for(uInt32 i = 0; i < 2048; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -229,14 +224,10 @@ bool CartridgeCM::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
myRamState = (uInt8) in.getByte(); myRamState = in.getByte();
myColumn = (uInt8) in.getByte(); myColumn = in.getByte();
in.getByteArray(myRAM, 2048);
// The 2048 bytes of RAM
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -20,13 +20,18 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include "OSystem.hxx"
#include "Serializer.hxx"
#include "System.hxx" #include "System.hxx"
#include "CartCTYTunes.hxx" #include "CartCTYTunes.hxx"
#include "CartCTY.hxx" #include "CartCTY.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeCTY::CartridgeCTY(const uInt8* image, uInt32 size, const Settings& settings) CartridgeCTY::CartridgeCTY(const uInt8* image, uInt32 size, const OSystem& osystem)
: Cartridge(settings) : Cartridge(osystem.settings()),
myOSystem(osystem),
myOperationType(0),
myRamAccessTimeout(0)
{ {
// Copy the ROM image into my buffer // Copy the ROM image into my buffer
memcpy(myImage, image, BSPF_min(32768u, size)); memcpy(myImage, image, BSPF_min(32768u, size));
@ -35,7 +40,7 @@ CartridgeCTY::CartridgeCTY(const uInt8* image, uInt32 size, const Settings& sett
// This cart contains 64 bytes extended RAM @ 0x1000 // This cart contains 64 bytes extended RAM @ 0x1000
registerRamArea(0x1000, 64, 0x40, 0x00); registerRamArea(0x1000, 64, 0x40, 0x00);
// Remember startup bank // Remember startup bank (not bank 0, since that's ARM code)
myStartBank = 1; myStartBank = 1;
} }
@ -49,10 +54,12 @@ void CartridgeCTY::reset()
{ {
// Initialize RAM // Initialize RAM
if(mySettings.getBool("ramrandom")) if(mySettings.getBool("ramrandom"))
for(uInt32 i = 0; i < 256; ++i) for(uInt32 i = 0; i < 64; ++i)
myScoreRAM[i] = mySystem->randGenerator().next(); myRAM[i] = mySystem->randGenerator().next();
else else
memset(myScoreRAM, 0, 256); memset(myRAM, 0, 64);
myRAM[0] = myRAM[1] = myRAM[2] = myRAM[3] = 0xFF;
// Upon reset we switch to the startup bank // Upon reset we switch to the startup bank
bank(myStartBank); bank(myStartBank);
@ -62,32 +69,16 @@ void CartridgeCTY::reset()
void CartridgeCTY::install(System& system) void CartridgeCTY::install(System& system)
{ {
mySystem = &system; mySystem = &system;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask(); uInt16 mask = mySystem->pageMask();
uInt16 shift = mySystem->pageShift();
// Make sure the system we're being installed in has a page size that'll work // Make sure the system we're being installed in has a page size that'll work
assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0)); assert(((0x1000 & mask) == 0) && ((0x1080 & mask) == 0));
// Map all RAM accesses to call peek and poke
System::PageAccess access(0, 0, 0, this, System::PA_READ); System::PageAccess access(0, 0, 0, this, System::PA_READ);
for(uInt32 i = 0x1000; i < 0x1080; i += (1 << shift))
// Set the page accessing method for the RAM writing pages mySystem->setPageAccess(i >> shift, access);
access.type = System::PA_WRITE;
for(uInt32 j = 0x1000; j < 0x1080; j += (1 << shift))
{
access.directPokeBase = &myScoreRAM[j & 0x007F];
access.codeAccessBase = &myCodeAccessBase[j & 0x007F];
mySystem->setPageAccess(j >> shift, access);
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.type = System::PA_READ;
for(uInt32 k = 0x1080; k < 0x1100; k += (1 << shift))
{
access.directPeekBase = &myScoreRAM[k & 0x007F];
access.codeAccessBase = &myCodeAccessBase[0x80 + (k & 0x007F)];
mySystem->setPageAccess(k >> shift, access);
}
// Install pages for the startup bank // Install pages for the startup bank
bank(myStartBank); bank(myStartBank);
@ -98,12 +89,14 @@ uInt8 CartridgeCTY::peek(uInt16 address)
{ {
uInt16 peekAddress = address; uInt16 peekAddress = address;
address &= 0x0FFF; address &= 0x0FFF;
uInt8 peekValue = myImage[(myCurrentBank << 12) + address];
// Switch banks if necessary // In debugger/bank-locked mode, we ignore all hotspots and in general
if((address >= 0x0FF4) && (address <= 0x0FFB)) // anything that can change the internal state of the cart
bank(address - 0x0FF4); if(bankLocked())
return peekValue;
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes) if(address < 0x0040) // Write port is at $1000 - $103F (64 bytes)
{ {
// Reading from the write port triggers an unwanted write // Reading from the write port triggers an unwanted write
uInt8 value = mySystem->getDataBusState(0xFF); uInt8 value = mySystem->getDataBusState(0xFF);
@ -113,28 +106,102 @@ uInt8 CartridgeCTY::peek(uInt16 address)
else else
{ {
triggerReadFromWritePort(peekAddress); triggerReadFromWritePort(peekAddress);
return myScoreRAM[address] = value; return myRAM[address] = value;
} }
} }
else if(address < 0x0080) // Read port is at $1040 - $107F (64 bytes)
// NOTE: This does not handle accessing RAM, however, this function {
// should never be called for RAM because of the way page accessing address -= 0x40;
// has been setup switch(address) // FIXME for actual return values (0-3)
return myImage[(myCurrentBank << 12) + address]; {
case 0x00: // Error code after operation
return myRAM[0];
case 0x01: // Get next Random Number (8-bit LFSR)
cerr << "Get next Random Number (8-bit LFSR)\n";
return 0xFF;
case 0x02: // Get Tune position (low byte)
cerr << "Get Tune position (low byte)\n";
return 0x00;
case 0x03: // Get Tune position (high byte)
cerr << "Get Tune position (high byte)\n";
return 0x00;
default:
return myRAM[address];
}
}
else // Check hotspots
{
switch(address)
{
case 0x0FF4:
// Bank 0 is ARM code and not actually accessed
return ramReadWrite();
case 0x0FF5:
case 0x0FF6:
case 0x0FF7:
case 0x0FF8:
case 0x0FF9:
case 0x0FFA:
case 0x0FFB:
// Banks 1 through 7
bank(address - 0x0FF4);
break;
default:
break;
}
return myImage[(myCurrentBank << 12) + address];
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCTY::poke(uInt16 address, uInt8) bool CartridgeCTY::poke(uInt16 address, uInt8 value)
{ {
address &= 0x0FFF; address &= 0x0FFF;
// Switch banks if necessary //cerr << "POKE: address=" << HEX4 << address << ", value=" << HEX2 << value << endl;
if((address >= 0x0FF4) && (address <= 0x0FFB)) if(address < 0x0040) // Write port is at $1000 - $103F (64 bytes)
bank(address - 0x0FF4); {
switch(address) // FIXME for functionality
// NOTE: This does not handle accessing RAM, however, this function {
// should never be called for RAM because of the way page accessing case 0x00: // Operation type for $1FF4
// has been setup myOperationType = value;
break;
case 0x01: // Set Random seed value
cerr << "Set random seed value = " << HEX2 << (int)value << endl;
break;
case 0x02: // Reset fetcher to beginning of tune
cerr << "Reset fetcher to beginning of tune\n";
break;
case 0x03: // Advance fetcher to next tune position
cerr << "Advance fetcher to next tune position\n";
break;
default:
myRAM[address] = value;
break;
}
}
else // Check hotspots
{
switch(address)
{
case 0x0FF4:
// Bank 0 is ARM code and not actually accessed
ramReadWrite();
break;
case 0x0FF5:
case 0x0FF6:
case 0x0FF7:
case 0x0FF8:
case 0x0FF9:
case 0x0FFA:
case 0x0FFB:
// Banks 1 through 7
bank(address - 0x0FF4);
break;
default:
break;
}
}
return false; return false;
} }
@ -159,7 +226,7 @@ bool CartridgeCTY::bank(uInt16 bank)
} }
// Setup the page access methods for the current bank // Setup the page access methods for the current bank
for(uInt32 address = 0x1100; address < (0x1FF4U & ~mask); for(uInt32 address = 0x1080; address < (0x1FF4U & ~mask);
address += (1 << shift)) address += (1 << shift))
{ {
access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; access.directPeekBase = &myImage[offset + (address & 0x0FFF)];
@ -186,12 +253,12 @@ bool CartridgeCTY::patch(uInt16 address, uInt8 value)
{ {
address &= 0x0FFF; address &= 0x0FFF;
if(address < 0x0100) if(address < 0x0080)
{ {
// Normally, a write to the read port won't do anything // Normally, a write to the read port won't do anything
// However, the patch command is special in that ignores such // However, the patch command is special in that ignores such
// cart restrictions // cart restrictions
myScoreRAM[address & 0x007F] = value; myRAM[address & 0x003F] = value;
} }
else else
myImage[(myCurrentBank << 12) + address] = value; myImage[(myCurrentBank << 12) + address] = value;
@ -212,12 +279,9 @@ bool CartridgeCTY::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByte(myOperationType);
// The 256 bytes of RAM out.putByteArray(myRAM, 64);
out.putInt(256);
for(uInt32 i = 0; i < 256; ++i)
out.putByte((char)myScoreRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -236,11 +300,9 @@ bool CartridgeCTY::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
myOperationType = in.getByte();
uInt32 limit = (uInt32) in.getInt(); in.getByteArray(myRAM, 64);
for(uInt32 i = 0; i < limit; ++i)
myScoreRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -253,3 +315,170 @@ bool CartridgeCTY::load(Serializer& in)
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::setRomName(const string& name)
{
myEEPROMFile = myOSystem.eepromDir() + name + "_eeprom.dat";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeCTY::ramReadWrite()
{
/* The following algorithm implements accessing Harmony cart EEPROM
1. Wait for an access to hotspot location $1FF4 (return 1 in bit 6
while busy).
2. Determine operation from myOperationType.
3. Save or load relevant EEPROM memory to/from a file.
4. Set byte 0 of RAM+ memory to zero to indicate success (will
always happen in emulation).
5. Return 0 (in bit 6) on the next access to $1FF4, if enough time has
passed to complete the operation on a real system (0.5 s for read,
1 s for write).
*/
if(bankLocked()) return 0xff;
// First access sets the timer
if(myRamAccessTimeout == 0)
{
// Opcode and value in form of XXXXYYYY (from myOperationType), where:
// XXXX = index and YYYY = operation
uInt8 index = myOperationType >> 4;
switch(myOperationType & 0xf)
{
case 1: // Load tune (index = tune)
if(index < 7)
{
// Add 0.5 s delay for read
myRamAccessTimeout = myOSystem.getTicks() + 500000;
loadTune(index);
}
break;
case 2: // Load score table (index = table)
if(index < 4)
{
// Add 0.5 s delay for read
myRamAccessTimeout = myOSystem.getTicks() + 500000;
loadScore(index);
}
break;
case 3: // Save score table (index = table)
if(index < 4)
{
// Add 1 s delay for write
myRamAccessTimeout = myOSystem.getTicks() + 1000000;
saveScore(index);
}
break;
case 4: // Wipe all score tables
// Add 1 s delay for write
myRamAccessTimeout = myOSystem.getTicks() + 1000000;
wipeAllScores();
break;
}
// Bit 6 is 1, busy
return myImage[(myCurrentBank << 12) + 0xFF4] | 0x40;
}
else
{
// Have we reached the timeout value yet?
if(myOSystem.getTicks() >= myRamAccessTimeout)
{
myRamAccessTimeout = 0; // Turn off timer
myRAM[0] = 0; // Successful operation
// Bit 6 is 0, ready/success
return myImage[(myCurrentBank << 12) + 0xFF4] & ~0x40;
}
else
// Bit 6 is 1, busy
return myImage[(myCurrentBank << 12) + 0xFF4] | 0x40;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::loadTune(uInt8 index)
{
cerr << "load tune " << (int)index << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::loadScore(uInt8 index)
{
Serializer serializer(myEEPROMFile, true);
if(serializer.isValid())
{
uInt8 scoreRAM[256];
try
{
serializer.getByteArray(scoreRAM, 256);
}
catch(const char* msg)
{
memset(scoreRAM, 0, 256);
}
// Grab 64B slice @ given index
memcpy(myRAM, scoreRAM + (index << 6), 64);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::saveScore(uInt8 index)
{
Serializer serializer(myEEPROMFile);
if(serializer.isValid())
{
// Load score RAM
uInt8 scoreRAM[256];
try
{
serializer.getByteArray(scoreRAM, 256);
}
catch(const char* msg)
{
memset(scoreRAM, 0, 256);
}
// Add 64B RAM to score table @ given index
memcpy(scoreRAM + (index << 6), myRAM, 64);
// Save score RAM
serializer.reset();
try
{
serializer.putByteArray(scoreRAM, 256);
}
catch(const char* msg)
{
// Maybe add logging here that save failed?
cerr << name() << ": ERROR saving score table " << (int)index << endl;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::wipeAllScores()
{
Serializer serializer(myEEPROMFile);
if(serializer.isValid())
{
// Erase score RAM
uInt8 scoreRAM[256];
memset(scoreRAM, 0, 256);
try
{
serializer.putByteArray(scoreRAM, 256);
}
catch(const char* msg)
{
// Maybe add logging here that save failed?
cerr << name() << ": ERROR wiping score tables" << endl;
}
}
}

View File

@ -39,9 +39,9 @@ class CartridgeCTY : public Cartridge
@param image Pointer to the ROM image @param image Pointer to the ROM image
@param size The size of the ROM image @param size The size of the ROM image
@param settings A reference to the various settings (read-only) @param osystem A reference to the OSystem currently in use
*/ */
CartridgeCTY(const uInt8* image, uInt32 size, const Settings& settings); CartridgeCTY(const uInt8* image, uInt32 size, const OSystem& osystem);
/** /**
Destructor Destructor
@ -119,6 +119,14 @@ class CartridgeCTY : public Cartridge
*/ */
string name() const { return "CartridgeCTY"; } string name() const { return "CartridgeCTY"; }
/**
Informs the cartridge about the name of the ROM file used when
creating this cart.
@param name The properties file name of the ROM
*/
void setRomName(const string& name);
public: public:
/** /**
Get the byte at the specified address. Get the byte at the specified address.
@ -137,18 +145,52 @@ class CartridgeCTY : public Cartridge
bool poke(uInt16 address, uInt8 value); bool poke(uInt16 address, uInt8 value);
private: private:
/**
Either load or save internal RAM to Harmony EEPROM (represented by
a file in emulation).
@return The value at $FF4 with bit 6 set or cleared (depending on
whether the RAM access was busy or successful)
*/
uInt8 ramReadWrite();
/**
Actions initiated by accessing $FF4 hotspot.
*/
void loadTune(uInt8 index);
void loadScore(uInt8 index);
void saveScore(uInt8 index);
void wipeAllScores();
private:
// OSsytem currently in use
const OSystem& myOSystem;
// Indicates which bank is currently active // Indicates which bank is currently active
uInt16 myCurrentBank; uInt16 myCurrentBank;
// The 32K ROM image of the cartridge // The 32K ROM image of the cartridge
uInt8 myImage[32768]; uInt8 myImage[32768];
// The 256 bytes of score-table RAM // The 64 bytes of RAM accessible at $1000 - $1080
uInt8 myScoreRAM[256]; uInt8 myRAM[64];
uInt8* myScorePtr;
// Operation type (written to $1000, used by hotspot $1FF4)
uInt8 myOperationType;
// The 8K Harmony RAM (used for tune data) // The 8K Harmony RAM (used for tune data)
// Data is accessed from Harmony EEPROM
uInt8 myTuneRAM[8192]; uInt8 myTuneRAM[8192];
// The time after which the first request of a load/save operation
// will actually be completed
// Due to flash RAM constraints, a read/write isn't instantaneous,
// so we need to emulate the delay as well
uInt64 myRamAccessTimeout;
// Full pathname of the file to use when emulating load/save
// of internal RAM to Harmony cart flash
string myEEPROMFile;
}; };
#endif #endif

View File

@ -204,11 +204,7 @@ bool CartridgeCV::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putByteArray(myRAM, 1024);
// Output RAM
out.putInt(1024);
for(uInt32 addr = 0; addr < 1024; ++addr)
out.putByte((char)myRAM[addr]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -227,10 +223,7 @@ bool CartridgeCV::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
// Input RAM in.getByteArray(myRAM, 1024);
uInt32 limit = (uInt32) in.getInt();
for(uInt32 addr = 0; addr < limit; ++addr)
myRAM[addr] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -477,40 +477,29 @@ bool CartridgeDPC::save(Serializer& out) const
{ {
try try
{ {
uInt32 i;
out.putString(name()); out.putString(name());
// Indicates which bank is currently active // Indicates which bank is currently active
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
// The top registers for the data fetchers // The top registers for the data fetchers
out.putInt(8); out.putByteArray(myTops, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myTops[i]);
// The bottom registers for the data fetchers // The bottom registers for the data fetchers
out.putInt(8); out.putByteArray(myBottoms, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myBottoms[i]);
// The counter registers for the data fetchers // The counter registers for the data fetchers
out.putInt(8); out.putShortArray(myCounters, 8);
for(i = 0; i < 8; ++i)
out.putInt(myCounters[i]);
// The flag registers for the data fetchers // The flag registers for the data fetchers
out.putInt(8); out.putByteArray(myFlags, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myFlags[i]);
// The music mode flags for the data fetchers // The music mode flags for the data fetchers
out.putInt(3); for(int i = 0; i < 3; ++i)
for(i = 0; i < 3; ++i)
out.putBool(myMusicMode[i]); out.putBool(myMusicMode[i]);
// The random number generator register // The random number generator register
out.putByte((char)myRandomNumber); out.putByte(myRandomNumber);
out.putInt(mySystemCycles); out.putInt(mySystemCycles);
out.putInt((uInt32)(myFractionalClocks * 100000000.0)); out.putInt((uInt32)(myFractionalClocks * 100000000.0));
@ -532,41 +521,30 @@ bool CartridgeDPC::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 i, limit;
// Indicates which bank is currently active // Indicates which bank is currently active
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
// The top registers for the data fetchers // The top registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myTops, 8);
for(i = 0; i < limit; ++i)
myTops[i] = (uInt8) in.getByte();
// The bottom registers for the data fetchers // The bottom registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myBottoms, 8);
for(i = 0; i < limit; ++i)
myBottoms[i] = (uInt8) in.getByte();
// The counter registers for the data fetchers // The counter registers for the data fetchers
limit = (uInt32) in.getInt(); in.getShortArray(myCounters, 8);
for(i = 0; i < limit; ++i)
myCounters[i] = (uInt16) in.getInt();
// The flag registers for the data fetchers // The flag registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myFlags, 8);
for(i = 0; i < limit; ++i)
myFlags[i] = (uInt8) in.getByte();
// The music mode flags for the data fetchers // The music mode flags for the data fetchers
limit = (uInt32) in.getInt(); for(int i = 0; i < 3; ++i)
for(i = 0; i < limit; ++i)
myMusicMode[i] = in.getBool(); myMusicMode[i] = in.getBool();
// The random number generator register // The random number generator register
myRandomNumber = (uInt8) in.getByte(); myRandomNumber = in.getByte();
// Get system cycles and fractional clocks // Get system cycles and fractional clocks
mySystemCycles = in.getInt(); mySystemCycles = (Int32)in.getInt();
myFractionalClocks = (double)in.getInt() / 100000000.0; myFractionalClocks = (double)in.getInt() / 100000000.0;
} }
catch(const char* msg) catch(const char* msg)

View File

@ -658,61 +658,41 @@ bool CartridgeDPCPlus::save(Serializer& out) const
{ {
try try
{ {
uInt32 i;
out.putString(name()); out.putString(name());
// Indicates which bank is currently active // Indicates which bank is currently active
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
// The top registers for the data fetchers // The top registers for the data fetchers
out.putInt(8); out.putByteArray(myTops, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myTops[i]);
// The bottom registers for the data fetchers // The bottom registers for the data fetchers
out.putInt(8); out.putByteArray(myBottoms, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myBottoms[i]);
// The counter registers for the data fetchers // The counter registers for the data fetchers
out.putInt(8); out.putShortArray(myCounters, 8);
for(i = 0; i < 8; ++i)
out.putInt(myCounters[i]);
// The counter registers for the fractional data fetchers // The counter registers for the fractional data fetchers
out.putInt(8); out.putIntArray(myFractionalCounters, 8);
for(i = 0; i < 8; ++i)
out.putInt(myFractionalCounters[i]);
// The fractional registers for the data fetchers // The fractional registers for the data fetchers
out.putInt(8); out.putByteArray(myFractionalIncrements, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myFractionalIncrements[i]);
// The Fast Fetcher Enabled flag // The Fast Fetcher Enabled flag
out.putBool(myFastFetch); out.putBool(myFastFetch);
out.putBool(myLDAimmediate); out.putBool(myLDAimmediate);
// Control Byte to update // Control Byte to update
out.putInt(8); out.putByteArray(myParameter, 8);
for(i = 0; i < 8; ++i)
out.putByte((char)myParameter[i]);
// The music counters // The music counters
out.putInt(3); out.putIntArray(myMusicCounters, 3);
for(i = 0; i < 3; ++i)
out.putInt(myMusicCounters[i]);
// The music frequencies // The music frequencies
out.putInt(3); out.putIntArray(myMusicFrequencies, 3);
for(i = 0; i < 3; ++i)
out.putInt(myMusicFrequencies[i]);
// The music waveforms // The music waveforms
out.putInt(3); out.putShortArray(myMusicWaveforms, 3);
for(i = 0; i < 3; ++i)
out.putInt(myMusicWaveforms[i]);
// The random number generator register // The random number generator register
out.putInt(myRandomNumber); out.putInt(myRandomNumber);
@ -737,65 +717,45 @@ bool CartridgeDPCPlus::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 i, limit;
// Indicates which bank is currently active // Indicates which bank is currently active
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
// The top registers for the data fetchers // The top registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myTops, 8);
for(i = 0; i < limit; ++i)
myTops[i] = (uInt8) in.getByte();
// The bottom registers for the data fetchers // The bottom registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myBottoms, 8);
for(i = 0; i < limit; ++i)
myBottoms[i] = (uInt8) in.getByte();
// The counter registers for the data fetchers // The counter registers for the data fetchers
limit = (uInt32) in.getInt(); in.getShortArray(myCounters, 8);
for(i = 0; i < limit; ++i)
myCounters[i] = (uInt16) in.getInt();
// The counter registers for the fractional data fetchers // The counter registers for the fractional data fetchers
limit = (uInt32) in.getInt(); in.getIntArray(myFractionalCounters, 8);
for(i = 0; i < limit; ++i)
myFractionalCounters[i] = (uInt32) in.getInt();
// The fractional registers for the data fetchers // The fractional registers for the data fetchers
limit = (uInt32) in.getInt(); in.getByteArray(myFractionalIncrements, 8);
for(i = 0; i < limit; ++i)
myFractionalIncrements[i] = (uInt8) in.getByte();
// The Fast Fetcher Enabled flag // The Fast Fetcher Enabled flag
myFastFetch = in.getBool(); myFastFetch = in.getBool();
myLDAimmediate = in.getBool(); myLDAimmediate = in.getBool();
// Control Byte to update // Control Byte to update
limit = (uInt32) in.getInt(); in.getByteArray(myParameter, 8);
for(i = 0; i < limit; ++i)
myParameter[i] = (uInt8) in.getByte();
// The music mode counters for the data fetchers // The music mode counters for the data fetchers
limit = (uInt32) in.getInt(); in.getIntArray(myMusicCounters, 3);
for(i = 0; i < limit; ++i)
myMusicCounters[i] = (uInt32) in.getInt();
// The music mode frequency addends for the data fetchers // The music mode frequency addends for the data fetchers
limit = (uInt32) in.getInt(); in.getIntArray(myMusicFrequencies, 3);
for(i = 0; i < limit; ++i)
myMusicFrequencies[i] = (uInt32) in.getInt();
// The music waveforms // The music waveforms
limit = (uInt32) in.getInt(); in.getShortArray(myMusicWaveforms, 3);
for(i = 0; i < limit; ++i)
myMusicWaveforms[i] = (uInt16) in.getInt();
// The random number generator register // The random number generator register
myRandomNumber = (uInt32) in.getInt(); myRandomNumber = in.getInt();
// Get system cycles and fractional clocks // Get system cycles and fractional clocks
mySystemCycles = in.getInt(); mySystemCycles = (Int32)in.getInt();
myFractionalClocks = (double)in.getInt() / 100000000.0; myFractionalClocks = (double)in.getInt() / 100000000.0;
} }
catch(const char* msg) catch(const char* msg)

View File

@ -235,10 +235,7 @@ bool CartridgeE0::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putShortArray(myCurrentSlice, 4);
out.putInt(4);
for(uInt32 i = 0; i < 4; ++i)
out.putInt(myCurrentSlice[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -257,9 +254,7 @@ bool CartridgeE0::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 limit = (uInt32) in.getInt(); in.getShortArray(myCurrentSlice, 4);
for(uInt32 i = 0; i < limit; ++i)
myCurrentSlice[i] = (uInt16) in.getInt();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -167,7 +167,7 @@ bool CartridgeE7::poke(uInt16 address, uInt8)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeE7::bankRAM(uInt16 bank) void CartridgeE7::bankRAM(uInt16 bank)
{ {
if(bankLocked()) return; if(bankLocked()) return;
// Remember what bank we're in // Remember what bank we're in
@ -300,20 +300,10 @@ bool CartridgeE7::save(Serializer& out) const
{ {
try try
{ {
uInt32 i;
out.putString(name()); out.putString(name());
out.putShortArray(myCurrentSlice, 2);
out.putInt(2); out.putShort(myCurrentRAM);
for(i = 0; i < 2; ++i) out.putByteArray(myRAM, 2048);
out.putInt(myCurrentSlice[i]);
out.putInt(myCurrentRAM);
// The 2048 bytes of RAM
out.putInt(2048);
for(i = 0; i < 2048; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -332,18 +322,9 @@ bool CartridgeE7::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
uInt32 i, limit; in.getShortArray(myCurrentSlice, 2);
myCurrentRAM = in.getShort();
limit = (uInt32) in.getInt(); in.getByteArray(myRAM, 2048);
for(i = 0; i < limit; ++i)
myCurrentSlice[i] = (uInt16) in.getInt();
myCurrentRAM = (uInt16) in.getInt();
// The 2048 bytes of RAM
limit = (uInt32) in.getInt();
for(i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -146,7 +146,7 @@ bool CartridgeEF::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -165,7 +165,7 @@ bool CartridgeEF::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -208,7 +208,8 @@ bool CartridgeEFSC::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 128);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -227,7 +228,8 @@ bool CartridgeEFSC::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 128);
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -160,7 +160,7 @@ bool CartridgeF0::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -179,7 +179,7 @@ bool CartridgeF0::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -151,7 +151,7 @@ bool CartridgeF4::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -170,7 +170,7 @@ bool CartridgeF4::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16)in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -211,12 +211,8 @@ bool CartridgeF4SC::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 128);
// The 128 bytes of RAM
out.putInt(128);
for(uInt32 i = 0; i < 128; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -235,11 +231,8 @@ bool CartridgeF4SC::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 128);
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -191,7 +191,7 @@ bool CartridgeF6::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -210,7 +210,7 @@ bool CartridgeF6::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -254,13 +254,8 @@ bool CartridgeF6SC::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 128);
// The 128 bytes of RAM
out.putInt(128);
for(uInt32 i = 0; i < 128; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -279,12 +274,8 @@ bool CartridgeF6SC::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 128);
// The 128 bytes of RAM
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -178,7 +178,7 @@ bool CartridgeF8::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -197,7 +197,7 @@ bool CartridgeF8::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -234,12 +234,8 @@ bool CartridgeF8SC::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 128);
// The 128 bytes of RAM
out.putInt(128);
for(uInt32 i = 0; i < 128; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -258,11 +254,8 @@ bool CartridgeF8SC::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 128);
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -244,12 +244,8 @@ bool CartridgeFA::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 256);
// The 256 bytes of RAM
out.putInt(256);
for(uInt32 i = 0; i < 256; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -268,11 +264,8 @@ bool CartridgeFA::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 256);
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -307,12 +307,8 @@ bool CartridgeFA2::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
out.putByteArray(myRAM, 256);
// The 256 bytes of RAM
out.putInt(256);
for(uInt32 i = 0; i < 256; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -331,11 +327,8 @@ bool CartridgeFA2::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16) in.getInt(); myCurrentBank = in.getShort();
in.getByteArray(myRAM, 256);
uInt32 limit = (uInt32) in.getInt();
for(uInt32 i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -391,8 +384,7 @@ uInt8 CartridgeFA2::ramReadWrite()
{ {
try try
{ {
for(uInt32 i = 0; i < 256; ++i) serializer.getByteArray(myRAM, 256);
myRAM[i] = (uInt8) serializer.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -404,8 +396,7 @@ uInt8 CartridgeFA2::ramReadWrite()
{ {
try try
{ {
for(uInt32 i = 0; i < 256; ++i) serializer.putByteArray(myRAM, 256);
serializer.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -164,8 +164,8 @@ bool CartridgeFE::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myLastAddress1); out.putShort(myLastAddress1);
out.putInt(myLastAddress2); out.putShort(myLastAddress2);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -184,8 +184,8 @@ bool CartridgeFE::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myLastAddress1 = (uInt16)in.getInt(); myLastAddress1 = in.getShort();
myLastAddress2 = (uInt16)in.getInt(); myLastAddress2 = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -249,18 +249,13 @@ bool CartridgeMC::save(Serializer& out) const
{ {
try try
{ {
uInt32 i;
out.putString(name()); out.putString(name());
// The currentBlock array // The currentBlock array
out.putInt(4); out.putByteArray(myCurrentBlock, 4);
for(i = 0; i < 4; ++i)
out.putByte((char)myCurrentBlock[i]);
// The 32K of RAM // The 32K of RAM
out.putInt(32 * 1024); out.putByteArray(myRAM, 32 * 1024);
for(i = 0; i < 32 * 1024; ++i)
out.putByte((char)myRAM[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -276,20 +271,14 @@ bool CartridgeMC::load(Serializer& in)
{ {
try try
{ {
uInt32 i, limit;
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
// The currentBlock array // The currentBlock array
limit = (uInt32) in.getInt(); in.getByteArray(myCurrentBlock, 4);
for(i = 0; i < limit; ++i)
myCurrentBlock[i] = (uInt8) in.getByte();
// The 32K of RAM // The 32K of RAM
limit = (uInt32) in.getInt(); in.getByteArray(myRAM, 32 * 1024);
for(i = 0; i < limit; ++i)
myRAM[i] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -178,7 +178,7 @@ bool CartridgeSB::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -197,7 +197,7 @@ bool CartridgeSB::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16)in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -186,7 +186,7 @@ bool CartridgeUA::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -205,7 +205,7 @@ bool CartridgeUA::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16)in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -170,7 +170,7 @@ bool CartridgeX07::save(Serializer& out) const
try try
{ {
out.putString(name()); out.putString(name());
out.putInt(myCurrentBank); out.putShort(myCurrentBank);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -189,7 +189,7 @@ bool CartridgeX07::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCurrentBank = (uInt16)in.getInt(); myCurrentBank = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -376,34 +376,34 @@ bool M6502::save(Serializer& out) const
{ {
out.putString(CPU); out.putString(CPU);
out.putByte((char)A); // Accumulator out.putByte(A); // Accumulator
out.putByte((char)X); // X index register out.putByte(X); // X index register
out.putByte((char)Y); // Y index register out.putByte(Y); // Y index register
out.putByte((char)SP); // Stack Pointer out.putByte(SP); // Stack Pointer
out.putByte((char)IR); // Instruction register out.putByte(IR); // Instruction register
out.putInt(PC); // Program Counter out.putShort(PC); // Program Counter
out.putBool(N); // N flag for processor status register out.putBool(N); // N flag for processor status register
out.putBool(V); // V flag for processor status register out.putBool(V); // V flag for processor status register
out.putBool(B); // B flag for processor status register out.putBool(B); // B flag for processor status register
out.putBool(D); // D flag for processor status register out.putBool(D); // D flag for processor status register
out.putBool(I); // I flag for processor status register out.putBool(I); // I flag for processor status register
out.putBool(notZ); // Z flag complement for processor status register out.putBool(notZ); // Z flag complement for processor status register
out.putBool(C); // C flag for processor status register out.putBool(C); // C flag for processor status register
out.putByte((char)myExecutionStatus); out.putByte(myExecutionStatus);
// Indicates the number of distinct memory accesses // Indicates the number of distinct memory accesses
out.putInt(myNumberOfDistinctAccesses); out.putInt(myNumberOfDistinctAccesses);
// Indicates the last address(es) which was accessed // Indicates the last address(es) which was accessed
out.putInt(myLastAddress); out.putShort(myLastAddress);
out.putInt(myLastPeekAddress); out.putShort(myLastPeekAddress);
out.putInt(myLastPokeAddress); out.putShort(myLastPokeAddress);
out.putInt(myLastSrcAddressS); out.putShort(myLastSrcAddressS);
out.putInt(myLastSrcAddressA); out.putShort(myLastSrcAddressA);
out.putInt(myLastSrcAddressX); out.putShort(myLastSrcAddressX);
out.putInt(myLastSrcAddressY); out.putShort(myLastSrcAddressY);
out.putInt(myDataAddressForPoke); out.putShort(myDataAddressForPoke);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -424,34 +424,34 @@ bool M6502::load(Serializer& in)
if(in.getString() != CPU) if(in.getString() != CPU)
return false; return false;
A = (uInt8) in.getByte(); // Accumulator A = in.getByte(); // Accumulator
X = (uInt8) in.getByte(); // X index register X = in.getByte(); // X index register
Y = (uInt8) in.getByte(); // Y index register Y = in.getByte(); // Y index register
SP = (uInt8) in.getByte(); // Stack Pointer SP = in.getByte(); // Stack Pointer
IR = (uInt8) in.getByte(); // Instruction register IR = in.getByte(); // Instruction register
PC = (uInt16) in.getInt(); // Program Counter PC = in.getShort(); // Program Counter
N = in.getBool(); // N flag for processor status register N = in.getBool(); // N flag for processor status register
V = in.getBool(); // V flag for processor status register V = in.getBool(); // V flag for processor status register
B = in.getBool(); // B flag for processor status register B = in.getBool(); // B flag for processor status register
D = in.getBool(); // D flag for processor status register D = in.getBool(); // D flag for processor status register
I = in.getBool(); // I flag for processor status register I = in.getBool(); // I flag for processor status register
notZ = in.getBool(); // Z flag complement for processor status register notZ = in.getBool(); // Z flag complement for processor status register
C = in.getBool(); // C flag for processor status register C = in.getBool(); // C flag for processor status register
myExecutionStatus = (uInt8) in.getByte(); myExecutionStatus = in.getByte();
// Indicates the number of distinct memory accesses // Indicates the number of distinct memory accesses
myNumberOfDistinctAccesses = (uInt32) in.getInt(); myNumberOfDistinctAccesses = in.getInt();
// Indicates the last address(es) which was accessed // Indicates the last address(es) which was accessed
myLastAddress = (uInt16) in.getInt(); myLastAddress = in.getShort();
myLastPeekAddress = (uInt16) in.getInt(); myLastPeekAddress = in.getShort();
myLastPokeAddress = (uInt16) in.getInt(); myLastPokeAddress = in.getShort();
myLastSrcAddressS = (uInt16) in.getInt(); myLastSrcAddressS = in.getShort();
myLastSrcAddressA = (uInt16) in.getInt(); myLastSrcAddressA = in.getShort();
myLastSrcAddressX = (uInt16) in.getInt(); myLastSrcAddressX = in.getShort();
myLastSrcAddressY = (uInt16) in.getInt(); myLastSrcAddressY = in.getShort();
myDataAddressForPoke = (uInt16) in.getInt(); myDataAddressForPoke = in.getShort();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -301,9 +301,7 @@ bool M6532::save(Serializer& out) const
out.putString(name()); out.putString(name());
// Output the RAM // Output the RAM
out.putInt(128); out.putByteArray(myRAM, 128);
for(uInt32 t = 0; t < 128; ++t)
out.putByte((char)myRAM[t]);
out.putInt(myTimer); out.putInt(myTimer);
out.putInt(myIntervalShift); out.putInt(myIntervalShift);
@ -311,14 +309,11 @@ bool M6532::save(Serializer& out) const
out.putBool(myInterruptEnabled); out.putBool(myInterruptEnabled);
out.putBool(myInterruptTriggered); out.putBool(myInterruptTriggered);
out.putByte((char)myDDRA); out.putByte(myDDRA);
out.putByte((char)myDDRB); out.putByte(myDDRB);
out.putByte((char)myOutA); out.putByte(myOutA);
out.putByte((char)myOutB); out.putByte(myOutB);
out.putByte((char)myOutTimer[0]); out.putByteArray(myOutTimer, 4);
out.putByte((char)myOutTimer[1]);
out.putByte((char)myOutTimer[2]);
out.putByte((char)myOutTimer[3]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -338,24 +333,19 @@ bool M6532::load(Serializer& in)
return false; return false;
// Input the RAM // Input the RAM
uInt32 limit = (uInt32) in.getInt(); in.getByteArray(myRAM, 128);
for(uInt32 t = 0; t < limit; ++t)
myRAM[t] = (uInt8) in.getByte();
myTimer = (uInt32) in.getInt(); myTimer = in.getInt();
myIntervalShift = (uInt32) in.getInt(); myIntervalShift = in.getInt();
myCyclesWhenTimerSet = (uInt32) in.getInt(); myCyclesWhenTimerSet = in.getInt();
myInterruptEnabled = in.getBool(); myInterruptEnabled = in.getBool();
myInterruptTriggered = in.getBool(); myInterruptTriggered = in.getBool();
myDDRA = (uInt8) in.getByte(); myDDRA = in.getByte();
myDDRB = (uInt8) in.getByte(); myDDRB = in.getByte();
myOutA = (uInt8) in.getByte(); myOutA = in.getByte();
myOutB = (uInt8) in.getByte(); myOutB = in.getByte();
myOutTimer[0] = (uInt8) in.getByte(); in.getByteArray(myOutTimer, 4);
myOutTimer[1] = (uInt8) in.getByte();
myOutTimer[2] = (uInt8) in.getByte();
myOutTimer[3] = (uInt8) in.getByte();
} }
catch(const char* msg) catch(const char* msg)
{ {

View File

@ -37,6 +37,7 @@ Serializer::Serializer(const string& filename, bool readonly)
if(str && str->is_open()) if(str && str->is_open())
{ {
myStream = str; myStream = str;
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
reset(); reset();
} }
else else
@ -59,6 +60,7 @@ Serializer::Serializer(const string& filename, bool readonly)
if(str && str->is_open()) if(str && str->is_open())
{ {
myStream = str; myStream = str;
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
reset(); reset();
} }
else else
@ -77,6 +79,7 @@ Serializer::Serializer(void)
// the stream before it is used for the first time // the stream before it is used for the first time
if(myStream) if(myStream)
{ {
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
putBool(true); putBool(true);
reset(); reset();
} }
@ -104,16 +107,14 @@ bool Serializer::isValid(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::reset(void) void Serializer::reset(void)
{ {
myStream->clear();
myStream->seekg(ios_base::beg); myStream->seekg(ios_base::beg);
myStream->seekp(ios_base::beg); myStream->seekp(ios_base::beg);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
char Serializer::getByte(void) uInt8 Serializer::getByte(void)
{ {
if(myStream->eof())
throw "Serializer::getByte() end of file";
char buf; char buf;
myStream->read(&buf, 1); myStream->read(&buf, 1);
@ -121,20 +122,41 @@ char Serializer::getByte(void)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Serializer::getInt(void) void Serializer::getByteArray(uInt8* array, uInt32 size)
{ {
if(myStream->eof()) myStream->read((char*)array, (streamsize)size);
throw "Serializer::getInt() end of file"; }
int val = 0; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unsigned char buf[4]; uInt16 Serializer::getShort(void)
myStream->read((char*)buf, 4); {
for(int i = 0; i < 4; ++i) uInt16 val = 0;
val += (int)(buf[i]) << (i<<3); myStream->read((char*)&val, sizeof(uInt16));
return val; return val;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::getShortArray(uInt16* array, uInt32 size)
{
myStream->read((char*)array, sizeof(uInt16)*size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Serializer::getInt(void)
{
uInt32 val = 0;
myStream->read((char*)&val, sizeof(uInt32));
return val;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::getIntArray(uInt32* array, uInt32 size)
{
myStream->read((char*)array, sizeof(uInt32)*size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Serializer::getString(void) string Serializer::getString(void)
{ {
@ -143,44 +165,49 @@ string Serializer::getString(void)
str.resize((string::size_type)len); str.resize((string::size_type)len);
myStream->read(&str[0], (streamsize)len); myStream->read(&str[0], (streamsize)len);
if(myStream->bad())
throw "Serializer::getString() file read failed";
return str; return str;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Serializer::getBool(void) bool Serializer::getBool(void)
{ {
char b = getByte(); return getByte() == (char)TruePattern;
if(b == (char)TruePattern)
return true;
else if(b == (char)FalsePattern)
return false;
else
throw "Serializer::getBool() data corruption";
return false; // to stop compiler from complaining
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putByte(char value) void Serializer::putByte(uInt8 value)
{ {
myStream->write(&value, 1); myStream->write((char*)&value, 1);
if(myStream->bad())
throw "Serializer::putByte() file write failed";
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putInt(int value) void Serializer::putByteArray(const uInt8* array, uInt32 size)
{ {
unsigned char buf[4]; myStream->write((char*)array, (streamsize)size);
for(int i = 0; i < 4; ++i) }
buf[i] = (value >> (i<<3)) & 0xff;
myStream->write((char*)buf, 4); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
if(myStream->bad()) void Serializer::putShort(uInt16 value)
throw "Serializer::putInt() file write failed"; {
myStream->write((char*)&value, sizeof(uInt16));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putShortArray(const uInt16* array, uInt32 size)
{
myStream->write((char*)array, sizeof(uInt16)*size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putInt(uInt32 value)
{
myStream->write((char*)&value, sizeof(uInt32));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putIntArray(const uInt32* array, uInt32 size)
{
myStream->write((char*)array, sizeof(uInt32)*size);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -28,11 +28,12 @@
read from/written to a binary stream in a system-independent way. The read from/written to a binary stream in a system-independent way. The
stream can be either an actual file, or an in-memory structure. stream can be either an actual file, or an in-memory structure.
Bytes are written as characters, integers are written as 4 characters Bytes are written as characters, shorts as 2 characters (16-bits),
(32-bit), strings are written as characters prepended by the length of the integers as 4 characters (32-bits), strings are written as characters
string, boolean values are written using a special character pattern. prepended by the length of the string, boolean values are written using
a special character pattern.
All bytes and ints should be cast to their appropriate data type upon All bytes, shorts and ints should be cast to their appropriate data type upon
method return. method return.
@author Stephen Anthony @author Stephen Anthony
@ -73,18 +74,50 @@ class Serializer
void reset(void); void reset(void);
/** /**
Reads a byte value (8-bit) from the current input stream. Reads a byte value (unsigned 8-bit) from the current input stream.
@result The char value which has been read from the stream. @result The byte value which has been read from the stream.
*/ */
char getByte(void); uInt8 getByte(void);
/** /**
Reads an int value (32-bit) from the current input stream. Reads a byte array (unsigned 8-bit) from the current input stream.
@param array The location to store the bytes read
@param size The size of the array (number of bytes to read)
*/
void getByteArray(uInt8* array, uInt32 size);
/**
Reads a short value (unsigned 16-bit) from the current input stream.
@result The short value which has been read from the stream.
*/
uInt16 getShort(void);
/**
Reads a short array (unsigned 16-bit) from the current input stream.
@param array The location to store the shorts read
@param size The size of the array (number of shorts to read)
*/
void getShortArray(uInt16* array, uInt32 size);
/**
Reads an int value (unsigned 32-bit) from the current input stream.
@result The int value which has been read from the stream. @result The int value which has been read from the stream.
*/ */
int getInt(void); uInt32 getInt(void);
/**
Reads an integer array (unsigned 32-bit) from the current input stream.
@param array The location to store the integers read
@param size The size of the array (number of integers to read)
*/
void getIntArray(uInt32* array, uInt32 size);
/** /**
Reads a string from the current input stream. Reads a string from the current input stream.
@ -101,18 +134,49 @@ class Serializer
bool getBool(void); bool getBool(void);
/** /**
Writes an byte value (8-bit) to the current output stream. Writes an byte value (unsigned 8-bit) to the current output stream.
@param value The byte value to write to the output stream. @param value The byte value to write to the output stream.
*/ */
void putByte(char value); void putByte(uInt8 value);
/** /**
Writes an int value (32-bit) to the current output stream. Writes a byte array (unsigned 8-bit) to the current output stream.
@param array The bytes to write
@param size The size of the array (number of bytes to write)
*/
void putByteArray(const uInt8* array, uInt32 size);
/**
Writes a short value (unsigned 16-bit) to the current output stream.
@param value The short value to write to the output stream.
*/
void putShort(uInt16 value);
/**
Writes a short array (unsigned 16-bit) to the current output stream.
@param array The short to write
@param size The size of the array (number of shorts to write)
*/
void putShortArray(const uInt16* array, uInt32 size);
/**
Writes an int value (unsigned 32-bit) to the current output stream.
@param value The int value to write to the output stream. @param value The int value to write to the output stream.
*/ */
void putInt(int value); void putInt(uInt32 value);
/**
Writes an integer array (unsigned 32-bit) to the current output stream.
@param array The integers to write
@param size The size of the array (number of integers to write)
*/
void putIntArray(const uInt32* array, uInt32 size);
/** /**
Writes a string to the current output stream. Writes a string to the current output stream.

View File

@ -30,7 +30,7 @@
#include "StateManager.hxx" #include "StateManager.hxx"
#define STATE_HEADER "03050500state" #define STATE_HEADER "03070000state"
#define MOVIE_HEADER "03030000movie" #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -113,7 +113,7 @@ bool Switches::save(Serializer& out) const
{ {
try try
{ {
out.putByte((char)mySwitches); out.putByte(mySwitches);
} }
catch(...) catch(...)
{ {
@ -128,7 +128,7 @@ bool Switches::load(Serializer& in)
{ {
try try
{ {
mySwitches = (uInt8) in.getByte(); mySwitches = in.getByte();
} }
catch(...) catch(...)
{ {

View File

@ -338,8 +338,8 @@ bool System::load(Serializer& in)
if(in.getString() != name()) if(in.getString() != name())
return false; return false;
myCycles = (uInt32) in.getInt(); myCycles = in.getInt();
myDataBusState = (uInt8) in.getByte(); myDataBusState = in.getByte();
// Next, load state for the CPU // Next, load state for the CPU
if(!myM6502->load(in)) if(!myM6502->load(in))

View File

@ -280,58 +280,52 @@ bool TIA::save(Serializer& out) const
out.putInt(myScanlineCountForLastFrame); out.putInt(myScanlineCountForLastFrame);
out.putInt(myVSYNCFinishClock); out.putInt(myVSYNCFinishClock);
out.putByte((char)myEnabledObjects); out.putByte(myEnabledObjects);
out.putByte((char)myDisabledObjects); out.putByte(myDisabledObjects);
out.putByte((char)myVSYNC); out.putByte(myVSYNC);
out.putByte((char)myVBLANK); out.putByte(myVBLANK);
out.putByte((char)myNUSIZ0); out.putByte(myNUSIZ0);
out.putByte((char)myNUSIZ1); out.putByte(myNUSIZ1);
out.putInt(myColor[P0Color]); out.putIntArray(myColor, 8);
out.putInt(myColor[P1Color]);
out.putInt(myColor[PFColor]);
out.putInt(myColor[BKColor]);
out.putInt(myColor[M0Color]);
out.putInt(myColor[M1Color]);
out.putInt(myColor[BLColor]);
out.putByte((char)myCTRLPF); out.putByte(myCTRLPF);
out.putByte((char)myPlayfieldPriorityAndScore); out.putByte(myPlayfieldPriorityAndScore);
out.putBool(myREFP0); out.putBool(myREFP0);
out.putBool(myREFP1); out.putBool(myREFP1);
out.putInt(myPF); out.putInt(myPF);
out.putByte((char)myGRP0); out.putByte(myGRP0);
out.putByte((char)myGRP1); out.putByte(myGRP1);
out.putByte((char)myDGRP0); out.putByte(myDGRP0);
out.putByte((char)myDGRP1); out.putByte(myDGRP1);
out.putBool(myENAM0); out.putBool(myENAM0);
out.putBool(myENAM1); out.putBool(myENAM1);
out.putBool(myENABL); out.putBool(myENABL);
out.putBool(myDENABL); out.putBool(myDENABL);
out.putByte((char)myHMP0); out.putByte(myHMP0);
out.putByte((char)myHMP1); out.putByte(myHMP1);
out.putByte((char)myHMM0); out.putByte(myHMM0);
out.putByte((char)myHMM1); out.putByte(myHMM1);
out.putByte((char)myHMBL); out.putByte(myHMBL);
out.putBool(myVDELP0); out.putBool(myVDELP0);
out.putBool(myVDELP1); out.putBool(myVDELP1);
out.putBool(myVDELBL); out.putBool(myVDELBL);
out.putBool(myRESMP0); out.putBool(myRESMP0);
out.putBool(myRESMP1); out.putBool(myRESMP1);
out.putInt(myCollision); out.putShort(myCollision);
out.putInt(myCollisionEnabledMask); out.putInt(myCollisionEnabledMask);
out.putByte((char)myCurrentGRP0); out.putByte(myCurrentGRP0);
out.putByte((char)myCurrentGRP1); out.putByte(myCurrentGRP1);
out.putBool(myDumpEnabled); out.putBool(myDumpEnabled);
out.putInt(myDumpDisabledCycle); out.putInt(myDumpDisabledCycle);
out.putInt(myPOSP0); out.putShort(myPOSP0);
out.putInt(myPOSP1); out.putShort(myPOSP1);
out.putInt(myPOSM0); out.putShort(myPOSM0);
out.putInt(myPOSM1); out.putShort(myPOSM1);
out.putInt(myPOSBL); out.putShort(myPOSBL);
out.putInt(myMotionClockP0); out.putInt(myMotionClockP0);
out.putInt(myMotionClockP1); out.putInt(myMotionClockP1);
@ -386,61 +380,55 @@ bool TIA::load(Serializer& in)
myClockStopDisplay = (Int32) in.getInt(); myClockStopDisplay = (Int32) in.getInt();
myClockAtLastUpdate = (Int32) in.getInt(); myClockAtLastUpdate = (Int32) in.getInt();
myClocksToEndOfScanLine = (Int32) in.getInt(); myClocksToEndOfScanLine = (Int32) in.getInt();
myScanlineCountForLastFrame = (uInt32) in.getInt(); myScanlineCountForLastFrame = in.getInt();
myVSYNCFinishClock = (Int32) in.getInt(); myVSYNCFinishClock = (Int32) in.getInt();
myEnabledObjects = (uInt8) in.getByte(); myEnabledObjects = in.getByte();
myDisabledObjects = (uInt8) in.getByte(); myDisabledObjects = in.getByte();
myVSYNC = (uInt8) in.getByte(); myVSYNC = in.getByte();
myVBLANK = (uInt8) in.getByte(); myVBLANK = in.getByte();
myNUSIZ0 = (uInt8) in.getByte(); myNUSIZ0 = in.getByte();
myNUSIZ1 = (uInt8) in.getByte(); myNUSIZ1 = in.getByte();
myColor[P0Color] = (uInt32) in.getInt(); in.getIntArray(myColor, 8);
myColor[P1Color] = (uInt32) in.getInt();
myColor[PFColor] = (uInt32) in.getInt();
myColor[BKColor] = (uInt32) in.getInt();
myColor[M0Color] = (uInt32) in.getInt();
myColor[M1Color] = (uInt32) in.getInt();
myColor[BLColor] = (uInt32) in.getInt();
myCTRLPF = (uInt8) in.getByte(); myCTRLPF = in.getByte();
myPlayfieldPriorityAndScore = (uInt8) in.getByte(); myPlayfieldPriorityAndScore = in.getByte();
myREFP0 = in.getBool(); myREFP0 = in.getBool();
myREFP1 = in.getBool(); myREFP1 = in.getBool();
myPF = (uInt32) in.getInt(); myPF = in.getInt();
myGRP0 = (uInt8) in.getByte(); myGRP0 = in.getByte();
myGRP1 = (uInt8) in.getByte(); myGRP1 = in.getByte();
myDGRP0 = (uInt8) in.getByte(); myDGRP0 = in.getByte();
myDGRP1 = (uInt8) in.getByte(); myDGRP1 = in.getByte();
myENAM0 = in.getBool(); myENAM0 = in.getBool();
myENAM1 = in.getBool(); myENAM1 = in.getBool();
myENABL = in.getBool(); myENABL = in.getBool();
myDENABL = in.getBool(); myDENABL = in.getBool();
myHMP0 = (uInt8) in.getByte(); myHMP0 = in.getByte();
myHMP1 = (uInt8) in.getByte(); myHMP1 = in.getByte();
myHMM0 = (uInt8) in.getByte(); myHMM0 = in.getByte();
myHMM1 = (uInt8) in.getByte(); myHMM1 = in.getByte();
myHMBL = (uInt8) in.getByte(); myHMBL = in.getByte();
myVDELP0 = in.getBool(); myVDELP0 = in.getBool();
myVDELP1 = in.getBool(); myVDELP1 = in.getBool();
myVDELBL = in.getBool(); myVDELBL = in.getBool();
myRESMP0 = in.getBool(); myRESMP0 = in.getBool();
myRESMP1 = in.getBool(); myRESMP1 = in.getBool();
myCollision = (uInt16) in.getInt(); myCollision = in.getShort();
myCollisionEnabledMask = in.getInt(); myCollisionEnabledMask = in.getInt();
myCurrentGRP0 = (uInt8) in.getByte(); myCurrentGRP0 = in.getByte();
myCurrentGRP1 = (uInt8) in.getByte(); myCurrentGRP1 = in.getByte();
myDumpEnabled = in.getBool(); myDumpEnabled = in.getBool();
myDumpDisabledCycle = (Int32) in.getInt(); myDumpDisabledCycle = (Int32) in.getInt();
myPOSP0 = (Int16) in.getInt(); myPOSP0 = (Int16) in.getShort();
myPOSP1 = (Int16) in.getInt(); myPOSP1 = (Int16) in.getShort();
myPOSM0 = (Int16) in.getInt(); myPOSM0 = (Int16) in.getShort();
myPOSM1 = (Int16) in.getInt(); myPOSM1 = (Int16) in.getShort();
myPOSBL = (Int16) in.getInt(); myPOSBL = (Int16) in.getShort();
myMotionClockP0 = (Int32) in.getInt(); myMotionClockP0 = (Int32) in.getInt();
myMotionClockP1 = (Int32) in.getInt(); myMotionClockP1 = (Int32) in.getInt();
@ -453,8 +441,8 @@ bool TIA::load(Serializer& in)
myStartM0 = (Int32) in.getInt(); myStartM0 = (Int32) in.getInt();
myStartM1 = (Int32) in.getInt(); myStartM1 = (Int32) in.getInt();
mySuppressP0 = (uInt8) in.getByte(); mySuppressP0 = in.getByte();
mySuppressP1 = (uInt8) in.getByte(); mySuppressP1 = in.getByte();
myHMP0mmr = in.getBool(); myHMP0mmr = in.getBool();
myHMP1mmr = in.getBool(); myHMP1mmr = in.getBool();
@ -492,9 +480,7 @@ bool TIA::saveDisplay(Serializer& out) const
{ {
out.putBool(myPartialFrameFlag); out.putBool(myPartialFrameFlag);
out.putInt(myFramePointerClocks); out.putInt(myFramePointerClocks);
out.putByteArray(myCurrentFrameBuffer, 160*320);
for(int i = 0; i < 160*320; ++i)
out.putByte(myCurrentFrameBuffer[i]);
} }
catch(const char* msg) catch(const char* msg)
{ {
@ -511,13 +497,13 @@ bool TIA::loadDisplay(Serializer& in)
try try
{ {
myPartialFrameFlag = in.getBool(); myPartialFrameFlag = in.getBool();
myFramePointerClocks = (uInt32) in.getInt(); myFramePointerClocks = in.getInt();
// Reset frame buffer pointer and data // Reset frame buffer pointer and data
clearBuffers(); clearBuffers();
myFramePointer = myCurrentFrameBuffer; myFramePointer = myCurrentFrameBuffer;
for(int i = 0; i < 160*320; ++i) in.getByteArray(myCurrentFrameBuffer, 160*320);
myCurrentFrameBuffer[i] = myPreviousFrameBuffer[i] = (uInt8) in.getByte(); memcpy(myPreviousFrameBuffer, myCurrentFrameBuffer, 160*320);
// If we're in partial frame mode, make sure to re-create the screen // If we're in partial frame mode, make sure to re-create the screen
// as it existed when the state was saved // as it existed when the state was saved