/**
* Test hardware for ATMEL 28C series EEPROMs.
*
* The hardware uses two 74LS164 shift registers as the low and
* high address registers.
**/

#include "Configure.h"

#define LED 13

PromDevice28C  prom(32 * 1024L, 64, 10, true);


/*****************************************************************************/
/*****************************************************************************/

/**
 * CLI parse functions
 */
const char hex[] = "0123456789abcdef";


// Read a line of data from the serial connection.
char * readLine(char * buffer, int len)
{
    for (int ix = 0; (ix < len); ix++)
    {
         buffer[ix] = 0;
    }

    // read serial data until linebreak or buffer is full
    char c = ' ';
    int ix = 0;
    do {
        if (Serial.available())
        {
            c = Serial.read();
            if ((c == '\b') && (ix > 0))
            {
                // Backspace, forget last character
                --ix;
            }
            buffer[ix++] = c;
            Serial.write(c);
        }
    } while ((c != '\n') && (ix < len));

    buffer[ix - 1] = 0;
    return buffer;
}




/************************************************************
* convert a single hex character [0-9a-fA-F] to its value
* @param char c single character (digit)
* @return byte value of the digit (0-15)
************************************************************/
byte hexDigit(char c)
{
    if ((c >= '0') && (c <= '9'))
    {
        return c - '0';
    }
    else if ((c >= 'a') && (c <= 'f'))
    {
        return c - 'a' + 10;
    }
    else if ((c >= 'A') && (c <= 'F'))
    {
        return c - 'A' + 10;
    }
    else
    {
        return 0xff;
    }
}


/************************************************************
* convert a hex byte (00 - ff) to byte
* @param c-string with the hex value of the byte
* @return byte represented by the digits
************************************************************/
byte hexByte(char * a)
{
    return (hexDigit(a[0]) << 4) | hexDigit(a[1]);
}


/************************************************************
* convert a hex word (0000 - ffff) to unsigned int
* @param c-string with the hex value of the word
* @return unsigned int represented by the digits
************************************************************/
unsigned int hexWord(char * data)
{
    return (hexDigit(data[0]) << 12) |
           (hexDigit(data[1]) <<  8) |
           (hexDigit(data[2]) <<  4) |
           (hexDigit(data[3]));
}


void printByte(byte b)
{
    char line[3];

    line[0] = hex[b >> 4];
    line[1] = hex[b & 0x0f];
    line[2] = '\0';

    Serial.print(line);
}


void printWord(word w)
{
    char line[5];

    line[0] = hex[(w >> 12) & 0x0f];
    line[1] = hex[(w >>  8) & 0x0f];
    line[2] = hex[(w >>  4) & 0x0f];
    line[3] = hex[(w)       & 0x0f];
    line[4] = '\0';

    Serial.print(line);
}


/************************************************
* MAIN
*************************************************/
word addr = 0;

void setup()
{
    // Do this first so that it initializes all of the hardware pins into a
    // non-harmful state.  The Arduino or the target EEPROM could be damaged
    // if both writing to the data bus at the same time.
    prom.begin();

    Serial.begin(115200);
}

void loop()
{
    commandLoop();
}

static void commandLoop()
{
    byte b;
    word w;
    char line[20];
    uint32_t numBytes;
    unsigned long timeStart;
    unsigned long timeEnd;
    bool cmdError = false;

    Serial.print("\n#");
    Serial.flush();
    readLine(line, sizeof(line));
    byte c = tolower(line[0]);
    if ((c >= 'A') && (c <= 'Z')) {
        c |= 0x20;
    }

    switch (c)
    {
    case 'a':
        if (hexDigit(line[1]) <= 15)
        {
            w = hexWord(line + 1);
            prom.setAddress(w);
        }
        else
            cmdError = true;
        break;

    case 'd':
        if (hexDigit(line[1]) <= 15)
        {
            prom.disableOutput();
            prom.setDataBusMode(OUTPUT);
            b = hexByte(line + 1);
            prom.writeDataBus(b);
        }
        else
            cmdError = true;
        break;

    case 'c':
    case 'o':
    case 'w':
        if ((line[1] != 'd') && (line[1] != 'e'))
        {
            cmdError = true;
        }
        else
        {
            bool enable = line[1] == 'e';
            if (c == 'c')
                if (enable) prom.enableChip(); else prom.disableChip();
            else if (c == 'w')
                if (enable) prom.enableWrite(); else prom.disableWrite();
            else { // c == 'o'
                if (enable) 
                {
                    // Don't allow the prom and the data bus to output at the same time
                    prom.setDataBusMode(INPUT);
                    prom.enableOutput();
                }
                else prom.disableOutput();
            }
        }
        break;

    case 'r':
        prom.setDataBusMode(INPUT);
        prom.enableOutput();
        b = prom.readDataBus();
        printByte(b);
        Serial.println();
        prom.disableOutput();
        break;

    case 'l':
        Serial.println(F("Writing the lock code to enable Software Write Protect mode."));
        prom.enableSoftwareWriteProtect();
        break;

    case 'u':
        Serial.println(F("Writing the unlock code to disable Software Write Protect mode."));
        timeStart = micros();
        prom.disableSoftwareWriteProtect();
        timeEnd = micros();
        Serial.print("Unlock command time in uSec=");
        Serial.println(timeEnd - timeStart);
        break;

    default:
        cmdError = true;
        break;
    }

    if (cmdError) {
        Serial.print(F("Hardware Verifier - "));
        Serial.println(prom.getName());
        Serial.println();
        Serial.println(F("Valid commands are:"));
        Serial.println(F("  Axxxx         - Set address bus to xxxx"));
        Serial.println(F("  Dxx           - Set Data bus to xx"));
        Serial.println(F("  Cs            - Set Chip enable to state (e=enable, d=disable)"));
        Serial.println(F("  Os            - Set Output enable to state (e=enable, d=disable)"));
        Serial.println(F("  Ws            - Set Write enable to state (e=enable, d=disable)"));
        Serial.println(F("  R             - Read and print the value on the data bus"));
        Serial.println(F("  L             - Send Lock sequence to enable device Software Data Protection"));
        Serial.println(F("  U             - Send Unlock sequence to disable device Software Data Protection"));
    }
}

