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 "Logger.hxx"
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
|
#include "PlusROM.hxx"
|
||||||
#include "CartEnhanced.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
|
// Only copy up to the amount of data the ROM provides; extra unused
|
||||||
// space will be filled with 0's from above
|
// space will be filled with 0's from above
|
||||||
std::copy_n(image.get(), std::min(mySize, size), myImage.get());
|
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);
|
System::PageAccess access(this, System::PageAccessType::READ);
|
||||||
|
|
||||||
// Set the page accessing method for the RAM writing pages
|
// 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;
|
access.type = System::PageAccessType::WRITE;
|
||||||
for(uInt16 addr = ROM_OFFSET + myWriteOffset; addr < ROM_OFFSET + myWriteOffset + myRamSize; addr += System::PAGE_SIZE)
|
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;
|
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
|
// hotspots in TIA range are reacting to pokes only
|
||||||
if (hotspot() >= 0x80)
|
if (hotspot() >= 0x80)
|
||||||
checkSwitchBank(address & ADDR_MASK);
|
checkSwitchBank(address & ADDR_MASK);
|
||||||
|
@ -170,6 +184,10 @@ uInt8 CartridgeEnhanced::peek(uInt16 address)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool CartridgeEnhanced::poke(uInt16 address, uInt8 value)
|
bool CartridgeEnhanced::poke(uInt16 address, uInt8 value)
|
||||||
{
|
{
|
||||||
|
// Is this a PlusROM?
|
||||||
|
if(myPlusROM.isValid() && myPlusROM.pokeHotspot(address, value))
|
||||||
|
return true;
|
||||||
|
|
||||||
// Switch banks if necessary
|
// Switch banks if necessary
|
||||||
if (checkSwitchBank(address & ADDR_MASK, value))
|
if (checkSwitchBank(address & ADDR_MASK, value))
|
||||||
return false;
|
return false;
|
||||||
|
@ -260,7 +278,7 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
|
||||||
myCurrentSegOffset[segment] = uInt32(mySize) + (ramBank << myBankShift);
|
myCurrentSegOffset[segment] = uInt32(mySize) + (ramBank << myBankShift);
|
||||||
|
|
||||||
// Set the page accessing method for the RAM writing pages
|
// 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 fromAddr = (ROM_OFFSET + segmentOffset + myWriteOffset) & ~System::PAGE_MASK;
|
||||||
uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset + (myBankSize >> 1)) & ~System::PAGE_MASK;
|
uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset + (myBankSize >> 1)) & ~System::PAGE_MASK;
|
||||||
System::PageAccess access(this, System::PageAccessType::WRITE);
|
System::PageAccess access(this, System::PageAccessType::WRITE);
|
||||||
|
@ -362,6 +380,8 @@ bool CartridgeEnhanced::save(Serializer& out) const
|
||||||
out.putIntArray(myCurrentSegOffset.get(), myBankSegs);
|
out.putIntArray(myCurrentSegOffset.get(), myBankSegs);
|
||||||
if(myRamSize > 0)
|
if(myRamSize > 0)
|
||||||
out.putByteArray(myRAM.get(), myRamSize);
|
out.putByteArray(myRAM.get(), myRamSize);
|
||||||
|
if(myPlusROM.isValid())
|
||||||
|
if(!myPlusROM.save(out)) return false;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
@ -380,6 +400,8 @@ bool CartridgeEnhanced::load(Serializer& in)
|
||||||
in.getIntArray(myCurrentSegOffset.get(), myBankSegs);
|
in.getIntArray(myCurrentSegOffset.get(), myBankSegs);
|
||||||
if(myRamSize > 0)
|
if(myRamSize > 0)
|
||||||
in.getByteArray(myRAM.get(), myRamSize);
|
in.getByteArray(myRAM.get(), myRamSize);
|
||||||
|
if(myPlusROM.isValid())
|
||||||
|
if(!myPlusROM.load(in)) return false;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,7 @@ class System;
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Cart.hxx"
|
#include "Cart.hxx"
|
||||||
|
#include "PlusROM.hxx"
|
||||||
#ifdef DEBUGGER_SUPPORT
|
#ifdef DEBUGGER_SUPPORT
|
||||||
#include "CartEnhancedWidget.hxx"
|
#include "CartEnhancedWidget.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
@ -159,7 +160,7 @@ class CartridgeEnhanced : public Cartridge
|
||||||
/**
|
/**
|
||||||
Get the hotspot in ROM address space.
|
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; }
|
virtual uInt16 hotspot() const { return 0; }
|
||||||
// TODO: handle cases where there the hotspots cover multiple pages
|
// TODO: handle cases where there the hotspots cover multiple pages
|
||||||
|
@ -224,6 +225,9 @@ class CartridgeEnhanced : public Cartridge
|
||||||
// The size of the ROM image
|
// The size of the ROM image
|
||||||
size_t mySize{0};
|
size_t mySize{0};
|
||||||
|
|
||||||
|
// Handle PlusROM functionality, if available
|
||||||
|
PlusROM myPlusROM;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// The mask for 6507 address space
|
// The mask for 6507 address space
|
||||||
static constexpr uInt16 ADDR_MASK = 0x1FFF;
|
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/MD5.o \
|
||||||
src/emucore/OSystem.o \
|
src/emucore/OSystem.o \
|
||||||
src/emucore/Paddles.o \
|
src/emucore/Paddles.o \
|
||||||
|
src/emucore/PlusROM.o \
|
||||||
src/emucore/PointingDevice.o \
|
src/emucore/PointingDevice.o \
|
||||||
src/emucore/ProfilingRunner.o \
|
src/emucore/ProfilingRunner.o \
|
||||||
src/emucore/Props.o \
|
src/emucore/Props.o \
|
||||||
|
|
Loading…
Reference in New Issue