text: //============================================================================
  //
  //   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-2016 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.
  //
  // $Id: KidVid.cxx 3244 2015-12-30 19:07:11Z stephena $
  //============================================================================
  
  #include <cstdlib>
  
  #include "KidVid.hxx"
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  KidVid::KidVid(Jack jack, const Event& event, const System& system,
      const string& rommd5)
      : Controller(jack, event, system, Controller::KidVid),
      myEnabled(myJack == Right),
      mySampleFile(nullptr),
      mySharedSampleFile(nullptr),
      myFileOpened(false),
      myTapeBusy(false),
      myFilePointer(0),
      mySongCounter(0),
      myBeep(false),
      mySharedData(false),
      mySampleByte(0),
      myGame(0),
      myTape(0),
      myIdx(0),
      myBlock(0),
      myBlockIdx(0)
  {
    // Right now, there are only two games that use the KidVid
      if (rommd5 == "ee6665683ebdb539e89ba620981cb0f6")
          myGame = KVBBEARS;    // Berenstain Bears
      else if (rommd5 == "a204cd4fb1944c86e800120706512a64")
          myGame = KVSMURFS;    // Smurfs Save the Day
      else
          myEnabled = false;
  
        // Analog pins are never used by the KidVid controller
        // (at least not in this implementation)
      myAnalogPinValue[Five] = myAnalogPinValue[Nine] = maximumResistance;
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  KidVid::~KidVid()
  {
      closeSampleFile();
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void KidVid::update()
  {
      if (!myEnabled)
          return;
  
      if (myEvent.get(Event::ConsoleReset))
      {
          myTape = 0; // rewind Kid Vid tape
          closeSampleFile();
      }
      if (myEvent.get(Event::KeyboardZero1))
      {
          myTape = 2;
          myIdx = myGame == KVBBEARS ? KVBLOCKBITS : 0;
          myBlockIdx = KVBLOCKBITS;
          myBlock = 0;
          openSampleFile();
          cerr << "myTape = " << myTape << endl;
      }
      else if (myEvent.get(Event::KeyboardZero2))
      {
          myTape = 3;
          myIdx = myGame == KVBBEARS ? KVBLOCKBITS : 0;
          myBlockIdx = KVBLOCKBITS;
          myBlock = 0;
          openSampleFile();
          cerr << "myTape = " << myTape << endl;
      }
      else if (myEvent.get(Event::KeyboardZero3))
      {
          if (myGame == KVBBEARS)  /* Berenstain Bears ? */
          {
              myTape = 4;
              myIdx = KVBLOCKBITS;
              cerr << "myTape = " << myTape << endl;
          }
          else                    /* no, Smurf Save The Day */
          {
              myTape = 1;
              myIdx = 0;
              cerr << "myTape = " << myTape << endl;
          }
          myBlockIdx = KVBLOCKBITS;
          myBlock = 0;
          openSampleFile();
      }
  
      // Convert separate pin states into a 'register'
      uInt8 IOPortA = 0xf0;
      if (myDigitalPinState[One])   IOPortA |= 0x01;
      if (myDigitalPinState[Two])   IOPortA |= 0x02;
      if (myDigitalPinState[Three]) IOPortA |= 0x04;
      if (myDigitalPinState[Four])  IOPortA |= 0x08;
  
      // Is the tape running?
      if ((myTape != 0) && ((IOPortA & 0x01) == 0x01) && !myTapeBusy)
      {
          IOPortA = (IOPortA & 0xf7) | (((ourKVData[myIdx >> 3] << (myIdx & 0x07)) & 0x80) >> 4);
  
          // increase to next bit
          myIdx++;
          myBlockIdx--;
  
          // increase to next block (byte)
          if (myBlockIdx == 0)
          {
              if (myBlock == 0)
                  myIdx = ((myTape * 6) + 12 - KVBLOCKS) * 8; //KVData00-KVData=12
              else
              {
                  if (myGame == KVSMURFS)
                  {
                      if (myBlock >= ourKVBlocks[myTape - 1])
                          myIdx = 42 * 8; //KVData80-KVData=42
                      else
                      {
                          myIdx = 36 * 8;//KVPause-KVData=36
                          setNextSong();
                      }
                  }
                  else
                  {
                      if (myBlock >= ourKVBlocks[myTape + 2 - 1])
                          myIdx = 42 * 8; //KVData80-KVData=42
                      else
                      {
                          myIdx = 36 * 8;//KVPause-KVData=36
                          setNextSong();
                      }
                  }
              }
              myBlock++;
              myBlockIdx = KVBLOCKBITS;
          }
      }
  
      // Now convert the register back into separate boolean values
      myDigitalPinState[One] = (IOPortA & 0x01) != 0;
      myDigitalPinState[Two] = (IOPortA & 0x02) != 0;
      myDigitalPinState[Three] = (IOPortA & 0x04) != 0;
      myDigitalPinState[Four] = (IOPortA & 0x08) != 0;
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void KidVid::openSampleFile()
  {
      static const char* kvNameTable[6] = {
        "kvs3.wav", "kvs1.wav", "kvs2.wav", "kvb3.wav", "kvb1.wav", "kvb2.wav"
      };
      static uInt32 StartSong[6] = {
        44 + 38, 0, 44, 44 + 38 + 42 + 62 + 80, 44 + 38 + 42, 44 + 38 + 42 + 62
      };
  
      if (!myEnabled)
          return;
  
      if (!myFileOpened)
      {
          int i = myGame == KVSMURFS ? 0 : 3;
          i += myTape - 1;
          if (myTape == 4) i -= 3;
  
          mySampleFile = fopen(kvNameTable[i], "rb");
          if (mySampleFile != NULL)
          {
              cerr << "opened file: " << kvNameTable[i] << endl;
              mySharedSampleFile = fopen("kvshared.wav", "rb");
              if (mySharedSampleFile == NULL)
              {
                  fclose(mySampleFile);
                  myFileOpened = false;
              }
              else
              {
                  cerr << "opened file: " << "kvshared.wav" << endl;
                  //         fseek(mySampleFile, 45, SEEK_SET);
                  myFileOpened = true;
              }
          }
          else
              myFileOpened = false;
  
          mySongCounter = 0;
          myTapeBusy = false;
          myFilePointer = StartSong[i];
      }
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void KidVid::closeSampleFile()
  {
      if (myFileOpened)
      {
          fclose(mySampleFile);
          fclose(mySharedSampleFile);
          myFileOpened = false;
      }
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void KidVid::setNextSong()
  {
      if (myFileOpened)
      {
          myBeep = (ourSongPositions[myFilePointer] & 0x80) ? false : true;
  
          uInt8 temp = ourSongPositions[myFilePointer] & 0x7f;
          mySharedData = (temp < 10);
          mySongCounter = ourSongStart[temp + 1] - ourSongStart[temp];
  
  #if 0
          if (mySharedData)
              ; // fseek(mySharedSampleFile, ourSongStart[temp], SEEK_SET);
          else
              ; // fseek(mySampleFile, ourSongStart[temp], SEEK_SET);
  #endif
  
          myFilePointer++;
          myTapeBusy = true;
      }
      else
      {
          myBeep = true;
          myTapeBusy = true;
          mySongCounter = 80 * 262;   /* delay needed for Harmony without tape */
      }
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void KidVid::getNextSampleByte()
  {
  //  static int oddeven = 0;
      if (mySongCounter == 0)
          mySampleByte = 0x80;
  #if 0
      else
      {
          oddeven = oddeven ^ 1;
          if (oddeven & 1)
          {
              mySongCounter--;
              myTapeBusy = (mySongCounter > 262 * 48) || !myBeep;
  
              if (myFileOpened)
              {
                  if (mySharedData)
                      mySampleByte = getc(mySharedSampleFile);
                  else
                      mySampleByte = getc(mySampleFile);
              }
              else
                  mySampleByte = 0x80;
  
              if (!myBeep && (mySongCounter == 0))
                  setNextSong();
          }
      }
  #endif
  }
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  const uInt8 KidVid::ourKVBlocks[6] = {
    2 + 40, 2 + 21, 2 + 35,     /* Smurfs tapes 3, 1, 2 */
    42 + 60, 42 + 78, 42 + 60   /* BBears tapes 1, 2, 3 (40 extra blocks for intro) */
  };
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  const uInt8 KidVid::ourKVData[6 * 8] = {
  /* KVData44 */
    0x7b,  // 0111 1011b  ; (1)0
    0x1e,  // 0001 1110b  ; 1
    0xc6,  // 1100 0110b  ; 00
    0x31,  // 0011 0001b  ; 01
    0xec,  // 1110 1100b  ; 0
    0x60,  // 0110 0000b  ; 0+
  
  /* KVData48 */
    0x7b,  // 0111 1011b  ; (1)0
    0x1e,  // 0001 1110b  ; 1
    0xc6,  // 1100 0110b  ; 00
    0x3d,  // 0011 1101b  ; 10
    0x8c,  // 1000 1100b  ; 0
    0x60,  // 0110 0000b  ; 0+
  
  /* KVData00 */
    0xf6,  // 1111 0110b
    0x31,  // 0011 0001b
    0x8c,  // 1000 1100b
    0x63,  // 0110 0011b
    0x18,  // 0001 1000b
    0xc0,  // 1100 0000b
  
  /* KVData01 */
    0xf6,  // 1111 0110b
    0x31,  // 0011 0001b
    0x8c,  // 1000 1100b
    0x63,  // 0110 0011b
    0x18,  // 0001 1000b
    0xf0,  // 1111 0000b
  
  /* KVData02 */
    0xf6,  // 1111 0110b
    0x31,  // 0011 0001b
    0x8c,  // 1000 1100b
    0x63,  // 0110 0011b
    0x1e,  // 0001 1110b
    0xc0,  // 1100 0000b
  
  /* KVData03 */
    0xf6,  // 1111 0110b
    0x31,  // 0011 0001b
    0x8c,  // 1000 1100b
    0x63,  // 0110 0011b
    0x1e,  // 0001 1110b
    0xf0,  // 1111 0000b
  
  /* KVPause */
    0x3f,  // 0011 1111b
    0xf0,  // 1111 0000b
    0x00,  // 0000 0000b
    0x00,  // 0000 0000b
    0x00,  // 0000 0000b
    0x00,  // 0000 0000b
  
  /* KVData80 */
    0xf7,  // 1111 0111b  ; marks end of tape (green/yellow screen)
    0xb1,  // 1011 0001b
    0x8c,  // 1000 1100b
    0x63,  // 0110 0011b
    0x18,  // 0001 1000b
    0xc0   // 1100 0000b
  };
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  const uInt8 KidVid::ourSongPositions[44 + 38 + 42 + 62 + 80 + 62] = {
  /* kvs1 44 */
    11, 12 + 0x80, 13 + 0x80, 14, 15 + 0x80, 16, 8 + 0x80, 17, 18 + 0x80, 19, 20 + 0x80,
    21, 8 + 0x80, 22, 15 + 0x80, 23, 18 + 0x80, 14, 20 + 0x80, 16, 18 + 0x80,
    17, 15 + 0x80, 19, 8 + 0x80, 21, 20 + 0x80, 22, 18 + 0x80, 23, 15 + 0x80,
    14, 20 + 0x80, 16, 8 + 0x80, 22, 15 + 0x80, 23, 18 + 0x80, 14, 20 + 0x80,
    16, 8 + 0x80, 9,
  
  /* kvs2 38 */
    25 + 0x80, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
    30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
    30 + 0x80, 9,
  
  /* kvs3 42 */
    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 34, 42, 36, 43, 40, 39, 38, 37,
    34, 43, 36, 39, 40, 37, 38, 43, 34, 37, 36, 43, 40, 39, 38, 37, 34, 43,
    36, 39, 40, 37, 38 + 0x80, 9,
  
  /* kvb1 62 */
    0, 1, 45, 2, 3, 46, 4, 5, 47, 6, 7, 48, 4, 3, 49, 2, 1, 50, 6, 7, 51,
    4, 5, 52, 6, 1, 53, 2, 7, 54, 6, 5, 45, 2, 1, 46, 4, 3, 47, 2, 5, 48,
    4, 7, 49, 6, 1, 50, 2, 5, 51, 6, 3, 52, 4, 7, 53, 2, 1, 54, 6 + 0x80, 9,
  
  /* kvb2 80 */
    0, 1, 56, 4, 3, 57, 2, 5, 58, 6, 7, 59, 2, 3, 60, 4, 1, 61, 6, 7, 62,
    2, 5, 63, 6, 1, 64, 4, 7, 65, 6, 5, 66, 4, 1, 67, 2, 3, 68, 6, 5, 69,
    2, 7, 70, 4, 1, 71, 2, 5, 72, 4, 3, 73, 6, 7, 74, 2, 1, 75, 6, 3, 76,
    4, 5, 77, 6, 7, 78, 2, 3, 79, 4, 1, 80, 2, 7, 81, 4 + 0x80, 9,
  
  /* kvb3 62 */
    0, 1, 83, 2, 3, 84, 4, 5, 85, 6, 7, 86, 4, 3, 87, 2, 1, 88, 6, 7, 89,
    2, 5, 90, 6, 1, 91, 4, 7, 92, 6, 5, 93, 4, 1, 94, 2, 3, 95, 6, 5, 96,
    2, 7, 97, 4, 1, 98, 6, 5, 99, 4, 3, 100, 2, 7, 101, 4, 1, 102, 2 + 0x80, 9
  };
  
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  const uInt32 KidVid::ourSongStart[104] = {
  /* kvshared */
    44,          /* Welcome + intro Berenstain Bears */
    980829,      /* boulders in the road */
    1178398,     /* standing ovations */
    1430063,     /* brother bear */
    1691136,     /* good work */
    1841665,     /* crossing a bridge */
    2100386,     /* not bad (applause) */
    2283843,     /* ourgame */
    2629588,     /* start the parade */
    2824805,     /* rewind */
    3059116,
  
  /* kvs1 */
    44,          /* Harmony into 1 */
    164685,      /* falling notes (into 2) */
    395182,      /* instructions */
    750335,      /* high notes are high */
    962016,      /* my hat's off to you */
    1204273,     /* 1 2 3 do re mi */
    1538258,     /* Harmony */
    1801683,     /* concratulations (all of the Smurfs voted) */
    2086276,     /* line or space */
    2399093,     /* hooray */
    2589606,     /* hear yeeh */
    2801287,     /* over the river */
    3111752,     /* musical deduction */
    3436329,
  
  /* kvs2 */
    44,          /* Handy intro + instructions */
    778557,      /* place in shape */
    1100782,     /* sailor mate + whistle */
  //  1281887,
    1293648,     /* attention */
    1493569,     /* colours */
    1801682,     /* congratulations (Handy and friends voted) */
    2086275,
  
  /* kvs3 */
    44,          /* Greedy and Clumsy intro + instructions */
    686829,      /* red */
    893806,      /* don't count your chicken */
    1143119,     /* yellow */
    1385376,     /* thank you */
    1578241,     /* mixin' and matchin' */
    1942802,     /* fun / colour shake */
    2168595,     /* colours can be usefull */
    2493172,     /* hip hip horay */
    2662517,     /* green */
    3022374,     /* purple */
    3229351,     /* white */
    3720920,
  
  /* kvb1 */
    44,          /* 3 */
    592749,      /* 5 */
    936142,      /* 2 */
    1465343,     /* 4 */
    1787568,     /* 1 */
    2145073,     /* 7 */
    2568434,     /* 9 */
    2822451,     /* 8 */
    3045892,     /* 6 */
    3709157,     /* 0 */
    4219542,
  
  /* kvb2 */
    44,          /* A */
    303453,      /* B */
    703294,      /* C */
    1150175,     /* D */
    1514736,     /* E */
    2208577,     /* F */
    2511986,     /* G */
    2864787,     /* H */
    3306964,     /* I */
    3864389,     /* J */
    4148982,     /* K */
    4499431,     /* L */
    4824008,     /* M */
    5162697,     /* N */
    5581354,     /* O */
    5844779,     /* P */
    6162300,     /* Q */
    6590365,     /* R */
    6839678,     /* S */
    7225407,     /* T */
    7552336,     /* U */
    7867505,     /* V */
    8316738,     /* W */
    8608387,     /* X */
    8940020,     /* Y */
    9274005,     /* Z */
    9593878,
  
  /* kvb3 */
    44,          /* cat */
    341085,      /* one */
    653902,      /* red */
    1018463,     /* two */
    1265424,     /* dog */
    1669969,     /* six */
    1919282,     /* hat */
    2227395,     /* ten */
    2535508,     /* mom */
    3057653,     /* dad */
    3375174,     /* ball */
    3704455,     /* fish */
    4092536,     /* nine */
    4487673,     /* bear */
    5026282,     /* four */
    5416715,     /* bird */
    5670732,     /* tree */
    6225805,     /* rock */
    6736190,     /* book */
    7110159,     /* road */
    7676992
  };
  