//============================================================================ // // 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 };