/* ----------------------------------------------------
 *  LISTING 5
 *
 *  Filename:           tape.c
 *  Summary:            State machine constructs for
 *                      tape device
 *  Author:             T.W. Nelson
 *  Compile options:    
 *  Version:            1.00
 *  Date:               07-Oct-1991
 *  Notes:
 *
 *  Source code Copyright (c) 1991 T.W. Nelson.
 *  May be used only with appropriate
 *  acknowledgement of copyright.
 * ------------------------------------------------- */

#include "driver.h"
#include "tape.h"

extern void putstr( const char *str );

extern int initialize(), status(), eject(), record(),
    play(), fforward(), rewind(), stop(), cstate(),
    shutdown();

//Arrays of function pointers to be executed
//in the order listed, to effect transition from
//the current state to next desired state.....
//
int (*f_init[])() = { initialize, cstate, 0 };

int (*f_stat[])() = { status, cstate, 0 };

int (*f_off[])() = { shutdown, cstate, 0 };

int (*f_ejoff[])() = { eject, shutdown, cstate, 0 };

int (*f_eject[])() = { eject, cstate, 0    };

int (*f_rec[])() = { record, cstate, 0     };

int (*f_play[])() = { play, cstate, 0     };

int (*f_ffwd[])() = { fforward, cstate, 0   };

int (*f_rwnd[])() = { rewind, cstate, 0    };

int (*f_stop[])() = { stop, cstate, 0    };

int (*f_steject[])() = { stop, eject, cstate, 0   };

int (*f_strec[])() = { stop, record, cstate, 0    };

int (*f_stplay[])() = { stop, play, cstate, 0    };

int (*f_stffwd[])() = { stop, fforward, cstate, 0  };

int (*f_strwnd[])() = { stop, rewind, cstate, 0    };

//Event (Command) Tables describing valid events for a
//state, the next state to transition to, as well as
//the functions to effect the transition......
//
S_TAB s_off[] = {
/*  command         n_state funcs
  --------------------------------- */
    CMD_POWER_ON,   ON,     f_init,
    CMD_CHK_STATUS, OFF,    f_stat,
    END,            END,    0,    };

S_TAB s_on[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_POWER_OFF,  OFF  ,      f_off,
    CMD_INSERT   ,  READY,      f_stat,
    CMD_CHK_STATUS, ON,         f_stat,
    END          ,  END  ,      0,    };

S_TAB s_ready[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_POWER_OFF , OFF     ,   f_ejoff,
    CMD_EJECT     , ON      ,   f_eject,
    CMD_RECORD    , RECORD  ,   f_rec,
    CMD_PLAY      , PLAY    ,   f_play,
    CMD_FFORWARD  , FFORWARD,   f_ffwd,
    CMD_REWIND    , REWIND  ,   f_rwnd,
    CMD_CHK_STATUS, READY   ,   f_stat,
    END           , END     ,   0,    };

S_TAB s_record[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_STOP      , READY   ,   f_stop,
    CMD_EJECT     , ON      ,   f_steject,
    CMD_FFORWARD  , FFORWARD,   f_stffwd,
    CMD_PLAY      , PLAY    ,   f_stplay,
    CMD_REWIND    , REWIND  ,   f_strwnd,
    CMD_CHK_STATUS, RECORD  ,   f_stat,
    END           , END     ,   0,    };

S_TAB s_play[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_STOP      , READY   ,   f_stop,
    CMD_EJECT     , ON      ,   f_steject,
    CMD_FFORWARD  , FFORWARD,   f_stffwd,
    CMD_REWIND    , REWIND  ,   f_strwnd,
    CMD_RECORD    , RECORD  ,   f_strec,
    CMD_CHK_STATUS, PLAY    ,   f_stat,
    END           , END     ,   0,    };

S_TAB s_fforward[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_STOP      , READY   ,   f_stop,
    CMD_EJECT     , ON      ,   f_steject,
    CMD_PLAY      , PLAY    ,   f_stplay,
    CMD_REWIND    , REWIND  ,   f_strwnd,
    CMD_RECORD    , RECORD  ,   f_strec,
    CMD_CHK_STATUS, FFORWARD,   f_stat,
    END           , END     ,   0,    };

S_TAB s_rewind[] = {
/*  command         n_state     funcs
  ------------------------------------ */
    CMD_STOP      , READY   ,   f_stop,
    CMD_EJECT     , ON      ,   f_steject,
    CMD_PLAY      , PLAY    ,   f_stplay,
    CMD_RECORD    , RECORD  ,   f_strec,
    CMD_FFORWARD  , FFORWARD,   f_stffwd,
    CMD_CHK_STATUS, REWIND  ,   f_stat,
    END           , END     ,   0,    };

//The actual "state table" that connects all
//the foregoing initialized data into one.  enum
//'device_states' is an index into this array
//of struct pointers. These MUST be in same order
//as the enum in 'tape.h' ........
//
S_TAB *s_table[] = {
    s_off, s_on, s_ready, s_record, s_play,
    s_fforward, s_rewind,
    };

unsigned tape_io( CMDARG far *arg )
{
   /* State Machine Driver. Called by DOS thru driver
    * interrupt routine, command codes 3 & 12.
    */

    int i;
    S_TAB *pcmd;        //-> event (cmd) table
    int (**pfun)(CMDARG far *arg);  //-> function list

    //point to the correct event table .....
    if( arg->c_state >= MAX_STATE )
            return IS_ERROR + INV_COMMAND;

    pcmd = s_table[arg->c_state];

    //find the specified command in the event table...
    for( i = 0; pcmd[i].event != END; i++ )
            if( pcmd[i].event == arg->cmd )
                    break;

    //check for invalid command .....
    if( pcmd[i].event == END ) {
            putstr("Invalid command specified\r\n");
            return IS_ERROR + INV_COMMAND;
    }

    //indicate next state for application .......
    arg->c_state = pcmd[i].n_state;

    //execute the function list ........
    pfun = pcmd[i].procs;

    for( i = 0; pfun[i]; i++ )
        (*pfun[i])(arg);

    return 0;
}

int status(CMDARG far *arg)
{
    arg->status = OK;
    return 0;
}

int initialize(CMDARG far *arg)
{
    putstr("Device initialized\r\n" );
    status(arg);
    return 0;
}

int eject(CMDARG far *arg)
{
    putstr( "Cassette ejected\r\n" );
    status(arg);
    return 0;
}

int record(CMDARG far *arg)
{
    putstr( "Recording\r\n" );
    status(arg);
    return 0;
}

int play(CMDARG far *arg)
{
    putstr( "Playing\r\n" );
    status(arg);
    return 0;
}

int fforward(CMDARG far *arg)
{
    putstr( "Fast forwarding\r\n" );
    status(arg);
    return 0;
}

int rewind(CMDARG far *arg)
{
    putstr( "Rewinding\r\n" );
    status(arg);
    return 0;
}

int stop(CMDARG far *arg)
{
    putstr( "Stopping\r\n" );
    status(arg);
    return 0;
}

int shutdown(CMDARG far *arg)
{
    putstr( "Powered down\r\n" );
    status(arg);
    return 0;
}

int cstate(CMDARG far *arg)
{
   /* Print current state, after transition .... */

    static char *s_name[] = {
        "OFF",        //0
        "ON",         //1
        "READY",      //2
        "RECORD",     //3
        "PLAY",       //4
        "FFORWARD",   //5
        "REWIND",     //6
        };

    putstr( "Current state is " );
    putstr( s_name[arg->c_state] );
    putstr( "\r\n" );
    return 0;
}

/* ----- End of File ------------------------------- */
