Thursday, August 13, 2015

Arduino Code

This is going to be a long post. It is parts of the Arduino code that runs the flash programmer and bus.
SetupLoop.cpp


#include "Flash.h"
#include "serialIF.h"
#include "Interpreter.h"
#include "Arduino.h"
#include "CPLD.h"

// Arduino uses 'setup' and 'loop' instead of 'main'

void setup() {

    pinMode(13, OUTPUT);

    // Blink LED twice
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
}

void loop() {

    Interpreter interpreter;
    interpreter.Loop();

}

SerialIF.h

#ifndef INCLUDE_SERIALIF_H
#define INCLUDE_SERIALIF_H

// The SerialIF class provides application level access to communication facilities
// c must be:
//      > command
//      < response
//      ! text
//      $ payload
//      . ready for payload
// Each exchange has an id
// packet ID is used for payloads
class SerialIF
{
 public:
     SerialIF();
     ~SerialIF();

public:
    // Basic send. See class doc for 'c'. response is a buffer of up to 256. length is 1-256, 
    bool Send(char c, const uint8_t* response, int16_t length, uint8_t id, uint16_t packetId);

    // Basic send. See class doc for 'c'. buffer is a buffer of 256 bytes. length is 1-256, 
    int16_t Receive(char *pC, uint8_t* buffer, uint8_t* pId, uint16_t *pPacketId);
};

#endif


SerialIF.cpp

#include "SerialIF.h"

#define HEADER_LENGTH (6)
#define TIMEOUT (10000)
#define BAUD_RATE (9600)

///////////////////////////////////////////////////////////////////////////////
//
//      Object section
//

SerialIF::SerialIF()
{
    Serial.begin(BAUD_RATE);
    Serial.setTimeout(TIMEOUT);
}

SerialIF::~SerialIF()
{
    Serial.end();
}

///////////////////////////////////////////////////////////////////////////////
//
//      Low Level Read/Write section
//

// A single communication consists of a header and the data
// Header is 6 bytes:
//      [0] A character:
//          '>' PC to Arduino command
//          '<' Arduino to PC response
//          '!' In progess Arduino -> PC text
//          '$' Payload packet (256 bytes unless the last one)
//          '.' OK to send next payload packet
//      [1] length 1-255, 0 means 256
//      [2] 8 bit transaction id
//      [3] sum adjustment. Adjust this so the sum of header and data is 0xff
//      [4] 2 byte packet # high byte
//      [5] 2 byte packet # low byte
//
//  The header is hidden from the interface. The user simply used the 'command', 'response', 
//  'eventString', or 'buffer' and the proper calling function
//

bool SerialIF::Send(char c, const uint8_t* response, int16_t length, uint8_t id, uint16_t packetId)
{
    // Setup up header
    uint8_t header[HEADER_LENGTH];
    header[0] = c;
    header[1] = (uint8_t)length; // 256 converts to 0 in cast
    header[2] = id;
    header[3] = 0;
    header[4] = (byte)(packetId >> 8);
    header[5] = (byte)packetId;

    // Determine sum for error checking
    uint8_t sum = 0;
    for (int16_t i = 0; i < HEADER_LENGTH; i++)
    {
        sum += header[i];
    }
    for (int16_t i = 0; i < length; i++)
    {
        sum += response[i];
    }
    header[3] = (uint8_t)~sum;
    
    // ensure it all gets send
    const uint8_t* p = header;
    int len = HEADER_LENGTH;
    int sent = 0;
    while (sent < len)
    {
        int got = Serial.write(p, len - sent);
        if (got < 0 || got > len - sent)
        {
            digitalWrite(13, HIGH);
            return false;
        }
        sent += got;
        p += got;
    }
    len = length;
    p = response;
    sent = 0;
    while (sent < len)
    {
        int got = Serial.write(p, len - sent);
        if (got < 0 || got > len - sent)
        {
            digitalWrite(13, HIGH);
            return false;
        }
        sent += got;
        p += got;
    }
    return true;
}

int16_t SerialIF::Receive(char *pC, uint8_t* buffer, uint8_t* pId, uint16_t *pPacketId)
{
    uint32_t stopOn = millis() + TIMEOUT;
    int16_t received = 0;
    int16_t expecting = HEADER_LENGTH;
    uint8_t header[HEADER_LENGTH];
    uint8_t* p;

    // Get header
    p = &header[0];
    while (millis() < stopOn && received < expecting)
    {
        size_t got = Serial.readBytes(p, expecting - received);
        if (got > (size_t)(expecting - received))
        {
            digitalWrite(13, HIGH);
            return -1;
        }
        p += got;
        received += got;
    }

    if (received == expecting)
    {
        // extract the type character
        *pC = header[0];

        // get the data size
        expecting = header[1];
        if (expecting == 0)
        {
            expecting = 256;
        }

        // get the ID
        *pId = header[2]; // return to user
        *pPacketId = (header[4] << 8) | header[5];
    }
    else
    {
        return -1;
    }

    // Get the data portion
    p = buffer;
    *p = 'x'; // destroy first data
    received = 0;
    while (millis() < stopOn && received < expecting)
    {
        size_t got = Serial.readBytes(p, expecting - received);
        if (got > (size_t)(expecting - received))
        {
            digitalWrite(13, HIGH);
            return -1;
        }
        received += got;
        p += got;
    }

    if (received == expecting)
    {
        // ensure the sum is 255
        uint8_t sum = 0;
        for (int16_t i = 0; i < HEADER_LENGTH; ++i)
        {
            sum += header[i];
        }
        for (int16_t i = 0; i < expecting; ++i)
        {
            sum += buffer[i];
        }
        if (sum != 255)
        {
            return -1;
        }
    }
    else
    {
        return -1;
    }

    return received;
}


CPLD.h

#ifndef INCLUDE_CPLD_H
#define INCLUDE_CPLD_H

// The CPLD class handles low level access to the CPLD which controls the 39SF020A flash device
class CPLD
{
protected:
    CPLD();

    // Send CPLD reset sequence
    void ResetSequence();

    // Update address bits 3:0
    void UpdateAddress_3_0(uint32_t addr);

    // Update address bits 7:4
    void UpdateAddress_7_4(uint32_t addr);

    // Update address bits 17:8
    void UpdateAddress_17_8(uint32_t addr);

    // Update chip control where bits 3-0 are drive, CS*, WR*, and RD*
    void UpdateChip(int16_t chip);

    // Update the CTRL register that tells which register to pulse data in and out
    void UpdateCtrl(int16_t ctrl);

    // Swaps a data byte on Din/Dout
    uint8_t ReadWrite(uint8_t data);

    // Pulse CLK high and low
    void PulseClk();

    // Pulse nCTRL low and high
    void PulseCtrl();

public:
    // Control pins directly
    bool DirectWrite(uint16_t mask, uint16_t values);

    // Read current state of pins
    bool DirectRead(uint16_t mask, uint16_t* pValues);

private:
    // Update an arbitrary part of the address bits
    void UpdateAddress_start_end(uint32_t addr, int16_t startBit, int16_t endBit);

    // Keep track of last control address to know which parts to avoid shifting out
    int16_t m_lastCtrl;

public:
    static const int16_t PIN_nCTRL = 2;
    static const int16_t PIN_Dout = 3;
    static const int16_t PIN_Din = 4;
    static const int16_t PIN_CLK = 5;
    static const int16_t PIN_ENABLE = 6;
};

#endif


CPLD.cpp

#include "CPLD.h"

// Adds delay if necessary for viewing pins with DMM
#define SUPER_SLO_MO()

#define LAST_CTRL_INIT (-1)

CPLD::CPLD()
{
    m_lastCtrl = LAST_CTRL_INIT;
}

///////////////////////////////////////////////////////////////////////////////
//
//      Reset section
//

void CPLD::ResetSequence()
{
    // initialize pins before setting output mode
    digitalWrite(PIN_ENABLE, HIGH);
    digitalWrite(PIN_CLK, HIGH);
    digitalWrite(PIN_nCTRL, HIGH);
    digitalWrite(PIN_Din, HIGH);

    // set pin modes
    pinMode(PIN_ENABLE, OUTPUT);
    pinMode(PIN_nCTRL, OUTPUT);
    pinMode(PIN_Dout, INPUT);
    pinMode(PIN_Din, OUTPUT);
    pinMode(PIN_CLK, OUTPUT);

    // Execute reset sequence
    PulseClk();
    PulseClk();
    digitalWrite(PIN_nCTRL, LOW);
    delayMicroseconds(1);
    PulseClk();
    PulseClk();
    PulseClk();
    digitalWrite(PIN_nCTRL, HIGH);
    digitalWrite(PIN_nCTRL, HIGH);
    delayMicroseconds(1);
    SUPER_SLO_MO();
    digitalWrite(PIN_ENABLE, LOW);
    delayMicroseconds(1);
    SUPER_SLO_MO();
}

///////////////////////////////////////////////////////////////////////////////
//
//      Address Shift Register section
//

void CPLD::UpdateAddress_3_0(uint32_t addr)
{
    UpdateCtrl(0x01);
    UpdateAddress_start_end(addr, 3, 0);
}

void CPLD::UpdateAddress_7_4(uint32_t addr)
{
    UpdateCtrl(0x02);
    UpdateAddress_start_end(addr, 7, 4);
}

