mirror of
https://github.com/TomNisbet/TommyPROM.git
synced 2024-10-05 11:56:08 +00:00
123 lines
3.9 KiB
C++
123 lines
3.9 KiB
C++
// This controls the shift register that generates the address lines for A0..A15 for most
|
|
// chip families. This is not used by the PromDevice8755A code.
|
|
//
|
|
// Note that this uses direct port control instead of digitalWrite calls so that the code
|
|
// can run fast enough to meet the tBLC requirements for SDP and block writes. This
|
|
// sacrifices portability and readability for speed. //
|
|
//
|
|
// This code will only work on Arduino Uno and Nano hardware. The ports for other
|
|
// Arduinos map to different IO pins.
|
|
|
|
#include "PromAddressDriver.h"
|
|
|
|
#define ADDR_CLK_HI A3
|
|
#define ADDR_CLK_LO A4
|
|
#define ADDR_DATA A5
|
|
|
|
// Define masks for the address clk and data lines on PC3..PC5 for direct port control.
|
|
#define ADDR_CLK_HI_MASK 0x08
|
|
#define ADDR_CLK_LO_MASK 0x10
|
|
#define ADDR_DATA_MASK 0x20
|
|
|
|
// For larger ROMs, address lines A16..A18 are controlled by D10..D12 (PB2..PB4).
|
|
#define UPPER_ADDR_MASK 0x1c
|
|
|
|
// When using the 74LS595 shift registers, the RCLK lines of both shift registers can be
|
|
// connected to D13 (PB5). Uncomment the #define SHIFT_REGISTER_IS_595 in Configure.h to
|
|
// enable the code for this.
|
|
#define RCLK_595_MASK 0x20
|
|
|
|
|
|
void PromAddressDriver::begin()
|
|
{
|
|
// The address control pins are always outputs.
|
|
pinMode(ADDR_DATA, OUTPUT);
|
|
pinMode(ADDR_CLK_LO, OUTPUT);
|
|
pinMode(ADDR_CLK_HI, OUTPUT);
|
|
digitalWrite(ADDR_DATA, LOW);
|
|
digitalWrite(ADDR_CLK_LO, LOW);
|
|
digitalWrite(ADDR_CLK_HI, LOW);
|
|
DDRB |= UPPER_ADDR_MASK | RCLK_595_MASK; // Set D10..D13 as outputs
|
|
|
|
// To save time, the setAddress only writes the hi byte if it has changed.
|
|
// The value used to detect the change is initialized to a non-zero value,
|
|
// so set an initial address to avoid the the case where the first address
|
|
// written is the 'magic' initial address.
|
|
setAddress(0x0000);
|
|
}
|
|
|
|
|
|
// 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;
|
|
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);
|
|
lastHi = hi;
|
|
}
|
|
setAddressRegister(ADDR_CLK_LO, lo);
|
|
}
|
|
|
|
|
|
void PromAddressDriver::setUpperAddress(byte addr)
|
|
{
|
|
// Set the upper address on pins D10..D12.
|
|
PORTB = (PORTB & ~UPPER_ADDR_MASK) | ((addr << 2) & UPPER_ADDR_MASK);
|
|
}
|
|
|
|
|
|
// 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.
|
|
void PromAddressDriver::setAddressRegister(uint8_t clkPin, byte addr)
|
|
{
|
|
byte mask = 0;
|
|
if (clkPin == ADDR_CLK_HI)
|
|
mask = ADDR_CLK_HI_MASK;
|
|
else if (clkPin == ADDR_CLK_LO)
|
|
mask = ADDR_CLK_LO_MASK;
|
|
|
|
// Make sure the clock is low to start.
|
|
PORTC &= ~mask;
|
|
|
|
// Shift 8 bits in, starting with the MSB.
|
|
for (int ix = 0; (ix < 8); ix++)
|
|
{
|
|
// Set the data bit
|
|
if (addr & 0x80)
|
|
{
|
|
PORTC |= ADDR_DATA_MASK;
|
|
}
|
|
else
|
|
{
|
|
PORTC &= ~ADDR_DATA_MASK;
|
|
}
|
|
|
|
// Toggle the clock high then low
|
|
PORTC |= mask;
|
|
delayMicroseconds(3);
|
|
PORTC &= ~mask;
|
|
addr <<= 1;
|
|
}
|
|
|
|
// Toggle the RCLK pin to output the data for 74LS595 shift registers. This pin is
|
|
// not connected when using 74LS164 shift registers.
|
|
PORTB &= ~RCLK_595_MASK;
|
|
delayMicroseconds(1);
|
|
PORTB |= RCLK_595_MASK;
|
|
delayMicroseconds(1);
|
|
PORTB &= ~RCLK_595_MASK;
|
|
}
|