First Release

Dependencies:   USBDevice

Out_FC/C_Out_FC.cpp

Committer:
sankichi
Date:
2013-07-27
Revision:
1:6c392ebcd4d4
Parent:
0:e1265f6b3565

File content as of revision 1:6c392ebcd4d4:

#include "mbed.h"
#include "NiseKabuto.h" // InputDeviceType
#include "C_Out_FC.h"

// mbed pins
DigitalOut  led2_FC(LED2); 
DigitalOut  led3_FC(LED3); 
InterruptIn *_INTR_LATCH;
DigitalOut  *_OUT_DATA;
InterruptIn *_INTR_CLOCK;
InterruptIn *_INTR_POWDETECT;

// File local variables
static volatile int     *_pButtons; //PhaseData[12]
static volatile char    *_pCh0;
static volatile char    *_pCh1;
static volatile char    *_pCh2;
static volatile char    *_pInputDeviceType;
static volatile char    _Flag_NowOutput;
static volatile char    _PhaseCounter;
static volatile char    _SpeedType;
static FunctionPointer  *_fp_EnableInput;
static FunctionPointer  *_fp_DisableInput;
static volatile char    _RapidFireValue;
static Ticker           _RapidStateTicker;

// Const variables
static const int    DIGITALPAD_SPEED_STARFORCE = 120;
static const int    DIGITALPAD_SPEED_RECCA     = 75;
static const int    RAPIDFIRE_PER_SEC = 15;     // 高橋名人連射速度-1

// File local functions
static void ISR_Rise_LATCH_STARFORCE(void); // 遅い読み取りルーチン for スターフォース
static void ISR_Rise_LATCH(void);           // フツー
static void ISR_Rise_LATCH_RECCA(void);     // 速い読み取りルーチン for サマーカーニバル'92 烈火
//static void ISR_Rise_POWERDETECT(void);
static void RenewData(void);
static char CheckSpeedType(void);
static int  CheckOneReadTime(void);
static void ConfigureSpeed(void);
static void RapidStateTickerMethod(void);

void C_Out_FC_Initialize(
    InterruptIn *intr_LATCH,
    DigitalOut  *out_DATA,
    InterruptIn *intr_CLOCK,
    InterruptIn *intr_POWDETECT,
    volatile int *pButtons,
    volatile char *pCh0,
    volatile char *pCh1,
    volatile char *pCh2,
    volatile char *pInputDeviceType,
    FunctionPointer *pFunc_InputEnable,
    FunctionPointer *pFunc_InputDisable
)
{
    // 入出力pin
    _INTR_LATCH     = intr_LATCH;
    _OUT_DATA       = out_DATA;
    _INTR_CLOCK     = intr_CLOCK;
    _INTR_POWDETECT = intr_POWDETECT;

    // 割り込み設定
    //_INTR_POWDETECT->rise(&ISR_Rise_POWERDETECT); // 電源ON/OFFでスピード切り替え判定を行う場合

    led2_FC.write(0);                               // 初期:フツー
    led3_FC.write(1);                               //
    _INTR_LATCH->rise(&ISR_Rise_LATCH);             //
    
    // 出力速度設定
    ConfigureSpeed();

    // ポインタ
    _pButtons         = pButtons;
    _pCh0             = pCh0;
    _pCh1             = pCh1;
    _pCh2             = pCh2;
    _pInputDeviceType = pInputDeviceType;
    _fp_EnableInput   = pFunc_InputEnable;
    _fp_DisableInput  = pFunc_InputDisable;
    
    _Flag_NowOutput = 0;
    _RapidFireValue = 1;
    
    //_pTemp = pTemp;
    
    // 連射ステータス変更設定
    _RapidStateTicker.attach_us(&RapidStateTickerMethod, (int)(1000000/(RAPIDFIRE_PER_SEC*2)));
    
    // Initialize pin status
    _PhaseCounter = 0;
    RenewData();
}

void ISR_Rise_LATCH(void)
{
    int loopCounter;

    // 止める
    if(
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG ||
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_DIGITAL
    )
    {
        (*_fp_DisableInput).call();
    }

    // この関数開始時点で、phase0読み取り(DATAの立下り1回分)は
    // 終了しているものと仮定する
    // (mbedのピン割り込み時の反応速度は7usくらい)
    //
    // ただしMetalMaxでは。Latch立ち上がりから初回DATA立下りの間が
    // 6.5-7.0usくらいで微妙なので、
    // 絶対にphase0から開始することを保証するため、ちょっと待つ
    wait_us(1);
    
    
    // Phase1に移行
    _PhaseCounter=1;
    RenewData();

    // Lの間待つ
    while( !(_INTR_CLOCK->read()) )
    {
        loopCounter = 400;

        loopCounter--;
        if( !loopCounter )
        {
            break;
        }
    }
    
    // 開始
    for(int i=1; i<8; i++ )
    {
        
        // Hの間待つ
        loopCounter = 5000;     
        while( _INTR_CLOCK->read() )
        {
            loopCounter--;
            if( !loopCounter )
            {
                break;
            }
        }

        // Set data for current phase
        _PhaseCounter++;
        RenewData();
        
        // Lの間待つ
        loopCounter = 200;
        while( !(_INTR_CLOCK->read()) )
        {
            loopCounter--;
            if( !loopCounter )
            {
                break;
            }
        }
    }

    _PhaseCounter = 0;
    RenewData();

    // 再開
    if(
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG ||
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_DIGITAL
    )
    {
        (*_fp_EnableInput).call();
    }

}

