You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

851 lines
39 KiB

// Protospace is running code version PinBallMemoryPort20230201
// The next version of code starts dated 2023 02 05
//
// 2023-02-19 Tim Gopaul, gameSave and gameLoad added to access game Ram.. only to be used when Pinball machine is powered off
// 2023-02-18 Tim Gopaul, trouble getting PCINT30 working. changed to interrup Pin = PIN_PD2 which gives digitalPinToInterrupt(interruptPin) as 0
// 2023-02-14 Tim Gopaul, attach an interrupt low edge to pin 20 PD6 PCINT30
// connect interrupt to _BusyRight to indicate the Pinball live memory was issued a wait.. which likey corrupted game ram
// What to do. ..maybe save high score to portal then restart the pinball machine or issue a warning to the LCD screen
//
// 2023-02-05 Tim Gopaul,, look into moving control pins to PC done
//
//
// 2023-01-29 Tim Gopaul troubleshoot bad first byte read
// 2023-01-25 Tim Gopaul, added a second CE signal to control two dual port RAMs
//
// If the Atmega is reading or writing an address that is requested by the Pinball 6800 the BusyR line will go low for the pinball machine
// if the Pinball 6800 is reading.. no problem.. it will get the byte requested
// if the Pinball 6800 is writing to the address in conflict the write will not be ignored
// currently there is no mechanism to avoid this failed write to RAM as the BusyR has no connection to the Pinball 6800
// If the write by the Pinball 6800 is a push of return address to the stack register then a crash is eminent
//
// To avoid a crash do not read or write to sensitive areas in the RAM. Stack area is a huge risk.
// There is a potential to miss a score update if a write to the monitored score location is blocked
//
// If the Pinball 6800 is busy using an address the BusyL signal is pulled low to case the Atmel to wait on the RAM read or write.
// as of 2023-01-21 this code below will let the Atmel complete the IO with only a small delay.
// BUSY_ is IDC7132 pin46 that is monitored by the Atmel PIN_PD7 physical pin 21
//
// 2023-01-21 working on while delay when busy signal is low during Atmel reads or writes
// 2023-01-18 Toighten up the memory read and write looks use define for macro to write port bits
// 2023-01-11 save and load testing to Dual Port RAM
// 2022-12-24 Look at parser to accept Hex input, Tim Gopaul
// 2022-10-18 Test program for Dual port RAM 7132
// Tim Gopaul
//
// Changelog
// 2022-11-05 Saturday - Check to see why preview text is not availalble from Uart 1, user \r\n at end of line
// 2022-10-18 Move serial IO all to main serial channel.
// Add define for debug mode build
//
// PORTA output Low byte of Address
// PORTC output High byte of Address althouth only bits 0,1,2 are used on for 11 bit address.
// PORTD bits 4, 5, and 6 are used as outputs, bit 7 is used as input for RAM busy wait signal
// PORTB alternates between input and output for use by data read and write.
// Serial is connected to USB and used for programming and diagnstics
// Serial1 is the communication channel to the Atmel284 to a ESP 32 for wifi
// Serial uses PIN_PD0 uart0 recieve and PIN_PD1 uart0 send
// Serial1 uses PIN_PD2 uart1 recieve and and PIN_PD3 uart1 send
// ...first implementation will use Serial for both programming and commands with Serial1 diagnostics
// Intel HEX read and write functions derived from Paul Stoffregen code
// https://www.pjrc.com/tech/8051/ihex.c
/* Intel HEX read/write functions, Paul Stoffregen, paul@ece.orst.edu */
/* This code is in the public domain. Please retain my name and */
/* email address in distributed copies, and let me know about any bugs */
//#define _DEBUG_
#define MAXHEXLINE 16 // for Hex record length
const int ramSize = 2048; // don't change this without also defining address bits PORTC has limited bits available
#define CommandMode 1 // inputMode will flip between command and data entry, commands defined in CommandLine.h file
#define DataMode 2 // 2023-01-09 Tim Gopaul.. in DataMode the UART stream is read as an Intel HEX file
#define gameDataMode 3 // 2023-01-09 Tim Gopaul.. in DataMode the UART stream is read as an Intel HEX file
int inputMode = CommandMode; // on startup the input is waiting for commands
// PORTB alternates between input and output for use by data read and write.
#define DDRB_Output DDRB = B11111111 // all 1's is output for Atmega1284 PortB to write to IDC-7132 RAM
#define DDRB_Input DDRB = B00000000 // set Atmega1284 Port B back to high inpeadence input all 0's
// 2023-02-06 try moving control lines to PORTC Tim Gopaul
// With control pins moved to Port C the Port D is left for Serial and other un assigned pin functions.
#define CEL2_LOW PORTC &=B01111111 // ChipEnable Left LOW PORTC PIN_PC7
#define CEL2_HIGH PORTC |=B10000000 // ChipEnable Left HIGH PORTC PIN_PC7
#define CEL_LOW PORTC &=B10111111 // ChipEnable Left LOW PORTC PIN_PC6
#define CEL_HIGH PORTC |=B01000000 // ChipEnable Left HIGH PORTC PIN_PC6
#define RWL_LOW PORTC &=B11011111 // R/W Left LOW PORTD PIN_PC5
#define RWL_HIGH PORTC |=B00100000 // R/W Left HIGH PORTD PIN_PC5
#define OEL_LOW PORTC &=B11101111 // OEL LEFT LOW PORTC PIN_PC4
#define OEL_HIGH PORTC |=B00010000 // OEL LEFT HIGH PORTC PIN_PC4
#define CEL_OEL_LOW PORTC&=B10101111 // ChipEnable with OutputEnable LOW PORTC PIN_PC6 PIN_PC4
#define CEL_OEL_HIGH PORTC|=B01010000 // ChipEnable with OutputEnable HIGH PORTC PIN_PC6 PIN_PC4
#define CEL2_OEL_LOW PORTC &=B01101111 // ChipEnable with OutputEnable LOW PORTD PIN_PC7 PIN_PC4
#define CEL2_OEL_HIGH PORTC |=B10010000 // ChipEnable with OutputEnable HIGH PORTDPIN_PC7 PIN_PC4
const byte BUSY_ = PIN_PD7; // BUSY# input pull up This is for the Atmega side Busy signals
const byte BUSY_IRQPIN = PIN_PD2; //BUSY_FAULT will go low if the live game ram receives a Busy signal
volatile byte BusyStateIRQ = HIGH;
volatile unsigned int BusyFaultAddress;
volatile unsigned int BusyFaultCount = 0; // count the number of busy faults and report when given the BusyFaultCount command
const byte SHADOW_IRQPIN = PIN_PD3; //BUSY_FAULT will go low if the live game ram receives a Busy signal
volatile byte ShadowStateIRQ = HIGH;
volatile unsigned int ShadowFaultAddress;
volatile unsigned int ShadowFaultCount = 0; // count the number of busy faults and report when given the BusyFaultCount command
volatile byte ramBuffer[ramSize]; // This is an array to hold the contents of memory
// Is there enough RAM to hold this on an ATMEGA1284? yes16KBytes
// The ATmega1284 provides the following features:
// 128Kbytes of In-System Programmable Flash with
// Read-While-Write capabilities, 4Kbytes EEPROM, 16Kbytes SRAM,
// 32 general purpose I/O lines, 32 general purpose working registers,
// Real Time Counter (RTC), three flexible Timer/Counters with compare
// modes and PWM, two serial ...
// ****** ramBufferInit *****
void ramBufferInit(){
for (int address = 0; address < ramSize; address++) {
ramBuffer[address] = 0;
}
}
// ****** helpText *****
int helpText(){
Serial.println(">****************************************");
Serial.println(">* *");
Serial.printf(">* Compile Date: %s\n", __DATE__ );
Serial.printf(">* Compile Time: %s\n", __TIME__ );
Serial.println(">* *");
Serial.println(">* Utility to read Pinball RAM *");
Serial.println(">* Tim Gopaul for Protospace *");
Serial.println(">* *");
Serial.println(">* Enter numbers as baseTen,or *");
Serial.println(">* Enter as 0xNN for hex format *");
Serial.println(">* *");
Serial.println(">* add integer integer *");
Serial.println(">* sub integer integer *");
Serial.println(">* read address *");
Serial.println(">* write address databyte *");
Serial.println(">* dump start count *");
Serial.println(">* dumpBuffer start count *");
Serial.println(">* fill start count databyte *");
Serial.println(">* fillRandom start count *");
Serial.println(">* save startAddress count *");
Serial.println(">* load Intelhex record line *");
Serial.println(">* testMemory start count *");
Serial.println(">* *");
Serial.println(">* game commands work directly *");
Serial.println(">* Game RAM. *");
Serial.println(">* Use only when pinball off *");
Serial.println(">* *");
Serial.println(">* Use game commands for direct access*");
Serial.println(">* to the live game Ram when Pinball *");
Serial.println(">* is powered off. *");
Serial.println(">* gameRead address *");
Serial.println(">* gameWrite address databyte *");
Serial.println(">* gameDump start count *");
Serial.println(">* gameSave startAddress count *");
Serial.println(">* gameLoad Intelhex record line *");
Serial.println(">* *");
Serial.println(">* *");
Serial.println(">* Enter numbers as decimal or *");
Serial.println(">* 0xNN 0X55 for HEX *");
Serial.println(">* *");
Serial.println(">* BusyFaultCount will give the *");
Serial.println(">* count of Busy Interruptes *");
Serial.println(">* PIN_PD2 IRQ 0 *");
Serial.println(">* *");
Serial.println(">* ShadowFaultCount gives the *");
Serial.println(">* count of Shadow Busy Interruptes *");
Serial.println(">* PIN_PD3 IRQ 1 *");
Serial.println(">* *");
Serial.println(">* *");
Serial.println(">****************************************");
Serial.println();
return(0);
}
unsigned int smaller( unsigned int a, unsigned int b){
return (b < a) ? b : a;
}
#include "CommandLine.h"
// ****** writeAddress *****
void writeAddress(unsigned int address, byte dataByte){
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
#ifdef _DEBUG_
Serial.printf("Writing Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
DDRB_Output; // all 1's is output for Atmega1284 PortB to write to IDC-7132 RAM
PORTB = dataByte; // put the data bits on the data output
RWL_LOW; //set RW Left to low for writing to RAM digitalWrite(RWL_, LOW)
CEL_LOW; //enable the memory chip digitalWrite(CEL_, LOW)
//Busy signal is activated low only when the other side is in the same RAM location and CE has gone low
//write memory cycle is 6580ns 6.58us with this wait check
while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
Serial.printf("> RAM BUSY_\r\n");
} // Wait if the dual port Memory is busy
CEL_HIGH; //digitalWrite(CEL_, HIGH) // CEL_ goes high before RWL_ this lets Data stay valid on rising edge of CEL_
RWL_HIGH; //digitalWrite(RWL_, HIGH)
DDRB_Input; // set Atmega1284 Port B back to high inpeadence input all 0's
}
// ****** readAddress *****
byte readAddress(unsigned int address){
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
// OEL_LOW; //Set Output enable Left to low for outputing from RAM
// CEL_LOW; //Chip Enable Left to low for reading from RAM
CEL_OEL_LOW; //Try a combined bit definition in a single instruction
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
// 332ns with one delay
// 264ns without the delay
// while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
// Serial.printf("> RAM BUSY_\r\n");
// } // Wait if the dual port Memory is busy
byte dataByte = PINB;
// CEL_HIGH; // deselect RAM chip
// OEL_HIGH; // disable the output
CEL_OEL_HIGH; //Try a combined bit definition in a single instruction
#ifdef _DEBUG_
Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
return dataByte;
}
// This is a bit of a hack
// I added the second upper RAM after writing the main memory read and right routines.
// Rather than recoding the routines to I have cut and pasted the working code with a small edit to work
// with the CE pin of the upper ROM
//
// if it works.. It would be good to recode the main program to incorperate the funciton rather the the 99$% duplicate code
// 2023-02-01 Tim Gopaul
//
// PIN_PD3 will allow save and load of game rom on second dual port ram when the pinball 6800 is powered down
// This is a second buffer that holds a copy of the live Game RAM
// This RAM should only be read or or written to when the main Pinball process is powered down.
// May add some provision that holding the Pinball Reset low could be used to alter the gameRAM
// The main reson for these routines are to check to make sure the shadow copy of the RAM is not far different
// than the live GAME rame
// Recall the Shadow RAM is a write only configuration from the PIN ball processors point of view.
// Writes to game RAM by the pinball process are writes to the shadow RAM
// A read from the game RAM passes a copy of the byte to refresh the Shadow RAM with a Write
// The reason.. if the Atmega 1284 is reading the Shadow RAM address the Shadow RAM by receive a Busy and not commit
// the write from the Pinball machine in to the shadow copy.
// ..On the Atmel some routines may be needed to validate that the Shadow RAM copy is close enough.
// For game scores the effect might be that occasionally the Atmega is a step behind in the score.. but at game
// end any update will catch up.
volatile byte gameRamBuffer[ramSize]; // This is an array to hold the contents of memory
// Is there enough RAM to hold this on an ATMEGA1284? yes16KBytes
// The ATmega1284 provides the following features:
// 128Kbytes of In-System Programmable Flash with
// Read-While-Write capabilities, 4Kbytes EEPROM, 16Kbytes SRAM,
// 32 general purpose I/O lines, 32 general purpose working registers,
// Real Time Counter (RTC), three flexible Timer/Counters with compare
// modes and PWM, two serial ...
// ****** gameWriteAddress *****
void gameWriteAddress(unsigned int address, byte dataByte){
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
#ifdef _DEBUG_
Serial.printf("Writing Game Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
DDRB_Output; // all 1's is output for Atmega1284 PortB to write to IDC-7132 RAM
PORTB = dataByte; // put the data bits on the data output
RWL_LOW; //set RW Left to low for writing to RAM digitalWrite(RWL_, LOW)
CEL2_LOW; //enable the memory chip digitalWrite(CEL2_, LOW)
//Busy signal is activated low only when the other side is in the same RAM location and CE has gone low
//write memory cycle is 6580ns 6.58us with this wait check
while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
Serial.printf("> RAM BUSY_\r\n");
} // Wait if the dual port Memory is busy
CEL2_HIGH; //digitalWrite(CEL2_, HIGH) // CEL2_ goes high before RWL_ this lets Data stay valid on rising edge of CEL2_
RWL_HIGH; //digitalWrite(RWL_, HIGH)
DDRB_Input; // set Atmega1284 Port B back to high inpeadence input all 0's
}
// ****** gameReadAddress *****
byte gameReadAddress(unsigned int address){
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
// OEL_LOW; //Set Output enable Left to low for outputing from RAM
// CEL2_LOW; //Chip Enable Left to low for reading from RAM
CEL2_OEL_LOW; //Try a combined bit definition in a single instruction
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
// 332ns with one delay
// 264ns without the delay
// while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
// Serial.printf("> RAM BUSY_\r\n");
// } // Wait if the dual port Memory is busy
byte dataByte = PINB;
// CEL2_HIGH; // deselect RAM chip
// OEL_HIGH; // disable the output
CEL2_OEL_HIGH; //Try a combined bit definition in a single instruction
#ifdef _DEBUG_
Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
return dataByte;
}
// ***** gameDumpRange *****
void gameDumpRange(unsigned int addrStart, unsigned int addrCount){
// 2023-01-26 call to fill the buffer with the range added. Tim G
gameRefreshBuffer(addrStart, addrCount); // The buffer has read the memory now dump to the screen
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on gameRamBuffer index
if ((addrStart % 16) != 0) Serial.printf("0x%04X: ", addrStart);
for (unsigned int address = addrStart; address < addrEnd; address++) {
if ((address % 16) == 0) Serial.printf("0x%04X: ", address);
Serial.printf("0x%02X ",gameRamBuffer[address]);
if (((address % 16) == 15) | (address == (addrEnd -1))) Serial.println();
}
}
// ***** refreshBuffer *****
void gameRefreshBuffer(unsigned int addrStart, unsigned int addrCount){
// this will fill the buffer first
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on gameRamBuffer index
// PORT C lower bits are used for Address.
// PORT C upper bits are used for control.
// Setting the PORT C address will always put HIGH into upper 5 bits of PORTC used for control.
// This is ok because the control bits in Upper C go low after address is set.
// Don't pull CE_ WE_ OE_ low unless it is in the loop with setting the address
for (unsigned int address = addrStart; address < addrEnd; address++) {
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address - ) 0xF8 will set control bits high
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
CEL2_OEL_LOW; // two NOP in Assembly code give a memory read time of 312 ns
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
// 2023-01-26 checking busy signal also gives time for address and dta to settle befoe reading locked in on CEL going high edge
// while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
// Serial.printf("> RAM BUSY_\r\n");
// } // Wait if the dual port Memory is busy
byte dataByte = PINB;
CEL2_OEL_HIGH; // deselect RAM chip as soon as read is done
gameRamBuffer[address] = dataByte; // load it into the buffer array to do printing later
}
} // void game refreshBuffer(unsigned int addrStart, unsigned int addrCount){
// 2023-02-19 Tim Gopaul remove the GameDumpBuffer that accesses the live RAM
// ***** gameSaveMemory *****
void gameSaveMemory(unsigned int addrStart, unsigned int addrCount){
gameRefreshBuffer(addrStart, addrCount); //This will copy the physical IDC7132 RAM to the Atmel gameRamBuffer[2048]
// Only refresh the buffer with the range of bytes needed (to avoid contention.)
// copy the RAM memory to a buffer array before processing output
// Global array is used gameRamBuffer[2048]
int bytesToSave = addrCount; //initialize to the number of bytes to save and decrement for each record / line
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize);
int recordType = 0x00; //Record Type
// tt is the field that represents the HEX record type, which may be one of the following:
// 00 - data record
// 01 - end-of-file record
int address = addrStart;
int bytesThisLine;
Serial.printf("\n> Save Memory: 0x%04X: To Address: 0x%04X: \n", addrStart, addrEnd -1);
while (bytesToSave > 0) {
if (bytesToSave > MAXHEXLINE)
bytesThisLine = MAXHEXLINE;
else
bytesThisLine = bytesToSave;
int chksum = bytesThisLine + highByte(address) + lowByte(address) + recordType;
chksum &= 0xFF;
int linePos = 0; // initiallize line position left and count the hex output to MAXHEXLINE
Serial.printf(":%02X%04X%02X", bytesThisLine, address, recordType);
while (linePos < bytesThisLine) {
Serial.printf("%02X", gameRamBuffer[address]);
chksum += gameRamBuffer[address] & 0xFF;
linePos+=1;
address+=1;
}
Serial.printf("%02X\n", (~chksum+1)& 0xFF);
bytesToSave -=bytesThisLine;
}
recordType = 0x01; // no address no databytes 01 - end-of-file record
Serial.printf(":00000001FF\n"); /* end of file marker */
}
// ***** fillRange *****
void fillRange(unsigned int addrStart, unsigned int addrCount, byte dataByte){
//configure to write to RAM
DDRB_Output; // all 1's is output for Atmega1284 PortB to write to IDC-7132 RAM
// RWL_LOW; //this is a bulk write so keep RWL_ low using CEL_ to trigger write
// 2023-02-06 Tim Gopaul Holding a control signal low can't be done now that Address setting always puts control lines high in PORTC
// Put the RWL_LOW inside the loop after the address is set
PORTB = dataByte; // filling the range with the same byte. set it out side of loop once
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
for (unsigned int address = addrStart; address < addrEnd; address++) {
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
#ifdef _DEBUG_
Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
RWL_LOW; //There will be a 1/16M delay between RWL_LOW and CEL_LOW this is minimum and is 62.5nano seconds on ATmega1284
CEL_LOW;
while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
Serial.printf("> RAM BUSY_\r\n");
} // Wait if the dual port Memory is busy
CEL_HIGH;
RWL_HIGH; //There will be a 1/16M delay between RWL_LOW and CEL_LOW this is minimum and is 62.5nano seconds on ATmega1284
} //loop back for next write
DDRB_Input; // set Atmega1284 Port B back to high inpeadence input all 0's
Serial.printf("> fillRange addrStart 0x%04X, addrCount 0x%04X, data 0x%02X\n", addrStart, addrCount, dataByte);
} //fillRange
// ****** fillRandomRange *****
// this function receives a random databyte but needs to make its own for the fill
void fillRandomRange(unsigned int addrStart, unsigned int addrCount ){
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
//configure to write to RAM
DDRB_Output; // all 1's is output for Atmega1284 PortB to write to IDC-7132 RAM
for (unsigned int address = addrStart; address < addrEnd; address++) {
#ifdef _DEBUG_
Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\r\n", address, dataByte);
#endif
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
byte dataByte = (byte)random(0x100);
PORTB = dataByte;
RWL_LOW; //try write low per byte rather than bulk
CEL_LOW;
while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
Serial.printf("> RAM BUSY_\r\n");
} // Wait if the dual port Memory is busy
CEL_HIGH;
RWL_HIGH;
} //go back for next address write
DDRB_Input; // set Atmega1284 Port B back to high inpeadence input all 0's
}
// ***** dumpRange *****
void dumpRange(unsigned int addrStart, unsigned int addrCount){
// 2023-01-26 call to fill the buffer with the range added. Tim G
refreshBuffer(addrStart, addrCount); // The buffer has read the memory now dump to the screen
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
if ((addrStart % 16) != 0) Serial.printf("0x%04X: ", addrStart);
for (unsigned int address = addrStart; address < addrEnd; address++) {
if ((address % 16) == 0) Serial.printf("0x%04X: ", address);
Serial.printf("0x%02X ",ramBuffer[address]);
if (((address % 16) == 15) | (address == (addrEnd -1))) Serial.println();
}
}
// ***** refreshBuffer *****
void refreshBuffer(unsigned int addrStart, unsigned int addrCount){
// this will fill the buffer first
#ifdef _DEBUG_
Serial.printf("> debug just called to refresh ramBuffer 0x%04X\n", (int)ramBuffer);
#endif
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
// PORT C lower bits are used for Address.
// PORT C upper bits are used for control.
// Setting the PORT C address will always put HIGH into upper 5 bits of PORTC used for control.
// This is ok because the control bits in Upper C go low after address is set.
// Don't pull CE_ WE_ OE_ low unless it is in the loop with setting the address
for (unsigned int address = addrStart; address < addrEnd; address++) {
PORTC = highByte(address) | 0xF8; //Set port C to the high byte of requested RAM address
PORTA = lowByte(address); //Set Port A to the low byte of the requested RAM address
CEL_OEL_LOW; // two NOP in Assembly code give a memory read time of 312 ns
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
__asm__ __volatile__ ("nop\n\t"); // take a nap.. a short nap 62.5 nanoseconds
// 2023-01-26 checking busy signal also gives time for address and dta to settle befoe reading locked in on CEL going high edge
// while (digitalRead(BUSY_) == LOW){ // 15 is PIN_PD7 in arduino assignment
// Serial.printf("> RAM BUSY_\r\n");
// } // Wait if the dual port Memory is busy
byte dataByte = PINB;
CEL_OEL_HIGH; // deselect RAM chip as soon as read is done
ramBuffer[address] = dataByte; // load it into the buffer array to do printing later
}
} // void refreshBuffer(unsigned int addrStart, unsigned int addrCount){
// ***** dumpBuffRange *****
void dumpBuffRange(unsigned int addrStart, unsigned int addrCount){
refreshBuffer(addrStart, addrCount); // The buffer has read the memory now dump to the screen
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
Serial.printf("> Dump Buffer: 0x%04X: To Address Data: 0x%04X: \n", addrStart, addrEnd -1);
if ((addrStart % 16) != 0) Serial.printf("\n0x%04X: ", addrStart);
for (unsigned int address = addrStart; address < addrEnd; address++) {
if ((address % 16) == 0) Serial.printf("\n0x%04X: ", address);
Serial.printf("0x%02X ", ramBuffer[address]);
#ifdef _DEBUG_
Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\n", address, ramBuffer[address]);
#endif
}
Serial.println();
Serial.println();
//Dump the buffer displaying contents as ASCII if printable
Serial.printf("> Dump Buffer ASCII: 0x%04X: To Address Data: 0x%04X: \n", addrStart, addrEnd -1);
Serial.println();
//creat column headings from low address nibble
Serial.print(" "); //print some leading space
for (unsigned int i = 0; i <= 0x0f;i++)
Serial.printf( "%1X ",i);
if ((addrStart % 16) != 0) Serial.printf("\n0x%04X: ", addrStart);
for (unsigned int address = addrStart; address < addrEnd ; address++) {
if ((address % 16) == 0) Serial.printf("\n0x%04X: ", address);
if (isPrintable(ramBuffer[address]))
Serial.printf("%c ", (char)ramBuffer[address]);
else
Serial.printf("%c ", ' ');
}
Serial.println();
Serial.println();
//call the saveMemory function to see if it displays the buffer properly
saveMemory(addrStart, addrCount);
} //void dumpBuffRange(unsigned int addrStart, unsigned int addrCount)
// ***** saveMemory *****
/* Intel HEX read/write functions, Paul Stoffregen, paul@ece.orst.edu */
/* This code is in the public domain. Please retain my name and */
/* email address in distributed copies, and let me know about any bugs */
// https://www.pjrc.com/tech/8051/ihex.c
/* Given the starting address and the ending address */
/* write out Intel Hex format file */
/*
* Record Format
An Intel HEX file is composed of any number of HEX records.
Each record is made up of five fields that are arranged in the following format:
:llaaaatt[dd...]cc
Each group of letters corresponds to a different field, and each letter represents a single hexadecimal digit.
Each field is composed of at least two hexadecimal digits-which make up a byte-as described below:
: is the colon that starts every Intel HEX record.
ll is the record-length field that represents the number of data bytes (dd) in the record.
aaaa is the address field that represents the starting address for subsequent data in the record.
tt is the field that represents the HEX record type, which may be one of the following:
00 - data record
01 - end-of-file record
02 - extended segment address record
04 - extended linear address record
05 - start linear address record (MDK-ARM only)
dd is a data field that represents one byte of data. A record may have multiple data bytes.
The number of data bytes in the record must match the number specified by the ll field.
cc is the checksum field that represents the checksum of the record.
The checksum is calculated by summing the values of all hexadecimal digit pairs in the record modulo 256
and taking the two's complement.
*/
// ***** saveMemory *****
void saveMemory(unsigned int addrStart, unsigned int addrCount){
refreshBuffer(addrStart, addrCount); //This will copy the physical IDC7132 RAM to the Atmel ramBuffer[2048]
// Only refresh the buffer with the range of bytes needed to avoid contention.
// copy the RAM memory to a buffer array before processing output
// Global array is used ramBuffer[2048]
int bytesToSave = addrCount; //initialize to the number of bytes to save and decrement for each record / line
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize);
int recordType = 0x00; //Record Type
// tt is the field that represents the HEX record type, which may be one of the following:
// 00 - data record
// 01 - end-of-file record
int address = addrStart;
int bytesThisLine;
Serial.printf("\n> Save Memory: 0x%04X: To Address: 0x%04X: \n", addrStart, addrEnd -1);
while (bytesToSave > 0) {
if (bytesToSave > MAXHEXLINE)
bytesThisLine = MAXHEXLINE;
else
bytesThisLine = bytesToSave;
int chksum = bytesThisLine + highByte(address) + lowByte(address) + recordType;
chksum &= 0xFF;
int linePos = 0; // initiallize line position left and count the hex output to MAXHEXLINE
Serial.printf(":%02X%04X%02X", bytesThisLine, address, recordType);
while (linePos < bytesThisLine) {
Serial.printf("%02X", ramBuffer[address]);
chksum += ramBuffer[address] & 0xFF;
linePos+=1;
address+=1;
}
Serial.printf("%02X\n", (~chksum+1)& 0xFF);
bytesToSave -=bytesThisLine;
}
recordType = 0x01; // no address no databytes 01 - end-of-file record
Serial.printf(":00000001FF\n"); /* end of file marker */
}
// ***** loadMemory *****
void loadMemory(){
Serial.printf("> Waiting for Intel Hex input records or end of file record :00000001FF\n");
inputMode = DataMode;
// This flips to DataMode so that main loop will dispatch input to build Intel Hex input line
// once in DataMode the main loop will add characters to a buffer line until enter is pressed Linefeed.
// in DataMode each line is interpretted as an Intel Hex record.. type 01 and type 00 supported
// to leave DataMode the input must receive the Intel Hex end of file record.
// :00000001FF
// .. add CTRL-C and esc as ways to terminate the input
}
// ***** gameLoadMemory *****
void gameLoadMemory(){
Serial.printf("> Waiting for Intel Hex input records or end of file record :00000001FF\n");
inputMode = gameDataMode;
// This flips to DataMode so that main loop will dispatch input to build Intel Hex input line
// once in DataMode the main loop will add characters to a buffer line until enter is pressed Linefeed.
// in DataMode each line is interpretted as an Intel Hex record.. type 01 and type 00 supported
// to leave DataMode the input must receive the Intel Hex end of file record.
// :00000001FF
// .. add CTRL-C and esc as ways to terminate the input
}
// ***** compareBuffer *****
void compareBuffer( unsigned int addrStart, unsigned int addrCount){
unsigned int addrEnd = smaller((addrStart + addrCount), ramSize); //bounds check on ramBuffer index
for (unsigned int address = addrStart; address < addrEnd; address++) {
byte ramByte = readAddress(address);
byte buffByte = ramBuffer[address];
if (ramByte != buffByte){
Serial.printf("address 0x%04X: ramBuffer 0x%02X buffByte 0x%02X\n", address, ramByte, buffByte );
Serial.println(" Subtest");
for (int i=0; i<10; i++) {
byte ramByte = readAddress(address);
byte buffByte = ramBuffer[address];
Serial.printf(" address 0x%04X: ramBuffer 0x%02X buffByte 0x%02X\n", address, ramByte, buffByte );
}
}
}
}
//compareBuffer
// ***** testMemory *****
void testMemory(unsigned int addrStart, unsigned int addrCount, int testLoops) {
for (int i = 0; i < testLoops; i++){
Serial.printf(">Memory loop test %d\n", i);
fillRandomRange(addrStart, addrCount); //dataByte is recreated for each address of range
refreshBuffer(addrStart, addrCount);
compareBuffer(addrStart, addrCount);
}
}
void BusyFaultWarning(){
BusyStateIRQ = LOW;
++BusyFaultCount;
BusyFaultAddress = (((PORTC & B0000111) << 8 ) | PORTA);
}
void ShadowFaultWarning(){
ShadowStateIRQ = LOW;
++ShadowFaultCount;
ShadowFaultAddress = (((PORTC & B0000111) << 8 ) | PORTA);
}
// ***** setup ***** -----------------------------------------------
void setup() {
pinMode(BUSY_IRQPIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUSY_IRQPIN),BusyFaultWarning, FALLING);
pinMode(SHADOW_IRQPIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(SHADOW_IRQPIN),ShadowFaultWarning, FALLING);
// seed the random mumber generator
randomSeed(millis()); //initialize pseudo random number
delay(200);
Serial.begin(115200);
delay(100);
helpText();
#ifdef _DEBUG_
Serial.println("_DEBUG_ is defined");
#endif
PINA= B11111111; //This might be the way to set input pull-up before changing PORTA direction to output
PORTA = B11111111; // Set low Address bits HIGH setting pins high before DDRA will make sure pins source current on output set
DDRA = B11111111; // PortA 0 to 7 are the low address out for the 2Kx8 RAM
PORTA = B11111111; // Set low Address bits HIGH setting pins high before DDRA will make sure pins source current on output set
PORTC = B11111111; //Set high Address bits HIGH setting pins high before DDRA will make sure pins source current on output set
PINC= B11111111; //This might be the way to set input pull-up before changing PORTC direction to output
DDRC = B11111111; // PORTC 0 - 7 are the high byte of address output only 0,1,2 used
PORTC = B11111111; // PORTC should be output high on initialization. Address all 1's and control bits all high
// With control pins moved to Port C the Port D is left for Serial and other un assigned pin functions.
// For DATA bus PortB will be used alternating between input and output
PORTB = B00000000; //set pullups but maybe not needed PORTB is output PINB is input
DDRB = B00000000; // all 0's is input for PortB
//configure control lines as output except busy line is input
//CEL_ = PIN_PD4; // CEL# output high
//RWL_ = PIN_PD5; // R/WL# output high
//OEL_ = PIN_PD6; // OEL# output high
//BUSY_ = PIN_PD7; // BUSY# input pull up
DDRD |= B01111100; // set DDR pins 4 5 6 to output..do this at setup
PORTD |=B01111100; // bits 3 and two are also outputs here. Bits 0 and 1 are reserved for Serial
CEL_HIGH; //PIN_PD4 CEL# output high
RWL_HIGH; //PIN_PD5 R/WL# output high
OEL_HIGH; //PIN_PD6 OEL# output high
pinMode(BUSY_,INPUT_PULLUP); // 15 is PIN_PD7 in arduino assignment this is the busy signal from dual port ram
} //endof Setup
// ***** loop ***** ----------------------------------------
void loop() {
if (BusyStateIRQ == LOW ) {
BusyStateIRQ = HIGH;
Serial.printf("\n> PIN_PD2 IRQ 0 Busy fault Live Game RAM issued a BUSY, Address: 0x%04X\n", BusyFaultAddress );
Serial.printf("> Cumlative fault count since last Atmega1284 reboot: %d\n", BusyFaultCount );
}
if (ShadowStateIRQ == LOW ) {
ShadowStateIRQ = HIGH;
Serial.printf("\n> PIN_PD3 IRQ 1 Busy fault Shadow RAM issued a BUSY, Address: 0x%04X\n", ShadowFaultAddress );
Serial.printf("> Cumlative Shadow fault count since last Atmega1284 reboot: %d\n", ShadowFaultCount );
}
bool received = getCommandLineFromSerialPort(CommandLine); //global CommandLine is defined in CommandLine.h
if (received) {
switch(inputMode){
case CommandMode:
DoMyCommand(CommandLine);
break;
case DataMode:
DoMyHexLine(CommandLine, DataMode);
break;
case gameDataMode:
DoMyHexLine(CommandLine, gameDataMode);
break;
default:
break;
}
}
}