Gamepad from Sega Mega Drive and Raspberry Pi Part 2 (the final six-button)



    We continue, the easiest behind! And now to the most difficult and interesting. If you are too lazy to read, then below (near the end of the article) there will be a link to the video, with the result and explanation of everything, including what is described in the first part. To whom it is interesting, then please follow.

    In the 6-button mode, reading occurs in 4 cycles or phases (if expressed in the emulator language). That is, once every 16 ms a cyclic (4 cycles) change of the state of the Select output occurs, and every fourth cycle at the output of the gamepad a state of additional buttons appears. Below is a diagram of the reading, for clarity, which must be repeated:



    It is good that I have a logic analyzer, with which I caught a bug, expressed in the fact that the cycle did not go beyond the fourth phase.

    I will not beat around the bush, I will immediately give a listing of this function:

    static u32 read_pad_6btn(int i, u32 out_bits){
    u32 pad = ~PicoIn.padInt[i]; // Get inverse of pad MXYZ SACB RLDUint phase = Pico.m.padTHPhase[i];
    u32 value = 0;
      if (i == 0 && phase == 0 && (out_bits & 0x40)) // TH
        {
          digitalWrite (Select, HIGH);
          delayMicroseconds (30);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data2) << 2; //read LEFT button
          value ^= digitalRead(Data3) << 3; //read RIGHT button
          value ^= digitalRead(Data4) << 4; //read B button
          value ^= digitalRead(Data5) << 5; //read C button
        }
    if (i == 0 && phase == 0 && !(out_bits & 0x40)) // TH
        {
          digitalWrite (Select, LOW);
          delayMicroseconds (30);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data4) << 4; //read A button
          value ^= digitalRead(Data5) << 5; //read Start button
          digitalWrite (Select, HIGH);
          delayMicroseconds (10);
        }
        if (i == 0 && phase == 1 && (out_bits & 0x40)) // TH
        {
          digitalWrite (Select, HIGH);
          delayMicroseconds (20);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data2) << 2; //read LEFT button
          value ^= digitalRead(Data3) << 3; //read RIGHT button
          value ^= digitalRead(Data4) << 4; //read B button
          value ^= digitalRead(Data5) << 5; //read C button
        }
    if (i == 0 && phase == 1 && !(out_bits & 0x40)) // TH
        {
          digitalWrite (Select, LOW);
          delayMicroseconds (30);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data4) << 4; //read A button
          value ^= digitalRead(Data5) << 5; //read Start button
          digitalWrite (Select, HIGH);
          delayMicroseconds (10);
        }
        if (i == 0 && phase == 2 && (out_bits & 0x40)) // TH
        {
          digitalWrite (Select, HIGH);
          delayMicroseconds (20);
          value ^= digitalRead(Data0) << 0; //read UP button
          value ^= digitalRead(Data1) << 1; //read DOWN button
          value ^= digitalRead(Data2) << 2; //read LEFT button
          value ^= digitalRead(Data3) << 3; //read RIGHT button
          value ^= digitalRead(Data4) << 4; //read B button
          value ^= digitalRead(Data5) << 5; //read C button
        }
       if (i == 0 && phase == 2 && !(out_bits & 0x40)) 
      {
        digitalWrite (Select, LOW);
        delayMicroseconds (30);
        value ^= digitalRead(Data4) << 4; //read A button
        value ^= digitalRead(Data5) << 5; //read Start button
        digitalWrite (Select, HIGH);
        delayMicroseconds (10);
      }
      if (i == 0 && phase == 3 && (out_bits & 0x40))
        {
          digitalWrite (Select, HIGH);
          delayMicroseconds (20);
          value ^= digitalRead(Data0) << 0; //read Z button
          value ^= digitalRead(Data1) << 1; //read Y button
          value ^= digitalRead(Data2) << 2; //read X button
          value ^= digitalRead(Data3) << 3; //read MODE button
          value ^= digitalRead(Data4) << 4; //read B button
          value ^= digitalRead(Data5) << 5; //read C button
        }
      if (i == 0 && phase == 3 && !(out_bits & 0x40))
        {
          digitalWrite (Select, LOW);
          delayMicroseconds (30);
          value ^= digitalRead(Data4) << 4; //read A button
          value ^= digitalRead(Data5) << 5; //read Start button
          digitalWrite (Select, HIGH);
          delayMicroseconds (10);
          value |= 0x0f;
        }
      if (i == 1 && phase == 0 && (out_bits & 0x40)) // TH
      {
        value = pad & 0x3f;                          // ?1CB RLDU
      }
        if (i == 1 && phase == 0 && !(out_bits & 0x40)) // TH
      {
        value = ((pad & 0xc0) >> 2) | (pad & 3);     // ?0SA 00DU
      }
        if (i == 1 && phase == 1 && (out_bits & 0x40)) // TH
      {
        value = pad & 0x3f;                          // ?1CB RLDU
      }
        if (i == 1 && phase == 1 && !(out_bits & 0x40)) // TH
      {
        value = ((pad & 0xc0) >> 2) | (pad & 3);     // ?0SA 00DU
      }
      if (i == 1 && phase == 2 && (out_bits & 0x40)) // TH
      {
        value = pad & 0x3f;                          // ?1CB RLDU
      }
      if (i == 1 && phase == 2 && !(out_bits & 0x40))
        {
        value = (pad & 0xc0) >> 2;                   // ?0SA 0000
        }
      if(i == 1 && phase == 3 && (out_bits & 0x40)) 
      {
        return (pad & 0x30) | ((pad >> 8) & 0xf);  // ?1CB MXYZ
      }
       if(i == 1 && phase == 3 && !(out_bits & 0x40))
       {
        return ((pad & 0xc0) >> 2) | 0x0f;         // ?0SA 1111
       }
     return value;
    }
    

    We analyze any of the conditions for example:

    if (i == 0 && phase == 1 && !(out_bits & 0x40)) // TH

    Here it is checked that we read from the first gamepad (i == 0) , the second reading phase (phase == 1) , and the Select output should be set to 0 ! (Out_bits & 0x40) . To understand how it works in the emulator, I compiled the code on Xubuntu, and Visual Studio Code, setting a bunch of breakpoints, ran the code in debug mode. The result is such a beautiful picture:



    Actually the result of the work here:


    Here I must say a few words about the emulator itself. Either I did not understand something, or it is a bug, but the emulator initially loads in 3-button mode, even if the opposite is indicated in the global settings. For 99% of games this is enough. In order to enter the mode of operation with a 6-button gamepad, you must go to the settings and go back into the game without changing anything.
    But there is one game that is out of this context, it is Lost Vikings, the X, Z, MODE buttons work perfectly in it without any dancing.

    PS

    But you can do even easier, one kind person has already written a driver for working with a gamepad, and at a very low level. I'm still far away from this.

    Thanks for attention!

    Also popular now: