/*****************************************************************************
 *                                                                           *
 * Module : UPD.C                                                            *
 *                                                                           *
 * Gestion du UPD 765 (Controleur disquettes)                                *
 *                                                                           *
 * Les adresses définies par AMSTRAD sont :                                  *
 *                                                                           *
 * FA7E : Commande du moteur par le bit 0 (non émulé, toujours en marche...) *
 * FB7E : Lecture du registre de status principal                            *
 * FB7F : Lecture/écriture du registre de commande                           *
 *                                                                           *
 *****************************************************************************/


#include  <exec/exec.h>
#include  <dos.h>
#include  <proto/xpkmaster.h>
#include  <proto/exec.h>
#include  <proto/dos.h>

#include  <string.h>


/*
// Constantes générales...
*/
#define SECTSIZE   512
#define NBSECT       9


#define TAILLE_DISK 200000


/* Bits de Status */
#define DIO         0x40
#define NDM         0x20
#define CB          0x10

/* Bits de ST0 */
#define NR          0x08

/* Bits de ST1 */
#define ND          0x02

/* Bits de ST3 */
#define RY          0x20
#define T0          0x10


typedef enum { REPOS = 0, COMMANDE } EtatUPD;

typedef UBYTE ( * pfctUPD )( UBYTE );


typedef struct
    {
    char  debut[ 0x30 ]; /* "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" */
    UBYTE NbTracks;
    UBYTE NbHeads;
    UBYTE Rien;
    UBYTE DataSize; 
    UBYTE Unused[ 0xCC ];
    } CPCEMUEnt;


typedef struct
    {
    UBYTE C;                 /* track */
    UBYTE H;                 /* head  */
    UBYTE R;                 /* sect  */
    UBYTE N;                 /* size  */
    SHORT Un1;
    SHORT Un2;
    } CPCEMUSect;


typedef struct
    {
    char        ID[ 0x10 ];   /* "Track-Info\r\n" */
    UBYTE       Track;
    UBYTE       Head;
    SHORT       Unused;
    UBYTE       SectSize; /* 2    */
    UBYTE       NbSect;   /* 9    */
    UBYTE       Gap3;     /* 0x4E */
    UBYTE       OctRemp;  /* 0xE5 */
    CPCEMUSect  Sect[ 29 ];
    } CPCEMUTrack;


struct Library * XpkBase = NULL;


static UBYTE Rien( UBYTE );


static UBYTE BufDisk[ TAILLE_DISK ];

static pfctUPD fct = Rien;

static EtatUPD etat = REPOS;

static CPCEMUTrack * CurrTrackDatas;

static int DataSize, SectSize;


/*
// Registres du uPD 765
*/
static UBYTE Status = 0x80;  /* status */
static UBYTE ST0    = 0x01;  /* status interne 0 (non émulé...) */
static UBYTE ST1    = 0x00;  /* status interne 1 */
static UBYTE ST2    = 0x00;  /* status interne 2 (non émulé...) */
static UBYTE ST3    = 0x29;  /* status interne 3 */
static UBYTE C      = 0x00;  /* Cylindre (n° piste) */
static UBYTE H      = 0x00;  /* Head     (n° tête) */
static UBYTE R      = 0xC1;  /* Record   (n° secteur) */
static UBYTE N      = 0x02;  /* Number   (nbre d'octet poids forts) */
static UBYTE Drive  = 0x00;  /* Drive    ( 0=A, 1=B) */
static UBYTE EOT    = 0xC1;  /* Secteur final; */


static void ChangeCurrTrack( UBYTE Track )
{
    ULONG pos = sizeof( CPCEMUEnt ) + DataSize * Track;
                                            
    if ( ! Track )
        ST3 |= T0;
    else
        ST3 &= ~T0;

    CurrTrackDatas = ( CPCEMUTrack * )&BufDisk[ pos ];
    C = CurrTrackDatas->Sect[ 0 ].C;
    H = CurrTrackDatas->Sect[ 0 ].H;
    R = CurrTrackDatas->Sect[ 0 ].R;
    N = CurrTrackDatas->Sect[ 0 ].N;
    SectSize = CurrTrackDatas->SectSize << 8;
}


static UBYTE RechercheSecteur( UBYTE newSect )
{
    UBYTE i;

    for ( i = 0; i < CurrTrackDatas->NbSect; i++ )
        if ( CurrTrackDatas->Sect[ i ].R == newSect )
            {
            ST1 &= ~ND;
            return( i );
            }
    ST1 |= ND;
    return( 0xFF );
}


