autoformatted. still stabilising to stella standard.

Tabs removed, replaced with 2-char spacing. 
constants for bit masking added
corrected the patch code for the bit-allocations for RAM/ROM banks switch


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2900 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
adavie 2014-06-03 13:53:49 +00:00
parent 6d6dd7632f
commit 40c8c81245
2 changed files with 259 additions and 276 deletions

View File

@ -24,11 +24,9 @@
#include "TIA.hxx"
#include "CartDASH.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDASH::CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings)
: Cartridge(settings),
mySize(size)
{
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDASH::CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings) :
Cartridge(settings), mySize(size) {
// Allocate array for the ROM image
myImage = new uInt8[mySize];
@ -46,20 +44,20 @@ CartridgeDASH::CartridgeDASH(const uInt8* image, uInt32 size, const Settings& se
registerRamArea(0x1400, RAM_BANK_SIZE, 0x00, RAM_WRITE_OFFSET); // 512 bytes RAM @ 0x1400
registerRamArea(0x1600, RAM_BANK_SIZE, 0x00, RAM_WRITE_OFFSET); // 512 bytes RAM @ 0x1600
myCurrentBank = -1; // nothing switched
// Remember startup bank (0 per spec, rather than last per 3E scheme).
// Set this to go to 3rd 1K Bank.
myStartBank = (3 << BANK_BITS) | 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDASH::~CartridgeDASH()
{
CartridgeDASH::~CartridgeDASH() {
delete[] myImage;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::reset()
{
void CartridgeDASH::reset() {
// Initialize RAM
if (mySettings.getBool("ramrandom"))
for (uInt32 i = 0; i < RAM_TOTAL_SIZE; ++i)
@ -73,8 +71,7 @@ void CartridgeDASH::reset()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::install(System& system)
{
void CartridgeDASH::install(System& system) {
mySystem = &system;
uInt16 shift = mySystem->pageShift();
@ -95,12 +92,11 @@ void CartridgeDASH::install(System& system)
// Setup the last segment (of 4, each 1K) to point to the first ROM slice
// Actually we DO NOT want "always". It's just on bootup, and can be out switched later
access.type = System::PA_READ;
for (uInt32 byte = 0; byte < ROM_BANK_SIZE; byte++)
{
uInt32 address = (0x1000 - ROM_BANK_SIZE) + (byte<<shift); // which byte in last bank of 2600 address space
for (uInt32 byte = 0; byte < ROM_BANK_SIZE; byte++) {
uInt32 address = (0x1000 - ROM_BANK_SIZE) + (byte << shift); // which byte in last bank of 2600 address space
access.directPeekBase = &myImage[byte]; // from base address 0x0000 in image, so just use 'byte'
access.codeAccessBase = &myCodeAccessBase[byte];
mySystem->setPageAccess(address>>shift, access);
mySystem->setPageAccess(address >> shift, access);
// TODO: Stephen: in this and other implementations we appear to be using "shift" as a system-dependant mangle for
// different access types (byte/int/32bit) on different architectures. I think I understand that much. However,
@ -112,7 +108,7 @@ void CartridgeDASH::install(System& system)
// Initialise bank values for the 4x 1K bank areas
// This is used to reverse-lookup from address to bank location
for(uInt32 b = 0; b < 3; b++)
for (uInt32 b = 0; b < 3; b++)
bankInUse[b] = BANK_UNDEFINED; // bank is undefined and inaccessible!
// Install pages for the startup bank into the first segment
@ -120,31 +116,29 @@ void CartridgeDASH::install(System& system)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeDASH::peek(uInt16 address)
{
uInt8 CartridgeDASH::peek(uInt16 address) {
uInt8 value = 0;
uInt32 bank = (address >> ROM_BANK_TO_POWER) & 3; // convert to 1K bank index (0-3)
Int16 imageBank = bankInUse[bank]; // the ROM/RAM bank that's here
if(imageBank == BANK_UNDEFINED) // an uninitialised bank?
{
if (imageBank == BANK_UNDEFINED) { // an uninitialised bank?
// accessing invalid bank, so return should be... random?
// TODO: Stephen -- throw some sort of error; looking at undefined data
assert(false);
value = mySystem->randGenerator().next();
}
else if (imageBank & ROMRAM) // a RAM bank
{
Int32 ramBank = imageBank & BIT_BANK_MASK; // discard irrelevant bits
Int32 offset = ramBank << RAM_BANK_TO_POWER; // base bank address in RAM
offset += (address & (RAM_BANK_SIZE-1)); // + byte offset in RAM bank
value = myRAM[offset];
}
else // accessing ROM
{
} else if (imageBank & BITMASK_ROMRAM) { // a RAM bank
Int32 ramBank = imageBank & BIT_BANK_MASK; // discard irrelevant bits
Int32 offset = ramBank << RAM_BANK_TO_POWER; // base bank address in RAM
offset += (address & BITMASK_RAM_BANK); // + byte offset in RAM bank
value = myRAM[offset];
} else { // accessing ROM
Int32 offset = imageBank << ROM_BANK_TO_POWER; // base bank address in image
offset += (address & (ROM_BANK_SIZE-1)); // + byte offset in image bank
offset += (address & BITMASK_ROM_BANK); // + byte offset in image bank
value = myImage[offset];
}
@ -152,15 +146,14 @@ uInt8 CartridgeDASH::peek(uInt16 address)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::poke(uInt16 address, uInt8 value)
{
bool CartridgeDASH::poke(uInt16 address, uInt8 value) {
address &= 0x0FFF; // restrict to 4K address range
// Check for write to the bank switch address. RAM/ROM and bank # are encoded in 'value'
// There are NO mirrored hotspots.
if ( address == BANK_SWITCH_HOTSPOT)
bank(value);
if (address == BANK_SWITCH_HOTSPOT)
bank(value);
// Pass the poke through to the TIA. In a real Atari, both the cart and the
// TIA see the address lines, and both react accordingly. In Stella, each
@ -172,9 +165,8 @@ bool CartridgeDASH::poke(uInt16 address, uInt8 value)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::bank(uInt16 bank)
{
if(bankLocked())
bool CartridgeDASH::bank(uInt16 bank) {
if (bankLocked())
return false; // TODO: Stephen -- ? no idea
uInt16 shift = mySystem->pageShift();
@ -182,12 +174,12 @@ bool CartridgeDASH::bank(uInt16 bank)
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7)
uInt16 bankID = bank & BIT_BANK_MASK; // The actual bank # to switch in (BITS D5-D0)
if(bank & ROMRAM) // switching to a 512 byte RAM bank
{
if (bank & BITMASK_ROMRAM) { // switching to a 512 byte RAM bank
// Wrap around/restrict to valid range
uInt16 currentBank = (bank & BIT_BANK_MASK) % RAM_BANK_COUNT;
uInt16 currentBank = bank & BIT_BANK_MASK;
// Record which bank switched in (marked as RAM)
myCurrentBank = bankInUse[bankNumber] = (Int16) (ROM_BANK_COUNT + currentBank);
myCurrentBank = bankInUse[bankNumber] = (Int16) (BITMASK_ROMRAM | currentBank);
// Effectively * 512 bytes
uInt32 startCurrentBank = currentBank << RAM_BANK_TO_POWER;
@ -195,8 +187,7 @@ bool CartridgeDASH::bank(uInt16 bank)
System::PageAccess access(0, 0, 0, this, System::PA_READ);
// Map read-port RAM image into the system
for(uInt32 byte = 0; byte < RAM_BANK_SIZE; byte += (1 << shift))
{
for (uInt32 byte = 0; byte < RAM_BANK_SIZE; byte += (1 << shift)) {
access.directPeekBase = &myRAM[startCurrentBank + byte];
// TODO: Stephen please explain/review the use of mySize as an offset for RAM access here....
@ -209,14 +200,12 @@ bool CartridgeDASH::bank(uInt16 bank)
access.type = System::PA_WRITE;
// Map write-port RAM image into the system
for (uInt32 byte = 0; byte < RAM_BANK_SIZE; byte += (1 << shift))
{
for (uInt32 byte = 0; byte < RAM_BANK_SIZE; byte += (1 << shift)) {
access.directPokeBase = &myRAM[startCurrentBank + RAM_WRITE_OFFSET + byte];
access.codeAccessBase = &myCodeAccessBase[mySize + startCurrentBank + RAM_WRITE_OFFSET + byte];
mySystem->setPageAccess((startCurrentBank + byte) >> shift, access);
}
}
else // ROM 1K banks
} else // ROM 1K banks
{
// Map ROM bank image into the system into the correct slot
// Memory map is 1K slots at 0x1000, 0x1400, 0x1800, 0x1C00
@ -231,8 +220,7 @@ bool CartridgeDASH::bank(uInt16 bank)
uInt32 bankStart = 0x1000 + (bankNumber << ROM_BANK_TO_POWER); // *1K
for (uInt32 byte = 0; byte < ROM_BANK_SIZE; byte += (1 << shift))
{
for (uInt32 byte = 0; byte < ROM_BANK_SIZE; byte += (1 << shift)) {
access.directPeekBase = &myImage[startCurrentBank + byte];
access.codeAccessBase = &myCodeAccessBase[startCurrentBank + byte];
mySystem->setPageAccess((bankStart + byte) >> shift, access);
@ -243,8 +231,7 @@ bool CartridgeDASH::bank(uInt16 bank)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeDASH::bank() const
{
uInt16 CartridgeDASH::bank() const {
// TODO: Stephen -- what to do here? We don't really HAVE a "current bank"; we have 4 banks
// and they are defined in bankInUse[...].
// What I've done is kept track of the last switched bank, and return that. But that doesn't tell us WHERE. :(
@ -253,27 +240,26 @@ uInt16 CartridgeDASH::bank() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeDASH::bankCount() const
{
// Because the RAM banks always start above the ROM banks (see ROM_BANK_COUNT) for value,
// we require the number of ROM banks to be == ROM_BANK_COUNT. Banks are therefore 0-63 ROM 64-127 RAM
// TODO: Stephen -- ROM banks are 1K. RAM banks are 512 bytes. How does this affect what this routine should return?
uInt16 CartridgeDASH::bankCount() const {
// We have a constant # banks for this scheme; 32 ROM and 32 RAM (or, at least, RAM_BANK_COUNT and ROM_BANK_COUNT)
// See usage of bank bits.
// TODO: Stephen -- What should this return, given the mangled bank value?
return ROM_BANK_COUNT + RAM_BANK_COUNT;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::patch(uInt16 address, uInt8 value)
{
bool CartridgeDASH::patch(uInt16 address, uInt8 value) {
// Patch the cartridge ROM
// TODO: Stephen... I assume this is for some sort of debugger support....?
myBankChanged = true;
uInt32 bankNumber = (address >> 10) & 3; // now 1K bank # (ie: 0-3)
uInt32 bankNumber = (address >> ROM_BANK_TO_POWER) & 3; // now 1K bank # (ie: 0-3)
Int32 whichBankIsThere = bankInUse[bankNumber]; // ROM or RAM bank reference
if(whichBankIsThere <= BANK_UNDEFINED)
{
if (whichBankIsThere == BANK_UNDEFINED) {
// We're trying to access undefined memory (no bank here yet)
// TODO: Stephen -- what to do here? Fail silently?
@ -281,64 +267,58 @@ bool CartridgeDASH::patch(uInt16 address, uInt8 value)
assert(false);
myBankChanged = false;
}
else if(whichBankIsThere < ROM_BANK_COUNT) // patching ROM (1K banks)
{
uInt32 byteOffset = address & (ROM_BANK_SIZE-1);
} else if (whichBankIsThere & BITMASK_ROMRAM) { // patching RAM (512 byte banks)
uInt32 byteOffset = address & BITMASK_RAM_BANK;
uInt32 baseAddress = ((whichBankIsThere & BIT_BANK_MASK) << RAM_BANK_TO_POWER) + byteOffset;
myRAM[baseAddress] = value; // write to RAM
} else { // patching ROM (1K banks)
uInt32 byteOffset = address & BITMASK_ROM_BANK;
uInt32 baseAddress = (whichBankIsThere << ROM_BANK_TO_POWER) + byteOffset;
myImage[baseAddress] = value; // write to the image
}
else // patching RAM (512 byte banks)
{
uInt32 byteOffset = address & (RAM_BANK_SIZE-1);
uInt32 baseAddress = ((whichBankIsThere - ROM_BANK_COUNT) << RAM_BANK_TO_POWER) + byteOffset;
myRAM[baseAddress] = value; // write to RAM
}
return myBankChanged;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeDASH::getImage(int& size) const
{
const uInt8* CartridgeDASH::getImage(int& size) const {
size = mySize;
return myImage;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::save(Serializer& out) const
{
try
{
bool CartridgeDASH::save(Serializer& out) const {
try {
out.putString(name());
out.putShort(myCurrentBank);
for(uInt32 bank = 0; bank < 4; bank++)
for (uInt32 bank = 0; bank < 4; bank++)
out.putShort(bankInUse[bank]);
out.putByteArray(myRAM, RAM_TOTAL_SIZE);
}
catch (...)
{
} catch (...) {
cerr << "ERROR: CartridgeDASH::save" << endl;
return false;
}
{
return true;
return false;
}
return true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::load(Serializer& in)
{
try
{
if(in.getString() != name())
bool CartridgeDASH::load(Serializer& in) {
try {
if (in.getString() != name())
return false;
myCurrentBank = in.getShort();
for(uInt32 bank = 0; bank < 4; bank++)
for (uInt32 bank = 0; bank < 4; bank++)
bankInUse[bank] = in.getShort();
in.getByteArray(myRAM, RAM_TOTAL_SIZE);
}
catch (...)
{
} catch (...) {
cerr << "ERROR: CartridgeDASH::load" << endl;
return false;
}

View File

@ -26,252 +26,255 @@ class System;
#include "Cart.hxx"
#ifdef DEBUGGER_SUPPORT
class CartridgeDASHWidget;
class CartridgeDASHWidget;
// #include "CartDASHWidget.hxx"
#endif
/**
Cartridge class for new tiling engine "Boulder Dash" format games with RAM.
Kind of a combination of 3F and 3E, with better switchability.
B.Watson's Cart3E was used as a template for building this implementation.
Cartridge class for new tiling engine "Boulder Dash" format games with RAM.
Kind of a combination of 3F and 3E, with better switchability.
B.Watson's Cart3E was used as a template for building this implementation.
Because a single bank number is used to define both the destination (0-3)
AND the type (ROM/RAM) there are only 5 bits left to indicate the actual bank
number. This sets the limits of 32K ROM and 16K RAM.
Because a single bank number is used to define both the destination (0-3)
AND the type (ROM/RAM) there are only 5 bits left to indicate the actual bank
number. This sets the limits of 32K ROM and 16K RAM.
D7 RAM/ROM flag (1=RAM)
D6D5 indicate the bank number (0-3)
D4D3D2D1D0 indicate the actual # (0-31) from the image/ram
D7 RAM/ROM flag (1=RAM)
D6D5 indicate the bank number (0-3)
D4D3D2D1D0 indicate the actual # (0-31) from the image/ram
Hotspot 0x3F is used for bank-switching, with the encoded bank # as above.
Hotspot 0x3F is used for bank-switching, with the encoded bank # as above.
ROM:
ROM:
In this scheme, the 4K address space is broken into four 1K ROM segments.
living at 0x1000, 0x1400, 0x1800, 0x1C00 (or, same thing, 0xF000... etc.),
and four 512 byte RAM segments, living at 0x1000, 0x1200, 0x1400, 0x1600
with write-mirrors +0x800 of these. The last 1K ROM ($FC00-$FFFF) segment
is initialised to point to the FIRST 1K of the ROM image, but it may be
switched out at any time. Note, this is DIFFERENT to 3E which switches in
the UPPER bank and this bank is fixed. This allows variable sized ROM
without having to detect size. First bank (0) in ROM is the default fixed
bank mapped to $FC00.
In this scheme, the 4K address space is broken into four 1K ROM segments.
living at 0x1000, 0x1400, 0x1800, 0x1C00 (or, same thing, 0xF000... etc.),
and four 512 byte RAM segments, living at 0x1000, 0x1200, 0x1400, 0x1600
with write-mirrors +0x800 of these. The last 1K ROM ($FC00-$FFFF) segment
is initialised to point to the FIRST 1K of the ROM image, but it may be
switched out at any time. Note, this is DIFFERENT to 3E which switches in
the UPPER bank and this bank is fixed. This allows variable sized ROM
without having to detect size. First bank (0) in ROM is the default fixed
bank mapped to $FC00.
The system requires the reset vectors to be valid on a reset, so either the
hardware first switches in the first bank, or the programmer must ensure
that the reset vector is present in ALL ROM banks which might be switched
into the last bank area. Currently the latter (programmer onus) is required,
but it would be nice for the cartridge hardware to auto-switch on reset.
The system requires the reset vectors to be valid on a reset, so either the
hardware first switches in the first bank, or the programmer must ensure
that the reset vector is present in ALL ROM banks which might be switched
into the last bank area. Currently the latter (programmer onus) is required,
but it would be nice for the cartridge hardware to auto-switch on reset.
ROM switching (write of block+bank number to $3F) D7=0 and D6D5 upper 2 bits of bank #
indicates the destination segment (0-3, corresponding to $F000, $F400, $F800, $FC00),
and lower 5 bits indicate the 1K bank to switch in. Can handle 32 x 1K ROM banks (32K total).
ROM switching (write of block+bank number to $3F) D7=0 and D6D5 upper 2 bits of bank #
indicates the destination segment (0-3, corresponding to $F000, $F400, $F800, $FC00),
and lower 5 bits indicate the 1K bank to switch in. Can handle 32 x 1K ROM banks (32K total).
D7 D6 D5 D4D3D2D1D0
0 0 0 x x x x x switch a 1K ROM bank xxxxx to $F000
0 0 1 switch a 1K ROM bank xxxxx to $F400
0 1 0 switch a 1K ROM bank xxxxx to $F800
0 1 1 switch a 1K ROM bank xxxxx to $FC00
D7 D6 D5 D4D3D2D1D0
0 0 0 x x x x x switch a 1K ROM bank xxxxx to $F000
0 0 1 switch a 1K ROM bank xxxxx to $F400
0 1 0 switch a 1K ROM bank xxxxx to $F800
0 1 1 switch a 1K ROM bank xxxxx to $FC00
RAM switching (write of segment+bank number to $3F) with D7=1 and D6D5 upper 2 bits of bank #
indicates the destination RAM segment (0-3, corresponding to $F000, $F200, $F400, $F600).
Note that this allows contiguous 2K of RAM to be configured by setting 4 consecutive RAM segments
each 512 bytes with consecutive addresses. However, as the write address of RAM is +0x800, this
invalidates ROM access as described below.
RAM switching (write of segment+bank number to $3F) with D7=1 and D6D5 upper 2 bits of bank #
indicates the destination RAM segment (0-3, corresponding to $F000, $F200, $F400, $F600).
Note that this allows contiguous 2K of RAM to be configured by setting 4 consecutive RAM segments
each 512 bytes with consecutive addresses. However, as the write address of RAM is +0x800, this
invalidates ROM access as described below.
can handle 32 x 512 byte RAM banks (16K total)
can handle 32 x 512 byte RAM banks (16K total)
D7 D6 D5 D4D3D2D1D0
1 0 0 x x x x x switch a 512 byte RAM bank xxxxx to $F000 with write @ $F800
0 1 switch a 512 byte RAM bank xxxxx to $F200 with write @ $FA00
1 0 switch a 512 byte RAM bank xxxxx to $F400 with write @ $FC00
1 1 switch a 512 byte RAM bank xxxxx to $F600 with write @ $FE00
D7 D6 D5 D4D3D2D1D0
1 0 0 x x x x x switch a 512 byte RAM bank xxxxx to $F000 with write @ $F800
0 1 switch a 512 byte RAM bank xxxxx to $F200 with write @ $FA00
1 0 switch a 512 byte RAM bank xxxxx to $F400 with write @ $FC00
1 1 switch a 512 byte RAM bank xxxxx to $F600 with write @ $FE00
It is possible to switch multiple RAM banks and ROM banks together
It is possible to switch multiple RAM banks and ROM banks together
For example,
F000-F1FF RAM bank A (512 byte READ)
F200-F3FF high 512 bytes of ROM bank previously loaded at F000
F400 ROM bank 0 (1K)
F800 RAM bank A (512 byte WRITE)
FA00-FBFF high 512 bytes of ROM bank previously loaded at F400
FC00 ROM bank 1
For example,
F000-F1FF RAM bank A (512 byte READ)
F200-F3FF high 512 bytes of ROM bank previously loaded at F000
F400 ROM bank 0 (1K)
F800 RAM bank A (512 byte WRITE)
FA00-FBFF high 512 bytes of ROM bank previously loaded at F400
FC00 ROM bank 1
This example shows 512 bytes of RAM, and 2 1K ROM banks and two 512 byte ROM
bank halves.
This example shows 512 bytes of RAM, and 2 1K ROM banks and two 512 byte ROM
bank halves.
Switching RAM blocks (D7D6 of $3E) partially invalidates ROM blocks, as below...
Switching RAM blocks (D7D6 of $3E) partially invalidates ROM blocks, as below...
RAM block Invalidates ROM block
0 0 (lower half), 2 (lower half)
1 0 (upper half), 2 (upper half)
2 1 (lower half), 3 (upper half)
3 1 (upper half), 3 (lower half)
RAM block Invalidates ROM block
0 0 (lower half), 2 (lower half)
1 0 (upper half), 2 (upper half)
2 1 (lower half), 3 (upper half)
3 1 (upper half), 3 (lower half)
For example, RAM block 1 uses address $F200-$F3FF and $FA00-$FBFF
ROM block 0 uses address $F000-$F3FF, and ROM block 2 uses address $F800-$FBFF
Switching in RAM block 1 makes F200-F3FF ROM inaccessible, however F000-F1FF is
still readable. So, care must be paid.
For example, RAM block 1 uses address $F200-$F3FF and $FA00-$FBFF
ROM block 0 uses address $F000-$F3FF, and ROM block 2 uses address $F800-$FBFF
Switching in RAM block 1 makes F200-F3FF ROM inaccessible, however F000-F1FF is
still readable. So, care must be paid.
TODO: THe partial reading of ROM blocks switched out by RAM is not yet implemented!!
TODO: THe partial reading of ROM blocks switched out by RAM is not yet implemented!!
This crazy RAM layout is useful as it allows contiguous RAM to be switched in,
up to 2K in one sequentially accessible block. This means you CAN have 2K of
consecutive RAM. If you don't detect ROM write area, then you would have NO ROM
switched in (don't forget to copy your reset vectors!)
This crazy RAM layout is useful as it allows contiguous RAM to be switched in,
up to 2K in one sequentially accessible block. This means you CAN have 2K of
consecutive RAM. If you don't detect ROM write area, then you would have NO ROM
switched in (don't forget to copy your reset vectors!)
@author Andrew Davie
*/
@author Andrew Davie
*/
class CartridgeDASH: public Cartridge
{
class CartridgeDASH: public Cartridge {
friend class CartridgeDASHWidget;
public:
/**
Create a new cartridge using the specified image and size
public:
/**
Create a new cartridge using the specified image and size
@param image Pointer to the ROM image
@param size The size of the ROM image
@param settings A reference to the various settings (read-only)
*/
CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings);
@param image Pointer to the ROM image
@param size The size of the ROM image
@param settings A reference to the various settings (read-only)
*/
CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings);
/**
Destructor
*/
virtual ~CartridgeDASH();
/**
Destructor
*/
virtual ~CartridgeDASH();
public:
/**
Reset device to its power-on state
*/
void reset();
public:
/**
Reset device to its power-on state
*/
void reset();
/**
Install cartridge in the specified system. Invoked by the system
when the cartridge is attached to it.
/**
Install cartridge in the specified system. Invoked by the system
when the cartridge is attached to it.
@param system The system the device should install itself in
*/
void install(System& system);
@param system The system the device should install itself in
*/
void install(System& system);
/**
Install pages for the specified bank in the system.
/**
Install pages for the specified bank in the system.
@param bank The bank that should be installed in the system
*/
bool bank(uInt16 bank);
@param bank The bank that should be installed in the system
*/
bool bank(uInt16 bank);
/**
Get the current bank.
*/
uInt16 bank() const;
/**
Get the current bank.
*/
uInt16 bank() const;
/**
Query the number of banks supported by the cartridge.
*/
uInt16 bankCount() const;
/**
Query the number of banks supported by the cartridge.
*/
uInt16 bankCount() const;
/**
Patch the cartridge ROM.
/**
Patch the cartridge ROM.
@param address The ROM address to patch
@param value The value to place into the address
@return Success or failure of the patch operation
*/
bool patch(uInt16 address, uInt8 value);
@param address The ROM address to patch
@param value The value to place into the address
@return Success or failure of the patch operation
*/
bool patch(uInt16 address, uInt8 value);
/**
Access the internal ROM image for this cartridge.
/**
Access the internal ROM image for this cartridge.
@param size Set to the size of the internal ROM image data
@return A pointer to the internal ROM image data
*/
const uInt8* getImage(int& size) const;
@param size Set to the size of the internal ROM image data
@return A pointer to the internal ROM image data
*/
const uInt8* getImage(int& size) const;
/**
Save the current state of this cart to the given Serializer.
/**
Save the current state of this cart to the given Serializer.
@param out The Serializer object to use
@return False on any errors, else true
*/
bool save(Serializer& out) const;
@param out The Serializer object to use
@return False on any errors, else true
*/
bool save(Serializer& out) const;
/**
Load the current state of this cart from the given Serializer.
/**
Load the current state of this cart from the given Serializer.
@param in The Serializer object to use
@return False on any errors, else true
*/
bool load(Serializer& in);
@param in The Serializer object to use
@return False on any errors, else true
*/
bool load(Serializer& in);
/**
Get a descriptor for the device name (used in error checking).
/**
Get a descriptor for the device name (used in error checking).
@return The name of the object
*/
string name() const { return "CartridgeDASH"; }
@return The name of the object
*/
string name() const {
return "CartridgeDASH";
}
#ifdef DEBUGGER_SUPPORT
/**
Get debugger widget responsible for accessing the inner workings
of the cart.
*/
CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont, int x, int y, int w, int h)
{
return 0;//new CartridgeDASHWidget(boss, lfont, nfont, x, y, w, h, *this);
}
/**
Get debugger widget responsible for accessing the inner workings
of the cart.
*/
CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont, int x, int y, int w, int h)
{
return 0; //new CartridgeDASHWidget(boss, lfont, nfont, x, y, w, h, *this);
}
#endif
public:
/**
Get the byte at the specified address
public:
/**
Get the byte at the specified address
@return The byte at the specified address
*/
uInt8 peek(uInt16 address);
@return The byte at the specified address
*/
uInt8 peek(uInt16 address);
/**
Change the byte at the specified address to the given value
/**
Change the byte at the specified address to the given value
@param address The address where the value should be stored
@param value The value to be stored at the address
@return True if the poke changed the device address space, else false
*/
bool poke(uInt16 address, uInt8 value);
@param address The address where the value should be stored
@param value The value to be stored at the address
@return True if the poke changed the device address space, else false
*/
bool poke(uInt16 address, uInt8 value);
private:
uInt16 myCurrentBank; // whatever the LAST switched bank was...
private:
Int16 myCurrentBank; // whatever the LAST switched bank was...
uInt32 mySize; // Size of the ROM image
uInt8* myImage; // Pointer to a dynamically allocated ROM image of the cartridge
uInt32 mySize; // Size of the ROM image
uInt8* myImage; // Pointer to a dynamically allocated ROM image of the cartridge
Int16 bankInUse[4]; // bank being used for ROM/RAM (-1 = undefined)
Int16 bankInUse[4]; // bank being used for ROM/RAM (-1 = undefined)
static const uInt16 BANK_SWITCH_HOTSPOT = 0x3F; // writes to this address cause bankswitching
static const uInt16 BANK_SWITCH_HOTSPOT = 0x3F; // writes to this address cause bankswitching
static const uInt8 BANK_BITS = 5; // # bits for bank
static const uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static const uInt8 ROMRAM = 0x80; // flags ROM or RAM bank switching (1==RAM)
static const uInt8 BANK_BITS = 5; // # bits for bank
static const uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static const uInt8 BITMASK_ROMRAM = 0x80; // flags ROM or RAM bank switching (1==RAM)
static const uInt16 RAM_BANK_COUNT = 32;
static const uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static const uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static const uInt32 RAM_TOTAL_SIZE = RAM_BANK_COUNT * RAM_BANK_SIZE;
static const uInt16 RAM_BANK_COUNT = 32;
static const uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static const uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static const uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
static const uInt32 RAM_TOTAL_SIZE = RAM_BANK_COUNT * RAM_BANK_SIZE;
static const uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static const uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static const uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static const uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static const uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE -1);
static const uInt16 ROM_BANK_COUNT = 32;
static const uInt16 ROM_BANK_MASK = (ROM_BANK_COUNT - 1);
static const uInt16 ROM_BANK_COUNT = 32;
static const uInt16 BITMASK_ROM_BANK = (ROM_BANK_COUNT - 1);
static const uInt16 RAM_WRITE_OFFSET = 0x800;
static const uInt16 RAM_WRITE_OFFSET = 0x800;
static const Int16 BANK_UNDEFINED = -1; // bank is undefined and inaccessible
static const Int16 BANK_UNDEFINED = -1; // bank is undefined and inaccessible
uInt8 myRAM[RAM_TOTAL_SIZE];
uInt8 myRAM[RAM_TOTAL_SIZE];
};
#endif