Extend addressing to 19 bits for larger chips like the 29C040

This commit is contained in:
Tom Nisbet 2019-07-19 23:21:36 -04:00
parent 8cd14cb22e
commit 463cbbf154
17 changed files with 368 additions and 141 deletions

View File

@ -5,6 +5,8 @@ There are currently two hardware flavors - one for 28C series EEPROMs and one sp
The basic hardware design, used for the 28C family. is much more adaptable to additional chip families. This design uses two shift registers to create 16 dedicated address lines from only 3 arduino pins. This design, plus manual switching of the program voltage, would be very adaptable to EPROMs like the 2716, 2764, 27040, and 272001. The hardware has already been used with these chips for read-only operations.
The current design can directly address chips as large as 512K bytes, like the 29C040. Use the shift registers to for A0..A15 and wire Arduino pins D10..D12 to A16..A18.
# Software
The software design is modular, allowing easy extenstion for chips with different programming algoritms. A new class can be added for each new chip family. This class will include code for byte reads, byte writes, and optional block writes, if the chip supports it. All of the chip-specific code will be in this single class.

View File

@ -1,11 +1,13 @@
# TommyPROM - An Arduino-based EEPROM programmer
This is a simple EEPROM programmer and reader that can be assembled using an Arduino and a few additional parts. It has been sucessfully built using the Arduino UNO, Nano and Boarduino models.
The original code was specific to the 28C256 32Kx8 EEPROM, but it has been extended to also support Intel 8755A EPROMS.
The original code was specific to the 28C256 32Kx8 EEPROM, but it has been extended to also support Intel 8755A EPROMS and some 29C010 Flash.
The 28C design can be used with other 5V EEPROMS as well. Many 5V chips, including UV EPROMs, such as the 2716, 2764, 27C2001 and 27C040, can be read, but not written, with the basic hardware. Some pin changes may be needed to get the signals to the correct pins on the device. See the [extension readme](README-extension.md) for details on suggested hardware and software changes needed to support new EPROM and EEPROM families.
The PROM-specific code is modular and can be easily adapted to support additional devices. There are currently drivers and hardware designs for 28C series EEPROMS and the Intel 8755A EPROM. Larger PROMs can be read or written in 64K chunks.
The PROM-specific code is modular and can be easily adapted to support additional devices. There are currently drivers and hardware designs for 28C series EEPROMS and the Intel 8755A EPROM.
Some 29C series chips, like the 29C010 can be written with the 28C hardware. The 29C series only differs from the 28C in that the 29C chips erase and write an entire sector at a time. The 29C010 and some 29C020 chips use a 128 byte sector, which matches the XModem buffer in the current code. Other 29C020s and all 29C040s use a 256 byte sector and cannot be written without code changes to buffer up an entire 256 byte block of data before writing.
Features include:
* Simple hardware design that can be assembled on a breadboard.
@ -36,11 +38,12 @@ To use the programmer, connect the Arduino USB to the host computer and run a te
Set the terminal's serial parameters to 115200 baud, 8 bits, no parity, 1 stop bit to match the Arduino. Press the Enter key. If the connection is successful, TommyPROM will display a menu of options.
Most of the commands take a start address parameter, always entered as 4 hex characters. If needed, the end address parameter is also 4 hex characters. For example, the command:
Most of the commands take a start address parameter, always entered as 1 to 5 hex characters. Leading zeros are not required. If needed, the end address parameter is also in hex. Parameters are separated by a space. For example, either of the commands:
d0000 01ff
d0 1ff
dumps memory from 0000H to 01ffH. Note that commands and parameters can be entered in uppercase or lowercase.
dump memory from 0000H to 01ffH. Note that commands and parameters can be entered in uppercase or lowercase.
The R command is used to read from a PROM and save a binary image on the host. The W command receives a file from the host and writes (burns) it into the device. The R command needs a start and end address. The W command determines the end address from the received file size.
@ -58,4 +61,3 @@ The files used for READ and WRITE are simple binary images. This can be created
## Further Work
* Add a new PromDevice class for 27 series EPROMS.
* Additional error checking in the CmdLine code.
* Extend the addressing code to use a U32 instead of a U16 to allow chips larger than 64K to be programmed in a single operation.

View File

@ -7,12 +7,15 @@
// compiled in.
#define PROM_IS_28C
//#define PROM_IS_27
//#define PROM_IS_8755A
// Don't change anything below this comment unless you are adding support for a new device type.
#if defined(PROM_IS_28C)
#include "PromDevice28C.h"
#elif defined(PROM_IS_27)
#include "PromDevice27.h"
#elif defined(PROM_IS_8755A)
#include "PromDevice8755A.h"
// Additional device support goes here...