void CPLD::UpdateAddress_17_8(uint32_t addr)
{
    UpdateCtrl(0x03);
    UpdateAddress_start_end(addr, 17, 8);
}
void CPLD::UpdateAddress_start_end(uint32_t addr, int16_t endBit, int16_t startBit)
{
    int16_t i;
    for (i = startBit; i <= endBit; ++i)
    {
        uint32_t mask = 1L << i;
        digitalWrite(PIN_Din, (addr & mask) ? HIGH : LOW);
        digitalWrite(PIN_Din, (addr & mask) ? HIGH : LOW); // delay
        SUPER_SLO_MO();
        PulseClk();
    }
}

// Chip bits are:
//  3   drive id not nRD low, or read on rising edge
//  2   nCS
//  1   nWR
//  0   nRD
void CPLD::UpdateChip(int16_t chip)
{
    int16_t i;
    UpdateCtrl(0x07);
    for (i = 0; i < 4; ++i)
    {
        digitalWrite(PIN_Din, (chip & 1) ? HIGH : LOW);
        digitalWrite(PIN_Din, (chip & 1) ? HIGH : LOW); // delay
        SUPER_SLO_MO();
        PulseClk();
        chip >>= 1;
    }
    PulseCtrl();
}

///////////////////////////////////////////////////////////////////////////////
//
//      CPLD Control section
//

// Control register
void CPLD::UpdateCtrl(int16_t ctrl)
{
    int16_t i;
    digitalWrite(PIN_nCTRL, LOW);
    SUPER_SLO_MO();
    if (true || m_lastCtrl != ctrl)
    {
        m_lastCtrl = ctrl;
        for (i = 0; i < 3; ++i)
        {
            int16_t bit = ctrl & 1;
            ctrl = ctrl >> 1;
            digitalWrite(PIN_Din, bit ? HIGH : LOW);
            digitalWrite(PIN_Din, bit ? HIGH : LOW); // delay
            SUPER_SLO_MO();
            PulseClk();
        }
    }
    else
    {
        delayMicroseconds(1);
    }
    digitalWrite(PIN_nCTRL, HIGH);
    SUPER_SLO_MO();
}

// clock exchange 8 bits on 'data'->Din and Dout->return.
uint8_t CPLD::ReadWrite(uint8_t data)
{
    int16_t i;
    uint8_t outData = 0;
    UpdateCtrl(0x00);
    for (i = 0; i < 8; ++i)
    {
        digitalWrite(PIN_Din, (data & 1) ? HIGH : LOW);
        digitalWrite(PIN_Din, (data & 1) ? HIGH : LOW); // delay
        SUPER_SLO_MO();
        PulseClk();
        outData >>= 1;
        if (digitalRead(PIN_Dout) == HIGH)
        {
            outData |= 0x80;
        }
        data >>= 1;
    }
    return outData;
}

// Brings clk high then low
void CPLD::PulseClk()
{
    digitalWrite(PIN_CLK, HIGH);
    digitalWrite(PIN_CLK, HIGH); // delay
    SUPER_SLO_MO();
    digitalWrite(PIN_CLK, LOW);
    digitalWrite(PIN_CLK, LOW); // delay
    SUPER_SLO_MO();
}

// Brings nCTRL low then high -- Use to strobe bus control
void CPLD::PulseCtrl()
{
    digitalWrite(PIN_nCTRL, LOW);
    digitalWrite(PIN_nCTRL, LOW); // delay
    SUPER_SLO_MO();
    digitalWrite(PIN_nCTRL, HIGH);
    digitalWrite(PIN_nCTRL, HIGH); // delay
    SUPER_SLO_MO();
}

///////////////////////////////////////////////////////////////////////////////
//
//      Direct Arduino Pin Access section
//

bool CPLD::DirectWrite(uint16_t mask, uint16_t values)
{
    for (int i = 0; i < 13; i++)
    {
        uint16_t bit = 1 << i;
        if (mask & bit)
        {
            digitalWrite(i, (values & bit) ? (HIGH) : (LOW));
        }
    }
    return true;
}

bool CPLD::DirectRead(uint16_t mask, uint16_t* pValues)
{
    *pValues = 0;
    for (int i = 0; i < 13; i++)
    {
        uint16_t bit = 1 << i;
        if (mask & bit)
        {
            if (digitalRead(i))
            {
                *pValues |= bit;
            }
        }
    }
    return true;
}


Bus.h

#ifndef INCLUDE_BUS_H
#define INCLUDE_BUS_H
#include "CPLD.h"

// The Bus class provides access to the bus on the output of the CPLD
class Bus : public CPLD
{
public:
    Bus();
    ~Bus();

    // Sets the input/output pin mode
    virtual bool EnablePins(bool enable);

    // Resets the Bus
    virtual bool Reset(bool force);

    // Write data to *ptr.
    // NOTE: This is a NOT toggle bit operation, just sends data to *ptr
    // returns true if successful
    void Write(uint32_t ptr, uint8_t data);

    // Read data at *ptr into *pData
    // returns true if successful
    void Read(uint32_t, uint8_t* pData);

    // Read data at last *ptr into *pData
    // returns true if successful
    void Read(uint8_t* pData);

    // Updates entire address bus
    void UpdateAddress(uint32_t addr);

protected:
    // Update address bits only on parts that are different
    void UpdateAddressDiff(uint32_t addr);

    // Pulse RD
    void PulseRD();

    // Pulse WR
    void PulseWR();

private:
    // Keep track of last programmed address to know which parts to avoid shifting out
    uint32_t m_lastAddr;

    // Keep track of whether the CPLD has been reset
    bool m_reset;
};

#endif


Bus.cpp

#include "Bus.h"

#define LAST_ADDR_INIT (0xffffffff)

Bus::Bus() : CPLD()
{
    m_lastAddr = LAST_ADDR_INIT;
    m_reset = false;
}

Bus::~Bus()
{
}

///////////////////////////////////////////////////////////////////////////////
//
//      Reset section
//

bool Bus::Reset(bool force)
{
    if (force || !m_reset)
    {
        ResetSequence();

        m_lastAddr = LAST_ADDR_INIT;
        m_reset = true;
    }
    return true;
}

bool Bus::EnablePins(bool enable)
{
    if (enable)
    {
        pinMode(PIN_ENABLE, OUTPUT);
        pinMode(PIN_nCTRL, OUTPUT);
        pinMode(PIN_Dout, INPUT);
        pinMode(PIN_Din, OUTPUT);
        pinMode(PIN_CLK, OUTPUT);
    }
    else
    {
        // TODO when pullups are on board, make all these revert to INPUT
        pinMode(PIN_ENABLE, INPUT_PULLUP);
        pinMode(PIN_nCTRL, INPUT_PULLUP);
        pinMode(PIN_Dout, INPUT);
        pinMode(PIN_Din, INPUT_PULLUP);
        pinMode(PIN_CLK, INPUT_PULLUP);
    }
    return true;
}


///////////////////////////////////////////////////////////////////////////////
//
//      Basic Operation section
//


void Bus::Write(uint32_t addr, uint8_t data)
{
    UpdateAddressDiff(addr);
    ReadWrite(data);
    PulseWR();
}

void Bus::Read(uint32_t addr, uint8_t* pData)
{
    UpdateAddressDiff(addr);
    PulseRD();
    *pData = ReadWrite(0);
}

void Bus::Read(uint8_t* pData)
{
    PulseRD();
    *pData = ReadWrite(0);
}

///////////////////////////////////////////////////////////////////////////////
//
//      Address Shift Register section
//

// Updates entire address bus
void Bus::UpdateAddress(uint32_t addr)
{
    UpdateAddress_3_0(addr);
    UpdateAddress_7_4(addr);
    UpdateAddress_17_8(addr);
}

