/*--------------------------------------------------------------------------
 プレイステーションパッド→USB変換(メガドライブミニ対応)
 
 プレイステーション用パッド(デジタルパッド)をメガドライブミニで使えるように
 変換する。
 
 注意:
 このスケッチは、Arduino Leonardo/Pro Micro(ATmega 32U4)専用です。
 パッド側のピン読み出しを最速で行うためポート(PinD)アクセスを利用しているため、
 他マイコンと互換性がありませんのでご注意ください。
[参考資料]
 このスケッチ作成には以下のURLを参考にしています。
 JoystickMD
 https://github.com/NibblesLab/JoystickMD
 メガドライブミニ を ジョイスティック で遊ぶ ジョイスティックアダプターの作成
 https://tounosumura302.netlify.app/posts/201910191643/
 Arduino Joystick Library
 https://github.com/MHeironimus/ArduinoJoystickLibrary
 
 Arduino PSX Library
 https://playground.arduino.cc/Main/PSXLibrary/
 
 SONY PLAYSTATIONR CONTROLLER INFORMATION
 https://gamesx.com/controldata/psxcont/psxcont.htm
 
 2020.4.22 BLUE
-----------------------------------------------------------------------------*/
#include <Joystick.h>
#include <Psx.h>            // Includes the Psx Library 
 
#define __Debug         0   // 1:シリアルデバッグON
#define __IF_SEGA       1   // 1:SEGA純正パッドを模擬する
 
// Joystickライブラリ定義
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK,
  10, 0,                    // Button Count, Hat Switch Count
  true, true, false,        // X, Y, and Z Axis
  false, false, false,      // Rx, but no Ry or Rz
  false, false,             // No rudder or throttle
  false, false, false);     // No accelerator, brake, or steering
 
// PSXコントローラライブラリ定義
Psx Psx;
 
//PSXコントローラー制御ピン定義
#define     dataPin     7   // データ信号
#define     cmndPin     8   // Command送信
#define     attPin      9   // セレクト信号
#define     clockPin    10  // 制御クロック信号
 
#define     DELAY_TIME  10  // 遅延時間(10μs/10ms)
 
// 方向キー移動量(MEGA DRIVE mini向け)
#define     AXIS_MIN    0   // アナログ方向キー最小値
#define     AXIS_MAX    255 // アナログ方向キー最大値
#define     AXIS_CENTER 127 // アナログ方向キーセンター位置
 
// Joystick PAD番号定義
#if __IF_SEGA
  //SEGA 純正パッド模擬(VID=0x0CA3、PID=0x0024)
  #define       PAD_X       3   // Xボタン
  #define       PAD_A       2   // Aボタン
  #define       PAD_B       1   // Bボタン
  #define       PAD_Y       0   // Yボタン
  #define       PAD_C       5   // Cボタン
  #define       PAD_Z       4   // Zボタン
  #define       PAD_MODE    8   // MODEボタン
  #define       PAD_START   9   // STARTボタン
#else
  //DragonRise製パッド模擬(VID=0x0079、PID=0x0011)
  #define       PAD_X       0   // Xボタン
  #define       PAD_A       1   // Aボタン
  #define       PAD_B       2   // Bボタン
  #define       PAD_Y       3   // Yボタン
  #define       PAD_C       4   // Cボタン
  #define       PAD_Z       5   // Zボタン
  #define       PAD_MODE    8   // MODEボタン
  #define       PAD_START   9   // STARTボタン
#endif
 
unsigned int cur_psx_data = 0;  // PSXコントローラボタン状態受信
 
//////////////////////////////////////
// PSXパッド→Joystickパッド変換
// mask = チェック対象ボタンマスク値
// 戻り:Joystickボタン値
//////////////////////////////////////
int get_psx2joy( unsigned int mask )
{
    switch(mask)
    {
    case psxStrt:
        return PAD_START;
    case psxSlct:
        return PAD_MODE;
    case psxSqu:
        return PAD_X;
    case psxX:
        return PAD_A;
    case psxO:
        return PAD_B;
    case psxTri:
        return PAD_Y;
    case psxR1:
        return PAD_C;
    case psxL1:
        return PAD_Z;
    case psxR2:
        return PAD_MODE;
    case psxL2:
    default:
        break;
    }
    return -1;
}
 
