Re-arranged the CompuMate controller code so that it can access both

controllers at once.  Stella wasn't initially designed to do this,
and the CompuMate is unique in this regard.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2412 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2012-03-14 01:19:23 +00:00
parent 80c43f175d
commit 47f151b87c
8 changed files with 132 additions and 122 deletions

View File

@ -17,37 +17,34 @@
// $Id$
//============================================================================
#include "Event.hxx"
#include "Control.hxx"
#include "System.hxx"
#include "CompuMate.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CompuMate::CompuMate(Jack jack, const Event& event, const System& system)
: Controller(jack, event, system, Controller::CompuMate),
CompuMate::CompuMate(const Event& event, const System& system)
: mySystem(system),
myLeftController(0),
myRightController(0),
myCycleAtLastUpdate(0),
myIOPort(0xff)
{
if(myJack == Left)
{
myAnalogPinValue[Five] = minimumResistance;
myAnalogPinValue[Nine] = maximumResistance;
}
else
{
myAnalogPinValue[Five] = maximumResistance;
myAnalogPinValue[Nine] = minimumResistance;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CompuMate::~CompuMate()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CompuMate::controlWrite()
{
myLeftController = new CMControl(*this, Controller::Left, event, system);
myRightController = new CMControl(*this, Controller::Right, event, system);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CompuMate::update()
{
uInt32 cycle = mySystem.cycles();
// Only perform update once for both ports in the same cycle
if(myCycleAtLastUpdate != cycle)
{
myCycleAtLastUpdate = cycle;
return;
}
myCycleAtLastUpdate = cycle;
// TODO - handle SWCHA changes
}

View File

@ -25,116 +25,113 @@
#include "Event.hxx"
/**
Cartridge class used for SpectraVideo CompuMate bankswitched games.
Handler for SpectraVideo CompuMate bankswitched games.
This is more than just a cartridge mapper - it's also a "computer" add-on.
There's two 8K EPROMs soldered on top of each other. There's two short
wires with DB-9's on them which you plug into the two controller ports.
A 42 or so key membrane keyboard with audio in and audio out, and 2K of RAM.
The specifics of the CompuMate format can be found in both the Cart side
(CartCM) and the Controller side (CMControl). The CompuMate device is
unique for the 2600 in that it requires close co-operation between the
cartridge and the left and right controllers.
There are 4 4K banks selectable at $1000 - $1FFF, and 2K RAM at
$1800 - $1FFF (R/W 'line' is available at SWCHA D5, so there's no separate
read and write ports).
This class acts as a 'parent' for both the left and right CMControl's,
taking care of their creation and communication between them.
Bankswitching is done though the controller ports
SWCHA: D7 = Audio input from tape player
D6 = Audio out to tape player and 4017 CLK
1 -> increase key column (0 to 9)
D5 = 4017 RST, and RAM direction. (high = write, low = read)
1 -> reset key column to 0 (if D4 = 0)
0 -> enable RAM writing (if D4 = 1)
D4 = RAM enable: 1 = disable RAM, 0 = enable RAM
D3 = keyboard row 1 input (0 = key pressed)
D2 = keyboard row 1 input (0 = key pressed)
D1 = bank select high bit
D0 = bank select low bit
INPT0: D7 = CTRL key input (0 on startup / 1 = key pressed)
INPT1: D7 = always HIGH input (pulled high thru 20K resistor)
INPT2: D7 = always HIGH input (pulled high thru 20K resistor)
INPT3: D7 = SHIFT key input (0 on startup / 1 = key pressed)
INPT4: D7 = keyboard row 0 input (0 = key pressed)
INPT5: D7 = keyboard row 2 input (0 = key pressed)
The keyboard's composed of a 4017 1 of 10 counter, driving the 10 columns of
the keyboard. It has 4 rows. The 4 row outputs are buffered by inverters.
Bit 5 of portA controls the reset line on the 4017. Pulling it high will reset
scanning to column 0. Pulling it low will allow the counter to be clocked.
Bit 6 of portA clocks the 4017. Each rising edge advances the column one
count.
There's 10 columns labelled 0-9, and 4 rows, labelled 0-3.
Column
0 1 2 3 4 5 6 7 8 9
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| 7 | | 6 | | 8 | | 2 | | 3 | | 0 | | 9 | | 5 | | 1 | | 4 | 0
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| U | | Y | | I | | W | | E | | P | | O | | T | | Q | | R | 1
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ Row
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| J | | H | | K | | S | | D | |ent| | L | | G | | A | | F | 2
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
| M | | N | | < | | X | | C | |spc| | > | | B | | Z | | V | 3
+---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+ +---+
Function and Shift are separate keys that are read by 2 of the paddle inputs.
These two buttons pull the specific paddle input low when pressed.
Because the inputs are inverted, a low indicates a pressed button, and a high
is an unpressed one.
The audio input/output are designed to drive a tape player. The audio output is
buffered through an inverter and 2 resistors and a capacitor to reduce the level
to feed it into the tape player.
The audio input is passed through a .1uf capacitor and is pulled to 1/2 supply
by two 20K resistors, then it goes through a hex inverting schmitt trigger to
square it up. This then runs into bit 7 of portA.
This code was heavily borrowed from z26, and uses conventions defined
there. Specifically, IOPortA is treated as a complete uInt8, whereas
the Stella core actually stores this information in boolean arrays
addressable by DigitalPin number.
@author Stephen Anthony & z26 team
@author Stephen Anthony
@version $Id$
*/
class CompuMate : public Controller
class CompuMate
{
public:
/**
Create a new MindLink controller plugged into the specified jack
Create a new CompuMate handler for both left and right ports.
Note that this class creates CMControl controllers for both ports,
but does not take responsibility for their deletion.
@param event The event object to use for events
@param system The system using this controller
*/
CompuMate(const Event& event, const System& system);
/**
Destructor
Controllers are deleted outside this class
*/
virtual ~CompuMate() { }
public:
/**
Called by the controller(s) when all pins have been written
*/
void update();
/**
Return the left and right CompuMate controllers
*/
Controller* leftController() { return (Controller*) myLeftController; }
Controller* rightController() { return (Controller*) myRightController; }
private:
// The actual CompuMate controller
// More information about these scheme can be found in CartCM.hxx
class CMControl : public Controller
{
public:
/**
Create a new CMControl controller plugged into the specified jack
@param handler Class which coordinates between left & right controllers
@param jack The jack the controller is plugged into
@param event The event object to use for events
@param system The system using this controller
*/
CompuMate(Jack jack, const Event& event, const System& system);
CMControl(class CompuMate& handler, Controller::Jack jack, const Event& event,
const System& system)
: Controller(jack, event, system, Controller::CompuMate),
myHandler(handler)
{
if(myJack == Left)
{
myAnalogPinValue[Five] = minimumResistance;
myAnalogPinValue[Nine] = maximumResistance;
}
else
{
myAnalogPinValue[Five] = maximumResistance;
myAnalogPinValue[Nine] = minimumResistance;
}
}
/**
Destructor
*/
virtual ~CompuMate();
virtual ~CMControl() { }
public:
/**
Called after *all* digital pins have been written on Port A.
*/
void controlWrite();
void controlWrite() { myHandler.update(); }
/**
Update the entire digital and analog pin state according to the
events currently set.
*/
void update();
void update() { }
private:
class CompuMate& myHandler;
};
private:
// System object
const System& mySystem;
// Left and right controllers
CMControl *myLeftController, *myRightController;
// System cycle at which the update() method is called
// Multiple calls at the same cycle should be ignored
uInt32 myCycleAtLastUpdate;
// Internal state of the port pins
uInt8 myIOPort;
};

View File

@ -74,6 +74,7 @@ Console::Console(OSystem* osystem, Cartridge* cart, const Properties& props)
mySwitches(0),
mySystem(0),
myCart(cart),
myCMHandler(0),
myDisplayFormat("NTSC"),
myFramerate(60.0),
myUserPaletteDefined(false)
@ -221,6 +222,7 @@ Console::~Console()
{
delete mySystem;
delete mySwitches;
delete myCMHandler;
delete myControllers[0];
delete myControllers[1];
}
@ -610,6 +612,17 @@ void Console::setControllers(const string& rommd5)
const string& left = myProperties.get(Controller_Left);
const string& right = myProperties.get(Controller_Right);
// Check for CompuMate controllers; they are special in that a handler
// creates them for us, and also that they must be used in both ports
if(left == "COMPUMATE" || right == "COMPUMATE")
{
delete myCMHandler;
myCMHandler = new CompuMate(myEvent, *mySystem);
myControllers[0] = myCMHandler->leftController();
myControllers[1] = myCMHandler->rightController();
return;
}
// Swap the ports if necessary
int leftPort, rightPort;
if(myProperties.get(Console_SwapPorts) == "NO")
@ -669,10 +682,6 @@ void Console::setControllers(const string& rommd5)
{
myControllers[leftPort] = new Genesis(Controller::Left, myEvent, *mySystem);
}
else if(left == "COMPUMATE")
{
myControllers[leftPort] = new CompuMate(Controller::Left, myEvent, *mySystem);
}
else if(left == "MINDLINK")
{
myControllers[leftPort] = new MindLink(Controller::Left, myEvent, *mySystem);
@ -740,10 +749,6 @@ void Console::setControllers(const string& rommd5)
{
myControllers[rightPort] = new Genesis(Controller::Right, myEvent, *mySystem);
}
else if(right == "COMPUMATE")
{
myControllers[rightPort] = new CompuMate(Controller::Right, myEvent, *mySystem);
}
else if(right == "KIDVID")
{
myControllers[rightPort] = new KidVid(Controller::Right, myEvent, *mySystem, rommd5);

View File

@ -27,6 +27,7 @@ class System;
class TIA;
class M6532;
class Cartridge;
class CompuMate;
#include "bspf.hxx"
#include "Control.hxx"
@ -339,6 +340,9 @@ class Console : public Serializable
// A RIOT of my own! (...with apologies to The Clash...)
M6532 *myRiot;
// Pointer to CompuMate handler (only used in CompuMate ROMs)
CompuMate* myCMHandler;
// The currently defined display format (NTSC/PAL/SECAM)
string myDisplayFormat;

View File

@ -67,6 +67,7 @@ class Controller : public Serializable
Riot debug class needs special access to the underlying controller state
*/
friend class RiotDebug;
friend class CompuMate;
public:
/**

View File

@ -2366,7 +2366,7 @@ static const char* DefProps[DEF_PROPS_SIZE][21] = {
{ "b9336ed6d94a5cc81a16483b0a946a73", "Atari, Jerome Domurat, Michael Sierchio", "CX2667, CX2667P", "RealSports Soccer (1983) (Atari) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "YES", "" },
{ "b958d5fd9574c5cf9ece4b9421c28ecd", "Piero Cavina", "", "Multi-Sprite Game V1.0 (Piero Cavina) (PD)", "", "New Release", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "b95a6274ca0e0c773bfdc06b4c3daa42", "Paul Slocum", "", "3-D Corridor (29-03-2003) (Paul Slocum)", "", "Homebrew", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "b98cc2c6f7a0f05176f74f0f62c45488", "Spectravideo", "SV-010", "CompuMate (1983) (Spectravideo)", "", "", "", "CM", "", "", "", "", "COMPUMATE", "COMPUMATE", "", "", "", "", "", "YES", "" },
{ "b98cc2c6f7a0f05176f74f0f62c45488", "Spectravideo", "SV-010", "CompuMate (1983) (Spectravideo)", "", "", "", "CM", "", "", "", "", "COMPUMATE", "COMPUMATE", "", "", "NTSC", "", "", "YES", "" },
{ "b9b4612358a0b2c1b4d66bb146767306", "Commavid, Ben Burch", "CM-010", "Rush Hour (1983) (Commavid) (Prototype)", "", "Prototype", "", "", "", "", "", "", "", "", "", "", "", "20", "230", "", "" },
{ "b9d1e3be30b131324482345959aed5e5", "Activision, Rex Bradford", "", "Kabobber (07-25-1983) (Activision) (Prototype)", "", "Prototype", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "b9f6fa399b8cd386c235983ec45e4355", "Parker Brothers, John Emerson", "931511", "Action Force (1983) (Parker Bros) (PAL)", "AKA G.I. Joe - Cobra Strike", "", "", "", "", "", "", "", "PADDLES", "", "", "01", "", "", "", "", "" },
@ -2979,7 +2979,7 @@ static const char* DefProps[DEF_PROPS_SIZE][21] = {
{ "e7864caaf9ec49ed67b1904ce8602690", "", "", "Donkey Kong 2K3 Pic (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "e7a758bb0b43d0f7004e92b9abf4bc83", "", "", "Troll's Adventure (Hack)", "Hack of Adventure", "Hack", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "e7dd8c2e6c100044002c1086d02b366e", "Activision, Steve Cartwright - Ariola", "EAX-013, PAX-013, 711 013-720", "Barnstorming (1982) (Activision) (PAL)", "AKA Die tollkeuhnen Flieger", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "e7f005ddb6902c648de098511f6ae2e5", "Spectravideo - Universum", "SV-010", "CompuMate (1983) (Spectravideo) (PAL)", "", "", "", "CM", "", "", "", "", "COMPUMATE", "COMPUMATE", "", "", "", "", "", "YES", "" },
{ "e7f005ddb6902c648de098511f6ae2e5", "Spectravideo - Universum", "SV-010", "CompuMate (1983) (Spectravideo) (PAL)", "", "", "", "CM", "", "", "", "", "COMPUMATE", "COMPUMATE", "", "", "PAL", "", "", "YES", "" },
{ "e800e4aec7c6c54c9cf3db0d1d030058", "", "", "Qb (2.06) (Retroactive) (Stella)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "YES", "" },
{ "e80a4026d29777c3c7993fbfaee8920f", "", "", "Frisco (Unknown)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" },
{ "e823b13751e4388f1f2a375d3560a8d7", "Arcadia Corporation, Stephen Harland Landrum", "AR-4105", "Official Frogger (Preview) (1983) (Arcadia) [a]", "", "", "", "", "", "", "", "", "", "", "", "", "", "32", "", "", "" },

View File

@ -279,14 +279,18 @@ void M6532::setPinState(bool swcha)
port0.write(Controller::Two, a & 0x20);
port0.write(Controller::Three, a & 0x40);
port0.write(Controller::Four, a & 0x80);
if(swcha) port0.controlWrite();
Controller& port1 = myConsole.controller(Controller::Right);
port1.write(Controller::One, a & 0x01);
port1.write(Controller::Two, a & 0x02);
port1.write(Controller::Three, a & 0x04);
port1.write(Controller::Four, a & 0x08);
if(swcha) port1.controlWrite();
if(swcha)
{
port0.controlWrite();
port1.controlWrite();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -2641,6 +2641,7 @@
"Cartridge.Type" "CM"
"Controller.Left" "COMPUMATE"
"Controller.Right" "COMPUMATE"
"Display.Format" "NTSC"
"Display.Phosphor" "YES"
""
@ -5268,6 +5269,7 @@
"Cartridge.Type" "CM"
"Controller.Left" "COMPUMATE"
"Controller.Right" "COMPUMATE"
"Display.Format" "PAL"
"Display.Phosphor" "YES"
""