Showing posts with label MCU. Show all posts
Showing posts with label MCU. Show all posts

Sunday, November 8, 2015

Board Works Without Arduino

I have been dealing with my board with its attached Arduino. And while I need the Arduino to unbrick the board, I don't need it for normal operation.

It took me a while to figure out what was going on. The behavior depended on whether the start up function did a run from RAM. That is, copy the current bank 0 into a RAM bank and then swap that RAM bank into bank 0. This is needed to program flash since programming flash requires no access from flash while it's programming. It is also necessary for setting breakpoints as a breakpoint is set by putting an 'rst 20h' instruction where the breakpoint belongs.

So when I was trying to run from RAM, it just bricked the computer. I finally recalled a choice I made to keep the CPLD from getting too complex. RESET changes the current banks, but I decided to rely on SW to set the next bank registers. But the runFromRAM function was just assuming that banks 2 and 3 were the same as the registers that specify the next banks 2 and 3. This is an invalid assumption. So, I added a memBankInit function that copies the current bank registers into the next bank registers.

Thursday, October 1, 2015

Progress

It took me a little while to figure out why my bank wasn't running. __asm__("call 4030") does not output machine code that jumps to 0x4030--it jumps to 4030 or 0x0fbe. That's wrong. So I added in the "0x" and all was well.

So now I am starting to wrap up a refactoring exercise.

I added a block that will go in all 16 sections. Each will have a pointer at 0x0026 that will point to this block.

#define BANKINFO_TYPE_UNKNOWN ((uint8_t)0x00)
#define BANKINFO_TYPE_CVECTOR ((uint8_t)0x01)
#define BANKINFO_TYPE_APPLET ((uint8_t)0x02)
#define BANKINFO_TYPE_EXECUTABLE ((uint8_t)0x03)

// Every bank has a pointer at 0026 that points to a const BankInfo* _BANK_INFO; both crt0.s and crt1.s
// expect a _BANK_INFO to be defined. This will typically be where main is defined.

typedef struct BankInfo {
    uint16_t CVersion; // 0{ix} 1(ix)
    const char* Name; // 2{ix} 3(ix)
    const char* TimeStamp; // 4{ix} 5(ix)
    uint8_t Flags; // 6{ix}
    uint8_t Type; // 7(ix) 1=c vector, 2=applet, 3=bank
    uint16_t W0102; // 8{ix} 9(ix) // valid only is this is 0102
    uint16_t W0304; // 10{ix} 11(ix)
} BankInfo;

This will be used for listing the contents of the flash. There will be 3 types of flash banks:

  • C Vector: These have the C library and drivers that the applets can use. But they don't really need anything else.
  • Applet: These will run the programs that I want to run
  • Executable: These will run standalone programs or menu items.


The applets require that they are linked with crt1.rel. The others with crt0.s. The C vectors need to link in c_vector.c.

At this point, I have to start reserving certain banks:
00: flash bank 0 is always run from reset. It will have a standalone executable with complex menu, but no c vector
01: flash bank 1 is always in bank 1 from reset. It will always be available for bank 00 to be able to hold programs larger than 16K.
10: RAM bank 10: This will be placed in bank 2 (i.e. 0x8000-0xbfff). It is there by default
11: RAM bank 11: This will be placed in bank 2 (i.e. 0xc000-0xffff). It is there by default
12: RAM bank 12: RAM bank 12 will be the standard bank for run from RAM in bank 0 (i.e. 0x0000-0x3fff)
13: RAM bank 13: RAM bank 13 will be the standard bank for run from RAM in bank 1 (i.e. 0x4000-0x7fff)
14: RAM bank 14: will be used for the standard bank that holds the C vector (0x0000-0x3fff)

I had to deal with the fact that the BankInfo block may be placed in blocks other than the block it expects, so the pointers to strings also have to point to the proper location.

All this bank switching has my brain running in circles, but it's finally getting into shape.

Monday, September 14, 2015

Flash Programming In System

After last night's discovery that I need to rethink how I program flash, the solution turned out to be pretty painless.

I modified the flash.c to handle the flash in system. Here is flash.c with a new function, activateFlash(uint8_t cmd, bool doCycle4_5):


#include <assert.h>
#include "common.h"
#include "flash.h"
#include "memBank.h"