static UBYTE Rien( UBYTE val )
{
    Status &= ~CB & ~DIO;
    etat = REPOS;
    val = 0;
    return( val );
}


static UBYTE MoveTrack( UBYTE val )
{
    switch( etat++ )
        {
        case 1 :
            Drive = val;
            Status |= NDM;
            break;

        case 2 :
            ChangeCurrTrack( C = val );
            etat = 0;
            Status &= ~CB & ~DIO & ~NDM;
            break;
        }
    return( 0 );
}


static UBYTE MoveTrack0( UBYTE val )
{
    Drive = val;
    ChangeCurrTrack( C = 0 );
    etat = 0;
    Status &= ~CB & ~DIO & ~NDM;
    return( 0 );
}


static UBYTE ReadST0( UBYTE val )
{
    if ( etat++ == 1)
        return( ST0 );

    etat = REPOS;
    Status &= ~CB & ~DIO;
    return( C );
}


static UBYTE ReadST3( UBYTE val )
{
    if ( etat++ == 1 )
        {
        Drive = val;
        Status |= DIO;
        return( 0 );
        }

    etat = REPOS;
    Status &= ~CB & ~DIO;
    return( ST3 );
}


static UBYTE Specify( UBYTE val )
{
    if ( etat++ == 1 )
        return( 0 );

    etat = REPOS;
    Status &= ~CB & ~DIO;
    return( 0 );
}


static UBYTE ReadData( UBYTE val )
{
    static int sect = 0, fin = 0, Pos = 0;
    static BOOL cond = TRUE;

    switch( etat++ )
        {
        case 1 :
            Drive = val;
            break;

        case 2 :
            C = val;
            break;

        case 3 :
            H = val;
            break;

        case 4 :
            cond = TRUE;
            sect = RechercheSecteur( R = val );
            if ( sect == 0xFF )
                cond = FALSE;
            break;

        case 5 :
            N = val;
            break;

        case 6 :
            EOT = val;
            fin = ( EOT - R + 1 ) * SectSize;
            break;

        case 7 :
            if ( cond == TRUE )
                {
                if ( ( C != CurrTrackDatas->Sect[ sect ].C ) ||
                     ( H != CurrTrackDatas->Sect[ sect ].H ) ||
                     ( R != CurrTrackDatas->Sect[ sect ].R ) ||
                     ( N != CurrTrackDatas->Sect[ sect ].N )
                   )
                    cond = FALSE;
                }
            Pos = sizeof( CPCEMUEnt ) + sizeof( CPCEMUTrack ) + DataSize * C + sect * SectSize;
            break;

        case 8 :
            Status |= DIO | NDM;
            break;

        case 9 :
            if ( cond )
                {
                if ( --fin )
                    etat--;
                else
                    Status &= ~NDM;

                return( BufDisk[ Pos++ ] );
                }
            Status &= ~NDM;
            return( 0 );

        case 10 :
            return( ST0 );

        case 11 :
            return( ST1 );

        case 12 :
            return( ST2 );

        case 13 :
            return( C );

        case 14 :
            return( H );

        case 15 :
            return( R );

        case 16 :
            etat = 0;
            Status &= ~CB & ~DIO;
            return( N );
        }
    return( 0 );
}


static UBYTE WriteData( UBYTE val )
{
    static USHORT sect = 0, fin = 0, Pos = 0;
    static BOOL cond = TRUE;

    switch( etat++ )
        {
        case 1 :
            Drive = val;
            break;

        case 2 :
            C = val;
            break;

        case 3 :
            H = val;
            break;

        case 4 :
            cond = TRUE;
            sect = RechercheSecteur( R = val );
            if ( sect == 0xFF )
                cond = FALSE;
            break;

        case 5 :
            N = val;
            break;

        case 6 :
            EOT = val;
            fin = ( EOT - R + 1 ) * SectSize;
            break;

        case 7 :
            if ( cond == TRUE )
                {
                if ( ( C != CurrTrackDatas->Sect[ sect ].C ) ||
                     ( H != CurrTrackDatas->Sect[ sect ].H ) ||
                     ( R != CurrTrackDatas->Sect[ sect ].R ) ||
                     ( N != CurrTrackDatas->Sect[ sect ].N )
                   )
                    cond = FALSE;
                }
            Pos = sizeof( CPCEMUEnt ) + sizeof( CPCEMUTrack ) + DataSize * C + sect * SectSize;
            break;

        case 8 :
            Status |= DIO | NDM;
            break;

        case 9 :
            if ( cond )
                {
                BufDisk[ Pos++ ] = val;
                if ( --fin )
                    etat--;
                else
                    Status &= ~NDM;

                return( 0 );
                }
            Status &= ~NDM;
            return( 0 );

        case 10 :
            return( ST0 );

        case 11 :
            return( ST1 );

        case 12 :
            return( ST2 );

        case 13 :
            return( C );

        case 14 :
            return( H );

        case 15 :
            return( R );

        case 16 :
            etat = 0;
            Status &= ~CB & ~DIO;
            return( N );
        }
    return( 0 );
}


