mirror of https://github.com/stella-emu/stella.git
Added beginning infrastructure for PlusROM support.
For now, none of the network functionality is present; PlusROM's are correctly detected, though.
This commit is contained in:
parent
026f64d69d
commit
37c61fe93e
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "Logger.hxx"
|
||||
#include "System.hxx"
|
||||
#include "PlusROM.hxx"
|
||||
#include "CartEnhanced.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -54,6 +55,11 @@ CartridgeEnhanced::CartridgeEnhanced(const ByteBuffer& image, size_t size,
|
|||
// Only copy up to the amount of data the ROM provides; extra unused
|
||||
// space will be filled with 0's from above
|
||||
std::copy_n(image.get(), std::min(mySize, size), myImage.get());
|
||||
|
||||
// Determine whether we have a PlusROM cart
|
||||
// PlusROM needs to call peek() method, so disable direct peeks
|
||||
if(myPlusROM.initialize(myImage, mySize))
|
||||
myDirectPeek = false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -91,7 +97,7 @@ void CartridgeEnhanced::install(System& system)
|
|||
System::PageAccess access(this, System::PageAccessType::READ);
|
||||
|
||||
// Set the page accessing method for the RAM writing pages
|
||||
// Note: Writes are mapped to poke() (NOT using direcPokeBase) to check for read from write port (RWP)
|
||||
// Note: Writes are mapped to poke() (NOT using directPokeBase) to check for read from write port (RWP)
|
||||
access.type = System::PageAccessType::WRITE;
|
||||
for(uInt16 addr = ROM_OFFSET + myWriteOffset; addr < ROM_OFFSET + myWriteOffset + myRamSize; addr += System::PAGE_SIZE)
|
||||
{
|
||||
|
@ -141,6 +147,14 @@ uInt8 CartridgeEnhanced::peek(uInt16 address)
|
|||
{
|
||||
const uInt16 peekAddress = address;
|
||||
|
||||
// Is this a PlusROM?
|
||||
if(myPlusROM.isValid())
|
||||
{
|
||||
uInt8 value = 0;
|
||||
if(myPlusROM.peekHotspot(address, value))
|
||||
return value;
|
||||
}
|
||||
|
||||
// hotspots in TIA range are reacting to pokes only
|
||||
if (hotspot() >= 0x80)
|
||||
checkSwitchBank(address & ADDR_MASK);
|
||||
|
@ -170,6 +184,10 @@ uInt8 CartridgeEnhanced::peek(uInt16 address)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeEnhanced::poke(uInt16 address, uInt8 value)
|
||||
{
|
||||
// Is this a PlusROM?
|
||||
if(myPlusROM.isValid() && myPlusROM.pokeHotspot(address, value))
|
||||
return true;
|
||||
|
||||
// Switch banks if necessary
|
||||
if (checkSwitchBank(address & ADDR_MASK, value))
|
||||
return false;
|
||||
|
@ -260,7 +278,7 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
|
|||
myCurrentSegOffset[segment] = uInt32(mySize) + (ramBank << myBankShift);
|
||||
|
||||
// Set the page accessing method for the RAM writing pages
|
||||
// Note: Writes are mapped to poke() (NOT using direcPokeBase) to check for read from write port (RWP)
|
||||
// Note: Writes are mapped to poke() (NOT using directPokeBase) to check for read from write port (RWP)
|
||||
uInt16 fromAddr = (ROM_OFFSET + segmentOffset + myWriteOffset) & ~System::PAGE_MASK;
|
||||
uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset + (myBankSize >> 1)) & ~System::PAGE_MASK;
|
||||
System::PageAccess access(this, System::PageAccessType::WRITE);
|
||||
|
@ -362,6 +380,8 @@ bool CartridgeEnhanced::save(Serializer& out) const
|
|||
out.putIntArray(myCurrentSegOffset.get(), myBankSegs);
|
||||
if(myRamSize > 0)
|
||||
out.putByteArray(myRAM.get(), myRamSize);
|
||||
if(myPlusROM.isValid())
|
||||
if(!myPlusROM.save(out)) return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -380,6 +400,8 @@ bool CartridgeEnhanced::load(Serializer& in)
|
|||
in.getIntArray(myCurrentSegOffset.get(), myBankSegs);
|
||||
if(myRamSize > 0)
|
||||
in.getByteArray(myRAM.get(), myRamSize);
|
||||
if(myPlusROM.isValid())
|
||||
if(!myPlusROM.load(in)) return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ class System;
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "Cart.hxx"
|
||||
#include "PlusROM.hxx"
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
#include "CartEnhancedWidget.hxx"
|
||||
#endif
|
||||
|
@ -159,7 +160,7 @@ class CartridgeEnhanced : public Cartridge
|
|||
/**
|
||||
Get the hotspot in ROM address space.
|
||||
|
||||
@return The first hotspot address (ususally in ROM) space or 0
|
||||
@return The first hotspot address (usually in ROM) space or 0
|
||||
*/
|
||||
virtual uInt16 hotspot() const { return 0; }
|
||||
// TODO: handle cases where there the hotspots cover multiple pages
|
||||
|
@ -224,6 +225,9 @@ class CartridgeEnhanced : public Cartridge
|
|||
// The size of the ROM image
|
||||
size_t mySize{0};
|
||||
|
||||
// Handle PlusROM functionality, if available
|
||||
PlusROM myPlusROM;
|
||||
|
||||
protected:
|
||||
// The mask for 6507 address space
|
||||
static constexpr uInt16 ADDR_MASK = 0x1FFF;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-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 "PlusROM.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PlusROM::initialize(const ByteBuffer& image, size_t size)
|
||||
{
|
||||
// Host and path are stored at the NMI vector
|
||||
size_t i = ((image[size - 5] - 16) << 8) | image[size - 6]; // NMI @ $FFFA
|
||||
if(i >= size)
|
||||
return myIsPlusROM = false; // Invalid NMI
|
||||
|
||||
// Convenience functions to detect valid path and host characters
|
||||
auto isValidPathChar = [](uInt8 c) {
|
||||
return ((c > 44 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 122));
|
||||
};
|
||||
auto isValidHostChar = [](uInt8 c) {
|
||||
return (c == 45 || c == 46 || (c > 47 && c < 58) ||
|
||||
(c > 64 && c < 91) || (c > 96 && c < 122));
|
||||
};
|
||||
|
||||
// Path stored first, 0-terminated
|
||||
while(i < size && isValidPathChar(image[i]))
|
||||
myPath += static_cast<char>(image[i++]);
|
||||
|
||||
// Did we get a 0-terminated path?
|
||||
if(i >= size || image[i] != 0)
|
||||
return myIsPlusROM = false; // Wrong delimiter
|
||||
|
||||
i++; // advance past 0 terminator
|
||||
|
||||
// Host stored next, 0-terminated
|
||||
while(i < size && isValidHostChar(image[i]))
|
||||
myHost += static_cast<char>(image[i++]);
|
||||
|
||||
// Did we get a valid, 0-terminated host?
|
||||
if(i >= size || image[i] != 0 || myHost.size() < 3 || myHost.find(".") == string::npos)
|
||||
return myIsPlusROM = false; // Wrong delimiter or dotless IP
|
||||
|
||||
cerr << "Path: " << myPath << endl;
|
||||
cerr << "Host: " << myHost << endl;
|
||||
|
||||
return myIsPlusROM = true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PlusROM::peekHotspot(uInt16 address, uInt8& value)
|
||||
{
|
||||
switch(address & 0x0FFF)
|
||||
{
|
||||
case 0x0FF2: // Read next byte from Rx buffer
|
||||
return false;
|
||||
|
||||
case 0x0FF3: // Get number of unread bytes in Rx buffer
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PlusROM::pokeHotspot(uInt16 address, uInt8 value)
|
||||
{
|
||||
switch(address & 0x0FFF)
|
||||
{
|
||||
case 0x0FF0: // Write byte to Tx buffer
|
||||
return false;
|
||||
|
||||
case 0x0FF1: // Write byte to Tx buffer and send to backend
|
||||
// (and receive into Rx buffer)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PlusROM::save(Serializer& out) const
|
||||
{
|
||||
try
|
||||
{
|
||||
out.putByteArray(myRxBuffer.data(), myRxBuffer.size());
|
||||
out.putByteArray(myTxBuffer.data(), myTxBuffer.size());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cerr << "ERROR: PlusROM::save" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool PlusROM::load(Serializer& in)
|
||||
{
|
||||
try
|
||||
{
|
||||
in.getByteArray(myRxBuffer.data(), myRxBuffer.size());
|
||||
in.getByteArray(myTxBuffer.data(), myTxBuffer.size());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cerr << "ERROR: PlusROM::load" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-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.
|
||||
//============================================================================
|
||||
|
||||
#ifndef PLUSROM_HXX
|
||||
#define PLUSROM_HXX
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Serializable.hxx"
|
||||
|
||||
/**
|
||||
Class used to emulate the 'PlusROM' meta-scheme, documented at
|
||||
http://pluscart.firmaplus.de/pico/?PlusROM
|
||||
|
||||
This scheme basically wraps a normal bankswitching scheme, but includes
|
||||
network functionality.
|
||||
|
||||
Host and path names are stored as 0-terminated strings, located at the
|
||||
NMI vector, stored path first and then host next.
|
||||
|
||||
PlusROMs functions use 4 hotspot addresses (before the bankswitching area):
|
||||
$1FF0 is for writing a byte to the send buffer (max 256 bytes)
|
||||
$1FF1 is for writing a byte to the send buffer and submit the buffer
|
||||
to the back end API
|
||||
$1FF2 contains the next byte of the response from the host, every read will
|
||||
increment the receive buffer pointer (receive buffer is max 256 bytes also!)
|
||||
$1FF3 contains the number of (unread) bytes left in the receive buffer
|
||||
(these bytes can be from multiple responses)
|
||||
|
||||
@author Stephen Anthony
|
||||
*/
|
||||
class PlusROM : public Serializable
|
||||
{
|
||||
public:
|
||||
PlusROM() = default;
|
||||
~PlusROM() override = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
Determine whether this is actually a PlusROM cart, and if so create
|
||||
and initialize all state variables it will use. This includes
|
||||
whether there is a valid hostname and path embedded in the ROM.
|
||||
|
||||
@param image Pointer to the ROM image
|
||||
@param size The size of the ROM image
|
||||
|
||||
@return Whether this is actually a valid PlusROM cart
|
||||
*/
|
||||
bool initialize(const ByteBuffer& image, size_t size);
|
||||
|
||||
/**
|
||||
Answer whether this is a PlusROM cart. Note that until the
|
||||
initialize method has been called, this will always return false.
|
||||
|
||||
@return Whether this is actually a PlusROM cart
|
||||
*/
|
||||
bool isValid() const { return myIsPlusROM; }
|
||||
|
||||
/**
|
||||
Read from hotspot addresses ($1FF2 and $1FF3).
|
||||
|
||||
@param address The hotspot where the value should be read
|
||||
@param value The value read from the hotspot
|
||||
|
||||
@return Indicates whether the peek succeeded or failed
|
||||
(ie, whether it hit a hotspot)
|
||||
On failure, 'value' is not considered valid
|
||||
*/
|
||||
bool peekHotspot(uInt16 address, uInt8& value);
|
||||
|
||||
/**
|
||||
Write to hotspot addresses ($1FF0 and $1FF1).
|
||||
|
||||
@param address The hotspot where the value should be written
|
||||
@param value The value to be stored at the hotspot
|
||||
|
||||
@return Indicates whether the poke succeeded or failed
|
||||
(ie, whether it hit a hotspot)
|
||||
*/
|
||||
bool pokeHotspot(uInt16 address, uInt8 value);
|
||||
|
||||
/**
|
||||
Save the current state of this device to the given Serializer.
|
||||
|
||||
@param out The Serializer object to use
|
||||
@return False on any errors, else true
|
||||
*/
|
||||
bool save(Serializer& out) const override;
|
||||
|
||||
/**
|
||||
Load the current state of this device from the given Serializer.
|
||||
|
||||
@param in The Serializer object to use
|
||||
@return False on any errors, else true
|
||||
*/
|
||||
bool load(Serializer& in) override;
|
||||
|
||||
private:
|
||||
bool myIsPlusROM{false};
|
||||
string myPath, myHost;
|
||||
|
||||
std::array<uInt8, 256> myRxBuffer, myTxBuffer;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
PlusROM(const PlusROM&) = delete;
|
||||
PlusROM(PlusROM&&) = delete;
|
||||
PlusROM& operator=(const PlusROM&) = delete;
|
||||
PlusROM& operator=(PlusROM&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -76,6 +76,7 @@ MODULE_OBJS := \
|
|||
src/emucore/MD5.o \
|
||||
src/emucore/OSystem.o \
|
||||
src/emucore/Paddles.o \
|
||||
src/emucore/PlusROM.o \
|
||||
src/emucore/PointingDevice.o \
|
||||
src/emucore/ProfilingRunner.o \
|
||||
src/emucore/Props.o \
|
||||
|
|
Loading…
Reference in New Issue