mirror of https://github.com/stella-emu/stella.git
333 lines
9.6 KiB
C++
333 lines
9.6 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//============================================================================
|
|
|
|
#include "TIA.hxx"
|
|
#include "M6502.hxx"
|
|
#include "System.hxx"
|
|
#include "CartWD.hxx"
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
CartridgeWD::CartridgeWD(const BytePtr& image, uInt32 size,
|
|
const Settings& settings)
|
|
: Cartridge(settings),
|
|
mySize(std::min(8195u, size)),
|
|
myCyclesAtBankswitchInit(0),
|
|
myPendingBank(0),
|
|
myCurrentBank(0)
|
|
{
|
|
// Copy the ROM image into my buffer
|
|
memcpy(myImage, image.get(), mySize);
|
|
createCodeAccessBase(8192);
|
|
|
|
// Remember startup bank
|
|
myStartBank = 0;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::reset()
|
|
{
|
|
initializeRAM(myRAM, 64);
|
|
|
|
myCyclesAtBankswitchInit = 0;
|
|
myPendingBank = 0xF0; // one more than the allowable bank #
|
|
|
|
// Setup segments to some default slices
|
|
bank(myStartBank);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::install(System& system)
|
|
{
|
|
mySystem = &system;
|
|
|
|
// Set the page accessing method for the RAM reading pages
|
|
System::PageAccess read(this, System::PA_READ);
|
|
for(uInt32 k = 0x1000; k < 0x1040; k += (1 << System::PAGE_SHIFT))
|
|
{
|
|
read.directPeekBase = &myRAM[k & 0x003F];
|
|
read.codeAccessBase = &myCodeAccessBase[k & 0x003F];
|
|
mySystem->setPageAccess(k >> System::PAGE_SHIFT, read);
|
|
}
|
|
|
|
// Set the page accessing method for the RAM writing pages
|
|
System::PageAccess write(this, System::PA_WRITE);
|
|
for(uInt32 j = 0x1040; j < 0x1080; j += (1 << System::PAGE_SHIFT))
|
|
{
|
|
write.directPokeBase = &myRAM[j & 0x003F];
|
|
write.codeAccessBase = &myCodeAccessBase[j & 0x003F];
|
|
mySystem->setPageAccess(j >> System::PAGE_SHIFT, write);
|
|
}
|
|
|
|
// Mirror all access in TIA; by doing so we're taking responsibility
|
|
// for that address space in peek and poke below.
|
|
mySystem->tia().installDelegate(system, *this);
|
|
|
|
// Setup segments to some default slices
|
|
bank(myStartBank);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt8 CartridgeWD::peek(uInt16 address)
|
|
{
|
|
// Is it time to do an actual bankswitch?
|
|
if(myPendingBank != 0xF0 && !bankLocked() &&
|
|
mySystem->cycles() > (myCyclesAtBankswitchInit + 3))
|
|
{
|
|
bank(myPendingBank);
|
|
myPendingBank = 0xF0;
|
|
}
|
|
|
|
if(!(address & 0x1000)) // Hotspots below 0x1000 are also TIA addresses
|
|
{
|
|
// Hotspots at $30 - $3F
|
|
// Note that a hotspot read triggers a bankswitch after at least 3 cycles
|
|
// have passed, so we only initiate the switch here
|
|
if(!bankLocked() && (address & 0x00FF) >= 0x30 && (address & 0x00FF) <= 0x3F)
|
|
{
|
|
myCyclesAtBankswitchInit = mySystem->cycles();
|
|
myPendingBank = address & 0x000F;
|
|
}
|
|
return mySystem->tia().peek(address);
|
|
}
|
|
else
|
|
{
|
|
uInt16 peekAddress = address;
|
|
address &= 0x0FFF;
|
|
|
|
if(address < 0x0040) // RAM read port
|
|
return myRAM[address];
|
|
else if(address < 0x0080) // RAM write port
|
|
{
|
|
// Reading from the write port @ $1040 - $107F triggers an unwanted write
|
|
uInt8 value = mySystem->getDataBusState(0xFF);
|
|
|
|
if(bankLocked())
|
|
return value;
|
|
else
|
|
{
|
|
triggerReadFromWritePort(peekAddress);
|
|
return myRAM[address & 0x003F] = value;
|
|
}
|
|
}
|
|
else if(address < 0x0400)
|
|
return myImage[myOffset[0] + (address & 0x03FF)];
|
|
else if(address < 0x0800)
|
|
return myImage[myOffset[1] + (address & 0x03FF)];
|
|
else if(address < 0x0C00)
|
|
return myImage[myOffset[2] + (address & 0x03FF)];
|
|
else
|
|
return mySegment3[address & 0x03FF];
|
|
}
|
|
|
|
return 0; // Make the compiler happy; we'll never reach this
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool CartridgeWD::poke(uInt16 address, uInt8 value)
|
|
{
|
|
// Only TIA writes will reach here
|
|
if(!(address & 0x1000))
|
|
return mySystem->tia().poke(address, value);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool CartridgeWD::bank(uInt16 bank)
|
|
{
|
|
if(bankLocked() || bank > 15) return false;
|
|
|
|
myCurrentBank = bank;
|
|
|
|
segmentZero(ourBankOrg[bank].zero);
|
|
segmentOne(ourBankOrg[bank].one);
|
|
segmentTwo(ourBankOrg[bank].two);
|
|
segmentThree(ourBankOrg[bank].three, ourBankOrg[bank].map3bytes);
|
|
|
|
return myBankChanged = true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::segmentZero(uInt8 slice)
|
|
{
|
|
uInt16 offset = slice << 10;
|
|
System::PageAccess access(this, System::PA_READ);
|
|
|
|
// Skip first 128 bytes; it is always RAM
|
|
for(uInt32 address = 0x1080; address < 0x1400;
|
|
address += (1 << System::PAGE_SHIFT))
|
|
{
|
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x03FF)];
|
|
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
|
|
}
|
|
myOffset[0] = offset;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::segmentOne(uInt8 slice)
|
|
{
|
|
uInt16 offset = slice << 10;
|
|
System::PageAccess access(this, System::PA_READ);
|
|
|
|
for(uInt32 address = 0x1400; address < 0x1800;
|
|
address += (1 << System::PAGE_SHIFT))
|
|
{
|
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x03FF)];
|
|
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
|
|
}
|
|
myOffset[1] = offset;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::segmentTwo(uInt8 slice)
|
|
{
|
|
uInt16 offset = slice << 10;
|
|
System::PageAccess access(this, System::PA_READ);
|
|
|
|
for(uInt32 address = 0x1800; address < 0x1C00;
|
|
address += (1 << System::PAGE_SHIFT))
|
|
{
|
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x03FF)];
|
|
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
|
|
}
|
|
myOffset[2] = offset;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CartridgeWD::segmentThree(uInt8 slice, bool map3bytes)
|
|
{
|
|
uInt16 offset = slice << 10;
|
|
|
|
// Make a copy of the address space pointed to by the slice
|
|
// Then map in the extra 3 bytes, if required
|
|
memcpy(mySegment3, myImage+offset, 1024);
|
|
if(mySize == 8195 && map3bytes)
|
|
{
|
|
mySegment3[0x3FC] = myImage[0x2000+0];
|
|
mySegment3[0x3FD] = myImage[0x2000+1];
|
|
mySegment3[0x3FE] = myImage[0x2000+2];
|
|
}
|
|
|
|
System::PageAccess access(this, System::PA_READ);
|
|
|
|
for(uInt32 address = 0x1C00; address < 0x2000;
|
|
address += (1 << System::PAGE_SHIFT))
|
|
{
|
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x03FF)];
|
|
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
|
|
}
|
|
myOffset[3] = offset;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt16 CartridgeWD::getBank() const
|
|
{
|
|
return myCurrentBank;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt16 CartridgeWD::bankCount() const
|
|
{
|
|
return 16;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool CartridgeWD::patch(uInt16 address, uInt8 value)
|
|
{
|
|
address &= 0x0FFF;
|
|
|
|
uInt16 idx = address >> 10;
|
|
myImage[myOffset[idx] + (address & 0x03FF)] = value;
|
|
|
|
// The upper segment is mirrored, so we need to patch its buffer too
|
|
if(idx == 3)
|
|
mySegment3[(address & 0x03FF)] = value;
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const uInt8* CartridgeWD::getImage(int& size) const
|
|
{
|
|
size = mySize;
|
|
return myImage;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool CartridgeWD::save(Serializer& out) const
|
|
{
|
|
try
|
|
{
|
|
out.putString(name());
|
|
out.putShort(myCurrentBank);
|
|
out.putByteArray(myRAM, 64);
|
|
out.putInt(myCyclesAtBankswitchInit);
|
|
out.putShort(myPendingBank);
|
|
}
|
|
catch(...)
|
|
{
|
|
cerr << "ERROR: CartridgeWD::save" << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool CartridgeWD::load(Serializer& in)
|
|
{
|
|
try
|
|
{
|
|
if(in.getString() != name())
|
|
return false;
|
|
|
|
myCurrentBank = in.getShort();
|
|
in.getByteArray(myRAM, 64);
|
|
myCyclesAtBankswitchInit = in.getInt();
|
|
myPendingBank = in.getShort();
|
|
|
|
bank(myCurrentBank);
|
|
}
|
|
catch(...)
|
|
{
|
|
cerr << "ERROR: CartridgeWD::load" << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
CartridgeWD::BankOrg CartridgeWD::ourBankOrg[16] = {
|
|
{ 0, 0, 1, 2, false }, // Bank 0
|
|
{ 0, 1, 3, 2, false }, // Bank 1
|
|
{ 4, 5, 6, 7, false }, // Bank 2
|
|
{ 7, 4, 3, 2, false }, // Bank 3
|
|
{ 0, 0, 6, 7, false }, // Bank 4
|
|
{ 0, 1, 7, 6, false }, // Bank 5
|
|
{ 3, 2, 4, 5, false }, // Bank 6
|
|
{ 6, 0, 5, 1, false }, // Bank 7
|
|
{ 0, 0, 1, 2, false }, // Bank 8
|
|
{ 0, 1, 3, 2, false }, // Bank 9
|
|
{ 4, 5, 6, 7, false }, // Bank 10
|
|
{ 7, 4, 3, 2, false }, // Bank 11
|
|
{ 0, 0, 6, 7, true }, // Bank 12
|
|
{ 0, 1, 7, 6, true }, // Bank 13
|
|
{ 3, 2, 4, 5, true }, // Bank 14
|
|
{ 6, 0, 5, 1, true } // Bank 15
|
|
};
|