stella/src/emucore/System.cxx

247 lines
6.3 KiB
C++

//============================================================================
//
// MM MM 6666 555555 0000 2222
// MMMM MMMM 66 66 55 00 00 22 22
// MM MMM MM 66 55 00 00 22
// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator"
// MM MM 66 66 55 00 00 22
// MM MM 66 66 55 55 00 00 22
// MM MM 6666 5555 0000 222222
//
// Copyright (c) 1995-2020 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 <cassert>
#include <iostream>
#include "Device.hxx"
#include "M6502.hxx"
#include "M6532.hxx"
#include "TIA.hxx"
#include "Cart.hxx"
#include "TimerManager.hxx"
#include "System.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
System::System(Random& random, M6502& m6502, M6532& m6532,
TIA& mTIA, Cartridge& mCart)
: myRandom(random),
myM6502(m6502),
myM6532(m6532),
myTIA(mTIA),
myCart(mCart)
{
// Initialize page access table
PageAccess access(&myNullDevice, System::PageAccessType::READ);
myPageAccessTable.fill(access);
myPageIsDirtyTable.fill(false);
// Bus starts out unlocked (in other words, peek() changes myDataBusState)
myDataBusLocked = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::initialize()
{
// Install all devices
myM6532.install(*this);
myTIA.install(*this);
myCart.install(*this);
myM6502.install(*this); // Must always be installed last
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::reset(bool autodetect)
{
// Provide hint to devices that autodetection is active (or not)
mySystemInAutodetect = autodetect;
// Reset all devices
myCycles = 0; // Must be done first (the reset() methods may use its value)
myM6532.reset();
myTIA.reset();
myCart.reset();
myM6502.reset(); // Must always be reset last
// There are no dirty pages upon startup
clearDirtyPages();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::consoleChanged(ConsoleTiming timing)
{
myM6532.consoleChanged(timing);
myTIA.consoleChanged(timing);
myCart.consoleChanged(timing);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool System::isPageDirty(uInt16 start_addr, uInt16 end_addr) const
{
uInt16 start_page = (start_addr & ADDRESS_MASK) >> PAGE_SHIFT;
uInt16 end_page = (end_addr & ADDRESS_MASK) >> PAGE_SHIFT;
for(uInt16 page = start_page; page <= end_page; ++page)
if(myPageIsDirtyTable[page])
return true;
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::clearDirtyPages()
{
myPageIsDirtyTable.fill(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 System::peek(uInt16 addr, uInt8 flags)
{
const PageAccess& access = getPageAccess(addr);
#ifdef DEBUGGER_SUPPORT
// Set access type
if(access.codeAccessBase)
*(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
else
access.device->setAccessFlags(addr, flags);
#endif
// See if this page uses direct accessing or not
uInt8 result;
if(access.directPeekBase)
result = *(access.directPeekBase + (addr & PAGE_MASK));
else
result = access.device->peek(addr);
#ifdef DEBUGGER_SUPPORT
if(!myDataBusLocked)
#endif
myDataBusState = result;
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::poke(uInt16 addr, uInt8 value, uInt8 flags)
{
uInt16 page = (addr & ADDRESS_MASK) >> PAGE_SHIFT;
const PageAccess& access = myPageAccessTable[page];
#ifdef DEBUGGER_SUPPORT
// Set access type
if (access.codeAccessBase)
*(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
else
access.device->setAccessFlags(addr, flags);
#endif
// See if this page uses direct accessing or not
if(access.directPokeBase)
{
// Since we have direct access to this poke, we can dirty its page
*(access.directPokeBase + (addr & PAGE_MASK)) = value;
myPageIsDirtyTable[page] = true;
}
else
{
// The specific device informs us if the poke succeeded
myPageIsDirtyTable[page] = access.device->poke(addr, value);
}
#ifdef DEBUGGER_SUPPORT
if(!myDataBusLocked)
#endif
myDataBusState = value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 System::getAccessFlags(uInt16 addr) const
{
#ifdef DEBUGGER_SUPPORT
const PageAccess& access = getPageAccess(addr);
if(access.codeAccessBase)
return *(access.codeAccessBase + (addr & PAGE_MASK));
else
return access.device->getAccessFlags(addr);
#else
return 0;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void System::setAccessFlags(uInt16 addr, uInt8 flags)
{
#ifdef DEBUGGER_SUPPORT
const PageAccess& access = getPageAccess(addr);
if(access.codeAccessBase)
*(access.codeAccessBase + (addr & PAGE_MASK)) |= flags;
else
access.device->setAccessFlags(addr, flags);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool System::save(Serializer& out) const
{
try
{
out.putLong(myCycles);
out.putByte(myDataBusState);
// Save the state of each device
if(!myM6502.save(out))
return false;
if(!myM6532.save(out))
return false;
if(!myTIA.save(out))
return false;
if(!myCart.save(out))
return false;
if(!randGenerator().save(out))
return false;
}
catch(...)
{
cerr << "ERROR: System::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool System::load(Serializer& in)
{
try
{
myCycles = in.getLong();
myDataBusState = in.getByte();
// Load the state of each device
if(!myM6502.load(in))
return false;
if(!myM6532.load(in))
return false;
if(!myTIA.load(in))
return false;
if(!myCart.load(in))
return false;
if(!randGenerator().load(in))
return false;
}
catch(...)
{
cerr << "ERROR: System::load" << endl;
return false;
}
return true;
}