static UBYTE ReadID( UBYTE val )
{
    switch( etat++ )
        {
        case 1 :
            Drive = val;
            Status |= DIO;
            break;

        case 2 :
            return( ST0 );

        case 3 :
            return( ST1 );

        case 4 :
            return( ST2 );

        case 5 :
            return( C );

        case 6 :
            return( H );

        case 7 :
            return( R );

        case 8 :
            etat = REPOS;
            Status &= ~CB & ~DIO;
            return( N );
        }
}


/*
static UBYTE FormatTrack( UBYTE val )
{
    etat = REPOS;
    return( 0 );
}
*/


UBYTE __asm ReadUPD( register __d0 USHORT port )
{
    if ( port == 0xFB7E ) 
        return( Status );

    return( fct( 0 ) );
}


void __asm WriteUPD( register __d1 UBYTE val )
{
    if ( etat )
        fct( val );
    else
        {
        Status |= CB;
        etat = 1;
        switch( val & 0x5F )
            {
            case 0x03 :
                /* Specify */
                fct = Specify;
                break;

            case 0x04 :
                /* Lecture ST3 */
                fct = ReadST3;
                break;

            case 0x07 :
                /* Déplacement tête piste 0 */
                Status |= NDM;
                fct = MoveTrack0;
                break;

            case 0x08 :
                /* Lecture ST0, track */
                Status |= DIO;
                fct = ReadST0;
                break;

            case 0x0F :
                /* Déplacement tête */
                fct = MoveTrack;
                break;

            case 0x45 :
            case 0x49 :
                /* Ecriture données */
                fct = WriteData;
                break;

            case 0x46 :
            case 0x4C :
                /* Lecture données */
                fct = ReadData;
                break;

            case 0x4A :
                /* Lecture champ ID */
                fct = ReadID;
                break;
/*
            case 0x4D :
                fct = FormatTrack;
                break;

            case 0x51 :
            case 0x71 :
                break;
*/
            default :
                fct = Rien;
            }
        }
}


void ResetUPD( void )
{
    /*
    // Reset des variables du UPD
    */
    Status = 0x80;
    ST0    = 0x01;
    ST1    = 0x00;
    ST2    = 0x00;
    ST3    = 0x29;
    C      = 0x00;
    H      = 0x00;
    R      = 0xC1;
    N      = 0x02;
    Drive  = 0x00;
    EOT    = 0xC1;
    fct    = Rien;
    etat = REPOS;
}


void InitUPD( char * nomFic )
{
    static UBYTE XpkErrBuff[ XPKERRMSGSIZE + 12 ];

    static struct TagItem tags[] =
        {
        XPK_InName, NULL,
        XPK_PassThru, TRUE,
        XPK_OutBuf, ( ULONG )BufDisk,
        XPK_OutBufLen, TAILLE_DISK,
        XPK_GetError, NULL,
        TAG_DONE
        };

    tags[ 0 ].ti_Data = ( LONG )nomFic;
    tags[ 4 ].ti_Data = ( ULONG )XpkErrBuff;
    if ( ( XpkBase = OpenLibrary( XPKNAME, 0 ) ) != 0 )
        {
        XpkUnpack( tags );
        CloseLibrary( XpkBase );
        }
    else
        {
        BPTR Handle = Open( nomFic, MODE_OLDFILE );
        Read( Handle, BufDisk, TAILLE_DISK );
        Close( Handle );
        }
    DataSize = ( ( CPCEMUEnt * )BufDisk )->DataSize << 8;
    ChangeCurrTrack( 0 );
}


void EndUPD( void )
{
}
