forked from Apple-2-HW/TommyPROM
212 lines
5.1 KiB
C++
212 lines
5.1 KiB
C++
#include "Configure.h"
|
|
|
|
#include "PromAddressDriver.h"
|
|
|
|
|
|
|
|
PromDevice28C::PromDevice28C(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling)
|
|
: PromDevice(size, blockSize, maxWriteTime, polling)
|
|
{
|
|
}
|
|
|
|
void PromDevice28C::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();
|
|
}
|
|
|
|
|
|
// Write the special six-byte code to turn off Software Data Protection.
|
|
void PromDevice28C::disableSoftwareWriteProtect()
|
|
{
|
|
disableOutput();
|
|
disableWrite();
|
|
setDataBusMode(OUTPUT);
|
|
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0x80, 0x5555);
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0x20, 0x5555);
|
|
|
|
setDataBusMode(INPUT);
|
|
disableChip();
|
|
}
|
|
|
|
|
|
// Write the special three-byte code to turn on Software Data Protection.
|
|
void PromDevice28C::enableSoftwareWriteProtect()
|
|
{
|
|
disableOutput();
|
|
disableWrite();
|
|
setDataBusMode(OUTPUT);
|
|
|
|
setByte(0xaa, 0x5555);
|
|
setByte(0x55, 0x2aaa);
|
|
setByte(0xa0, 0x5555);
|
|
|
|
setDataBusMode(INPUT);
|
|
disableChip();
|
|
}
|
|
|
|
|
|
// BEGIN PRIVATE METHODS
|
|
//
|
|
|
|
// Use the PromAddressDriver to set a 16 bit address in the two address shift registers.
|
|
void PromDevice28C::setAddress(word address)
|
|
{
|
|
PromAddressDriver::setAddress(address);
|
|
}
|
|
|
|
|
|
// Read a byte from a given address
|
|
byte PromDevice28C::readByte(word 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 PromDevice28C::burnByte(byte value, word address)
|
|
{
|
|
bool status = false;
|
|
|
|
disableOutput();
|
|
disableWrite();
|
|
|
|
setAddress(address);
|
|
setDataBusMode(OUTPUT);
|
|
writeDataBus(value);
|
|
|
|
enableChip();
|
|
delayMicroseconds(1);
|
|
enableWrite();
|
|
delayMicroseconds(1);
|
|
disableWrite();
|
|
|
|
status = waitForWriteCycleEnd(value);
|
|
|
|
disableChip();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
bool PromDevice28C::burnBlock(byte data[], word len, word address)
|
|
{
|
|
bool status = false;
|
|
|
|
if (len == 0) return true;
|
|
|
|
disableOutput();
|
|
disableWrite();
|
|
enableChip();
|
|
|
|
// 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++)
|
|
{
|
|
setAddress(address + ix);
|
|
writeDataBus(data[ix]);
|
|
|
|
delayMicroseconds(1);
|
|
enableWrite();
|
|
delayMicroseconds(1);
|
|
disableWrite();
|
|
}
|
|
|
|
status = waitForWriteCycleEnd(data[len - 1]);
|
|
disableChip();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
bool PromDevice28C::waitForWriteCycleEnd(byte lastValue)
|
|
{
|
|
if (mSupportsDataPoll)
|
|
{
|
|
// Verify programming complete by reading the last value back until it matches the
|
|
// value written twice in a row. The D7 bit will read the inverse of last written
|
|
// data and the D6 bit will toggle on each read while in programming mode.
|
|
//
|
|
// Note that the max readcount is set to the device's maxReadTime (in uSecs)
|
|
// divided by two because there are two 1 uSec delays in the loop. In reality,
|
|
// the loop could run for longer because this does not account for the time needed
|
|
// to run all of the loop code. In actual practice, the loop will terminate much
|
|
// earlier because it will detect the end of the write well before the max time.
|
|
setDataBusMode(INPUT);
|
|
delayMicroseconds(1);
|
|
for (int readCount = mMaxWriteTime * 1000 / 2; (readCount > 0); readCount--)
|
|
{
|
|
enableOutput();
|
|
delayMicroseconds(1);
|
|
byte b1 = readDataBus();
|
|
disableOutput();
|
|
enableOutput();
|
|
delayMicroseconds(1);
|
|
byte b2 = readDataBus();
|
|
disableOutput();
|
|
if ((b1 == b2) && (b1 == lastValue))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// No way to detect success. Just wait the max write time.
|
|
delayMicroseconds(mMaxWriteTime * 1000L);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
// Set an address and data value and toggle the write control. This is used
|
|
// 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)
|
|
{
|
|
setAddress(address);
|
|
writeDataBus(value);
|
|
|
|
delayMicroseconds(1);
|
|
enableChip();
|
|
enableWrite();
|
|
delayMicroseconds(1);
|
|
disableWrite();
|
|
disableChip();
|
|
}
|
|
|
|
|