Go Down

Topic: I2C EEPROM programmer (Read 2167 times) previous topic - next topic

PlaneCrazy

Greetings, all

Total newbie here.

I have searched on this forum for a while now, looking at all the interesting projects. But I have either not found what I'm looking for, or failed to recognise it when I saw it.

I would like to make an EEPROM programmer. Specifically, the first chip I have to program is an FM24C128.

http://za.rs-online.com/web/p/fram-memory-chips/8282780/

I have come across several parallel EEPROM programmers using Arduino, but no more than passing mentions of i2c serial programmers - demos and images, even libraries, but no projects.

1. How do I read what's on an EEPROM?
2. How do I save the data to a file on my PC?
3. How do I write the data from my PC onto a chip?

Thank you.

GautamD

Hi, since you are a newbie you should take a look at Nick Gammon's website for I2C and EEPROM. Following is the link: http://gammon.com.au/i2c

Cheers!!

cyberjupiter

#2
Oct 26, 2017, 10:15 am Last Edit: Oct 26, 2017, 02:27 pm by cyberjupiter Reason: Fixed mistake. I thought it was internal eeprom. It is I2C external eeprom.
1. You find a robust I2C EEPROM library and you read data inside the external eeprom by using the given function in the library built functions, EEPROM.get() or EEPROM.read()

2. You build an application on a PC. That application should be able to communicate serially. You send the saved data in the EEPROM to Arduino serial port. The PC listen using the app and save it to a file.


3. Same as (2) but this time the app send data from PC to Arduino serial port. Arduino will perform the job to write the data into the EEPROM, again by using the function provided by the I2C EEPROM library. built functions, EEPROM.put or EEPROM.write()
https://github.com/cyberjupiter
if you want to help me create (almost useless) libraries.

PlaneCrazy

My thanks to both of you.

cyberjupiter

To be honest, I think this is a cool project. Though, I am sure this project has been done before. But I don't know if there is a library for I2C external eeprom that has the same functions as the official internal EEPROM library. If you could get a library that has the same function as EEPROM.put() or EEPROM.write(), this project is quite easy.
https://github.com/cyberjupiter
if you want to help me create (almost useless) libraries.

PlaneCrazy

Jack Christensen has a library for external i2c EEPROMs. I think I'll start there.

PlaneCrazy

Greetings, all

I have found many links on how to transfer data using wire.h to an I2C device. However, when I simulate it using Proteus I apparently get nothing.

Using the code from this reference page, and running the sketch in Proteus, the only change in bits that I see are these:



Changing the data to an array (as I ultimately need to write a 16 kiB array to my EEPROM) to the following:

Code: [Select]
#include <Wire.h>

byte test[] = {1,2,3,4};

void setup()
{
  Wire.begin(); // join i2c bus
}

void loop()
{
  Wire.beginTransmission(44); // transmit to device #44 (0x2c)
                              // device address is specified in datasheet
  Wire.write(test,4); 
  Wire.endTransmission();     // stop transmitting

  delay(500);
}
 

I get exactly the same display on Proteus's oscilloscope.

Why am I only seeing 44? Where is the data that I'm supposed to be sending? How do I know my code is working?

Thank you


GolamMostafa

#7
Jan 28, 2018, 05:53 pm Last Edit: Jan 28, 2018, 05:55 pm by GolamMostafa
Quote
1. How do I read what's on an EEPROM?
2. How do I save the data to a file on my PC?
3. How do I write the data from my PC onto a chip?
1. You may collect an Arduino UNO which will hopefully answer your above questions.
2. Build the following circuit between UNO, 24C128, and I2C LCD. (I2C LCD is given as a bonus!).


3. Store (write) 0x23 into location 0x1234 of the EEPROM; read it back, and show it on Serail Monitor and LCD.

Code: [Select]
#include<Wire.h>
#define eeDeviceAddress 0b1010010
#define adrLocUbyte 0x12
#define adrLocLbyte 0x34
#define dataByte 0x23

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  
  Wire.beginTransmission(eeDeviceAddress); //select EEPROM by doing a roll call
  Wire.write(adrLocUbyte);        //select (point) EEPROM location 1234h
  Wire.write(adrLocLbyte);
  Wire.write(dataByte);          //data byte 23h fro the location 1234h
  Wire.endTransmission();        //end all the above queued data and STOP
  delay(5);                      //wait for the data byte to get written into EEPROM location                
  //-------------------------------
  Wire.beginTransmission(eeDeviceAddress); //select EEPROM by doing a roll call
  Wire.write(adrLocUbyte);        //select (point) EEPROM location 1234h
  Wire.write(adrLocLbyte);
  Wire.endTransmission();        //end all the above queued data and STOP
  //--------------------------------------  
  Wire.requestFrom(eeDeviceAddress, 1); //read 1 byte data from location 1234 and save in buffer
  byte x = Wire.read();                 //bring data byte from buffer into x
  Serial.println(x, HEX);               //show value on Serial Monitor ; shows: 23
}

