/*
 * (c) d.m.broad. June 1995 (originally for the C= Amiga) & July 2005 (for FREEBSD on the PC).
 *
 * see build notes elsewhere.
 *
 * BSD LICENSE APPLIES.
 */

#include "pep.h"

/*
 * output port storage register
 *
 * internal usage only.
 *
 * nb. initPEP must be called before using pep related functions
 */
volatile unsigned short opdata;

/*
 * output bit to one of the 2 output control lines
 *
 *  out= 0 or 1
 *  bit= 0 or 1 (bits are inverted inside pep)
 *
 * this version is unlike the original (1995) and has been rewritten
 * for clarity rather than speed.
 */
void outPEP(unsigned char out, unsigned char bit)
{
    switch(out)
    {
        case 0: switch(bit)
                {
                    case 0: outb(DATA, inb(DATA) | OUTPUT_0);
                            break;
                    case 1: outb(DATA, inb(DATA) & ~OUTPUT_0);
                            break;
                }
                break;

        case 1: switch(bit)
                {
                    case 0: outb(DATA, inb(DATA) | OUTPUT_1);
                            break;
                    case 1: outb(DATA, inb(DATA) & ~OUTPUT_1);
                            break;
                }
                break;
    }
}

/*
 * input bit from one of the 2 input control lines
 *
 * in = 0 or 1 (inputs are inverted inside pep, plus input 0 is inverted inside pc interface)
 */
unsigned char inPEP(unsigned char in)
{
    unsigned char bit=inb(STATUS);

    switch(in)
    {
            case 0: bit= bit & INPUT_0;
                    break;
            case 1: bit= bit & INPUT_1;
                    bit= bit ^ INPUT_1;
                    break;
    }
    return bit;
}

/*
 * write 16 bit data to output port
 */
void writePEP(unsigned short d)
{
    unsigned short i;

    opdata=d;                                       /* save output data */

    for(i=0; i<16; i++)
    {
        if( d & 0x8000 )                            /* prepare output bit */
            outb(DATA, inb(DATA) | OUTPUT_DATA);
        else
            outb(DATA, inb(DATA) & ~OUTPUT_DATA);

        outb(DATA, inb(DATA) | OUTPUT_CLOCK);       /* clock high (shift bit in) */
        outb(DATA, inb(DATA) & ~OUTPUT_CLOCK);      /* clock low */

        d= d << 1;                                  /* shift to next output bit */
    }
    outb(DATA, inb(DATA) | OUTPUT_STROBE);          /* strobe high (latch data) */
    outb(DATA, inb(DATA) & ~OUTPUT_STROBE);         /* strobe low */
}

/*
 * read 16 bit data from input port
 */
unsigned short readPEP(void)
{
    unsigned short i, d=0;

    outb(DATA, inb(DATA) | INPUT_STROBE);           /* strobe high (parallel load data) */
    outb(DATA, inb(DATA) &~ INPUT_STROBE);          /* strobe low */

    for(i=0; i<16; i++)
    {
        d= d << 1;                                  /* shift input bits */

        if( inb(STATUS) & INPUT_DATA )              /* read bit */
            d= d | 1;

        outb(DATA, inb(DATA) | INPUT_CLOCK);        /* clock high (shift bit out) */
        outb(DATA, inb(DATA) & ~INPUT_CLOCK);       /* clock low */
    }
    return d;
}

/*
 * test an input bit (0..15)
 */
unsigned short testPEP(unsigned char bit)
{
    return ( readPEP() & (1 << bit) );
}

/*
 * set an output bit (0..15)
 */
void setPEP(unsigned char bit)
{
    writePEP( opdata | (1 << bit) );
}

/*
 * clear an output bit (0..15)
 */
void clrPEP(unsigned char bit)
{
    writePEP( opdata & ~(1 << bit) );
}

/*
 * toggle an output bit (0..15)
 */
void chgPEP(unsigned char bit)
{
    writePEP( opdata ^ (1 << bit) );
}

/*
 * enable output port
 *
 * taking OUTPUT_ENABLE low enables the outputs on both 4094 registers
 *
 * this only functions when switch is in the P3 position (internal EO)
 */
void enablePEP_OP(void)
{
    outb(DATA, inb(DATA) & ~OUTPUT_ENABLE);
}

/*
 * disable output port
 *
 * taking OUTPUT_ENABLE high disables the outputs on both 4094 registers
 *
 * this only functions when switch is in the P3 position (internal EO)
 */
void disablePEP_OP(void)
{
    outb(DATA, inb(DATA) | OUTPUT_ENABLE);
}

/*
 * initialise interface
 *
 * always call this before using the PEP interface
 */
void initPEP_IO(void)
{
    FILE *io=fopen("/dev/io", "rw");
    if(io==NULL)
        exit(-1);

    disablePEP_OP();        /* all o/p 3-state */
    writePEP(0);            /* all o/p low */
    outPEP(OUT0, LOW);      /* out0 low */
    outPEP(OUT1, LOW);      /* out1 low */
}
