diff --git a/src/emucore/CompuMate.cxx b/src/emucore/CompuMate.cxx index 7d28ad12e..37527dcb9 100644 --- a/src/emucore/CompuMate.cxx +++ b/src/emucore/CompuMate.cxx @@ -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 } diff --git a/src/emucore/CompuMate.hxx b/src/emucore/CompuMate.hxx index 4a8e98b82..57f8a7269 100644 --- a/src/emucore/CompuMate.hxx +++ b/src/emucore/CompuMate.hxx @@ -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 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); + CompuMate(const Event& event, const System& system); /** Destructor + Controllers are deleted outside this class */ - virtual ~CompuMate(); + virtual ~CompuMate() { } public: /** - Called after *all* digital pins have been written on Port A. - */ - void controlWrite(); - - /** - Update the entire digital and analog pin state according to the - events currently set. + 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 + */ + 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 ~CMControl() { } + + public: + /** + Called after *all* digital pins have been written on Port A. + */ + void controlWrite() { myHandler.update(); } + + /** + Update the entire digital and analog pin state according to the + events currently set. + */ + 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; }; diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 1e3eced4b..6dca643ec 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -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); diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index ab274e0b5..d061056cb 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -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; diff --git a/src/emucore/Control.hxx b/src/emucore/Control.hxx index 2ae9feaa9..87e6933d4 100644 --- a/src/emucore/Control.hxx +++ b/src/emucore/Control.hxx @@ -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: /** diff --git a/src/emucore/DefProps.hxx b/src/emucore/DefProps.hxx index ac87cf57d..d1cfc135f 100644 --- a/src/emucore/DefProps.hxx +++ b/src/emucore/DefProps.hxx @@ -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", "", "", "" }, diff --git a/src/emucore/M6532.cxx b/src/emucore/M6532.cxx index 5ef947a58..acf3cda87 100644 --- a/src/emucore/M6532.cxx +++ b/src/emucore/M6532.cxx @@ -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(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/stella.pro b/src/emucore/stella.pro index 5f7a8c924..5c9ad1c11 100644 --- a/src/emucore/stella.pro +++ b/src/emucore/stella.pro @@ -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" ""