void ISR_Rise_LATCH_RECCA(void)
{
    // おまじない(Phase0を長引かせる)
    wait_us(2);
    
    // Phase1に移行
    _PhaseCounter=1;
    RenewData();
    
    // 開始
    for(int i=1; i<8; i++ )
    {
        wait_us(6); 

        // Set data for current phase
        _PhaseCounter++;
        RenewData();
    }

    _PhaseCounter = 0;
    RenewData();

}

// 遅い読み取りルーチン
void ISR_Rise_LATCH_STARFORCE(void)
{
    int loopCounter;

    // 止める
    if(
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG ||
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_DIGITAL
    )
    {
        (*_fp_DisableInput).call();
    }

    // Hの間待つ
    loopCounter = 5000;     
    while( _INTR_CLOCK->read() )
    {
        loopCounter--;
        if( !loopCounter )
        {
            break;
        }
    }

    
    // Phase1に移行
    _PhaseCounter=1;
    RenewData();

    // Lの間待つ
    while( !(_INTR_CLOCK->read()) )
    {
        loopCounter = 400;

        loopCounter--;
        if( !loopCounter )
        {
            break;
        }
    }
    
    // 開始
    for(int i=1; i<8; i++ )
    {
        
        // Hの間待つ
        loopCounter = 5000;     
        while( _INTR_CLOCK->read() )
        {
            loopCounter--;
            if( !loopCounter )
            {
                break;
            }
        }

        // Set data for current phase
        _PhaseCounter++;
        RenewData();
        
        // Lの間待つ
        loopCounter = 200;
        while( !(_INTR_CLOCK->read()) )
        {
            loopCounter--;
            if( !loopCounter )
            {
                break;
            }
        }
    }

    _PhaseCounter = 0;
    RenewData();

    // 再開
    if(
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG ||
        (*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_DIGITAL
    )
    {
        (*_fp_EnableInput).call();
    }

}




void RenewData(void)
{
    switch(_PhaseCounter)
    {
        case 0:
            //
            // A
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write(((*_pButtons) & 0x0100) ? 1 : 0);  // Digital B
            }
            else if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_MD6B) //2:MD6B
            {
                // Zが押されてたら連射、押されてなかったらCの結果
                if( ((*_pButtons) & 0x010000) ) // Z
                {
                    // 真:押されてない
                    _OUT_DATA->write(((*_pButtons) & 0x0020) ? 1 : 0);  // Digital C
                }
                else
                {
                    // 偽:押されてる
                    _OUT_DATA->write(_RapidFireValue&1);
                }
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0100) ? 1 : 0);  // Digital B
            }
            break;

        case 1:
            //
            // B
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write(((*_pButtons) & 0x0200) ? 1 : 0);  // Digital A
            }
            else if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_MD6B) //2:MD6B
            {
                // Yが押されてたら連射、押されてなかったらBの結果
                if( ((*_pButtons) & 0x020000) ) // Y
                {
                    // 真:押されてない
                    _OUT_DATA->write(((*_pButtons) & 0x0100) ? 1 : 0);  // Digital B
                }
                else
                {
                    // 偽:押されてる
                    _OUT_DATA->write(_RapidFireValue&1);
                }
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0200) ? 1 : 0);  // Digital A
            }
            break;

        case 2: 
            //
            // Select
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write(((*_pButtons) & 0x0001) ? 1 : 0);  // G(Sel,Mode)
            }
            else if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_MD6B) //2:MD6B
            {
                _OUT_DATA->write(((*_pButtons) & 0x0001) ? 1 : 0);  // G(Sel,Mode)
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0020) ? 1 : 0);  // Digital C
            }
            break;

        case 3:
            //
            // Start
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write(
                    ( ((*_pButtons) & 0x0002) ? 1 : 0) &    // F(Start)
                    ( (*_pCh2)>0xf0? 0:1 )                  // スロットル引く
                
                );
                //_OUT_DATA->write( ((*_pButtons) & 0x0002) ? 1 : 0) );
                
            }
            else if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_MD6B) //2:MD6B
            {
                _OUT_DATA->write(((*_pButtons) & 0x0002) ? 1 : 0);  // F(Start)
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0010) ? 1 : 0);  // Digital C
            }
            break;

        case 4:
            //
            // Up
            //
            //  ch1>0xf0? 0:1,  //  右
            //  ch1<0x0f? 0:1,  //  左
            //  ch0>0xf0? 0:1,  //  下
            //  ch0<0x0f? 0:1   //  上
            //  ch2>0xf0? 0:1,  //  スロットル引く

            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write((*_pCh0)<0x40? 0:1);
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x2000) ? 1 : 0);
            }
            break;

        case 5:
            //
            // Down
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write((*_pCh0)>0xb0? 0:1);
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x1000) ? 1 : 0);
            }
            break;

        case 6:
            //
            // Left
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write((*_pCh1)<0x40? 0:1);
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0800) ? 1 : 0);
            }
            break;

        case 7:     
            //
            // Right
            //
            if((*_pInputDeviceType) == NiseKabuto::CONFIG_INMODE_CYBERSTICK_ANALOG) //サイバースティック(アナログ)
            {
                _OUT_DATA->write((*_pCh1)>0xb0? 0:1);
            }
            else
            {
                _OUT_DATA->write(((*_pButtons) & 0x0400) ? 1 : 0);
            }
            break;
        
        default:
            _OUT_DATA->write(1);    // H
    }
}

