Memcpy strange behaviour

Good day
I have the following code.

void initUCode() 
{
  // ZF = 0, CF = 0
  memcpy_P(ucode[FLAGS_Z0C0], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z0C0 = 0

  // ZF = 0, CF = 1
  memcpy_P(ucode[FLAGS_Z0C1], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z0C1 = 1
  ucode[FLAGS_Z0C1][JC][2] = IOJ;                                    //JC = 0111  JZ = 1000

  // ZF = 1, CF = 0
  memcpy_P(ucode[FLAGS_Z1C0], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z1C0 = 2                                     
  ucode[FLAGS_Z1C0][JZ][2] = IOJ;

  // ZF = 1, CF = 1
  memcpy_P(ucode[FLAGS_Z1C1], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z1C1 = 3
  ucode[FLAGS_Z1C1][JC][2] = IOJ;
  ucode[FLAGS_Z1C1][JZ][2] = IOJ;
}

After a lot of puzzling I finally figured out that the second block is overwriting the first block and the fourth block is overwriting the third block. How do I prevent this?

Here is the full code.

/**
 * This sketch programs the microcode EEPROMs for the 8-bit breadboard computer
 * It includes support for a flags register with carry and zero flags
 * See this video for more: https://youtu.be/Zg1NdPKoosU
 */
#define SHIFT_DATA 2
#define SHIFT_CLK 3
#define SHIFT_LATCH 4
#define EEPROM_D0 5
#define EEPROM_D7 12
#define WRITE_EN 13

// Negative enabled control lines put out a 1 when disabled
//                   _ ______ _  ___
#define HLT       0b1101111110100111  // Halt clock
#define MICO      0b0001111110100011  // Memory address register in, Counter out
#define ROIICE    0b0100101110101111  // Ram out, Instruction in, Counter enable
#define IOMI      0b0001011110100111  // Instruction out, Memory in
#define IOAI      0b0101010110100111  // Instruction register out, A register in
#define IOJ       0b0101011110100101  // Instruction register out, Jump
#define AOOI      0b0101111010110111  // A register out, Output register in
#define ROAI      0b0100110110100111  // Ram out, A register in
#define ROBI      0b0100111110000111  // Ram out, B register in
#define AORI      0b0111111010100111  // A register out, Ram in
#define EOAIFI    0b0101110100100110  // Sum out, A register in, Flags in
#define EOAISUFI  0b0101110101100110  // Sum out, A register in, Subtract, Flags in
#define NOP       0b0101111110100111  // All controls off 


#define FLAGS_Z0C0 0
#define FLAGS_Z0C1 1
#define FLAGS_Z1C0 2
#define FLAGS_Z1C1 3

#define JC  0b0111
#define JZ  0b1000

const PROGMEM uint16_t UCODE_TEMPLATE1[16][8] = 
{
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 0000 - NOP
  { MICO,  ROIICE,  IOMI,  ROAI,  NOP,      NOP,  NOP,  NOP },   // 0001 - LDA
  { MICO,  ROIICE,  IOMI,  ROBI,  EOAIFI,   NOP,  NOP,  NOP },   // 0010 - ADD
  { MICO,  ROIICE,  IOMI,  ROBI,  EOAISUFI, NOP,  NOP,  NOP },   // 0011 - SUB
  { MICO,  ROIICE,  IOMI,  AORI,  NOP,      NOP,  NOP,  NOP },   // 0100 - STA
  { MICO,  ROIICE,  IOAI,  NOP,   NOP,      NOP,  NOP,  NOP },   // 0101 - LDI
  { MICO,  ROIICE,  IOJ,   NOP,   NOP,      NOP,  NOP,  NOP },   // 0110 - JMP
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 0111 - JC
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1000 - JZ
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1001
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1010
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1011
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1100
  { MICO,  ROIICE,  NOP,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1101
  { MICO,  ROIICE,  AOOI,  NOP,   NOP,      NOP,  NOP,  NOP },   // 1110 - OUT
  { MICO,  ROIICE,  HLT,   NOP,   NOP,      NOP,  NOP,  NOP },   // 1111 - HLT
};

uint16_t ucode[4][16][8];

void initUCode() 
{
  // ZF = 0, CF = 0
  memcpy_P(ucode[FLAGS_Z0C0], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z0C0 = 0

  //ZF = 0, CF = 1
  memcpy_P(ucode[FLAGS_Z0C1], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z0C1 = 1
  ucode[FLAGS_Z0C1][JC][2] = IOJ;                                        //JC = 0111  JZ = 1000

  // ZF = 1, CF = 0
  memcpy_P(ucode[FLAGS_Z1C0], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z1C0 = 2                                     
  ucode[FLAGS_Z1C0][JZ][2] = IOJ;

  // ZF = 1, CF = 1
  memcpy_P(ucode[FLAGS_Z1C1], UCODE_TEMPLATE1, sizeof(UCODE_TEMPLATE1)); //FLAGS_Z1C1 = 3
  ucode[FLAGS_Z1C1][JC][2] = IOJ;
  ucode[FLAGS_Z1C1][JZ][2] = IOJ;
}

/*
 * Output the address bits and outputEnable signal using shift registers.
 */
void setAddress(int address, bool outputEnable) 
{
  shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, (address >> 8) | (outputEnable ? 0x00 : 0x80));
  shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, address);

  digitalWrite(SHIFT_LATCH, LOW);
  digitalWrite(SHIFT_LATCH, HIGH);
  digitalWrite(SHIFT_LATCH, LOW);
}

 //Read a byte from the EEPROM at the specified address.
 
  byte readEEPROM(int address) 
{
  for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) 
  {
    pinMode(pin, INPUT);
  }
  setAddress(address, /*outputEnable*/ true);

  byte data = 0;
  for (int pin = EEPROM_D7; pin >= EEPROM_D0; pin -= 1) 
  {
    data = (data << 1) + digitalRead(pin);
  }
  return data;
}

 //Write a byte to the EEPROM at the specified address.
 
void writeEEPROM(int address, byte data) 
{
  setAddress(address, /*outputEnable*/ false);
  for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) 
  {
    pinMode(pin, OUTPUT);
  }

  for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) 
  {
    digitalWrite(pin, data & 1);
    data = data >> 1;
  }
  
  digitalWrite(WRITE_EN, LOW);
  delayMicroseconds(1);
  digitalWrite(WRITE_EN, HIGH);
  delay(10);
}

 //Read the contents of the EEPROM and print them to the serial monitor.
 
void printContents(int start, int length) 
{
  for (int base = start; base < length; base += 16) 
  {
    byte data[16];
    for (int offset = 0; offset <= 15; offset += 1) 
    {
      data[offset] = readEEPROM(base + offset);
    }

    char buf[80];
    sprintf(buf, "%03x:  %02x %02x %02x %02x %02x %02x %02x %02x,     %02x %02x %02x %02x %02x %02x %02x %02x",
            base, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]);

    Serial.println(buf);
  }
}

void setup() 
{
  // put your setup code here, to run once:
  initUCode();
  pinMode(SHIFT_DATA, OUTPUT);
  pinMode(SHIFT_CLK, OUTPUT);
  pinMode(SHIFT_LATCH, OUTPUT);
  digitalWrite(WRITE_EN, HIGH);
  pinMode(WRITE_EN, OUTPUT);
  Serial.begin(57600);
  
  // Program data bytes
  Serial.println("Programming EEPROM");

  // Program the entire EEPROM
for (int address = 0; address < 1024; address += 1) 
  {
    int flags       = (address & 0b1100000000) >> 8;
    int byte_sel    = (address & 0b0010000000) >> 7;
    int instruction = (address & 0b0001111000) >> 3;
    int step        = (address & 0b0000000111);

    if (byte_sel) 
    {
      writeEEPROM(address, ucode[flags][instruction][step]);
    } else 
    {
      writeEEPROM(address, ucode[flags][instruction][step] >> 8);
    }

    if (address % 64 == 0) 
    {
      Serial.print(".");
    }
  }

  Serial.println(" done");
  
   //Read and print out the contents of the EEPROM
  Serial.println("Reading EEPROM");
  printContents(0, 1024);
}

void loop() {
  // put your main code here, to run repeatedly:

}

what is ucode? can you post more of the code?

The obvious question, is each element of ucode large enough to hold UCODE_TEMPLARE1? Is UCODE_TEMPLATE1 actually stored in PROGMEM?

Hi Dave

Thanks for the prompt replies. I need to change the code back to what it was before sending it to you. There are four blocks of data required to run various programs. Most of them are identical with up to 2 exceptions in each block. The original programmer dealt with them by using memcpy and adding exceptions for the few cases of each block. As I mentioned this process results in the first block being overwritten by the second (the second block is correct). The same is with the third and fourth data blocks. To test a theory I extended the template to include so that it covered the full values of the first and second scenarios. Strangely again the first block is again the same as the second (the correct one). I need to change it back to the original. How much of the code do you need?

Yes the template is stored in program memory to eliminate the storage warning. The code is const PROGMEM uint16_t UCODE_TEMPLATE1. You probably realise that my programming knowledge is sketchy at best.

Many thanks.

Tony

not sure you can get much help without answering questions

Hi Greg

Thanks for the reply. I am a novice in programming and I used someone else’s sketch modified for a different layout of my electronics project. I guess ucode is the array of values generated by the sketch. Would it help to provide a bigger chunk of the code?

Thanks for the support.

Best regards

Tony

The full code, yes. Just pieces of it will not help anybody to help you :wink:

PS:
Thanks for using code tags in your first post :+1:

I presume I should post it to the forum?

Like you did in your opening post. Add the code in a new reply.

Thanks for your reply. Unfortunately it is not as obvious to me. Z0_C0 etc cycles through 0 to 3 so should be writing to 4 separate blocks. Maybe you can explain what I am doing wrong?

what is ucode defined as and what are its values? it is the destination address for memcpy)(

What I mean is there are 4 values equal to 0, 1, 2, 3. Each memcpy uses only one of these. So there should not be an overlap.

What is your electronics project? Please provide a schematic (including typenumbers of the components used).

Please post a link to the project that someone else created; full project, not just the code.

It is too complicated to add a single schematic. If you want to get an idea here is a link to a long series of videos and schematics for the whole project. [Ben Eater]. I have completed the build but it needs the EEPROMS to be programmed properly so that it jumps to a different address when the carry flag or zero flag is on. There are 4 cases, both off, CF on, ZF on, and both on that is why there are 4 copies of the data with exceptions to the both off case.
I have been playing around with various cases to try and understand what is going on. I do this by commenting out the memcpy instructions so that only one is active. The results are as follows:
1 With only the first active. Only 0s result 2 With only the second active. Both first and second data blocks have the same data and this data is only correct for the second block 3 With only the third active. Only 0s result
4 With only the fourth active. Both the third and fourth data blocks have the same data and this is only correct for the fourth block
I appreciate your interest and help

What type eeprom are you using?

The data being written appears to be correct, possibly a wiring problem. Can you post the serial monitor output from the sketch?

You may be onto something. The common problem seems to be when CF is zero which is the first case and the third case which both write zeroes if i run them alone. In the second and fourth case CF is a 1. CF is pin 8 on the 28C64. Don`t understand but bears looking into.

Oh. So you are loading an EEPROM chip with microcode for an 8-bit CPU built from scratch.

"If neither flag is set, don't do anything special for "Jump If Cary" (JC) or "Jump If Zero" (JZ) opcodes. If the Cary flag is set, the third step of the JC opcode becomes 'IOJ'. If the Zero flag is set, the third step of the JZ opcode becomes 'IOJ'."

That implements the conditional jumps.

Thanks for your reply. On the suggestion of David I had a look at the wiring. I seems to be sound but i noticed that the EEPROM is not seating well in the breadboard which may mean that some of the pins could be not contacting properly and therefore floating high. If this is the A8 (CF) address pin it would explain the behavior. I intend to make a soldered version of the programmer but this will take a few days. i will report if this solves the problem.
I the meantime my thanks to everyone who gave this some thought. It was very helpful.

Well, I thought I had it. I made a hardwired version of the EEPROM programmer but i still have the same problem. The data in the EEPROM looks like the address A8 is always on. Is there any way i can print out the data of ucode so that i can see whether it is the write issue or the memcpy issue?

First question is why are you using memcpy_P, given that tries to write a pointer into program memory space, when you can just use memcpy.

You already declared everything as constants so let the compiler decide on where it stores something.

Thanks. It was recommended to use memcpy_P because otherwise you get a memory warning so the template is stored in Program memory and this solves the memory warning. I have tried both methods and get the same result. Any way to print the array and not the EEPROM contents? Appreciate your help.

Just to summarize. I can switch the exceptions on and off by commenting them out. This is what happens when i read the EEPROM.
JC off in block 2. JC off in block 1 and 2.
JC on in block 2. JC on in block 1 and 2.
Summary: What happens in block 2 overwrites block 1.
JZ on in block 3. JZ off in block 3.
JZ on in block 4. JZ on in block 3 and 4.
Summary: What happens in block 4 overwrites block 3.
This is why i would like to be able to print the array so that i can see whether the array is correct which would point to the problem being in the EEPROM.