diff --git a/Changes.txt b/Changes.txt index 7383fb052..1e0d4285c 100644 --- a/Changes.txt +++ b/Changes.txt @@ -25,6 +25,8 @@ * Added Joy2B+ controller support + * Enhanced Kid Vid support to play tape audio + * Added BUS bankswitching support for some older demos * Fixed broken 7800 pause key support diff --git a/docs/index.html b/docs/index.html index e7acb1993..168d97e0e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -21,7 +21,7 @@

A multi-platform Atari 2600 VCS emulator

-

Release 6.7

+

Release 7.0


User's Guide

@@ -282,6 +282,7 @@
  • Emulates Spectravideo CompuMate system using your computer's keyboard, including mapping of CompuMate 'Backspace', 'Space' and 'Enter' functionality to to the actual keys on your keyboard
  • +
  • Emulates the Kid Vid Voice Module
  • Supports autodetection for most common controller types
  • Support for real Atari 2600 controllers using the Stelladaptor and @@ -1047,6 +1048,17 @@ +
    +

    Kid Vid Voice Module (can be remapped via Keyboard Controller)

    + + + + + + + +
    Function Key
    Start game #18 (same as Right Pad Button '1')
    Start game #29 (same as Right Pad Button '2')
    Start game #30 (same as Right Pad Button '3')
    Skip current songP (same as Right Pad Button '6')
    +

    CompuMate Controller (cannot be remapped)

    @@ -1071,7 +1083,6 @@ [ [ or Shift + 8 ] ] or Shift + 9 " " (Shift + ') or Shift + 0 -
    @@ -5023,7 +5034,12 @@ Ms Pac-Man (Stella extended codes): CompuMate ¹Spectravideo CompuMate (if either left or right is set, CompuMate is used for both). LightgunAtari XG-1 compatible Light Gun. MindLink ¹MindLink controller. - KidVidKid Vid Voice Module, limited support (Right Keyboard controller buttons 1, 2 and 3 start the games, default mapping is 8, 9 and 0). + KidVidKid Vid Voice Module.
    + Audio files can be downloaded e.g. from AtariAge. + Put unzipped .WAV files into Stella's base directory (see properties + or palette files below for details).
    + Note: Stella supports playing the games without audio files too. + QuadTariQuadTari controller, limited support (see below). diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx index d23f1ad52..679a7e641 100644 --- a/src/common/SoundSDL2.cxx +++ b/src/common/SoundSDL2.cxx @@ -453,7 +453,7 @@ void SoundSDL2::stopWav() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 SoundSDL2::wavSize() const { - return myWavBuffer ? myWavLen /*SDL_GetQueuedAudioSize(myWavDevice)*/ : 0; + return myWavBuffer ? myWavLen : 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/StateManager.hxx b/src/common/StateManager.hxx index e5b265877..6383accf2 100644 --- a/src/common/StateManager.hxx +++ b/src/common/StateManager.hxx @@ -18,7 +18,7 @@ #ifndef STATE_MANAGER_HXX #define STATE_MANAGER_HXX -#define STATE_HEADER "06070000state" +#define STATE_HEADER "06070002state" class OSystem; class RewindManager; diff --git a/src/emucore/KidVid.cxx b/src/emucore/KidVid.cxx index adc87a140..0246b8691 100644 --- a/src/emucore/KidVid.cxx +++ b/src/emucore/KidVid.cxx @@ -29,9 +29,9 @@ KidVid::KidVid(Jack jack, const Event& event, const System& system, { // Right now, there are only two games that use the KidVid if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6") - myGame = BBears; // Berenstain Bears + myGame = Game::BBears; // Berenstain Bears else if(romMd5 == "a204cd4fb1944c86e800120706512a64") - myGame = Smurfs; // Smurfs Save the Day + myGame = Game::Smurfs; // Smurfs Save the Day else myEnabled = false; } @@ -58,12 +58,29 @@ void KidVid::update() if(!myEnabled) return; - if(myEvent.get(Event::ConsoleReset)) + if(myContinueSong) + { + // Continue playing song after state load + const uInt8 temp = ourSongPositions[mySongPointer - 1] & 0x7f; + const uInt32 songLength = ourSongStart[temp + 1] - ourSongStart[temp]; + + // Play the remaining WAV file + const string& fileName = myBaseDir + ((temp < 10) ? "KVSHARED.WAV" : getFileName()); + mySound.playWav(fileName, ourSongStart[temp] + (songLength - mySongLength), mySongLength); + + myContinueSong = false; + } + + if(myGame == Game::Smurfs && myEvent.get(Event::ConsoleReset)) // Reset does not work with BBears! { myTape = 0; // rewind Kid Vid tape myFilesFound = mySongPlaying = false; mySound.stopWav(); } + else if(myEvent.get(Event::RightKeyboard6)) + { + mySound.stopWav(); + } if(!myTape) { if(myEvent.get(Event::RightKeyboard1)) @@ -71,10 +88,10 @@ void KidVid::update() else if(myEvent.get(Event::RightKeyboard2)) myTape = 3; else if(myEvent.get(Event::RightKeyboard3)) - myTape = myGame == BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day? + myTape = myGame == Game::BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day? if(myTape) { - myIdx = myGame == BBears ? NumBlockBits : 0; + myIdx = myGame == Game::BBears ? NumBlockBits : 0; // KVData48/KVData44 myBlockIdx = NumBlockBits; myBlock = 0; openSampleFiles(); @@ -94,17 +111,17 @@ void KidVid::update() if(!myBlockIdx) { if(!myBlock) - myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; //KVData00-KVData=12 + myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; // KVData00 - ourData = 12 (2 * 6) else { - const uInt32 lastBlock = myGame == Smurfs + const uInt32 lastBlock = myGame == Game::Smurfs ? ourBlocks[myTape - 1] : ourBlocks[myTape + 2 - 1]; if(myBlock >= lastBlock) - myIdx = 42 * 8; //KVData80-KVData=42 + myIdx = 42 * 8; // KVData80 - ourData = 42 (7 * 6) else { - myIdx = 36 * 8;//KVPause-KVData=36 + myIdx = 36 * 8; // KVPause - ourData = 36 (6 * 6) setNextSong(); } } @@ -117,9 +134,10 @@ void KidVid::update() { if(mySongPlaying) { - myTapeBusy = (mySound.wavSize() > 262 * 48) || !myBeep; + mySongLength = mySound.wavSize(); + myTapeBusy = (mySongLength > 262 * 48) || !myBeep; // Check for end of played sample - if(mySound.wavSize() == 0) + if(mySongLength == 0) { mySongPlaying = false; myTapeBusy = !myBeep; @@ -139,12 +157,62 @@ void KidVid::update() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void KidVid::openSampleFiles() +bool KidVid::save(Serializer& out) const +{ + // Save WAV player state + out.putInt(myTape); + out.putBool(myFilesFound); + out.putBool(myTapeBusy); + out.putBool(myBeep); + out.putBool(mySongPlaying); + out.putInt(mySongPointer); + out.putInt(mySongLength); + // Save tape input simulation state + out.putInt(myIdx); + out.putInt(myBlockIdx); + out.putInt(myBlock); + + return Controller::save(out); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool KidVid::load(Serializer& in) +{ + // Load WAV player state + myTape = in.getInt(); + myFilesFound = in.getBool(); + myTapeBusy = in.getBool(); + myBeep = in.getBool(); + mySongPlaying = in.getBool(); + mySongPointer = in.getInt(); + mySongLength = in.getInt(); + // Load tape input simulation state + myIdx = in.getInt(); + myBlockIdx = in.getInt(); + myBlock = in.getInt(); + + myContinueSong = myFilesFound && mySongPlaying; + + return Controller::load(in); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* KidVid::getFileName() const { static constexpr const char* fileNames[6] = { "KVS3.WAV", "KVS1.WAV", "KVS2.WAV", "KVB3.WAV", "KVB1.WAV", "KVB2.WAV" }; + + int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2; + if(myTape == 4) i = 3; + + return fileNames[i]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void KidVid::openSampleFiles() +{ static constexpr uInt32 firstSongPointer[6] = { 44 + 38, 0, @@ -156,13 +224,11 @@ void KidVid::openSampleFiles() if(!myFilesFound) { - int i = myGame == Smurfs ? myTape - 1 : myTape + 2; + int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2; if(myTape == 4) i = 3; - mySampleFile = myBaseDir + fileNames[i]; - std::ifstream f1, f2; - f1.open(mySampleFile); + f1.open(myBaseDir + getFileName()); f2.open(myBaseDir + "KVSHARED.WAV"); myFilesFound = f1.is_open() && f2.is_open(); @@ -170,7 +236,7 @@ void KidVid::openSampleFiles() #ifdef DEBUG_BUILD if(myFilesFound) cerr << endl - << "found file: " << fileNames[i] << endl + << "found file: " << getFileName() << endl << "found file: " << "KVSHARED.WAV" << endl; #endif @@ -191,8 +257,8 @@ void KidVid::setNextSong() mySongLength = ourSongStart[temp + 1] - ourSongStart[temp]; // Play the WAV file - const string& fileName = (temp < 10) ? myBaseDir + "KVSHARED.WAV" : mySampleFile; - mySound.playWav(fileName, ourSongStart[temp], mySongLength); + const string& fileName = (temp < 10) ? "KVSHARED.WAV" : getFileName(); + mySound.playWav(myBaseDir + fileName, ourSongStart[temp], mySongLength); #ifdef DEBUG_BUILD cerr << fileName << ": " << (ourSongPositions[mySongPointer] & 0x7f) << endl; #endif @@ -216,7 +282,7 @@ const std::array KidVid::ourBlocks = { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const std::array KidVid::ourData = { -/* KVData44 */ +/* KVData44, Smurfs */ 0x7b, // 0111 1011b ; (1)0 0x1e, // 0001 1110b ; 1 0xc6, // 1100 0110b ; 00 @@ -224,7 +290,7 @@ const std::array KidVid::ourData = { 0xec, // 1110 1100b ; 0 0x60, // 0110 0000b ; 0+ -/* KVData48 */ +/* KVData48, BBears */ 0x7b, // 0111 1011b ; (1)0 0x1e, // 0001 1110b ; 1 0xc6, // 1100 0110b ; 00 diff --git a/src/emucore/KidVid.hxx b/src/emucore/KidVid.hxx index 57743ce64..5f65711f6 100644 --- a/src/emucore/KidVid.hxx +++ b/src/emucore/KidVid.hxx @@ -67,12 +67,31 @@ class KidVid : public Controller */ void update() override; + /** + Saves the current state of this controller to the given Serializer. + + @param out The serializer device to save to. + @return The result of the save. True on success, false on failure. + */ + bool save(Serializer& out) const override; + + /** + Loads the current state of this controller from the given Serializer. + + @param in The serializer device to load from. + @return The result of the load. True on success, false on failure. + */ + bool load(Serializer& in) override; + /** Returns the name of this controller. */ string name() const override { return "KidVid"; } private: + // Get name of the current sample file + const char* getFileName() const; + // Open/close a WAV sample file void openSampleFiles(); @@ -80,36 +99,38 @@ class KidVid : public Controller void setNextSong(); private: + enum class Game { + Smurfs, + BBears + }; static constexpr uInt32 - Smurfs = 0x44, - BBears = 0x48, - NumBlocks = 6, // number of bytes / block - NumBlockBits = NumBlocks*8, // number of bits / block - SongPosSize = 44+38+42+62+80+62, + NumBlocks = 6, // number of bytes / block + NumBlockBits = NumBlocks * 8, // number of bits / block + SongPosSize = 44 + 38 + 42 + 62 + 80 + 62, SongStartSize = 104 ; // Whether the KidVid device is enabled (only for games that it - // supports, and if it's plugged into the right port + // supports, and if it's plugged into the right port) bool myEnabled{false}; string myBaseDir; Sound& mySound; - // Path and name of the current sample file - string mySampleFile; // Indicates if the sample files have been found bool myFilesFound{false}; - uInt32 mySongPointer{0}; - // Is the tape currently 'busy' / in use? bool myTapeBusy{false}; bool mySongPlaying{false}; + // Continue song after loading state? + bool myContinueSong{false}; + uInt32 mySongPointer{0}; uInt32 mySongLength{0}; bool myBeep{false}; - uInt32 myGame{0}, myTape{0}; + Game myGame{Game::Smurfs}; + uInt32 myTape{0}; uInt32 myIdx{0}, myBlock{0}, myBlockIdx{0}; // Number of blocks and data on tape