View File

@ -14,6 +14,7 @@ void PromAddressDriver::begin()
digitalWrite(ADDR_DATA, LOW);
digitalWrite(ADDR_CLK_LO, LOW);
digitalWrite(ADDR_CLK_HI, LOW);
DDRB |= 0x1c; // Set D10..D12 as outputs
// To save time, the setAddress only writes the hi byte if it has changed.
@ -24,13 +25,21 @@ void PromAddressDriver::begin()
}
// Set a 16 bit address in the two address shift registers.
void PromAddressDriver::setAddress(word address)
// Set a 16 bit address in the two address shift registers and
// the upper bits on the extened address pins.
void PromAddressDriver::setAddress(uint32_t address)
{
static byte lastHi = 0xca;
byte hi = address >> 8;
static byte lastUpper = 0xca;
byte upper = (address >> 16) & 0xff;
byte hi = (address >> 8) & 0xff;
byte lo = address & 0xff;
if (upper != lastUpper)
{
setUpperAddress(upper);
lastUpper = upper;
}
if (hi != lastHi)
{
setAddressRegister(ADDR_CLK_HI, hi);
@ -40,6 +49,13 @@ void PromAddressDriver::setAddress(word address)
}
void PromAddressDriver::setUpperAddress(byte addr)
{
// Set the upper address on pins D10..D12.
PORTB = (PORTB & 0xe3) | ((addr << 2) & 0x1c);
}
// Shift an 8-bit value into one of the address shift registers. Note that
// the data pins are tied together, selecting the high or low address register
// is a matter of using the correct clock pin to shift the data in.
@ -74,4 +90,3 @@ void PromAddressDriver::setAddressRegister(uint8_t clkPin, byte addr)
addr <<= 1;
}
}

View File

@ -6,12 +6,12 @@
class PromAddressDriver {
public:
static void begin();
static void setAddress(word address);
static void setAddress(uint32_t address);
private:
static void setUpperAddress(byte addr);
static void setAddressRegister(uint8_t clkPin, byte addr);
};
#endif // #define INCLUDE_PROM_ADDRESS_DRIVER_H

View File