void loop()
{
  
}


Tested for: 24C512. The program should also be working for 24128 as 24C512 and 24C128 are pin-compatible and TWI-bus compatible chips.

cattledog

#8
Jan 28, 2018, 06:46 pm Last Edit: Feb 02, 2018, 12:10 am by cattledog
You are leaving out the eeprom memory location from your code.

Code: [Select]
void writeBytes(int device, unsigned int Address, byte* data, byte len)
{
  Wire.beginTransmission(device);
  Wire.write(Address >>8 ); //MSB
  Wire.write(Adderess& 0xFF); // LSB
  Wire.write(data,len);
  Wire.endTransmission();
  delay(5);//small delay for eeprom to save data
 }


You need to be careful of page boundaries with block writes to an external eeprom. I would echo the sentiments of reply#5 and consider using the Christensen library.

PlaneCrazy

OK, pardon me for being really new and really dumb (I am, after all, but a humble mechanical engineer), but I am failing to understand anything I see. And I really need to get my radio working.

Here is the J Christensen example, as per you recommendations:

Code: [Select]

//Test extEEPROM library.
//Writes the EEPROM full of 32-bit integers and reads them back to verify.
//Wire a button from digital pin 6 to ground, this is used as a start button
//so the sketch doesn't do unnecessary EEPROM writes every time it's reset.
//Jack Christensen 09Jul2014

#include <extEEPROM.h>    //http://github.com/JChristensen/extEEPROM/tree/dev
#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/
#include <Wire.h>         //http://arduino.cc/en/Reference/Wire

//Two 24LC256 EEPROMs on the bus
const uint32_t totalKBytes = 64;         //for read and write test functions
extEEPROM eep(kbits_256, 2, 64);         //device size, number of devices, page size

const uint8_t btnStart = 6;              //start button

void setup(void)
{
    pinMode(btnStart, INPUT_PULLUP);
    Serial.begin(115200);
    uint8_t eepStatus = eep.begin(extEEPROM::twiClock400kHz);   //go fast!
    if (eepStatus) {
        Serial << endl << F("extEEPROM.begin() failed, status = ") << eepStatus << endl;
        while (1);
    }
   
    Serial << endl << F("Press button to start...") << endl;
    while (digitalRead(btnStart) == HIGH) delay(10);    //wait for button push

    uint8_t chunkSize = 64;    //this can be changed, but must be a multiple of 4 since we're writing 32-bit integers
//    eeErase(chunkSize, 0, totalKBytes * 1024 - 1);
    eeWrite(chunkSize);
    eeRead(chunkSize);

    dump(0, 16);            //the first 16 bytes
    dump(32752, 32);        //across the device boundary
    dump(65520, 16);        //the last 16 bytes
}

void loop(void)
{
}

//write test data (32-bit integers) to eeprom, "chunk" bytes at a time
void eeWrite(uint8_t chunk)
{
    chunk &= 0xFC;                //force chunk to be a multiple of 4
    uint8_t data[chunk];
    uint32_t val = 0;
    Serial << F("Writing...") << endl;
    uint32_t msStart = millis();
   
    for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
        if ( (addr &0xFFF) == 0 ) Serial << addr << endl;
        for (uint8_t c = 0; c < chunk; c += 4) {
            data[c+0] = val >> 24;
            data[c+1] = val >> 16;
            data[c+2] = val >> 8;
            data[c+3] = val;
            ++val;
        }
        eep.write(addr, data, chunk);
    }
    uint32_t msLapse = millis() - msStart;
    Serial << "Write lapse: " << msLapse << " ms" << endl;
}

//read test data (32-bit integers) from eeprom, "chunk" bytes at a time
void eeRead(uint8_t chunk)
{
    chunk &= 0xFC;                //force chunk to be a multiple of 4
    uint8_t data[chunk];
    uint32_t val = 0, testVal;
    Serial << F("Reading...") << endl;
    uint32_t msStart = millis();
   
    for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
        if ( (addr &0xFFF) == 0 ) Serial << addr << endl;
        eep.read(addr, data, chunk);
        for (uint8_t c = 0; c < chunk; c += 4) {
            testVal =  ((uint32_t)data[c+0] << 24) + ((uint32_t)data[c+1] << 16) + ((uint32_t)data[c+2] << 8) + (uint32_t)data[c+3];
            if (testVal != val) Serial << F("Error @ addr ") << addr+c << F(" Expected ") << val << F(" Read ") << testVal << F(" 0x") << _HEX(testVal) << endl;
            ++val;
        }
    }
    uint32_t msLapse = millis() - msStart;
    Serial << "Last value: " << --val << " Read lapse: " << msLapse << " ms" << endl;
}