//////////////////////////////////////
// PSXパッド制御処理
// psx_data = PSXコントローラ状態
// mask = チェック対象ボタンマスク値
//////////////////////////////////////
bool check_psx_button(unsigned int psx_data ,unsigned int mask )
{
    // ボタン状態変化あり
    if ( (cur_psx_data & mask) != (psx_data & mask) )
    {
        // Joystickボタン値取得
        int pad = get_psx2joy(mask);
        // ボタン押下?
        if((psx_data & mask))
        {
            // 方向キーである
            if(mask<=psxUp)
            {
                switch(mask)
                {
                case psxUp:
                    // Y軸を最小
                    Joystick.setYAxis(AXIS_MIN);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setYAxis(AXIS_MIN)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                case psxDown:
                    // Y軸を最大
                    Joystick.setYAxis(AXIS_MAX);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setYAxis(AXIS_MAX)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                case psxLeft:
                    // X軸を最小
                    Joystick.setXAxis(AXIS_MIN);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setXAxis(AXIS_MIN)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                case psxRight:
                    // X軸を最大
                    Joystick.setXAxis(AXIS_MAX);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setXAxis(AXIS_MAX)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                default:
                    break;
                }
            }
            else
            {
    #if __Debug
        ///////////////////////////////////////////////////////////////////////////////
            Serial.print(pad,DEC);
            Serial.print(" Press\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                // 無効ボタンなら処理終了
                if( pad== -1 )
                    return false;
                
                // ボタン押下セット
                Joystick.pressButton(pad);
            }
        }
        // 離した
        else
        {
            // 方向キーである
            if(mask<=psxUp)
            {
                switch(mask)
                {
                case psxUp:
                case psxDown:
                    // Y軸をセンターに戻す
                    Joystick.setYAxis(AXIS_CENTER);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setYAxis(AXIS_CENTER)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                case psxLeft:
                case psxRight:
                    // X軸をセンターに戻す
                    Joystick.setXAxis(AXIS_CENTER);
    #if __Debug
    ///////////////////////////////////////////////////////////////////////////////
            Serial.print("Joystick.setXAxis(AXIS_CENTER)\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                    break;
                default:
                    break;
                }
                
            }
            else
            {
    #if __Debug
        ///////////////////////////////////////////////////////////////////////////////
            Serial.print(pad,DEC);
            Serial.print(" Release\n");
    ///////////////////////////////////////////////////////////////////////////////
    #endif //__Debug
                // 無効ボタンなら処理終了
                if( pad== -1 )
                    return false;
                // ボタン離す
                Joystick.releaseButton(pad);
            }
        }
        // ボタン状態を変更する
        cur_psx_data ^= mask;
        
        // 変化あり
        return true;
    }
    // 変化なし
    return false;
}
 
//////////////////////////////////////
// 初期化
//////////////////////////////////////
void setup() {
// put your setup code here, to run once:
 
    // Joystick Library初期化
    Joystick.begin(false);
 
    // PAD方向キー初期化
    Joystick.setXAxisRange(AXIS_MIN, AXIS_MAX);
    Joystick.setYAxisRange(AXIS_MIN, AXIS_MAX);
    Joystick.setXAxis(AXIS_CENTER);
    Joystick.setYAxis(AXIS_CENTER);
 
    // PSX制御I/O初期化
    Psx.setupPins(dataPin, cmndPin, attPin, clockPin, DELAY_TIME);
    // 各I/Oピン番号を指定する(data,cmd,att,clock)
    // Delay:各ステート応答遅延時間。μs単位で指定
    // (5より小さいと動作しない)
 
#if __Debug
    Serial.begin(38400);
#endif //__Debug
}
 
//////////////////////////////////////
// メインループ
//////////////////////////////////////
void loop() {
// put your main code here, to run repeatedly:
 
bool change=false;  //JoyStick状態変化なし(通信制御しない)
    
    ////////////////////////////////////////////
    //プレステコントローラ状態読み出し
    ////////////////////////////////////////////
    unsigned int psx_data = Psx.read();
    
    // 各ボタン制御
    change = false;
    change |= check_psx_button(psx_data,psxLeft);
    change |= check_psx_button(psx_data,psxDown);
    change |= check_psx_button(psx_data,psxRight);
    change |= check_psx_button(psx_data,psxUp);
    change |= check_psx_button(psx_data,psxStrt);
    change |= check_psx_button(psx_data,psxSlct);
    change |= check_psx_button(psx_data,psxSqu);
    change |= check_psx_button(psx_data,psxX);
    change |= check_psx_button(psx_data,psxO);
    change |= check_psx_button(psx_data,psxTri);
    change |= check_psx_button(psx_data,psxR1);
    change |= check_psx_button(psx_data,psxL1);
    change |= check_psx_button(psx_data,psxR2);
    change |= check_psx_button(psx_data,psxL2);
    
    // Joystick情報送信
    if(change)
    {
        Joystick.sendState();
    }
 
    //1シーケンス終了
    delay(DELAY_TIME);
}