// reads the flash and waits for the toggle bit to stop toggling.
// returns true if the read byte matched expected. Use 0xff when
// erasing
bool toggle(volatile uint8_t* vaddr, uint8_t expected)
{
    uint8_t b0 = *vaddr;
    uint8_t b1 = *vaddr;
    while (b0 != b1) {
        b0 = b1;
        b1 = *vaddr;
    }
    return b0 == expected;
}

// Flash activation from within the system requires the following:
// 1) program must be running from RAM
// 2) to activate the flash, specific bytes must be written to
//    2aaa and 5555 of the flash AFTER bank switching. This
//    means that banks 0 and 1 must be mapped into memory and
//    2aaa+offset and 5555+offset be programmed
// 3) the bank the user is interested in must be swapped in
//    before the action address is written.
void activateFlash(uint8_t cmd, bool doCycle4_5)
{
    volatile uint8_t* v2aaa = (uint8_t*)(0x2aaa + 0x4000);
    volatile uint8_t* v5555 = (uint8_t*)(0x5555 + 0x4000);
    uint8_t oldBanks[4];
    uint8_t banks[4];

    getBanks(banks);
    getBanks(oldBanks);
    banks[1] = 0;
    banks[2] = 1;
    setBanks(banks);
    switchBanks();

    *v5555 = (uint8_t)0xaa;
    *v2aaa = (uint8_t)0x55;
    *v5555 = cmd;

    if (doCycle4_5)
    {
        *v5555 = (uint8_t)0xaa;
        *v2aaa = (uint8_t)0x55;
    }

    setBanks(oldBanks);
    switchBanks();
}

// Programs a byte to flash. Returns true if the byte matches
// when toggling is finished
bool flashProgramByte(uint8_t b, uint8_t* addr)
{
    volatile uint8_t* vaddr = addr;
    activateFlash(0xa0, false);
    *vaddr = b;
    return toggle(vaddr, b);
}

// Programs a block to flash. Returns true if all the bytes match
// when toggling is finished.
bool flashProgramBlock(uint8_t* b, uint16_t count, uint8_t* addr)
{
    uint16_t i;
    bool ok = true;
    volatile uint8_t* vaddr = addr;
    for (i = 0; i < count; i++)
    {
        ok = ok && flashProgramByte(b[i], vaddr + i);
    }
    return ok;
}

// Erase a 4k sector. Sector must be 0-15
bool flashEraseSector(uint8_t sector)
{
    volatile uint8_t* vaddr = (uint8_t*)(sector<<12);
    assert(sector < 16);
    activateFlash(0x80, true);
    *vaddr = (uint8_t)0x30;
    return toggle(vaddr, 0xff);
}

// Returns true if all bytes in a sector are 0xff
bool flashVerifyErased(uint8_t sector)
{
    volatile uint8_t* vaddr = (uint8_t*)(sector << 12);
    uint16_t i;
    assert(sector < 16);
    for (i = 0; i < 0x1000; i++)
    {
        if (vaddr[i] != 0xff)
        {
            return false;
        }
    }
    return true;
}


I added a new function to activate the flash. It gets the current bank setup, swaps in banks 0 and 1 of flash, activates the flash, and restores the banks.

And here is my menu running:


Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 2
This will copy memory to RAM memory.
Source (hex)? 0
Dest (hex)? 8000
Length (hex)? 4000
Copied
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 8000
Length (hex)? 20
-8000: c3 69 00 ff ff ff ff ff  c3 a3 13 ff ff ff ff ff .i...... ........
-7ff0: c3 b4 13 ff ff ff ff ff  c3 c5 13 ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 6
This allows to switch all 4 banks
Only switch bank 0 when
Use hex where 0-f are flash and 10-17 are RAM
  bank #0 [now==00]? 10
  bank #1 [now==01]? 2
  bank #2 [now==10]? 2
  bank #3 [now==11]? 11
Switching...
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 3ff0
Length (hex)? 10
3ff0: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 5
This will write a byte.
Address (hex)? 3fff
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 3ff0
Length (hex)? 10
3ff0: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff 00 ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 4
This will program a byte.
Address (hex)? 4000
value (hex)? fe
Programmed

Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 4
This will program a byte.
Address (hex)? 4001
value (hex)? fb
Programmed

Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe fb ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) dump copy blocktext
3) erase bank
4) program byte
5) write byte
6) bank switch
>