//write 0xFF to eeprom, "chunk" bytes at a time
void eeErase(uint8_t chunk, uint32_t startAddr, uint32_t endAddr)
{
    chunk &= 0xFC;                //force chunk to be a multiple of 4
    uint8_t data[chunk];
    Serial << F("Erasing...") << endl;
    for (int i = 0; i < chunk; i++) data[i] = 0xFF;
    uint32_t msStart = millis();
   
    for (uint32_t a = startAddr; a <= endAddr; a += chunk) {
        if ( (a &0xFFF) == 0 ) Serial << a << endl;
        eep.write(a, data, chunk);
    }
    uint32_t msLapse = millis() - msStart;
    Serial << "Erase lapse: " << msLapse << " ms" << endl;
}

//dump eeprom contents, 16 bytes at a time.
//always dumps a multiple of 16 bytes.
void dump(uint32_t startAddr, uint32_t nBytes)
{
    Serial << endl << F("EEPROM DUMP 0x") << _HEX(startAddr) << F(" 0x") << _HEX(nBytes) << ' ' << startAddr << ' ' << nBytes << endl;
    uint32_t nRows = (nBytes + 15) >> 4;

    uint8_t d[16];
    for (uint32_t r = 0; r < nRows; r++) {
        uint32_t a = startAddr + 16 * r;
        eep.read(a, d, 16);
        Serial << "0x";
        if ( a < 16 * 16 * 16 ) Serial << '0';
        if ( a < 16 * 16 ) Serial << '0';
        if ( a < 16 ) Serial << '0';
        Serial << _HEX(a) << ' ';
        for ( int c = 0; c < 16; c++ ) {
            if ( d[c] < 16 ) Serial << '0';
            Serial << _HEX( d[c] ) << ( c == 7 ? "  " : " " );
        }
        Serial << endl;
    }
}


If I compile and export this, nothing happens. That is because no data has been given yet.

I have 16384 bytes of data to upload. How, explicitly, and where do I give the address(es) that I need to upload to, and how, explicitly, do I specify the data in the above sketch? How many bytes can I upload at a time?

Do I create an array with data bytes in hex format? Do I break it up into smaller chunks? If so, how small? (I know the sketch also breaks data up in chunks, but does it break up an entire array into chunks of 32 bits?) Do I create a counter to count down the addresses and put them in a loop?

Thank you for your patience with me.

GolamMostafa

#10
Feb 01, 2018, 07:22 am Last Edit: Feb 01, 2018, 07:53 am by GolamMostafa
1. It is assumed that your set up is: UNO and 24C128 EEPROM (capacity 16384 bytes; address range: 0000 - 3FFF).

2.  You wish to write 16384 bytes data into the EEPROM.

3. You can follow page write mode or byte write mode for the storage of data into the EEPROM. In page mode, 24C128 EEPROM allows 64-byte data (page size) to be written in ONE GO (within 5 ms time -- the data write time). In byte mode, data are written byte-by-byte, and it will require 16384x5 ms ~= 82 sec time to finish writing in stead of 256x5 = 1.3 sec time (in page mode).

4.  It is recommended that the 16834 bytes data to be kept in a byte/page oriented array.

5.  Post#7/Post#8 could be followed to store data into the EEPROM.

cattledog

#11
Feb 01, 2018, 08:37 am Last Edit: Feb 01, 2018, 08:39 am by cattledog
Quote
I would like to make an EEPROM programmer. Specifically, the first chip I have to program is an FM24C128.
http://za.rs-online.com/web/p/fram-memory-chips/8282780/
The link is to a FRAM storage device FM24V01-G Serial-I2C FRAM  while FM24C128 is a conventional EEPROM. The FRAM does not have page boundaries, the EEPROM does.

Which one are you trying to fill with data?

Quote
I have 16384 bytes of data to upload.
Where does this data file reside, what format is it in, and how do you plan to transfer the data to the Arduino?

Are you still working with Proteus? Do you have an EEPROM and an Arduino?  Can you read and write data to and from the EEPROM using the Wire library commands? You can work up to large block writes and optimized speed with the Christensen library after you work through the basics of byte read/write or small transfers of 4 byte blocks which will always work with page boundaries.


Quote
And I really need to get my radio working.
What does the radio have to do with the transfer of data into an EEPROM or FRAM?
What are you trying to do with your project?

PlaneCrazy