// Updates address bus shifting only those shift registers that have changed
void Bus::UpdateAddressDiff(uint32_t addr)
{
    if (m_lastAddr == LAST_ADDR_INIT)
    {
        UpdateAddress(addr);
    }
    else
    {
        uint32_t diffs = (uint32_t)(addr ^ m_lastAddr);
        if (diffs & 0x0000f)
        {
            UpdateAddress_3_0(addr);
        }
        if (diffs & 0x000f)
        {
            UpdateAddress_7_4(addr);
        }
        if (diffs & 0x3ff00UL)
        {
            UpdateAddress_17_8(addr);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
//
//      Bus Control Control section
//

//  D[7:0]  -------------------------
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^\_________________/^^^
//  nWR     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  read                   *
//  Reads when drive goes high (*)
void Bus::PulseRD()
{
    UpdateChip(0xa); // CS, RD
    UpdateChip(0x2); // drive, CS, RD
    UpdateChip(0xa); // CS, RD
    UpdateChip(0xf); // done
}

//  D[7:0]  -[                     ]-
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  nWR     ^^^^^^^\_________/^^^^^^^
//  Writes when nWR goes low
void Bus::PulseWR()
{
    UpdateChip(0x3); // drive, CS
    UpdateChip(0x1); // drive, CS, WR
    UpdateChip(0x3); // drive, CS
    UpdateChip(0xf); // done
}



Flash.h

#ifndef INCLUDE_FLASH_H
#define INCLUDE_FLASH_H

#include "Bus.h"

// The Flash class handles application level interface to the flash
class Flash
{
public:
    Flash(Bus& bus);

    // Resets the CPLD
    bool ForceReset();

    // Resets the CPLD if it hasn't been reset yet
    bool Reset();

    // Programs data to *ptr.
    // NOTE: This is a toggle bit operation
    // i.e. it puts the flash into byte program mode
    // and waits for the flash to finish
    // returns true if successful
    bool Program(uint32_t addr, uint8_t data);

    // Read data at *ptr into *pData
    // returns true if successful
    bool Read(uint32_t addr, uint8_t* pData);

    // Erases 4KB block with addr
    // NOTE: This is a toggle bit operation
    // returns true if successful
    bool EraseSector(uint32_t addr);

    // Erases entire chip
    // NOTE: This is a toggle bit operation
    // returns true if successful
    bool EraseChip();

protected:
    // Reads until the toggle bits stop toggling
    // Returns true if the read data matches expected
    // Use expected of 0xff for erase operations
    bool Toggle(uint8_t expected);

private:
    // Low level interface
    Bus m_bus;
};

#endif



Flash.cpp

#include "Bus.h"
#include "Flash.h"

Flash::Flash(Bus& bus)
{
    m_bus = bus;
}

///////////////////////////////////////////////////////////////////////////////
//
//      Byte Access section
//

bool Flash::Program(uint32_t addr, uint8_t data)
{
    m_bus.Write(0x5555, 0xaa);
    m_bus.Write(0x2aaa, 0x55);
    m_bus.Write(0x5555, 0xa0);
    m_bus.Write(addr, data);
    return Toggle(data);
}

bool Flash::Read(uint32_t addr, uint8_t* pData)
{
    m_bus.Read(addr, pData);
    return true;
}


///////////////////////////////////////////////////////////////////////////////
//
//      Erase section
//

bool Flash::EraseSector(uint32_t addr)
{
    m_bus.Write(0x5555, 0xaa);
    m_bus.Write(0x2aaa, 0x55);
    m_bus.Write(0x5555, 0x80);
    m_bus.Write(0x5555, 0xaa);
    m_bus.Write(0x2aaa, 0x55);
    m_bus.Write(addr, 0x30);
    return Toggle(0xff);
    m_bus.Write(0x2aaa, 0xf0);
}

bool Flash::EraseChip()
{
    m_bus.Write(0x5555, 0xaa);
    m_bus.Write(0x2aaa, 0x55);
    m_bus.Write(0x5555, 0x80);
    m_bus.Write(0x5555, 0xaa);
    m_bus.Write(0x2aaa, 0x55);
    m_bus.Write(0x5555, 0x10);
    return Toggle(0xff);
}

///////////////////////////////////////////////////////////////////////////////
//
//      Reset section
//

bool Flash::Reset()
{
    return m_bus.Reset(false);
}

bool Flash::ForceReset()
{
    return m_bus.Reset(true);
}

///////////////////////////////////////////////////////////////////////////////
//
//      Support section
//

bool Flash::Toggle(uint8_t expected)
{
    bool toggling = true;
    uint8_t data0;
    uint8_t data1;
    m_bus.Read(&data0);
    while (toggling)
    {
        m_bus.Read(&data1);
        if ((data1 ^ data0) & 0x40)
        {
            // toggle bit toggling
            data0 = data1; // set up for next toggle check
        }
        else
        {
            toggling = false;
        }
    }
    return data1 == expected;
}


MCUBus.h

#ifndef INCLUDE_MCUBUS_H
#define INCLUDE_MCUBUS_H
#include "Bus.h"

// The MCUBus class is used to allow the flash programmer to test the MCU chip before
// connecting the CPU and SRAM to the bus.
class MCUBus : public Bus
{
public:
    MCUBus();
    ~MCUBus();

    // Sets the input/output pin mode
    virtual bool EnablePins(bool enable);

    // Resets the Bus
    virtual bool Reset(bool force);

    // Writes 'data' to 16 bit address 'addr'
    bool WriteMem(uint16_t addr, uint8_t data);

    // Gets pData from 16 bit address 'addr'
    bool FetchMem(uint16_t addr, uint8_t *pData);

    // Gets pData from 16 bit address 'addr'
    bool ReadMem(uint16_t addr, uint8_t *pData);

    // Writes 'data' to io port 'port'
    bool WriteIO(uint8_t port, uint8_t data);

    // Reads 'data' from io port 'port'
    bool ReadIO(uint8_t port, uint8_t *pData);

    // Sets the HALT line
    void HaltLine(bool level);

    // Sets the BUSACK line
    void BusAckLine(bool level);

    // Sets the IOREQ line
    void IOReqLine(bool level);

    // Sets the MREQ line
    void MemReqLine(bool level);

    // Sets the M1 line
    void M1Line(bool level);

    // Sets the CLK line
    void Clock(bool level);

protected:
    void PulseIORD();
    void PulseIOWR();
    void PulseFetch();
    void PulseMemRD();
    void PulseMemWR();

public:
    static const int PIN_IOREQ = 7;
    static const int PIN_MREQ = 8;
    static const int PIN_M1 = 9;
    static const int PIN_HALT = 10;
    static const int PIN_BUSACK = 11;
    static const int PIN_CLOCK = 12;
};

#endif


MCUBus.cpp

#include "MCUBus.h"

//#define SUPER_SLO_MO() delay(1000)
#define SUPER_SLO_MO() delay(1)
//#define SUPER_SLO_MO()

MCUBus::MCUBus()
{
}

MCUBus::~MCUBus()
{
}


// Sets the input/output pin mode
bool MCUBus::EnablePins(bool enable)
{
    if (enable)
    {
        digitalWrite(PIN_HALT, HIGH);
        digitalWrite(PIN_MREQ, HIGH);
        digitalWrite(PIN_IOREQ, HIGH);
        digitalWrite(PIN_M1, HIGH);
        digitalWrite(PIN_CLOCK, HIGH);
        digitalWrite(PIN_BUSACK, HIGH);

        pinMode(PIN_HALT, OUTPUT);
        pinMode(PIN_MREQ, OUTPUT);
        pinMode(PIN_IOREQ, OUTPUT);
        pinMode(PIN_M1, OUTPUT);
        pinMode(PIN_CLOCK, OUTPUT);
        pinMode(PIN_BUSACK, OUTPUT);
    }
    else
    {
        // TODO when pullups are on board, make all these revert to INPUT
        pinMode(PIN_HALT, INPUT_PULLUP);
        pinMode(PIN_MREQ, INPUT_PULLUP);
        pinMode(PIN_IOREQ, INPUT_PULLUP);
        pinMode(PIN_M1, INPUT_PULLUP);
        pinMode(PIN_CLOCK, INPUT_PULLUP);
        pinMode(PIN_BUSACK, INPUT_PULLUP);
    }
    return Bus::EnablePins(enable);
}

// Resets the Bus
bool MCUBus::Reset(bool force)
{
    Clock(HIGH);
    Clock(LOW);
    Clock(HIGH);
    Clock(LOW);
    Clock(HIGH);
    Clock(LOW);
    return Bus::Reset(force);
}


bool MCUBus::WriteMem(uint16_t addr, uint8_t data)
{
    UpdateAddressDiff(addr);
    ReadWrite(data);
    PulseMemWR();
    return true;
}

bool MCUBus::FetchMem(uint16_t addr, uint8_t *pData)
{
    UpdateAddressDiff(addr);
    PulseFetch();
    *pData = ReadWrite(0);
    return true;
}

bool MCUBus::ReadMem(uint16_t addr, uint8_t *pData)
{
    UpdateAddressDiff(addr);
    PulseMemRD();
    *pData = ReadWrite(0);
    return true;
}

bool MCUBus::WriteIO(uint8_t port, uint8_t data)
{
    UpdateAddressDiff(port);
    ReadWrite(data);
    PulseIOWR();
    return true;
}

bool MCUBus::ReadIO(uint8_t port, uint8_t *pData)
{
    UpdateAddressDiff(port);
    PulseIORD();
    *pData = ReadWrite(0);
    return true;
}


void MCUBus::HaltLine(bool level)
{
    digitalWrite(PIN_HALT, level);
}

void MCUBus::BusAckLine(bool level)
{
    digitalWrite(PIN_BUSACK, level);
}

void MCUBus::IOReqLine(bool level)
{
    digitalWrite(PIN_IOREQ, level);
}

void MCUBus::MemReqLine(bool level)
{
    digitalWrite(PIN_MREQ, level);
}

void MCUBus::M1Line(bool level)
{
    digitalWrite(PIN_M1, level);
}

void MCUBus::Clock(bool level)
{
    digitalWrite(PIN_CLOCK, level);
}

//  D[7:0]  -------------------------
//  CLK     ^^^^^^\___/^^^^\____/^^^^
//  nIOREQ  ^^^\_________________/^^^
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^\_________________/^^^
//  nWR     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  read                   *
//  Reads when drive goes high (*)
void MCUBus::PulseIORD()
{
    Clock(LOW);
    UpdateChip(0xe); // RD
    IOReqLine(LOW);
    SUPER_SLO_MO();

    Clock(HIGH);
    UpdateChip(0x6); // drive, RD
    SUPER_SLO_MO();

    Clock(LOW);
    UpdateChip(0xe); // RD
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

    Clock(LOW);
    IOReqLine(HIGH);
    UpdateChip(0xf); // done
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

}
//  D[7:0]  -[                     ]-
//  CLK     ^^^^^^\___/^^^^\____/^^^^
//  nIOREQ  ^^^\_________________/^^^
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  nWR     ^^^^^^^\_________/^^^^^^^
//  Writes when nWR goes low
void MCUBus::PulseIOWR()
{
    Clock(LOW);
    UpdateChip(0x7); // drive
    IOReqLine(LOW);
    SUPER_SLO_MO();

    Clock(HIGH);
    UpdateChip(0x5); // drive, WR
    SUPER_SLO_MO();

    Clock(LOW);
    UpdateChip(0x7); // drive
    UpdateChip(0xf); // done
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

    Clock(LOW);
    IOReqLine(HIGH);
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

}

//  D[7:0]  -------------------------
//  CLK     ^^^^^^\___/^^^^\____/^^^^
//  nMREQ   ^^^\_________________/^^^
//  nM1     ^^^\_________________/^^^
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^\_________________/^^^
//  nWR     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  read                   *
//  Reads when drive goes high (*)
void MCUBus::PulseFetch()
{
    Clock(LOW);
    M1Line(LOW);
    UpdateChip(0xe); // RD
    MemReqLine(LOW);
    SUPER_SLO_MO();

    Clock(HIGH);
    UpdateChip(0x6); // drive, RD
    SUPER_SLO_MO();

    Clock(LOW);
    UpdateChip(0xe); // RD
    M1Line(HIGH);
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

    Clock(LOW);
    MemReqLine(HIGH);
    UpdateChip(0xf); // done
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

}

//  D[7:0]  -------------------------
//  CLK     ^^^^^^\___/^^^^\____/^^^^
//  nMREQ   ^^^\_________________/^^^
//  nM1     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^\_________________/^^^
//  nWR     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  read                   *
//  Reads when drive goes high (*)
void MCUBus::PulseMemRD()
{
    Clock(LOW);
    UpdateChip(0xe); // RD
    MemReqLine(LOW);
    SUPER_SLO_MO();

    Clock(HIGH);
    UpdateChip(0x6); // drive, RD
    SUPER_SLO_MO();

    Clock(LOW);
    UpdateChip(0xe); // RD
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

    Clock(LOW);
    MemReqLine(HIGH);
    UpdateChip(0xf); // done
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

}

//  D[7:0]  -[                     ]-
//  CLK     ^^^^^^\___/^^^^\____/^^^^
//  nMREQ   ^^^\_________________/^^^
//  nCS     ^^^\_________________/^^^
//  nRD     ^^^^^^^^^^^^^^^^^^^^^^^^^
//  nWR     ^^^^^^^\_________/^^^^^^^
//  Writes when nWR goes low
void MCUBus::PulseMemWR()
{
    Clock(LOW);
    UpdateChip(0x7); // drive
    MemReqLine(LOW);
    SUPER_SLO_MO();

    Clock(HIGH);
    UpdateChip(0x5); // drive, WR
    SUPER_SLO_MO();

    Clock(LOW);
    UpdateChip(0x7); // drive
    UpdateChip(0xf); // done
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

    Clock(LOW);
    MemReqLine(HIGH);
    SUPER_SLO_MO();

    Clock(HIGH);
    SUPER_SLO_MO();

}



Exchanger.h

#ifndef INCLUDED_EXCHANGER_H
#define INCLUDED_EXCHANGER_H

#include "SerialIF.h"

// The exchanger handles communication between the PC and the Arduino
class Exchanger {
public:
    Exchanger();
    ~Exchanger();

    // Expect a command. User supplied buffer. It provides the actual length received. Returns true on success
    bool WaitForCommand(uint8_t* receiveBuffer256, int* pLengthReceived);

    // Send a response in sendBuffer256. 'lengthToSend' is 1-255 and 0 means 256
    bool SendResponse(const uint8_t* sendBuffer256, uint8_t lengthToSend);

    // Sends a payload part. packetId is sequential from 0. lengthToSend is 1-255 and 0 means 256
    bool SendPayload(uint16_t packetId, const uint8_t* sendBuffer256, uint8_t lengthToSend);

    // Waits for a payload part. pLengthReceived is 1-255 and 0 means 256
    bool WaitForPayload(uint16_t expectedPacketId, uint8_t* receiveBuffer256, uint8_t* pLengthReceived);

    // Sends a ready to indicate ready for the next payload part
    bool SendReady(uint16_t packetId);

    // Waits for ready to send next packet. buffer is a buffer that can be destroyed
    bool WaitForReady(uint16_t packetId, uint8_t* buffer);

    // Sends a text string (up to but not including the NUL terminator)
    bool SendText(const char* text);

private:
    SerialIF m_serial;
    byte m_id;
};

#endif

Exchanger.cpp

#include "Exchanger.h"

Exchanger::Exchanger() : m_serial()
{
}

Exchanger::~Exchanger()
{
}

///////////////////////////////////////////////////////////////////////////////
//
//      PC -> Arduiono Receive section
//

bool Exchanger::WaitForCommand(uint8_t* receiveBuffer256, int* pLengthReceived)
{
    bool ok = false;
    char c;
    uint16_t packetId;
    uint16_t length = m_serial.Receive(&c, receiveBuffer256, &m_id, &packetId);
    if ((c == '>') & (length > 0))
    {
        *pLengthReceived = length;
        ok = true;
    }
    return ok;
}

bool Exchanger::WaitForReady(uint16_t expectedPacketId, uint8_t* buffer)
{
    bool ok = false;
    char c;
    uint16_t packetId;
    int recv = m_serial.Receive(&c, buffer, &m_id, &packetId);
    if (c == '.' && packetId == expectedPacketId && recv == 1)
    {
        ok = true;
    }
    return ok;
}


bool Exchanger::WaitForPayload(uint16_t expectedPacketId, uint8_t* receiveBuffer256, uint8_t* pLengthReceived)
{
    bool ok = false;
    char c;
    uint16_t packetId;
    uint16_t length = m_serial.Receive(&c, receiveBuffer256, &m_id, &packetId);
    if (c == '$' && length > 0 && packetId == expectedPacketId)
    {
        *pLengthReceived = length;
        ok = true;
    }
    return ok;
}


///////////////////////////////////////////////////////////////////////////////
//
//      Arduoino -> PC section
//

bool Exchanger::SendResponse(const uint8_t* sendBuffer256, uint8_t lengthToSend)
{
    return m_serial.Send('<', sendBuffer256, lengthToSend, m_id, 0);
}

bool Exchanger::SendReady(uint16_t packetId)
{
    uint8_t buffer[1];
    return m_serial.Send('.', buffer, 1, m_id, packetId);
}

bool Exchanger::SendPayload(uint16_t packetId, const uint8_t* sendBuffer256, uint8_t lengthToSend)
{
    int length = lengthToSend;
    if (length == 0)
    {
        length = 256;
    }
    bool ok = m_serial.Send('$', sendBuffer256, length, m_id, packetId);
    return ok;
}

bool Exchanger::SendText(const char* text)
{
    int16_t length = strlen(text);
    bool ok = m_serial.Send('!', (uint8_t*)text, length, m_id, 0xdead);
    return ok;
}



Interpreter.h

#ifndef INCLUDE_INTERPRETER_H
#define INCLUDE_INTERPRETER_H
#include "MCUBus.h"
#include "Flash.h"
#include "SerialIF.h"
#include "Exchanger.h"

class Interpreter
{
public:
    Interpreter();
    ~Interpreter();

    // Run the interpreter
    void Loop();

    // Execute a command
    bool Execute(const uint8_t* command, int commmandLength, uint8_t* pResponse, int* pResponseLength);

    // Send a diag message up to 100 chars
    static void DiagnosticText(const char* format, ...);

protected:
    bool CRC(uint32_t addr, uint32_t length, uint32_t* pCRC);
    uint32_t IncrementalCRC(uint32_t crc, uint8_t data);

private:
    static Interpreter* m_instance;

    uint16_t m_configuration;
    uint16_t m_status;

    MCUBus m_bus;
    SerialIF m_serialIF;
    Flash m_fp;
    Exchanger m_exch;
    uint8_t m_id; // rotating transaction ID
};

#endif




Interpreter.cpp

#include "Interpreter.h"
#include "Exchanger.h"
#include "Flash.h"
#include "CRC32.h"
#include "CommandCodeEnum.h"
#include "Commands.h"
#include "Responses.h"

#define BLINK_ON_SUCCESS 0
#define BLINK_ON_FAILURE 0
#define VERBOSE 1

///////////////////////////////////////////////////////////////////////////////
//
//      Object section
//

Interpreter::Interpreter() : m_fp(m_bus)
{
    m_status = 0;
    m_configuration = 0;
    m_instance = this;
}

Interpreter::~Interpreter()
{
}

Interpreter* Interpreter::m_instance;

void Interpreter::Loop()
{
    uint8_t command[256];
    uint8_t response[256];
    while (1)
    {
        int receivedLength;
        int responseLength;
        if (m_exch.WaitForCommand(command, &receivedLength) > 0)
        {
            m_fp.Reset();

            bool ok = Execute(command, receivedLength, response, &responseLength);

            if (ok)
            {
                m_exch.SendResponse(response, responseLength);
                if (BLINK_ON_SUCCESS)
                {
                    // half second beep bo beep
                    digitalWrite(13, HIGH);
                    delay(200);
                    digitalWrite(13, LOW);
                    delay(100);
                    digitalWrite(13, HIGH);
                    delay(200);
                    digitalWrite(13, LOW);
                }
            }
            else
            {
                response[0] = '?';
                response[1] = 0xde;
                response[2] = 0xad;
                response[3] = 0xbe;
                response[4] = 0xef;
                m_exch.SendResponse(response, 5);
                if (BLINK_ON_FAILURE)
                {
                    // flutter LED
                    for (int16_t i = 0; i < 10; ++i)
                    {
                        digitalWrite(13, HIGH);
                        delay(50);
                        digitalWrite(13, LOW);
                        delay(50);
                    }
                }
            }
        }
    }
}

void Interpreter::DiagnosticText(const char* format, ...)
{
#define DIAGNOSTIC_TEXT_SIZE 100
    char text[DIAGNOSTIC_TEXT_SIZE];
    va_list ap;
    va_start(ap, format);
    vsnprintf(text, DIAGNOSTIC_TEXT_SIZE, format, ap);
    va_end(ap);
    m_instance->m_exch.SendText(text);
}

///////////////////////////////////////////////////////////////////////////////
//
//      Command Dispatch section
//

bool Interpreter::Execute(const uint8_t* command, int commmandLength, uint8_t* pResponse, int* pResponseLength)
{
    bool ok = false;

    switch (command[0])
    {
    case NOP:
    {
        CmdNOP cmd(command, commmandLength);
        ok = true;
        RespNOP resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case Status:
    {
        CmdStatus cmd(command, commmandLength);
        ok = true;

        RespStatus resp(cmd, pResponse);
        uint32_t status = m_configuration;
        status <<= 16;
        status |= m_status;
        resp.SetStatus(status);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case Configure:
    {
        CmdConfigure cmd(command, commmandLength);
        ok = true;
        m_configuration = (uint16_t)cmd.GetConfig();
        RespConfigure resp(cmd, pResponse);
        resp.SetResult(resp.Length());
        *pResponseLength = resp.Length();
    }
    break;
    case ForceReset:
    {
        CmdForceReset cmd(command, commmandLength);
        ok = m_fp.ForceReset();
        RespForceReset resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case EnablePins:
    {
        CmdEnablePins cmd(command, commmandLength);
        bool enable = cmd.GetEnable();
        ok = m_bus.EnablePins(enable);
        RespEnablePins resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case ReadByte:
    {
        CmdReadByte cmd(command, commmandLength);
        uint8_t data;
        ok = m_fp.Read(cmd.GetAddress(), &data);
        RespReadByte resp(cmd, pResponse);
        resp.SetData(data);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case WriteByte:
    {
        CmdWriteByte cmd(command, commmandLength);
        m_bus.Write(cmd.GetAddress(), cmd.GetData());
        ok = true;
        //DiagnosticText("Write byte %05lx <- data-blogger-escaped--="" data-blogger-escaped-addr="cmd.GetAddress();" data-blogger-escaped-break="" data-blogger-escaped-case="" data-blogger-escaped-cmd.getaddress="" data-blogger-escaped-cmd.getdata="" data-blogger-escaped-cmd="" data-blogger-escaped-cmdprogrambyte="" data-blogger-escaped-cmdreadblock="" data-blogger-escaped-command="" data-blogger-escaped-commmandlength="" data-blogger-escaped-for="" data-blogger-escaped-if="" data-blogger-escaped-int32_t="" data-blogger-escaped-length="" data-blogger-escaped-load="" data-blogger-escaped-m_exch.waitforready="" data-blogger-escaped-offset="" data-blogger-escaped-ok="ok" data-blogger-escaped-packet="" data-blogger-escaped-packetid="" data-blogger-escaped-partlen="" data-blogger-escaped-presponse="" data-blogger-escaped-presponselength="resp.Length();" data-blogger-escaped-programbyte:="" data-blogger-escaped-readblock:="" data-blogger-escaped-ready="" data-blogger-escaped-resp.setresult="" data-blogger-escaped-resp="" data-blogger-escaped-respprogrambyte="" data-blogger-escaped-respwritebyte="" data-blogger-escaped-send="" data-blogger-escaped-to="" data-blogger-escaped-uint16_t="" data-blogger-escaped-uint32_t="" data-blogger-escaped-wait="" data-blogger-escaped-while="" data-blogger-escaped-x=""> 256)
                {
                    partLen = 256;
                }
                for (int i = 0; i < partLen; ++i)
                {
                    uint8_t b;
                    ok = ok && m_fp.Read(addr + offset + i, &b);
                    b = (byte)(addr + offset + i);
                    pResponse[i] = b;
                }

                // send it out
                ok = ok && m_exch.SendPayload(packetId, pResponse, partLen);

                // prepare for next packet
                offset += partLen;
                packetId++;
            }
        }
        RespReadBlock resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case ProgramBlock:
    {
        CmdReadBlock cmd(command, commmandLength);
        uint32_t addr = cmd.GetAddress();
        uint32_t length = cmd.GetLength();
        uint32_t offset = 0;
        uint16_t packetId = 0;
        uint8_t buffer[256];
        ok = true;
        while (offset < length)
        {
            if (ok)
            {
                uint8_t partLen;
                ok = ok && m_exch.SendReady(packetId);
                ok = ok && m_exch.WaitForPayload(packetId, buffer, &partLen);
                int len = partLen;
                if (len == 0)
                {
                    len = 256;
                }

                // program packet
                for (int i = 0; i < len; ++i)
                {
                    uint8_t b = buffer[i];
                    ok = ok && m_fp.Program(addr + offset + i, b);
                }

                // prepare for next packet
                offset += len;
                packetId++;
            }
        }
        RespReadBlock resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case EraseSector:
    {
        CmdEraseSector cmd(command, commmandLength);
        int sector = cmd.GetSector();
        int count = cmd.GetCount();
        ok = sector >= 0 && sector < 64 && count >= 1 && (sector + count) < 64;
        for (int i = sector; i < sector + count; ++i)
        {
            if (ok)
            {
                ok = m_fp.EraseSector(sector << 12);
            }
        }
        RespEraseSector resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case EraseChip:
    {
        CmdEraseChip cmd(command, commmandLength);
        ok = m_fp.EraseChip();
        RespEraseChip resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case VerifyErase:
    {
        CmdVerifyErase cmd(command, commmandLength);
        uint8_t data;
        uint32_t errorCount = 0;
        uint32_t addr = cmd.GetAddress();
        uint32_t length = cmd.GetLength();
        ok = true;
        for (uint32_t i = 0; i < length; i++)
        {
            ok = ok && m_fp.Read(addr + i, &data);
            if (data != 0xff)
            {
                //DiagnosticText("Error %d at %x", data, addr + i);
                errorCount++;
            }
        }
        RespVerifyErase resp(cmd, pResponse);
        resp.SetErrorCount(errorCount);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case BlockCRC:
    {
        CmdBlockCRC cmd(command, commmandLength);
        uint8_t data;
        uint32_t addr = cmd.GetAddress();
        uint32_t length = cmd.GetLength();
        uint32_t crc = 0xffffffffUL;
        ok = true;
        for (uint32_t i = 0; i < length; i++)
        {
            ok = ok && m_fp.Read(addr + length, &data);
            data = 0xff; // TODO remove after test
            crc = CRC32::IncrementalCRC(crc, data);
        }
        RespBlockCRC resp(cmd, pResponse);
        resp.SetCRC(crc ^ 0xffffffffUL);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case DirectPinRead:
    {
        CmdDirectPinRead cmd(command, commmandLength);
        uint16_t levels;
        ok = m_bus.DirectRead(cmd.GetMask(), &levels);
        RespDirectPinRead resp(cmd, pResponse);
        resp.SetPins(levels);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case DirectPinWrite:
    {
        CmdDirectPinWrite cmd(command, commmandLength);
        ok = m_bus.DirectWrite(cmd.GetMask(), cmd.GetLevels());
        RespDirectPinWrite resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case DirectPinMode:
    {
        CmdDirectPinMode cmd(command, commmandLength);
        uint8_t pin = cmd.GetPin();
        uint8_t mode = cmd.GetMode();
        ok = pin < 14 && (mode == INPUT || mode == INPUT_PULLUP || mode == OUTPUT);
        if (ok)
        {
            pinMode(pin, mode);
        }
        RespDirectPinMode resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;

    case ReadMem:
    {
        CmdReadMem cmd(command, commmandLength);
        uint8_t data;
        ok = m_bus.ReadMem(cmd.GetAddress(), &data);
        RespReadMem resp(cmd, pResponse);
        resp.SetData(data);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case FetchMem:
    {
        CmdFetchMem cmd(command, commmandLength);
        uint8_t data;
        ok = m_bus.FetchMem(cmd.GetAddress(), &data);
        RespFetchMem resp(cmd, pResponse);
        resp.SetData(data);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case WriteMem:
    {
        CmdWriteMem cmd(command, commmandLength);
        ok = m_bus.WriteMem(cmd.GetAddress(), cmd.GetData());
        RespWriteMem resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case ReadPort:
    {
        CmdReadPort cmd(command, commmandLength);
        uint8_t data;
        ok = m_bus.ReadIO(cmd.GetPort(), &data);
        RespReadPort resp(cmd, pResponse);
        resp.SetData(data);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    case WritePort:
    {
        CmdWritePort cmd(command, commmandLength);
        ok = m_bus.WriteIO(cmd.GetPort(), cmd.GetData());
        RespWritePort resp(cmd, pResponse);
        resp.SetResult(ok);
        *pResponseLength = resp.Length();
    }
    break;
    }
    return ok;
}



ArgInfo.h

#ifndef INCLUDED_ARGINFO_H
#define INCLUDED_ARGINFO_H
#include "CommandCodeEnum.h"

struct ArgTypes
{
    CommandCodes code;
    const char* name;
    int location;
    int length;
};

// The ArgInfo class contains the information needed for commands and responses to location
// and extract the information in the communication packets
class ArgInfo
{
public:
    ArgInfo();
    ~ArgInfo();

    // Gets data from a command
    bool CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint32_t* pData);
    bool CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint16_t* pData);
    bool CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint8_t* pData);
    bool CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, int* pData);
    bool CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, bool* pData);

    // Inserts data into a response
    bool ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint32_t data);
    bool ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint16_t data);
    bool ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint8_t data);
    bool ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, int data);
    bool ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, bool data);

    // Gets the total length of the command
    int CommandLength(CommandCodes code);

    // Gets the total length of the response which is the length of the command, the status, and arguments
    int ResponseLength(CommandCodes code);

protected:
    // Gets the location and length of argument #'argNo' for command of 'commandCode'
    bool GetCommandInfo(CommandCodes code, int argNo, int* pLocation, int* pLength);

    // Gets the location and length of argument #'argNo' for response of 'commandCode'
    bool GetResponseInfo(CommandCodes code, int argNo, int* pLocation, int* pLength);

    // Gets the actual data at 'location'
    bool ExtractFrom(const uint8_t* buffer, int location, int length, uint32_t* pData);

    // Inserts the actual data at 'location'
    bool InsertInto(uint8_t* buffer, int location, int length, uint32_t data);

    // Number of ArgTypes in COMMAND_ARGS
    static int COMMAND_ARGC;

    // Number of ArgTypes in RESPONSE_ARGS
    static int RESPONSE_ARGC;

    // Array of info about command arguments
    static ArgTypes COMMAND_ARGS[];

    // Array of info abaout response arguments
    static ArgTypes RESPONSE_ARGS[];
};

#endif


ArgInfo.cpp

#include "ArgInfo.h"


#include "Interpreter.h" // TODO for diag

// locations start at 0. THey will be set in the c'tor
ArgTypes ArgInfo::COMMAND_ARGS[] =
{
    { Configure, "Configure", 0, 2 },

    { ReadByte, "Address", 0, 3 },

    { WriteByte, "Address", 0, 3 },
    { WriteByte, "Data", 0, 1 },

    { ProgramByte, "Address", 0, 3},
    { ProgramByte, "Data", 0, 1},

    { ReadBlock, "Address", 0, 3},
    { ReadBlock, "Length", 0, 3},

    { ProgramBlock, "Address", 0, 3},
    { ProgramBlock, "Length", 0, 3},

    { EraseSector, "Sector", 0, 1},
    { EraseSector, "Count", 0, 1},

    { VerifyErase, "Address", 0, 3},
    { VerifyErase, "Length", 0, 3},

    { BlockCRC, "Address", 0, 3},
    { BlockCRC, "Length", 0, 3},

    { EnablePins, "Pin", 0, 1},
    { EnablePins, "Mode", 0, 1},

    { DirectPinWrite, "Mask", 0, 2},
    { DirectPinWrite, "Levels", 0, 2},

    { DirectPinRead, "Mask", 0, 2},

    { ReadMem, "Address", 0, 2},

    { FetchMem, "Address", 0, 2},

    { WriteMem, "Address", 0, 2},
    { WriteMem, "Data", 0, 1},

    { ReadPort, "Port", 0, 1},

    { WritePort, "Port", 0, 1},
    { WritePort, "Data", 0, 1},
};

// locations all default to 1. These may not be set in the c'tor
ArgTypes ArgInfo::RESPONSE_ARGS[] =
{
    { ReadByte, "Data", 1, 1 },
    { ReadMem, "Data", 1, 1 },
    { FetchMem, "Data", 1, 1 },
    { ReadPort, "Data", 1, 1 },

    { Status, "Status", 1, 4 },

    { VerifyErase, "ErrorCount", 1, 4 },

    { BlockCRC, "CRC", 1, 4 },

    { DirectPinRead, "Levels", 1, 2 },
};

// Gets sizes at compile time
int ArgInfo::COMMAND_ARGC = sizeof(COMMAND_ARGS) / sizeof(COMMAND_ARGS[0]);
int ArgInfo::RESPONSE_ARGC = sizeof(RESPONSE_ARGS) / sizeof(RESPONSE_ARGS[0]);

ArgInfo::ArgInfo()
{
    // Figure out locations from sizes
    CommandCodes lastCommandCode = (CommandCodes)-1;
    int cumulativeLocation = 1;
    for (int i = 0; i < COMMAND_ARGC; ++i)
    {
        if (COMMAND_ARGS[i].code != lastCommandCode)
        {
            // add into response
            for (int j = 0; j < RESPONSE_ARGC; ++j)
            {
                if (RESPONSE_ARGS[j].code == lastCommandCode)
                {
                    RESPONSE_ARGS[j].location = cumulativeLocation;
                    cumulativeLocation += RESPONSE_ARGS[j].length;
                }
            }

            lastCommandCode = COMMAND_ARGS[i].code;
            cumulativeLocation = 1;
        }
        COMMAND_ARGS[i].location = cumulativeLocation;
        cumulativeLocation += COMMAND_ARGS[i].length;
    }
}

ArgInfo::~ArgInfo()
{
}

int ArgInfo::CommandLength(CommandCodes code)
{
    int cumulativeLength = 1;
    for (int i = 0; i < COMMAND_ARGC; ++i)
    {
        CommandCodes thisCommandCode = COMMAND_ARGS[i].code;

        if (thisCommandCode < code)
        {
        }
        else if (thisCommandCode == code)
        {
            cumulativeLength += COMMAND_ARGS[i].length;
        }
        else
        {
            //break; // not in order--cannot break
        }
    }
    return cumulativeLength;
}

int ArgInfo::ResponseLength(CommandCodes code)
{
    int cumulativeLength = CommandLength(code) + 1; // +1 for result
    for (int i = 0; i < RESPONSE_ARGC; ++i)
    {
        CommandCodes thisCommandCode = RESPONSE_ARGS[i].code;

        if (thisCommandCode < code)
        {
        }
        else if (thisCommandCode == code)
        {
            cumulativeLength += RESPONSE_ARGS[i].length;
        }
        else
        {
            //break; // not in order--cannot break
        }
    }
    return cumulativeLength;
}


bool ArgInfo::CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint32_t* pData)
{
    int location;
    int length;
    uint32_t data32 = 0;

    bool ok = GetCommandInfo(code, argNo, &location, &length);

    if (ok)
    {
        ok = ExtractFrom(buffer, location, length, &data32);
    }
    if (ok)
    {
        *pData = (uint32_t)data32;
    }

    return ok;
}

bool ArgInfo::CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint16_t* pData)
{
    int location;
    int length;
    uint32_t data32 = 0;

    bool ok = GetCommandInfo(code, argNo, &location, &length);

    if (ok)
    {
        ok = ExtractFrom(buffer, location, length, &data32);
    }
    if (ok)
    {
        *pData = (uint16_t)data32;
    }

    return ok;
}

bool ArgInfo::CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, uint8_t* pData)
{
    int location;
    int length;
    uint32_t data32 = 0;

    bool ok = GetCommandInfo(code, argNo, &location, &length);

    if (ok)
    {
        ok = ExtractFrom(buffer, location, length, &data32);
    }
    if (ok)
    {
        *pData = (uint8_t)data32;
    }

    return ok;
}

bool ArgInfo::CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, int* pData)
{
    int location;
    int length;
    uint32_t data32 = 0;

    bool ok = GetCommandInfo(code, argNo, &location, &length);

    if (ok)
    {
        ok = ExtractFrom(buffer, location, length, &data32);
    }
    if (ok)
    {
        *pData = (int)data32;
    }

    return ok;
}

bool ArgInfo::CommandExtract(CommandCodes code, int argNo, const uint8_t* buffer, bool* pData)
{
    int location;
    int length;
    uint32_t data32 = 0;

    bool ok = GetCommandInfo(code, argNo, &location, &length);

    if (ok)
    {
        ok = ExtractFrom(buffer, location, length, &data32);
    }
    if (ok)
    {
        *pData = data32 != 0;
    }

    return ok;
}


bool ArgInfo::ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint32_t data)
{
    int location;
    int length;
    uint32_t data32 = data;

    bool ok = GetResponseInfo(code, argNo, &location, &length);
    if (ok)
    {
        ok = InsertInto(buffer, location, length, data32);
    }
    return ok;
}

bool ArgInfo::ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint16_t data)
{
    int location;
    int length;
    uint32_t data32 = data;

    bool ok = GetResponseInfo(code, argNo, &location, &length);
    if (ok)
    {
        ok = InsertInto(buffer, location, length, data32);
    }
    return ok;
}

bool ArgInfo::ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, uint8_t data)
{
    int location;
    int length;
    uint32_t data32 = data;

    bool ok = GetResponseInfo(code, argNo, &location, &length);
    if (ok)
    {
        ok = InsertInto(buffer, location, length, data32);
    }
    return ok;
}

bool ArgInfo::ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, int data)
{
    int location;
    int length;
    uint32_t data32 = data;

    bool ok = GetResponseInfo(code, argNo, &location, &length);
    if (ok)
    {
        ok = InsertInto(buffer, location, length, data32);
    }
    return ok;
}

bool ArgInfo::ResponseInsert(CommandCodes code, int argNo, uint8_t* buffer, bool data)
{
    int location;
    int length;
    uint32_t data32 = data;

    bool ok = GetResponseInfo(code, argNo, &location, &length);
    if (ok)
    {
        ok = InsertInto(buffer, location, length, data32 ? 1 : 0);
    }
    return ok;
}

bool ArgInfo::GetCommandInfo(CommandCodes code, int argNo, int* pLocation, int* pLength)
{
    int i = 0;
    while (i < COMMAND_ARGC && COMMAND_ARGS[i].code != code)
    {
        ++i;
    }
    if (i == COMMAND_ARGC)
    {
        return false;
    }
    int argC = 0;
    while (i < COMMAND_ARGC && COMMAND_ARGS[i].code == code && argC < argNo)
    {
        ++i;
        ++argC;
    }
    if (i == COMMAND_ARGC || COMMAND_ARGS[i].code != code)
    {
        return false;
    }
    *pLocation = COMMAND_ARGS[i].location;
    *pLength = COMMAND_ARGS[i].length;
    return true;
}

bool ArgInfo::GetResponseInfo(CommandCodes code, int argNo, int* pLocation, int* pLength)
{
    int i = 0;
    while (i < RESPONSE_ARGC && RESPONSE_ARGS[i].code != code)
    {
        ++i;
    }
    if (i == RESPONSE_ARGC)
    {
        return false;
    }
    int argC = 0;
    while (i < RESPONSE_ARGC && RESPONSE_ARGS[i].code == code && argC < argNo)
    {
        ++i;
        ++argC;
    }
    if (i == RESPONSE_ARGC || RESPONSE_ARGS[i].code != code)
    {
        return false;
    }
    *pLocation = RESPONSE_ARGS[i].location;
    *pLength = RESPONSE_ARGS[i].length;
    return true;
}

bool ArgInfo::ExtractFrom(const uint8_t* buffer, int location, int length, uint32_t* pData)
{
    bool ok = length <= 4 && location >= 0 && location < 256;

    if (ok)
    {
        uint32_t data = 0;
        for (int i = location; i < location + length; ++i)
        {
            data <<= 8;
            data |= buffer[i];
        }
        *pData = data;
    }
    return ok;
}

bool ArgInfo::InsertInto(uint8_t* buffer, int location, int length, uint32_t data)
{
    bool ok = length <= 4 && location >= 0 && (location+length) < 256;

    if (ok)
    {
        for (int i = location + length - 1; i >= location; --i)
        {
            buffer[i] = (uint8_t)data;
            data >>= 8;
        }
    }
    return ok;
}


Commands.h

#ifndef INCLUDED_COMMANDS_H
#define INCLUDED_COMMANDS_H

#include "CmdResp.h"
#include "ArgInfo.h"

// Abstract base class for all commands
class Command : public CmdResp
{
public:
    // Gets the CommandCode for the command
    CommandCodes Code();

    // Gets the length of the buffer
    int Length();

    // Gets the buffer
    const byte* Buffer();

protected:
    Command(const byte* buffer, int length);

    const byte* m_buffer;
    int m_length;
    //static ArgInfo argInfo;
};

// The rest of these are derived command classes

class CmdNOP : public Command
{
public:
    CmdNOP(const byte* buffer, int length);

protected:
    static const CommandCodes MY_CODE = NOP;
};

class CmdStatus : public Command
{
public:
    CmdStatus(const byte* buffer, int length);

protected:
    static const CommandCodes MY_CODE = Status;
};

class CmdConfigure : public Command
{
public:
    CmdConfigure(const byte* buffer, int length);

    uint16_t GetConfig();

protected:
    static const CommandCodes MY_CODE = Configure;
};

class CmdForceReset : public Command
{
public:
    CmdForceReset(const byte* buffer, int length);

protected:
    static const CommandCodes MY_CODE = ForceReset;
};

class CmdEnablePins : public Command
{
public:
    CmdEnablePins(const byte* buffer, int length);

    bool GetEnable();

protected:
    static const CommandCodes MY_CODE = EnablePins;
};


class CmdReadByte : public Command
{
public:
    CmdReadByte(const byte* buffer, int length);

    uint32_t GetAddress();

protected:
    static const CommandCodes MY_CODE = ReadByte;
};

class CmdWriteByte : public Command
{
public:
    CmdWriteByte(const byte* buffer, int length);

    uint32_t GetAddress();
    uint8_t GetData();

protected:
    static const CommandCodes MY_CODE = WriteByte;
};

class CmdProgramByte : public Command
{
public:
    CmdProgramByte(const byte* buffer, int length);

    uint32_t GetAddress();
    uint8_t GetData();

protected:
    static const CommandCodes MY_CODE = ProgramByte;
};


class CmdReadBlock : public Command
{
public:
    CmdReadBlock(const byte* buffer, int length);

    uint32_t GetAddress();
    uint32_t GetLength();

protected:
    static const CommandCodes MY_CODE = ReadBlock;
};

class CmdProgramBlock : public Command
{
public:
    CmdProgramBlock(const byte* buffer, int length);

    uint32_t GetAddress();
    uint32_t GetLength();

protected:
    static const CommandCodes MY_CODE = ProgramBlock;
};


class CmdEraseSector : public Command
{
public:
    CmdEraseSector(const byte* buffer, int length);

    uint8_t GetSector();
    uint8_t GetCount();

protected:
    static const CommandCodes MY_CODE = EraseSector;
};

class CmdEraseChip : public Command
{
public:
    CmdEraseChip(const byte* buffer, int length);

protected:
    static const CommandCodes MY_CODE = EraseChip;
};

class CmdVerifyErase : public Command
{
public:
    CmdVerifyErase(const byte* buffer, int length);

    uint32_t GetAddress();
    uint32_t GetLength();

protected:
    static const CommandCodes MY_CODE = VerifyErase;
};

class CmdBlockCRC : public Command
{
public:
    CmdBlockCRC(const byte* buffer, int length);

    uint32_t GetAddress();
    uint32_t GetLength();

protected:
    static const CommandCodes MY_CODE = BlockCRC;
};


class CmdDirectPinRead : public Command
{
public:
    CmdDirectPinRead(const byte* buffer, int length);

    uint16_t GetMask();

protected:
    static const CommandCodes MY_CODE = DirectPinRead;
};

class CmdDirectPinWrite : public Command
{
public:
    CmdDirectPinWrite(const byte* buffer, int length);

    uint16_t GetMask();
    uint16_t GetLevels();

protected:
    static const CommandCodes MY_CODE = DirectPinWrite;
};

class CmdDirectPinMode : public Command
{
public:
    CmdDirectPinMode(const byte* buffer, int length);

    uint8_t GetPin();
    uint8_t GetMode();

protected:
    static const CommandCodes MY_CODE = DirectPinMode;
};


class CmdReadMem : public Command
{
public:
    CmdReadMem(const byte* buffer, int length);

    uint16_t GetAddress();

protected:
    static const CommandCodes MY_CODE = ReadMem;
};

class CmdFetchMem : public Command
{
public:
    CmdFetchMem(const byte* buffer, int length);

    uint16_t GetAddress();

protected:
    static const CommandCodes MY_CODE = FetchMem;
};

class CmdWriteMem : public Command
{
public:
    CmdWriteMem(const byte* buffer, int length);

    uint16_t GetAddress();
    uint8_t GetData();

protected:
    static const CommandCodes MY_CODE = WriteMem;
};

class CmdReadPort : public Command
{
public:
    CmdReadPort(const byte* buffer, int length);

    uint8_t GetPort();

protected:
    static const CommandCodes MY_CODE = ReadPort;

};

class CmdWritePort : public Command
{
public:
    CmdWritePort(const byte* buffer, int length);

    uint8_t GetPort();
    uint8_t GetData();

protected:
    static const CommandCodes MY_CODE = WritePort;
};

#endif


Commands.cpp

#include "Commands.h"

Command::Command(const byte* buffer, int length) : m_buffer(buffer), m_length(length)
{
}

CommandCodes Command::Code()
{
    return (CommandCodes)m_buffer[0];
}

int Command::Length()
{
    return m_length;
}

const byte* Command::Buffer()
{
    return m_buffer;
}

ArgInfo CmdResp::argInfo;

CmdNOP::CmdNOP(const byte* buffer, int length) : Command(buffer, length) { }

CmdStatus::CmdStatus(const byte* buffer, int length) : Command(buffer, length) { }

CmdConfigure::CmdConfigure(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdConfigure::GetConfig()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdForceReset::CmdForceReset(const byte* buffer, int length) : Command(buffer, length) { }

CmdEnablePins::CmdEnablePins(const byte* buffer, int length) : Command(buffer, length) { }

bool CmdEnablePins::GetEnable()
{
    bool data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdReadByte::CmdReadByte(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdReadByte::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdWriteByte::CmdWriteByte(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdWriteByte::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint8_t CmdWriteByte::GetData()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}


CmdProgramByte::CmdProgramByte(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdProgramByte::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint8_t CmdProgramByte::GetData()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}


CmdReadBlock::CmdReadBlock(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdReadBlock::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint32_t CmdReadBlock::GetLength()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}


CmdProgramBlock::CmdProgramBlock(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdProgramBlock::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint32_t CmdProgramBlock::GetLength()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdEraseSector::CmdEraseSector(const byte* buffer, int length) : Command(buffer, length) { }

uint8_t CmdEraseSector::GetSector()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint8_t CmdEraseSector::GetCount()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdEraseChip::CmdEraseChip(const byte* buffer, int length) : Command(buffer, length) { }

CmdVerifyErase::CmdVerifyErase(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdVerifyErase::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint32_t CmdVerifyErase::GetLength()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdBlockCRC::CmdBlockCRC(const byte* buffer, int length) : Command(buffer, length) { }

uint32_t CmdBlockCRC::GetAddress()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint32_t CmdBlockCRC::GetLength()
{
    uint32_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdDirectPinRead::CmdDirectPinRead(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdDirectPinRead::GetMask()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdDirectPinWrite::CmdDirectPinWrite(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdDirectPinWrite::GetMask()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

uint16_t CmdDirectPinWrite::GetLevels()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdDirectPinMode::CmdDirectPinMode(const byte* buffer, int length) : Command(buffer, length) { }

uint8_t CmdDirectPinMode::GetPin()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}
uint8_t CmdDirectPinMode::GetMode()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdReadMem::CmdReadMem(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdReadMem::GetAddress()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdFetchMem::CmdFetchMem(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdFetchMem::GetAddress()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdWriteMem::CmdWriteMem(const byte* buffer, int length) : Command(buffer, length) { }

uint16_t CmdWriteMem::GetAddress()
{
    uint16_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}
uint8_t CmdWriteMem::GetData()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}

CmdReadPort::CmdReadPort(const byte* buffer, int length) : Command(buffer, length) { }

uint8_t CmdReadPort::GetPort()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}

CmdWritePort::CmdWritePort(const byte* buffer, int length) : Command(buffer, length) { }

uint8_t CmdWritePort::GetPort()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 0, m_buffer, &data);
    return data;
}
uint8_t CmdWritePort::GetData()
{
    uint8_t data;
    argInfo.CommandExtract(MY_CODE, 1, m_buffer, &data);
    return data;
}


Responses.h

#ifndef INCLUDED_RESPONSES_H
#define INCLUDED_RESPONSES_H

#include "Commands.h"
#include "CommandCodeEnum.h"
#include "CmdResp.h"

// Abstract base class for all responses
class Response : public CmdResp
{
protected:
    Response(Command& command, uint8_t* buffer256);

public:
    void SetResult(int result);
    int Length();
    CommandCodes Code();

protected:
    Command& m_command;
    uint8_t* m_buffer256;
    //static ArgInfo argInfo;
};

// The rest of these are derived response classes

class RespNOP : public Response
{
public:
    RespNOP(CmdNOP& cmd, uint8_t* buffer256); 
};

class RespStatus : public Response 
{
public:
    RespStatus(CmdStatus& cmd, uint8_t* buffer256);

    bool SetStatus(uint32_t data);
};

class RespConfigure : public Response 
{ 
public:
    RespConfigure(CmdConfigure& cmd, uint8_t* buffer256);
};

class RespForceReset : public Response 
{
public:
    RespForceReset(CmdForceReset& cmd, uint8_t* buffer256);
};

class RespEnablePins : public Response 
{ 
public:
    RespEnablePins(CmdEnablePins& cmd, uint8_t* buffer256);
};

class RespReadByte : public Response 
{ 
public:
    RespReadByte(CmdReadByte& cmd, uint8_t* buffer256);

    bool SetData(uint8_t data);
};
class RespWriteByte : public Response 
{ 
public:
    RespWriteByte(CmdWriteByte& cmd, uint8_t* buffer256);
};

class RespProgramByte : public Response 
{ 
public:
    RespProgramByte(CmdProgramByte& cmd, uint8_t* buffer256);
};

class RespReadBlock : public Response 
{ 
public:
    RespReadBlock(CmdReadBlock& cmd, uint8_t* buffer256);
};

class RespProgramBlock : public Response 
{ 
public:
    RespProgramBlock(CmdProgramBlock& cmd, uint8_t* buffer256);
};

class RespEraseSector : public Response 
{ 
public:
    RespEraseSector(CmdEraseSector& cmd, uint8_t* buffer256);
};
class RespEraseChip : public Response 
{ 
public:
    RespEraseChip(CmdEraseChip& cmd, uint8_t* buffer256);
};

class RespVerifyErase : public Response 
{ 
public:
    RespVerifyErase(CmdVerifyErase& cmd, uint8_t* buffer256);

    bool SetErrorCount(uint32_t data);
};

class RespBlockCRC : public Response 
{ 
public:
    RespBlockCRC(CmdBlockCRC& cmd, uint8_t* buffer256);

    bool SetCRC(uint32_t crc);
};

class RespDirectPinRead : public Response 
{ 
public:
    RespDirectPinRead(CmdDirectPinRead& cmd, uint8_t* buffer256);
    bool SetPins(uint16_t pins);
};

class RespDirectPinWrite : public Response 
{ 
public:
    RespDirectPinWrite(CmdDirectPinWrite& cmd, uint8_t* buffer256);
};

class RespDirectPinMode : public Response 
{ 
public:
    RespDirectPinMode(CmdDirectPinMode& cmd, uint8_t* buffer256);
};

class RespReadMem : public Response 
{ 
public:
    RespReadMem(CmdReadMem& cmd, uint8_t* buffer256);

    bool SetData(uint8_t data);
};

class RespFetchMem : public Response 
{ 
public:
    RespFetchMem(CmdFetchMem& cmd, uint8_t* buffer256);

    bool SetData(uint8_t data);
};

class RespWriteMem : public Response 
{ 
public:
    RespWriteMem(CmdWriteMem& cmd, uint8_t* buffer256);
};

class RespReadPort : public Response 
{ 
public:
    RespReadPort(CmdReadPort& cmd, uint8_t* buffer256);

    bool SetData(uint8_t data);
};

class RespWritePort : public Response 
{ 
public:
    RespWritePort(CmdWritePort& cmd, uint8_t* buffer256);
};


#endif

Responses.cpp

#include "Responses.h"

Response::Response(Command& command, uint8_t* buffer256) : m_command(command), m_buffer256(buffer256)
{
    memcpy(m_buffer256, m_command.Buffer(), m_command.Length());
}

void Response::SetResult(int result)
{
    int length = Length();
    m_buffer256[length - 1] = result;
}

int Response::Length()
{
    return argInfo.ResponseLength(m_command.Code());
}

CommandCodes Response::Code()
{
    return m_command.Code();
}

RespNOP::RespNOP(CmdNOP& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespStatus::RespStatus(CmdStatus& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespConfigure::RespConfigure(CmdConfigure& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespForceReset::RespForceReset(CmdForceReset& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespEnablePins::RespEnablePins(CmdEnablePins& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespReadByte::RespReadByte(CmdReadByte& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespWriteByte::RespWriteByte(CmdWriteByte& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespProgramByte::RespProgramByte(CmdProgramByte& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespReadBlock::RespReadBlock(CmdReadBlock& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespProgramBlock::RespProgramBlock(CmdProgramBlock& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespEraseSector::RespEraseSector(CmdEraseSector& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespEraseChip::RespEraseChip(CmdEraseChip& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespVerifyErase::RespVerifyErase(CmdVerifyErase& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespBlockCRC::RespBlockCRC(CmdBlockCRC& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespDirectPinRead::RespDirectPinRead(CmdDirectPinRead& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespDirectPinWrite::RespDirectPinWrite(CmdDirectPinWrite& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespDirectPinMode::RespDirectPinMode(CmdDirectPinMode& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespReadMem::RespReadMem(CmdReadMem& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespFetchMem::RespFetchMem(CmdFetchMem& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespWriteMem::RespWriteMem(CmdWriteMem& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespReadPort::RespReadPort(CmdReadPort& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}
RespWritePort::RespWritePort(CmdWritePort& cmd, uint8_t* buffer256) : Response(cmd, buffer256) {}

bool RespStatus::SetStatus(uint32_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespReadByte::SetData(uint8_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespReadMem::SetData(uint8_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespFetchMem::SetData(uint8_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespReadPort::SetData(uint8_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespVerifyErase::SetErrorCount(uint32_t data)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, data);
}

bool RespBlockCRC::SetCRC(uint32_t crc)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, crc);
}

bool RespDirectPinRead::SetPins(uint16_t pins)
{
    return argInfo.ResponseInsert(Code(), 0, m_buffer256, pins);
}



CmdResp.h

#ifndef INCLUDED_CMDRESP_H
#define INCLUDED_CMDRESP_H

#include "ArgInfo.h"
class CmdResp
{
 protected:
    static ArgInfo argInfo;
};

#endif


CommandCodeEnum.h

#ifndef INCLUDED_COMMANDCODEENUM_H
#define INCLUDED_COMMANDCODEENUM_H

enum CommandCodes
{
    NOP = 0x00,
    Status = 0x01,
    Configure = 0x02,
    ForceReset = 0x08,
    EnablePins = 0x09,

    ReadByte = 0x10,
    WriteByte = 0x11,
    ProgramByte = 0x12,

    ReadBlock = 0x20,
    ProgramBlock = 0x22,

    EraseSector = 0x30,
    EraseChip = 0x33,

    VerifyErase = 0x40,
    BlockCRC = 0x41,

    DirectPinRead = 0x80,
    DirectPinWrite = 0x81,
    DirectPinMode = 0x82,

    ReadMem = 0x90,
    FetchMem = 0x91,
    WriteMem = 0x92,
    ReadPort = 0x98,
    WritePort = 0x99
};

#endif

No comments:

Post a Comment