The next step was to see if I could erase it--but that took a couple of code changes first... But here is the result:

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 2
This will copy memory to RAM memory.
Source (hex)? 0
Dest (hex)? 8000
Length (hex)? 4000
Copied
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 6
This allows to switch all 4 banks
Only switch bank 0 when
Use hex where 0-f are flash and 10-17 are RAM
  bank #0 [now==00]? 10
  bank #1 [now==01]? 2
  bank #2 [now==10]? 2
  bank #3 [now==11]? 11
Switching...
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: fe fb ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 3
This allows erase a sector. Ensure the sector is flash
sector (hex: 0-f)? 4
Erased and verified
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
> 1
Address (hex)? 4000
Length (hex)? 10
4000: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff ........ ........
Done!

Mark Hamann's Z80 Computer

Version: 0.1 beta
Menu

1) dump text
2) copy block
3) erase sector
4) program byte
5) write byte
6) bank switch
>


As you can see, the fe and fb that I programmed into bank 2 were erased. Yay!!!!

With this, I'm closer to having the ihx file uploading. I just need to do a visual inspection of my ihx upload code and then I'll be ready to load it into my menu. But first, I need a program block menu item that will copy a block into flash.

I also need to handle backspace better. It seems to work on the screen, but my hex conversion returns an error if I use the gets string where backspace happened. Also, the buffer limit for gets isn't working quite right.

Sunday, September 13, 2015

Bank Switching and Native Flash Programming

I think I got my bank switching stuff working. In fact, it worked right out of the box--no debugging necessary. Which is a little worrisome. But, I added some menu items for programming flash and writing RAM and bank switching is working as expected.

But, then I ran into a problem.... To put the flash chip into the modes for programming and/or erasing, I need to write to 2aaa and 5555 in specific sequences. But those addresses are from the flash chip's point of view--after any bank switching. It's obvious now, but it didn't occur to me before.

That means, I need the lowest 32K of flash to live in 32k of addressable memory for that brief time. And once in that mode, I can't touch the flash except for whatever programming/erasing I need to do.

Since my bank granularity is 16k, this is a bit of a problem. It's going to take a little creativity.

Bit 15 doesn't matter. But bit 14 does. So when I write to 2aaa, it has to be a to bank where bit 14 is 0. And when I write 5555, it has to be to a bank where bit 14 is 1. Right now, the flash code just writes 2aaa and 5555, but actually, I'm going to need to ensure than either 1) 2 banks are loaded in and the flash programmer has the appropriate addresses that map to post-bank switch of 2aaa and 5555 or 2) that the banks are switched between the writes to 2aaa, 5555, and whatever the user wants to write.

Obviously the first one is easier.

Basically, the steps I need to follow are these:

  1. copy the flash programming code into a RAM bank
  2. swap that RAM bank into bank 0 and run from there
  3. swap the flash banks 00 and 01 into banks 1 and 2
  4. activate the flash mode with 2aaa+4000 and 5555+4000. These map to 2aaa and 5555
  5. swap the flash bank to be programmed into bank 1
  6. perform the write that will program or erase
So back to the drawing board on my flash driver. It's not going to be too bad. Unless there is something else I'm overlooking.

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

MCU Seems To Be Working

I spent the morning frustrated by my MCU. It seemed to work intermittently. Eventually, I tracked down the problem to the fact that address lines 14 and 15 weren't connected to the MCU. I had connected them from the MCU to the Z-80 socket, but no connections to the flash programmer/turned bus controller. I realized when I tried measuring the voltage on the pins and the voltage kept switching between 0 and 5. The M4A5 CPLDs have a circuit that weakly holds the last output, but it is easy to make it swap by measuring the voltage.

So, once I got that figured out and got my test code all worked out, it finally started working. I still have a little more testing to do before I rewire the A14-A15 and A14-A17 and plug in the Z-80 and SRAM, but it will be very soon.

I suppose I have to start figuring out something to put into flash. Oh, and I have to ensure that my flash file program code works so that I can upload a file. Also, I think the sdcc tool-chain makes Intel Hex files, so I need to make sure I can upload an Intel Hex file. Right now, it just does binary files.

Wednesday, August 12, 2015

MCU Connected For Test

Since the next step is to test the MCU, I spent part of the morning wire wrapping the input side of the MCU CPLD.

Here it is:

The blue wires are address and data and the yellow are the bus control. I also connected the 6 new Arduino lines to the chip. I confirmed continuity. I have just started the process of testing. I'm starting out making sure the new lines are asserted properly. Once I know they are, I'll start actually trying to use the MCU and ensuring it works as expected.