#12
Feb 01, 2018, 09:39 am Last Edit: Feb 01, 2018, 01:20 pm by PlaneCrazy
I couldn't get the FM24V01, so I purchased 24LC128 instead. I have already checked that these two are compatible for my application.

The background is this: I purchased a radio for my car. The car is a 1995 model, but the radio is a 2014 OEM from another manufacturer. As it turns out, this radio was never commissioned, so instead of asking me for a passcode, it requests a diagnostic tool.

The cure for this is to collect a dump file for the radio, then burn it onto the radio's EEPROM. This OEM radio is quite a popular alternative to the garish aftermarket stuff we can buy.

I have the dump file in binary format. But I also have a text file with the addresses and bytes in hex format.

I decided to practice on EEPROMs that I purchased, rather than accidentally destroying the radio. For this purpose I have purchased several EEPROMs. I have built the circuitry, but I want to know that the code works.

This is the short-term goal. I have longer term goals and projects as well, but this radio was my excuse for finally getting into Arduino.

Now, Proteus.

I can simulate writing an EEPROM. But I cannot (to my knowledge) simulate reading an already written EEPROM, so I can't simulate first writing the EEPROM and then reading it to check a) that all the data is there and b) all the data is where I expect them to be. So I would expect to at least detect on the oscilloscope which bytes are active when, right?

cattledog

It sounds like you have some eeproms to use, so I would forget about proteus and just use an eeprom and the Arduino. You can write and read from the test eeprom before you try and program the radio eeprom.

Your test eeproms are 128Kbits (16Kbytes). What is the size of the eeprom in the radio? Is the eeprom in the radio addressable with i2c? How did you obtain the "dump file?

Quote
ultimately need to write a 16 kiB array to my EEPROM
In your link to the "dump file" you show 720 bytes of data. Is that the data you have? Where does the 16K bytes come in? Is all data after the 720 either 0x00 or 0xFF or is there real data to deal with?

I am asking this because originally whey you talked about an eeprom programmer it was not clear that you had only one eeprom to write, and you were not developing a production tool. With 720 bytes, of actual data you don't need to develop a tool set to deal with large files and use a write procedure which is optimized for speed.

With 720 bytes, you should be able to hand input a 2 dimensional array 45x16 which contains all the data. Writing that data to eeprom and reading it back will be very simple, even without the Christensen library. You should be able to use the Wire library with the wire.write(data,16) and avoid any page boundary issues.


cattledog

#14
Feb 01, 2018, 10:22 pm Last Edit: Feb 03, 2018, 04:59 pm by cattledog
Here is some code which demonstrates how to set  up block data and write it to eeprom. You should be able to adapt it to your data.

Code: [Select]

//possible page boundary issue unless all writes at 16 bytes
#include <Wire.h>
#define ADDRESS 0x50 //Address of EEPROM

unsigned int memAddress = 0;//starting eeprom address
int totalBytes = 720;//total byte number multiple of 16
const byte blockLength = 16; //will avoid page boundary in eeprom
const byte numberOfBlocks = totalBytes/blockLength;

//test data visuallyto confirm pattern
//EDIT: correct declaration of byte fileData
byte fileData[][blockLength] =
{
  {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0},
};

void setup() {
  Serial.begin(115200);
  Wire.begin();

  for (int i = 0; i <= numberOfBlocks - 1; i++)
  {
    writeBytes( ADDRESS, memAddress, fileData[i], blockLength);
    memAddress += blockLength;
  }

  memAddress = 0;//reset address for read

  for (int i = 0; i <= numberOfBlocks - 1; i++)
  {
    readBytes(ADDRESS, memAddress, blockLength);
    memAddress += blockLength;
  }
}

void loop() {}

void writeBytes(int device, unsigned int Address, byte* data, byte len)
{
  Wire.beginTransmission(device);
  Wire.write(Address >> 8 ); //MSB
  Wire.write(Address & 0xFF); // LSB
  Wire.write(data, len);
  Wire.endTransmission();
  delay(5);//small delay for eeprom to save data
}

void readBytes(int device, unsigned int Address, byte len )
{
  byte readBuffer[len];
  Wire.beginTransmission(device); // I2C address
  Wire.write(Address >> 8); // bit shift for high byte of pointer address
  Wire.write(Address & 0xFF); // mask for the low byte
  Wire.endTransmission();
  Wire.requestFrom(device, len);
  Wire.readBytes(readBuffer, len);
  for (byte j = 0; j < 16; j++)
  {
    Serial.print(readBuffer[j], HEX);
    Serial.print(" ");
  }
  Serial.println();
}


OUTPUT
Code: [Select]
FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 FF
0 0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0
0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0
0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0
0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0
0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0
0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0
0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0
0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0
0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0
0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0
0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0
0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0
0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0
0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0
FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 FF 0

Go Up