// 
// ファミコンからの読み取り処理の長さを得る(1回分)
// 
// return:
//      0     = 読み取りが行えない(烈火の可能性がある)
//      0以外 = 読み取り処理長さ[us]
int CheckOneReadTime(void)
{
    int   retVal = 0;
    char  stat = 1;
    int   intLoopCounter;   // ZanacはGunnac&MMに比べて2倍
    Timer t;
    
    // Latch = Hまで待つ
    intLoopCounter = 500000;
    while( !(_INTR_LATCH->read()) )
    {
        intLoopCounter--;
        if( !intLoopCounter )
        {
            stat = 0;
            break;
        }
    }
    
    if(stat)
    {
        // 時刻計測開始
        t.start();
        
        // Clockが8回Lになることを確認
        for(int i=0; i<8; i++ )
        {
            intLoopCounter = 1000;
            while( _INTR_CLOCK->read() )
            {
                intLoopCounter--;
                if( !intLoopCounter )
                {
                    stat = 0;
                    break;
                }
            }
            
            intLoopCounter = 100;
            while( !(_INTR_CLOCK->read()) )
            {
                intLoopCounter--;
                if( !intLoopCounter )
                {
                    break;
                }
            }           
        }
    }
    
    if(stat)
    {
        t.stop();
        retVal =  t.read_us();
    }
    
    return retVal;
}



// 
// SpeedTypeを判定する
// 
// return:
//      0 = 普通モード(普通のゲーム)
//      1 = 高速モード(烈火)
//      2 = 低速モード(スターフォース)
//      よくわからないときは0を返す
char CheckSpeedType(void)
{
    char retVal = 0;
    int readTime[8];
    char counter_recca     = 0;
    char counter_starforce = 0;
    
    // Dataは常にH
    _OUT_DATA->write(1);
    
    // 8回くらいチェックする
    for(int i=0; i<8; i++ )
    {
        readTime[i] = CheckOneReadTime();
        printf("readTime[%d]: %d\r\n",i,readTime[i]);
    }
    
    // 個別の結果判定
    for(int i=0; i<8; i++ )
    {
        if( readTime[i]>DIGITALPAD_SPEED_STARFORCE )
        {
            counter_starforce++;
        }
        else if( readTime[i]<DIGITALPAD_SPEED_RECCA )
        {
            counter_recca++;
        }
        else
        {
        }
    }
    
    // 判定
    if(counter_starforce >= 4)
    {
        retVal = 2;
    }
    else if(counter_recca >= 4)
    {
        retVal = 1;
    }
    
    return retVal;
}

//
// Configure speed setting
//
void ConfigureSpeed(void)
{
    char speed;
    
    // ISR登録解除
    _INTR_LATCH->rise(0);
    
    // ちょっと待つ
    wait_ms(500);
    
    speed = CheckSpeedType();
    
    // Interrupt Setting
    if( speed==1 )
    {
        led2_FC.write(1);
        led3_FC.write(1);
        _INTR_LATCH->rise(&ISR_Rise_LATCH_RECCA);
    }
    else if( speed==2 )
    {
        led2_FC.write(0);
        led3_FC.write(0);
        _INTR_LATCH->rise(&ISR_Rise_LATCH_STARFORCE);
    }
    else
    {
        led2_FC.write(0);
        led3_FC.write(1);
        _INTR_LATCH->rise(&ISR_Rise_LATCH);
    }

}

//
// ISR for POWERDETECT L->H
//
void ISR_Rise_POWERDETECT(void)
{
    ConfigureSpeed();
}

void RapidStateTickerMethod(void)
{
    _RapidFireValue = !_RapidFireValue;
}