@ -2,7 +2,7 @@
#include "PromDevice.h"
PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling)
PromDevice::PromDevice(uint32_t size, word blockSize, unsigned maxWriteTime, bool polling)
: mSize(size),
mBlockSize(blockSize),
mMaxWriteTime(maxWriteTime),
@ -14,14 +14,14 @@ PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime
// Write a block of data to the device. If the device supports block writes,
// the data will be broken into chunks and written using the block mode.
// Otherwise, each byte will be individually written and verified.
bool PromDevice::writeData(byte data[], word len, word address)
bool PromDevice::writeData(byte data[], uint32_t len, uint32_t address)
{
bool status = true;
if (mBlockSize == 0)
{
// Device does not support block writes.
for (word ix = 0; (ix < len); ix++)
for (uint32_t ix = 0; (ix < len); ix++)
{
if (burnByte(data[ix], address + ix) == false)
{
@ -32,8 +32,8 @@ bool PromDevice::writeData(byte data[], word len, word address)
}
else
{
word offset = 0;
word chunkSize;
uint32_t offset = 0;
uint32_t chunkSize;
if (address & (mBlockSize - 1))
{
// Address does not start on a block boundary. Adjust the size of

View File

@ -17,9 +17,9 @@
class PromDevice
{
public:
PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling);
bool writeData(byte data[], word len, word address);
byte readData(word address) { return readByte(address); }
PromDevice(uint32_t size, word blockSize, unsigned maxWriteTime, bool polling);
bool writeData(byte data[], uint32_t len, uint32_t address);
byte readData(uint32_t address) { return readByte(address); }
virtual void begin() = 0;
virtual const char * getName() = 0;
@ -27,7 +27,7 @@ class PromDevice
virtual void enableSoftwareWriteProtect() {}
protected:
unsigned int mSize; // Size of the device, in bytes
uint32_t mSize; // Size of the device, in bytes
unsigned int mBlockSize; // Block size for page writes, zero if N/A
unsigned int mMaxWriteTime; // Max time (in ms) to wait for write cycle to complete
bool mSupportsDataPoll; // End of write detected by data polling
@ -36,10 +36,10 @@ class PromDevice
byte readDataBus();
void writeDataBus(byte data);
virtual void setAddress(word address) = 0;
virtual byte readByte(word address) = 0;
virtual bool burnByte(byte value, word address) = 0;
virtual bool burnBlock(byte data[], word len, word address) { return false; }
virtual void setAddress(uint32_t address) = 0;
virtual byte readByte(uint32_t address) = 0;
virtual bool burnByte(byte value, uint32_t address) = 0;
virtual bool burnBlock(byte data[], uint32_t len, uint32_t address) { return false; }
};

142
TommyPROM/PromDevice27.cpp Normal file
View File

@ -0,0 +1,142 @@
// NOTE - The 27 series device support is a work in progress. It
// has not been tested or documented.
#include "Configure.h"
#if defined(PROM_IS_27)
#include "PromAddressDriver.h"
// IO lines for the EPROM device control
// Pins D2..D9 are used for the data bus.
#define WE A0
#define CE A1
#define OE A2
// Set the status of the device control pins
static void enableChip() { digitalWrite(CE, LOW); }
static void disableChip() { digitalWrite(CE, HIGH);}
static void enableOutput() { digitalWrite(OE, LOW); }
static void disableOutput() { digitalWrite(OE, HIGH);}
static void enableWrite() { digitalWrite(WE, LOW); }
static void disableWrite() { digitalWrite(WE, HIGH);}
PromDevice27::PromDevice27(uint32_t size, unsigned long pulseWidthUsec,
unsigned writeAttempts, unsigned overwriteMultiplier)
: PromDevice(size, 0, 0, false),
mPulseWidthUsec(pulseWidthUsec),
mWriteAttempts(writeAttempts),
mOverwriteMultiplier(overwriteMultiplier)
{
}
void PromDevice27::begin()
{
// Define the data bus as input initially so that it does not put out a
// signal that could collide with output on the data pins of the EEPROM.
setDataBusMode(INPUT);
// Define the EEPROM control pins as output, making sure they are all
// in the disabled state.
digitalWrite(OE, HIGH);
pinMode(OE, OUTPUT);
digitalWrite(CE, HIGH);
pinMode(CE, OUTPUT);
digitalWrite(WE, HIGH);
pinMode(WE, OUTPUT);
// This chip uses the shift register hardware for addresses, so initialize that.
PromAddressDriver::begin();
}
// BEGIN PRIVATE METHODS
//
// Use the PromAddressDriver to set a 16 bit address in the two address shift registers.
void PromDevice27::setAddress(uint32_t address)
{
PromAddressDriver::setAddress(address);
}
// Read a byte from a given address
byte PromDevice27::readByte(uint32_t address)
{
byte data = 0;
setAddress(address);
setDataBusMode(INPUT);
disableOutput();
disableWrite();
enableChip();
enableOutput();
data = readDataBus();
disableOutput();
disableChip();
return data;
}
// Burn a byte to the chip and verify that it was written.
bool PromDevice27::burnByte(byte value, uint32_t address)
{
bool status = false;
unsigned writeCount = 0;
disableOutput();
disableWrite();
enableChip();
setAddress(address);
while (!status && (writeCount < mWriteAttempts))
{
setDataBusMode(OUTPUT);
writeDataBus(value);
delayMicroseconds(1);
enableWrite();
myDelay(mPulseWidthUsec);
disableWrite();
++writeCount;
setDataBusMode(INPUT);
enableOutput();
data = readDataBus();
disableOutput();
status = (readDataBus() == value);
}
if (status && (mOverwriteMultiplier > 0))
{
setDataBusMode(OUTPUT);
writeDataBus(value);
delayMicroseconds(1);
enableWrite();
myDelay(mPulseWidthUsec * mOverwriteMultiplier);
disableWrite();
}
disableChip();
return status;
}
void PromDevice27::myDelay(unsigned int us)
{
if (us > 16000)
{
// The delayMicroseconds code can't do delays longer than 16ms, so use the
// ms delay code for larger values. This rounds down to the nearest ms, so
// it is not possible to delay for 40.5 ms, for example.
delay(us / 1000);
}
else
{
delayMicroseconds((unsigned int) us);
}
}
#endif // #if defined(PROM_IS_27)

48
TommyPROM/PromDevice27.h Normal file
View File

@ -0,0 +1,48 @@
// NOTE - The 27 series device support is a work in progress. It
// has not been tested or documented.
#ifndef INCLUDE_PROM_DEVICE_27_H
#define INCLUDE_PROM_DEVICE_27_H
#include "Arduino.h"
#include "PromDevice.h"
/*****************************************************************************/
/*****************************************************************************/
/**
* PromDevice27 class
*
* Provides the device-specific interface to read and write data from a
* 27 or 27C series parallel EPROM using the Arduino. Supported chips
* include 2716, 2732, 2764, 27C040, and 27C2001.
*
* Intelligent programming algorithms, like TurboProgram, are supported.
* These allow a variable number of shorter program pulses until the byte
* verifies. This is optionally followed by an overprogram pulse that is
* a multiple of the number of program pulses that were written.
*
* See the constructor definition for an explanation of the parameters that
* control programming.
*/
class PromDevice27 : public PromDevice
{
public:
PromDevice27(uint32_t size, unsigned long mPulseWidthUsec, unsigned writeAttempts, unsigned overwriteMultiplier);
void begin();
const char * getName() { return "27 series EPROM"; }
protected:
void setAddress(uint32_t address);
byte readByte(uint32_t address);
bool burnByte(byte value, uint32_t address);
void myDelay(unsigned int us);
unsigned long mPulseWidthUsec;
unsigned mWriteAttempts;
unsigned mOverwriteMultiplier;
};
#endif // #define INCLUDE_PROM_DEVICE_27_H

View File

@ -18,7 +18,7 @@ static void enableWrite() { digitalWrite(WE, LOW); }
static void disableWrite() { digitalWrite(WE, HIGH);}
PromDevice28C::PromDevice28C(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling)
PromDevice28C::PromDevice28C(uint32_t size, word blockSize, unsigned maxWriteTime, bool polling)
: PromDevice(size, blockSize, maxWriteTime, polling)
{
}
@ -84,15 +84,15 @@ void PromDevice28C::enableSoftwareWriteProtect()
// BEGIN PRIVATE METHODS
//
// Use the PromAddressDriver to set a 16 bit address in the two address shift registers.
void PromDevice28C::setAddress(word address)
// Use the PromAddressDriver to set an address in the two address shift registers.
void PromDevice28C::setAddress(uint32_t address)
{
PromAddressDriver::setAddress(address);
}
// Read a byte from a given address
byte PromDevice28C::readByte(word address)
byte PromDevice28C::readByte(uint32_t address)
{
byte data = 0;
setAddress(address);
@ -110,7 +110,7 @@ byte PromDevice28C::readByte(word address)
// Burn a byte to the chip and verify that it was written.
bool PromDevice28C::burnByte(byte value, word address)
bool PromDevice28C::burnByte(byte value, uint32_t address)
{
bool status = false;
@ -135,7 +135,7 @@ bool PromDevice28C::burnByte(byte value, word address)
}
bool PromDevice28C::burnBlock(byte data[], word len, word address)
bool PromDevice28C::burnBlock(byte data[], uint32_t len, uint32_t address)
{
bool status = false;
@ -148,7 +148,7 @@ bool PromDevice28C::burnBlock(byte data[], word len, word address)
// Write all of the bytes in the block out to the chip. The chip will
// program them all at once as long as they are written fast enough.
setDataBusMode(OUTPUT);
for (word ix = 0; (ix < len); ix++)
for (uint32_t ix = 0; (ix < len); ix++)
{
setAddress(address + ix);
writeDataBus(data[ix]);
@ -212,7 +212,7 @@ bool PromDevice28C::waitForWriteCycleEnd(byte lastValue)
// to write control sequences, like the software write protect. This is not a
// complete byte write function because it does not set the chip enable or the
// mode of the data bus.
void PromDevice28C::setByte(byte value, word address)
void PromDevice28C::setByte(byte value, uint32_t address)
{
setAddress(address);
writeDataBus(value);

View File

@ -18,19 +18,19 @@
class PromDevice28C : public PromDevice
{
public:
PromDevice28C(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling);
PromDevice28C(uint32_t size, word blockSize, unsigned maxWriteTime, bool polling);
void begin();
const char * getName() { return "28C series EEPROM"; }
void disableSoftwareWriteProtect();
void enableSoftwareWriteProtect();
protected:
void setAddress(word address);
byte readByte(word address);
bool burnByte(byte value, word address);
bool burnBlock(byte data[], word len, word address);
void setAddress(uint32_t address);
byte readByte(uint32_t address);
bool burnByte(byte value, uint32_t address);
bool burnBlock(byte data[], uint32_t len, uint32_t address);
bool waitForWriteCycleEnd(byte lastValue);
void setByte(byte value, word address);
void setByte(byte value, uint32_t address);
};
#endif // #define INCLUDE_PROM_DEVICE_28C_H

View File

@ -12,7 +12,7 @@
#define VDDCTL 12
PromDevice8755A::PromDevice8755A(unsigned long size)
PromDevice8755A::PromDevice8755A(uint32_t size)
: PromDevice(size, 0, 0, false)
{
}
@ -52,7 +52,7 @@ void PromDevice8755A::begin()
// Set an 11 bit address using the 8 address/data bus lines and three more dedicated
// address lines. The read and burn code will take care of the ALE line
void PromDevice8755A::setAddress(word address)
void PromDevice8755A::setAddress(uint32_t address)
{
setDataBusMode(OUTPUT);
writeDataBus(byte(address & 0xff));
@ -63,7 +63,7 @@ void PromDevice8755A::setAddress(word address)
// Read a byte from a given address
byte PromDevice8755A::readByte(word address)
byte PromDevice8755A::readByte(uint32_t address)
{
byte data = 0;
digitalWrite(RD, HIGH);
@ -72,8 +72,8 @@ byte PromDevice8755A::readByte(word address)
// Put the address on the bus and latch it with ALE
digitalWrite(CE2, HIGH);
setAddress(address);
digitalWrite(ALE, HIGH);
digitalWrite(ALE, LOW);
digitalWrite(ALE, HIGH);
digitalWrite(ALE, LOW);
// Read a byte
setDataBusMode(INPUT);
@ -88,7 +88,7 @@ byte PromDevice8755A::readByte(word address)
// Burn a byte to the chip and verify that it was written.
bool PromDevice8755A::burnByte(byte value, word address)
bool PromDevice8755A::burnByte(byte value, uint32_t address)
{
// Latch the address and the CE lines
digitalWrite(ALE, HIGH);

View File

@ -18,16 +18,15 @@
class PromDevice8755A : public PromDevice
{
public:
PromDevice8755A(unsigned long size);
PromDevice8755A(uint32_t size);
void begin();
const char * getName() { return "Intel 8755A EPROM"; }
protected:
void setAddress(word address);
byte readByte(word address);
bool burnByte(byte value, word address);
void setAddress(uint32_t address);
byte readByte(uint32_t address);
bool burnByte(byte value, uint32_t address);
};
#endif // #define INCLUDE_PROM_DEVICE_8755A_H

View File

@ -1,13 +1,13 @@
/**
* Read and write parallel EEPROMS with an interctive command-line interface.
* Modules are available for ATMEL 28C series EEPROMs and Intel 8755A EPROMS.
* Modules are available for ATMEL 28C series EEPROMs and Intel 8755A EPROMS.
* Many other parallel EPROM/EEPROMs can be read, but not written, using the
* 28C code.
*
* The 28C module supports block writes for better performance and
* Software Data Protection (SDP) unlocking.
* Software Data Protection (SDP) unlocking.
*
* ROM images are moved to and from a host computer using XMODEM. This is
* ROM images are moved to and from a host computer using XMODEM. This is
* available in a number of terminal programs, such as TeraTerm and Minicom.
*
* The default hardware uses two 74LS164 shift registers as the low and
@ -19,7 +19,7 @@
#include "XModem.h"
static const char * MY_VERSION = "1.8";
static const char * MY_VERSION = "2.0";
// Global status
@ -42,14 +42,13 @@ PromDevice28C prom(32 * 1024L, 64, 10, true);
// 1000us (1ms) write pulse
// 15 write attempts
// 4x overwrite pulse
PromDevice27 prom(8 * 1024L, 1000L, 15, 4); // 2764 with SEEQ intelligent programming
PromDevice27 prom(8 * 1024L, 1000L, 15, 4); // 2764 with SEEQ intelligent programming
//PromDevice27 prom(32 * 1024L, 1000L, 25, 3); // 27C256 with SEEQ intelligent programming
//PromDevice27 prom(2 * 1024L, 50000L, 1, 0); // 2716 with single 50ms write
//PromDevice27 prom(64 * 1024L, 100L, 11, 0); // 27C040 with Atmel rapid programming
//PromDevice27 prom(2 * 1024L, 50000L, 1, 0); // 2716 with single 50ms write
//PromDevice27 prom(64 * 1024L, 100L, 11, 0); // 27C040 with Atmel rapid programming
#elif defined(PROM_IS_8755A)
// Define a device for an 8755A. This has a fixed size of 2K and no
// other parameters.
// Define a device for an Intel 8755A with a fixed size of 2K and no other parameters.
PromDevice8755A prom(2 * 1024L);
// Additional device-specific code goes here...
@ -59,6 +58,7 @@ PromDevice8755A prom(2 * 1024L);
#error "Must define a PROM type in Configure.h"
#endif
// Global XModem driver
XModem xmodem(prom, cmdStatus);
@ -151,6 +151,8 @@ byte parseCommand(char c)
}
/************************************************************
* convert a single hex character [0-9a-fA-F] to its value
* @param char c single character (digit)
@ -176,29 +178,38 @@ byte hexDigit(char c)
}
}
/************************************************************
* convert a hex byte (00 - ff) to byte
* @param c-string with the hex value of the byte
* @return byte represented by the digits
************************************************************/
byte hexByte(char * a)
{
return (hexDigit(a[0]) << 4) | hexDigit(a[1]);
}
/************************************************************
* convert a hex word (0000 - ffff) to unsigned int
* @param c-string with the hex value of the word
* Convert a hex string to a uint32_t value.
* Skips leading spaces and terminates on the first non-hex
* character. Leading zeroes are not required.
*
* No error checking is performed - if no hex is found then
* zero is returned. Similarly, a hex string of more than
* 8 digits will return the value of the last 8 digits.
* @param pointer to string with the hex value of the word (modified)
* @return unsigned int represented by the digits
************************************************************/
unsigned int hexWord(char * data)
uint32_t getHex32(char *& pData, uint32_t defaultValue=0)
{
return (hexDigit(data[0]) << 12) |
(hexDigit(data[1]) << 8) |
(hexDigit(data[2]) << 4) |
(hexDigit(data[3]));
uint32_t u32 = 0;
while (isspace(*pData))
{
++pData;
}
if (isxdigit(*pData))
{
while (isxdigit(*pData)) {
u32 = (u32 << 4) | hexDigit(*pData++);
}
}
else
{
u32 = defaultValue;
}
return u32;
}
@ -228,6 +239,26 @@ void printWord(word w)
}
/*
* Prints a 32 bit value as a hex.
*
* Note that no values over 5 digits are used in
* this appication, so only 5 digits are printed.*/
void printHex32(uint32_t u32)
{
char line[6];
line[0] = hex[(u32 >> 16) & 0x0f];
line[1] = hex[(u32 >> 12) & 0x0f];
line[2] = hex[(u32 >> 8) & 0x0f];
line[3] = hex[(u32 >> 4) & 0x0f];
line[4] = hex[(u32) & 0x0f];
line[5] = '\0';
Serial.print(line);
}
// If the user presses a key then pause until they press another. Return true if
// Ctrl-C is pressed.
bool checkForBreak()
@ -239,7 +270,7 @@ bool checkForBreak()
return true;
}
while (!Serial.available())
{;}
{}
if (Serial.read() == 0x03)
{
return true;
@ -266,23 +297,15 @@ bool checkForBreak()
* address if an odd number of bytes is specified by start and
* end.
*/
word checksumBlock(word start, word end)
word checksumBlock(uint32_t start, uint32_t end)
{
word checksum = 0;
for (word addr = start; (addr <= end); addr += 2)
for (uint32_t addr = start; (addr <= end); addr += 2)
{
word w = prom.readData(addr);
w <<= 8;
w |= prom.readData(addr + 1);
checksum += w;
if (addr >= 0xfffe)
{
// This is a really kludgy check to make sure the counter doesn't wrap
// around to zero. Could replace addr and end with longs to fix this,
// but that might not be any faster.
break;
}
}
return checksum;
@ -292,24 +315,25 @@ word checksumBlock(word start, word end)
/**
* Read data from the device and dump it in hex and ascii.
**/
void dumpBlock(word start, word end)
void dumpBlock(uint32_t start, uint32_t end)
{
char line[81];
// 01234567891 234567892 234567893 234567894 234567895 234567896 234567897 23456789
// 1234: 01 23 45 67 89 ab cf ef 01 23 45 67 89 ab cd ef 1.2.3.4. 5.6.7.8.
// 01234: 01 23 45 67 89 ab cf ef 01 23 45 67 89 ab cd ef 1.2.3.4. 5.6.7.8.
int count = 0;
memset(line, ' ', sizeof(line));
char * pHex = line;
char * pChar = line + 58;
for (word addr = start; (addr <= end); addr++)
for (uint32_t addr = start; (addr <= end); addr++)
{
if (count == 0)
{
//print out the address at the beginning of the line
pHex = line;
pChar = line + 58;
*pHex++ = hex[(addr >> 16) & 0x0f];
*pHex++ = hex[(addr >> 12) & 0x0f];
*pHex++ = hex[(addr >> 8) & 0x0f];
*pHex++ = hex[(addr >> 4) & 0x0f];
@ -359,7 +383,7 @@ void dumpBlock(word start, word end)
* @param end - end address
* @param val - data byte to write to all addresses
*/
void fillBlock(word start, word end, byte val)
void fillBlock(uint32_t start, uint32_t end, byte val)
{
enum { BLOCK_SIZE = 32 };
byte block[BLOCK_SIZE];
@ -369,9 +393,9 @@ void fillBlock(word start, word end, byte val)
block[ix] = val;
}
for (word addr = start; (addr <= end); addr += BLOCK_SIZE)
for (uint32_t addr = start; (addr <= end); addr += BLOCK_SIZE)
{
unsigned writeLen = ((end - addr + 1) < BLOCK_SIZE) ? (end - addr + 1) : BLOCK_SIZE;
uint32_t writeLen = ((end - addr + 1) < BLOCK_SIZE) ? (end - addr + 1) : BLOCK_SIZE;
if (!prom.writeData(block, writeLen, addr))
{
cmdStatus.error("Write failed");
@ -387,9 +411,9 @@ void fillBlock(word start, word end, byte val)
* @param start - start address
* @param end - end address
*/
void erasedBlockCheck(word start, word end)
void erasedBlockCheck(uint32_t start, uint32_t end)
{
for (word addr = start; (addr <= end); addr ++)
for (uint32_t addr = start; (addr <= end); addr ++)
{
byte val = prom.readData(addr);
if (val != 0xff)
@ -413,11 +437,11 @@ void erasedBlockCheck(word start, word end)
* @param start - start address
* @param end - end address
*/
void scanBlock(word start, word end)
void scanBlock(uint32_t start, uint32_t end)
{
enum { SCAN_TESTS = 10 };
for (word addr = start; (addr <= end); addr++)
for (uint32_t addr = start; (addr <= end); addr++)
{
byte values[SCAN_TESTS];
values[0] = prom.readData(addr);
@ -432,7 +456,7 @@ void scanBlock(word start, word end)
}
if (fail)
{
printWord(addr);
printHex32(addr);
Serial.print(": ");
for (int ix = 0; (ix < SCAN_TESTS); ix++)
{
@ -456,7 +480,7 @@ void scanBlock(word start, word end)
*
* @param addr - address to test
*/
void testAddr(word addr)
void testAddr(uint32_t addr)
{
enum { NUM_TESTS = 100 };
@ -493,7 +517,7 @@ void testAddr(word addr)
*
* @param start - start address
*/
void zapTest(word start)
void zapTest(uint32_t start)
{
byte testData[] =
{
@ -531,7 +555,7 @@ void zapTest(word start)
/************************************************
* MAIN
*************************************************/
word addr = 0;
uint32_t addr = 0;
void setup()
{
@ -568,13 +592,13 @@ byte charTest[] =
};
*/
word start = 0;
word end = 0xff;
uint32_t start = 0;
uint32_t end = 0xff;
byte val = 0xff;
void loop()
{
word w;
uint32_t w;
char line[20];
uint32_t numBytes;
@ -583,12 +607,10 @@ void loop()
readLine(line, sizeof(line));
Serial.println();
byte cmd = parseCommand(line[0]);
if (hexDigit(line[1]) <= 15)
start = hexWord(line + 1);
if (hexDigit(line[6]) <= 15)
end = hexWord(line + 6);
if (hexDigit(line[6]) <= 11)
val = hexByte(line + 11);
char * pCursor = line+1;
start = getHex32(pCursor, 0);
end = getHex32(pCursor, 0xff);
val = (byte) getHex32(pCursor, 0);
if ((cmd != CMD_LAST_STATUS) && (cmd != CMD_INVALID))
{
@ -599,11 +621,11 @@ void loop()
{
case CMD_CHECKSUM:
w = checksumBlock(start, end);
Serial.print("Checksum ");
Serial.print(F("Checksum "));
printWord(start);
Serial.print("-");
Serial.print(F("-"));
printWord(end);
Serial.print(" = ");
Serial.print(F(" = "));
printWord(w);
Serial.println();
break;
@ -678,19 +700,19 @@ void loop()
Serial.println(prom.getName());
Serial.println();
Serial.println(F("Valid commands are:"));
Serial.println(F(" Cssss eeee - Compute checksum from device"));
Serial.println(F(" Dssss eeee - Dump bytes from device to terminal"));
Serial.println(F(" Essss eeee - Check to see if device range is Erased (all FF)"));
Serial.println(F(" Fssss eeee dd - Fill block on device with fixed value"));
Serial.println(F(" L - Lock (enable) device Software Data Protection"));
Serial.println(F(" Rssss eeee - Read from device and save to XMODEM CRC file"));
Serial.println(F(" U - Unlock (disable) device Software Data Protection"));
Serial.println(F(" Wssss - Write to device from XMODEM CRC file"));
Serial.println(F(" Csssss eeeee - Compute checksum from device"));
Serial.println(F(" Dsssss eeeee - Dump bytes from device to terminal"));
Serial.println(F(" Esssss eeeee - Check to see if device range is Erased (all FF)"));
Serial.println(F(" Fsssss eeeee dd - Fill block on device with fixed value"));
Serial.println(F(" L - Lock (enable) device Software Data Protection"));
Serial.println(F(" Rsssss eeeee - Read from device and save to XMODEM CRC file"));
Serial.println(F(" U - Unlock (disable) device Software Data Protection"));
Serial.println(F(" Wsssss - Write to device from XMODEM CRC file"));
#ifdef ENABLE_DEBUG_COMMANDS
Serial.println();
Serial.println(F(" Sssss eeee - Scan addresses (read each 10x)"));
Serial.println(F(" Tssss - Test read address (read 100x)"));
Serial.println(F(" Zssss - Zap (burn) a 32 byte test pattern"));
Serial.println(F(" Ssssss eeeee - Scan addresses (read each 10x)"));
Serial.println(F(" Tsssss - Test read address (read 100x)"));
Serial.println(F(" Zsssss - Zap (burn) a 32 byte test pattern"));
#endif /* ENABLE_DEBUG_COMMANDS */
break;
}
@ -701,4 +723,3 @@ void loop()
cmdStatus.printStatus();
}
}

View File

@ -1,7 +1,7 @@
#include "XModem.h"
#include "CmdStatus.h"
uint32_t XModem::ReceiveFile(uint16_t address)
uint32_t XModem::ReceiveFile(uint32_t address)
{
uint8_t buffer[PKTLEN];
int c;
@ -71,7 +71,7 @@ uint32_t XModem::ReceiveFile(uint16_t address)
// within one second then the transfer will fail. Unlike in the dial-up
// days of old, this is designed to be run on a 3 foot cable betwee two fast
// hosts, so communication errors or timeouts are extremely unlikely.
bool XModem::SendFile(uint16_t address, uint32_t fileSize)
bool XModem::SendFile(uint32_t address, uint32_t fileSize)
{
uint8_t seq = 1;
int rxChar = -1;
@ -175,7 +175,7 @@ bool XModem::StartReceive()
}
bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint16_t destAddr)
bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr)
{
int c;
uint8_t rxSeq1, rxSeq2;
@ -218,16 +218,12 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
else
{
// The data is good. Process the packet then ACK it to the sender.
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
if (!promDevice.writeData(buffer, bufferSize, destAddr))
{
cmdStatus.error("Write failed");
cmdStatus.setValueHex(0, "address", destAddr);
return false;
}
digitalWrite(13, LOW);
Serial.write(XMDM_ACK);
}
@ -235,7 +231,7 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
}
void XModem::SendPacket(uint16_t address, uint8_t seq)
void XModem::SendPacket(uint32_t address, uint8_t seq)
{
uint16_t crc = 0;

View File

@ -22,8 +22,8 @@ class XModem
{
public:
XModem(PromDevice & pd, CmdStatus & cs) : promDevice(pd), cmdStatus(cs) {}
uint32_t ReceiveFile(uint16_t address);
bool SendFile(uint16_t address, uint32_t fileSize);
uint32_t ReceiveFile(uint32_t address);
bool SendFile(uint32_t address, uint32_t fileSize);
void Cancel();
private:
@ -50,8 +50,8 @@ class XModem
int GetChar(int msWaitTime = 3000);
uint16_t UpdateCrc(uint16_t crc, uint8_t data);
bool StartReceive();
bool ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint16_t destAddr);
void SendPacket(uint16_t address, uint8_t seq);
bool ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr);
void SendPacket(uint32_t address, uint8_t seq);
};
#endif // #define INCLUDE_CONFIGURE_H

View File

@ -7,14 +7,13 @@ The basic circuit is as follows:
* Pins D2..D9 are wired to the data lines on the target PROM.
* Pins A0..A2 are wired to the WE, CE, and OE control lines on the target PROM.
* Pins A3..A5 control shift registers to produce the address lines.
* Pins D10..D12 control A16..A18 for chips larger than 64K bytes.
Note that the existing design uses 74LS164 shift registers, but another 8-bit parallel out
shift register, like the 74LS594 or 74LS595, could be used instead with some pin changes.
The two shift registers can produce a sixteen bit address, although the 28C256 only needs 15 addresses.
Chips larger than 64K can be supported by manually tying the additional lines high or low and working
with 64K blocks at a time. Unused pins on the Arduino could also be directly tied to additional
address lines to do bank selecting.
Chips larger than 64K are supported by using the shift registers for A0..A15 and connecting Arduino pins D10..D12 to the chip's A16..A18
![TommyPROM Nano Schematic](../docs/TommyPROM-nano-sch.png)