commit e01c48c50eafbd968fa9b8f029d2a31b82f23349 Author: Tim Gopaul Date: Sun Feb 5 18:30:01 2023 -0700 add .ino and .h file diff --git a/CommandLine.h b/CommandLine.h new file mode 100644 index 0000000..35a5e67 --- /dev/null +++ b/CommandLine.h @@ -0,0 +1,537 @@ +/***************************************************************************** + 2023-01-11/12 Tim Gopaul Removed getHexLineFromSerialPort.. use the main parsing code get command + 2023-01-11 Tim Gopaul added call to make commands case insensitive. + 2023-01-08 Tim Gopaul - add load command to read in Hex format string and place in RAM memory + 2023-01-07 Tim Gopaul - Intel Hex format saveMemory to console + - have serial run at 1Mhz + 2022-12-30 Tim Gopaul changed the readNumber to read inputs in base 16 + return atoi(numTextPtr); //K&R string.h pg. 251 + return int(strtol(numTextPtr, NULL, 0)); //https://stackoverflow.com/questions/10156409/convert-hex-string-char-to-int + //strlol string to long will accept 0x for Hex, leading zero for octal, 0b for binary + // strtol returns a long integer so shorten to int ..or byte. + + How to Use CommandLine: + Create a sketch. Look below for a sample setup and main loop code and copy and paste it in into the new sketch. + + Create a new tab. (Use the drop down menu (little triangle) on the far right of the Arduino Editor. + Name the tab CommandLine.h + Paste this file into it. + + Test: + Download the sketch you just created to your Arduino as usual and open the Serial Window. Type these commands followed by return: + add 5, 10 + subtract 10, 5 + + Look at the add and subtract commands included and then write your own! + + +***************************************************************************** + Here's what's going on under the covers +***************************************************************************** + Simple and Clear Command Line Interpreter + + This file will allow you to type commands into the Serial Window like, + add 23,599 + blink 5 + playSong Yesterday + + to your sketch running on the Arduino and execute them. + + Implementation note: This will use C strings as opposed to String Objects based on the assumption that if you need a commandLine interpreter, + you are probably short on space too and the String object tends to be space inefficient. + + 1) Simple Protocol + Commands are words and numbers either space or comma spearated + The first word is the command, each additional word is an argument + "\n" terminates each command + + 2) Using the C library routine strtok: + A command is a word separated by spaces or commas. A word separated by certain characters (like space or comma) is called a token. + To get tokens one by one, I use the C lib routing strtok (part of C stdlib.h see below how to include it). + It's part of C language library which you can look up online. Basically you: + 1) pass it a string (and the delimeters you use, i.e. space and comman) and it will return the first token from the string + 2) on subsequent calls, pass it NULL (instead of the string ptr) and it will continue where it left off with the initial string. + I've written a couple of basic helper routines: + readNumber: uses strtok and atoi (atoi: ascii to int, again part of C stdlib.h) to return an integer. + Note that atoi returns an int and if you are using 1 byte ints like uint8_t you'll have to get the lowByte(). + readWord: returns a ptr to a text word + 2022-12-28 Tim Gopaul - return atoi(numTextPtr);//K&R string.h pg. 251, ia replaced with strtol string to long that allows selection of base 16 + - return int(strtol(numTextPtr, NULL, 16)); + 4) DoMyCommand: A list of if-then-elses for each command. You could make this a case statement if all commands were a single char. + Using a word is more readable. + For the purposes of this example we have: + Add + Subtract + nullCommand + + 2022-10-18 added commands + read + write + dump + dumpBuffer + fill +*/ +/******************sample main loop code ************************************ + + #include "CommandLine.h" + + void + setup() { + Serial.begin(115200); + } + + void + loop() { + bool received = getCommandLineFromSerialPort(CommandLine); //global CommandLine is defined in CommandLine.h + if (received) DoMyCommand(CommandLine); + } + +**********************************************************************************/ + +//Name this tab: CommandLine.h + +#include +#include +#include // https://stackoverflow.com/questions/26080829/detecting-strtol-failure when strtol fails it returns zero and an errno +#include // used to find LONG_MIN and LONG_MAX + + + +//Function Prototypes from .ino file +void writeAddress(unsigned int address, byte dataByte); + +byte readAddress(unsigned int address); +void fillRange(unsigned int addrStart, unsigned int addrCount, byte dataByte); +void fillRandomRange(unsigned int addrStart, unsigned int addrCount, byte dataByte); +void dumpRange(unsigned int addrStart, unsigned int addrCount); +void gameDumpRange(unsigned int addrStart, unsigned int addrCount); +void dumpBuffRange(unsigned int addrStart, unsigned int addrCount); +void saveMemory(unsigned int addrStart, unsigned int addrCount); +void testMemory(unsigned int addrStart, unsigned int addrCount); +void loadMemory(); +int helpText(); +void testMemory(unsigned int addrStart, unsigned int addrCount, int testLoops); + +void gameWriteAddress(unsigned int address, byte dataByte); +byte gameReadAddress(unsigned int address); +void gameDumpRange(unsigned int addrStart, unsigned int addrCount); + +//this following macro is good for debugging, e.g. print2("myVar= ", myVar); +#define print2(x,y) (Serial.print(x), Serial.println(y)) + + +#define CR '\r' +#define LF '\n' +#define BS '\b' +#define NULLCHAR '\0' +#define SPACE ' ' +#define ESC 'Q' + +#define COMMAND_BUFFER_LENGTH 60 //length of Serial buffer for incoming commands +char CommandLine[COMMAND_BUFFER_LENGTH + 1]; //Read commands into this buffer from Serial. +1 in length for a termination char + +const char *delimiters = ", \n"; //commands can be separated by return, space or comma + +/************************************************************************************************************* + your Command Names Here +*/ +const char *addCommandToken = "add"; //Modify here +const char *subtractCommandToken = "sub"; //Modify here +const char *readCommandToken = "read"; // read address ignore +const char *writeCommandToken = "write"; // write address byte +const char *dumpCommandToken = "dump"; // Dumps memory from starting address with byte count +const char *dumpBuffCommandToken = "dumpbuffer"; // Dumps memory held in the buffer +const char *fillCommandToken = "fill"; // Fills the RAM starting at address with byte +const char *fillRandomCommandToken = "fillrandom"; // Fills with random byte the RAM starting at address with byte +const char *saveMemoryCommandToken = "save"; // creates Intel Hex output from ram range. +const char *helpCommandToken = "help"; +const char *loadMemoryCommandToken = "load"; // takes an Intel Hex formatted line and writes it to RAM +const char *testMemoryCommandToken = "testmemory"; // destructive test read from memory rotate bits and write then compare + +const char *gameReadCommandToken = "gameread"; // read address ignore +const char *gameWriteCommandToken = "gamewrite"; // write address byte +const char *gameDumpCommandToken = "gameDump"; // Dumps game memory from starting address with byte count + +/************************************************************************************************************* + getCommandLineFromSerialPort() + Return the string of the next command. Commands are delimited by return" + Handle BackSpace character + Make all chars lowercase +*************************************************************************************************************/ + +bool +getCommandLineFromSerialPort(char * commandLine) +{ + static uint8_t charsRead = 0; //note: COMAND_BUFFER_LENGTH must be less than 255 chars long + //read asynchronously until full command input + while (Serial.available()) { + char c = Serial.read(); + switch (c) { + case CR: //likely have full command in buffer now, commands are terminated by CR and/or LF + case LF: + commandLine[charsRead] = NULLCHAR; //null terminate our command char array + if (charsRead > 0) { + charsRead = 0; //charsRead is static, so have to reset + // Serial.println(commandLine); + return true; + } + break; + case BS: // handle backspace in input: put a space in last char + if (charsRead > 0) { //and adjust commandLine and charsRead + commandLine[--charsRead] = NULLCHAR; + Serial.print(" \b"); + } + break; + case ESC: // ESC escape should clear the command line and start over without execiting commad + if (charsRead > 0) { + charsRead = 0; + commandLine[charsRead] = NULLCHAR; + Serial.print(" ESC\n"); + inputMode = CommandMode; + while(Serial.available() > 0) Serial.read(); //eat what's left comming in. + } + break; + + default: + //c = tolower(c); //switches all characters to lower case.( not needed switched to strcasecmp() + if (charsRead < COMMAND_BUFFER_LENGTH) { //if the buffer is not full add the c charcter read + commandLine[charsRead++] = c; //add the character and increment the buffer count charsRead + } + commandLine[charsRead] = NULLCHAR; //the buffer has a NULLCHAR waiting to be overwritten. + break; + } + } + return false; +} + +/* **************************** + readNumber: return a 16bit (for Arduino Uno) signed integer from the command line + readWord: get a text word from the command line + +*/ +int readNumber () { + char *numTextPtr = strtok(NULL, delimiters); //K&R string.h pg. 250 Continue parsing the first call of strtok + char *endptr = NULL; + long int number = 0; + + // reset errno to 0 before call + errno = 0; + // call to strtol assigning return to number, strtol returns a long integer so shorten to int ..or byte. + + number = strtol(numTextPtr, &endptr, 0); //strlol string to long will accept 0x for Hex, leading zero for octal, 0b for binary + +// { +// Serial.printf("errno: %d\n", errno); +// +// /* test return to number and errno values */ +// if (numTextPtr == endptr) +// Serial.printf (" number : %lu invalid (no digits found, 0 returned)\n", number); +// else if (errno == ERANGE && number == LONG_MIN) +// Serial.printf (" number : %lu invalid (underflow occurred)\n", number); +// else if (errno == ERANGE && number == LONG_MAX) +// Serial.printf (" number : %lu invalid (overflow occurred)\n", number); +// else if (errno == EINVAL) /* not in all c99 implementations - gcc OK */ +// printf (" number : %lu invalid (base contains unsupported value)\n", number); +// else if (errno != 0 && number == 0) +// Serial.printf (" number : %lu invalid (unspecified error occurred)\n", number); +// else if (errno == 0 && numTextPtr && !*endptr) +// Serial.printf (" number : %lu valid (and represents all characters read)\n", number); +// else if (errno == 0 && numTextPtr && *endptr != 0) +// Serial.printf (" number : %lu valid (but additional characters remain)\n", number); +// } + + return int(number); + } + + +// ***** readWord ***** +char * readWord() { + char * word = strtok(NULL, delimiters); //K&R string.h pg. 250 + return word; +} + +void +nullCommand(char * ptrToCommandName) { + print2("Command not found: ", ptrToCommandName); //see above for macro print2 +} + + +/**************************************************** + Add your commands here +*/ + +int addCommand() { //Modify here + int firstOperand = readNumber(); + int secondOperand = readNumber(); + return firstOperand + secondOperand; +} + +int subtractCommand() { //Modify here + int firstOperand = readNumber(); + int secondOperand = readNumber(); + return firstOperand - secondOperand; +} + +int readCommand() { //read a byte from RAM + int address = readNumber(); + byte dataByte = readAddress(address); + Serial.printf("0x%04X: 0x%02X\n", address, dataByte); + return dataByte; //return the byte but printing is done here +} + +int writeCommand() { //write a byte to RAM + int address = readNumber(); + int dataByte = readNumber(); + //read before writing + Serial.printf("0x%04X: 0x%02X\n", address, readAddress(address)); + writeAddress(address, dataByte); + Serial.printf("0x%04X: 0x%02X\n", address, dataByte); + return dataByte; //return the byte but printing is done here +} + +int dumpCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + + dumpRange(addrStart, addrCount); + + return addrStart; +} + +int dumpBuffCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + + dumpBuffRange(addrStart, addrCount); + + return addrStart; +} + +int fillCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + byte dataByte = readNumber(); + + fillRange(addrStart, addrCount, dataByte); + + return dataByte; +} + +int fillRandomCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + byte dataByte = (byte)random(0x100); + + fillRandomRange(addrStart, addrCount, dataByte); //dataByte is recreated for each address of range + + return dataByte; +} + +// ***** saveMemoryCommand ***** +int saveMemoryCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + + saveMemory(addrStart, addrCount); + return 0; +} + +// ***** loadMemoryCommand ***** +int loadMemoryCommand() { + loadMemory(); + return 0; +} + +// ***** Help Text ***** +int helpCommand() { + helpText(); + return 0; +} + +int gameReadCommand() { //read a byte from RAM + int address = readNumber(); + byte dataByte = gameReadAddress(address); + Serial.printf("0x%04X: 0x%02X\n", address, dataByte); + return dataByte; //return the byte but printing is done here +} + +int gameWriteCommand() { //write a byte to RAM + int address = readNumber(); + int dataByte = readNumber(); + //read before writing + Serial.printf("0x%04X: 0x%02X\n", address, readAddress(address)); + gameWriteAddress(address, dataByte); + Serial.printf("0x%04X: 0x%02X\n", address, dataByte); + return dataByte; //return the byte but printing is done here +} + +int gameDumpCommand() { + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + + gameDumpRange(addrStart, addrCount); + + return addrStart; +} + + +// ***** testMemmory ***** +int testMemoryCommand(){ + unsigned int addrStart = readNumber(); + unsigned int addrCount = readNumber(); + unsigned int testLoops = readNumber(); + Serial.println(addrCount); + + testMemory(addrStart, addrCount, testLoops); + return 0; +} + +/**************************************************** + DoMyHexLine +*/ +bool +DoMyHexLine(char * HexLine) { + +// char * endOfFile = ":00000001FF"; //For Intel Hex file transfer a the final line must match.. to switch to command inputMode + + + Serial.printf("%s\n", HexLine); + + int HexLineLength = strlen(HexLine); +// Serial.printf("> HexLineLength: 0x%02X\n", HexLineLength); + if (HexLineLength < 11){ + Serial.println("> HexLine minimum valid line length is 11 characters"); + } + if (HexLine[0]!=':') { + Serial.println("> HexLine must start with : character"); + } + if (HexLineLength % 2 == 0) { + Serial.println("> HexLine must be odd when including colon start character"); + } + + #define HEXLINEBYTESSIZE MAXHEXLINE *2 + 5 + byte HexLineBytes[HEXLINEBYTESSIZE]; //Buffer will hold the hex values for input record + int HLBIndex = 0; + int checkSum = 0; + + for (int i= 1; i< HexLineLength; i+=2){ + char inByteText[3] = { HexLine[i], HexLine[i+1] , NULLCHAR }; + byte inByte = (byte)strtol(inByteText,NULL,16); + HexLineBytes[HLBIndex++] = inByte; //Fill the HexLineBytes buffer with Byte values of HEX input +// Serial.printf( "%s 0x%02X ", inByteText, inByte); + checkSum += inByte; + checkSum &= 0xFF; +// Serial.printf( " 0x%02X 0x%02X 0x%02X \n", HexLine[i], HexLine[i+1], checkSum); + } + if (checkSum != 0 ) Serial.println("> Bad CheckSum"); + +//Write the bytes to RAM +//HexLineBytes holds all the byte values from the input HEX ASCII pull off addrCount and addrStart +//And save them to the dual port RAM afer checks +//HEX record format :llaaaatt[dd...]cc + + unsigned int hexCount = HexLineBytes[0]; //if there is junk in these buffers the checkSum will prevent use + unsigned int addrStart = ((HexLineBytes[1]<<8) + HexLineBytes[2]); + int recordType = HexLineBytes[3]; //Record Type 00 for data 01 for end of file +// HLBIndex =4; //Data starts at index 4 in HexLineBytes + + if ((checkSum == 0) && (HexLineBytes[0] > 0) && + (HexLineBytes[0] < HEXLINEBYTESSIZE) && (recordType == 0)){ + int address = addrStart; + HLBIndex =4; //Data starts at index 4 in HexLineBytes + for(unsigned int i = 0; i < hexCount; i++){ + writeAddress( address++, HexLineBytes[HLBIndex++]); + } + } + + Serial.printf("\n> HexLine %s\n", HexLine); + + if (strcasecmp(HexLine, ":00000001FF") == 0){ //use strcasecmp for case insensitive compare + inputMode = CommandMode; + Serial.println("> Enter Command"); + } + else{ + Serial.println("> Send next Hex record. To terminate: :00000001FF"); + } + return(true); +} + +/**************************************************** + DoMyCommand +*/ +bool +DoMyCommand(char * commandLine) { + // print2("\nCommand: ", commandLine); + int result; + + char * ptrToCommandName = strtok(commandLine, delimiters); //on first call to strtok pass it a pointer to a string, on subsequent calls it NULL to continue parsing + // print2("commandName= ", ptrToCommandName); + + if (strcasecmp(ptrToCommandName, addCommandToken) == 0) { //Modify here + result = addCommand(); + Serial.printf("> The sum is = %d 0x%04X\n", result, result); + } + else if (strcasecmp(ptrToCommandName, subtractCommandToken) == 0) { //Modify here + result = subtractCommand(); + Serial.printf("> The difference is = %d 0x%04X\n", result, result); + } + else if (strcasecmp(ptrToCommandName, readCommandToken) == 0) { //Modify here + result = readCommand(); + } + else if (strcasecmp(ptrToCommandName, writeCommandToken) == 0) { //Modify here + result = writeCommand(); + + } + else if (strcasecmp(ptrToCommandName, dumpCommandToken) == 0) { //Modify here + result = dumpCommand(); + // Serial.println(); + + } + + else if (strcasecmp(ptrToCommandName, gameReadCommandToken) == 0) { //Modify here + result = gameReadCommand(); + } + else if (strcasecmp(ptrToCommandName, gameWriteCommandToken) == 0) { //Modify here + result = gameWriteCommand(); + + } + else if (strcasecmp(ptrToCommandName, gameDumpCommandToken) == 0) { //Modify here + result = gameDumpCommand(); + // Serial.println(); + + } + else if (strcasecmp(ptrToCommandName, dumpBuffCommandToken) == 0) { //Modify here + result = dumpBuffCommand(); + Serial.println(); + + } + else if (strcasecmp(ptrToCommandName, fillCommandToken) == 0) { //Modify here + result = fillCommand(); + Serial.println(); + } + else if (strcasecmp(ptrToCommandName, fillRandomCommandToken) == 0) { //Modify here + result = fillRandomCommand(); + Serial.println(); + } + else if (strcasecmp(ptrToCommandName, saveMemoryCommandToken) == 0) { //Modify here + result = saveMemoryCommand(); + Serial.println(); + } + else if (strcasecmp(ptrToCommandName, loadMemoryCommandToken) == 0) { //Modify here + result = loadMemoryCommand(); + } + + else if (strcasecmp(ptrToCommandName, helpCommandToken) == 0) { //Modify here + result = helpText(); + } + + else if (strcasecmp(ptrToCommandName, testMemoryCommandToken) == 0) { //Modify here + result = testMemoryCommand(); + } + else { + nullCommand(ptrToCommandName); + } + return true; + } + + diff --git a/PinBallMemoryPort20230205.ino b/PinBallMemoryPort20230205.ino new file mode 100644 index 0000000..1541087 --- /dev/null +++ b/PinBallMemoryPort20230205.ino @@ -0,0 +1,806 @@ +// Protospace is running code version PinBallMemoryPort20230201 +// The next version of code starts dated 2023 02 05 +// +// 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; + +#define CommandMode 1 //inputMode will flip between command and data entry +#define DataMode 2 // 2023-01-09 Tim Gopaul +int inputMode = 1; + +#include "CommandLine.h" + + +#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 + +#define CEL_LOW PORTD &=B11101111 // ChipEnable Left LOW PORTD PIN_PD4 +#define CEL_HIGH PORTD |=B00010000 // ChipEnable Left HIGH PORTD PIN_PD4 + +#define RWL_LOW PORTD &=B11011111 // R/W Left LOW PORTD PIN_PD5 +#define RWL_HIGH PORTD |=B00100000 // R/W Left HIGH PORTD PIN_PD5 + +#define OEL_LOW PORTD &=B10111111 // OEL LEFT LOW PORTD PIN_PD6 +#define OEL_HIGH PORTD |=B01000000 // OEL LEFT HIGH PORTD PIN_PD6 + +#define CEL_OEL_LOW PORTD &=B10101111 // ChipEnable with OutputEnable LOW PORTD PIN_PD6 PIN_PD4 +#define CEL_OEL_HIGH PORTD |=B01010000 // ChipEnable with OutputEnable HIGH PORTDPIN_PD6 PIN_PD4 + +#define CEL2_LOW PORTD &=B11110111 // ChipEnable Left LOW PORTD PIN_PD3 +#define CEL2_HIGH PORTD |=B00001000 // ChipEnable Left HIGH PORTD PIN_PD3 + +#define CEL2_OEL_LOW PORTD &=B10110111 // ChipEnable with OutputEnable LOW PORTD PIN_PD6 PIN_PD3 +#define CEL2_OEL_HIGH PORTD |=B01001000 // ChipEnable with OutputEnable HIGH PORTDPIN_PD6 PIN_PD3 + + +const byte BUSY_ = PIN_PD7; // BUSY# input pull up + + +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(">* Untility program to read 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(">* gameRead address *"); + Serial.println(">* gameWrite address databyte *"); + Serial.println(">* gameDump start count *"); + Serial.println(">* *"); + + Serial.println(">* *"); + Serial.println(">* Enter numbers as decimal or *"); + Serial.println(">* 0xNN 0X55 for HEX *"); + Serial.println(">* *"); + Serial.println(">**********************************"); + Serial.println(); + return(0); +} + +unsigned int smaller( unsigned int a, unsigned int b){ + return (b < a) ? b : a; +} + + +// ****** writeAddress ***** +void writeAddress(unsigned int address, byte dataByte){ + PORTC = highByte(address); //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); //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); //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) + 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); //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 + + OEL_LOW; //OEL_ low in preparation for CEL2_ + + + for (unsigned int address = addrStart; address < addrEnd; address++) { + PORTC = highByte(address); //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 + + CEL2_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_HIGH; // deselect RAM chip as soon as read is done + gameRamBuffer[address] = dataByte; // load it into the buffer array to do printing later + + } + + OEL_HIGH; // disable the output +} // void refreshBuffer(unsigned int addrStart, unsigned int addrCount){ + + +// ***** dumpBuffRange ***** +void gameDumpBuffRange(unsigned int addrStart, unsigned int addrCount){ + + 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 + + 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 ", gameRamBuffer[address]); + + #ifdef _DEBUG_ + Serial.printf("Reading Address: 0x%04X: Data: 0x%02X\n", address, gameRamBuffer[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(gameRamBuffer[address])) + Serial.printf("%c ", (char)gameRamBuffer[address]); + else + Serial.printf("%c ", ' '); + } + + Serial.println(); + Serial.println(); + + //call the saveMemory function to see if it displays the buffer properly + gameSaveMemory(addrStart, addrCount); + +} //void gameDumpBuffRange(unsigned int addrStart, unsigned int addrCount) + + + +// ***** 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 + + 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); //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 + + 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; + + } //loop back for next write + RWL_HIGH; //this is a bulk write so keep RWL_ low using CEL_ to trigger write return to data input direction + 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); +} + +// ****** fillRandomRange ***** +// this function receives a random databyte but needs to make its own for the fill +void fillRandomRange(unsigned int addrStart, unsigned int addrCount, byte dataByte){ + +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 + +//RWL_LOW; //this is a bulk write so keep RWL_ low using CEL_ to trigger write + + 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); //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 + + 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 + + dataByte = (byte)random(0x100); + PORTB = dataByte; + + RWL_LOW; //try write low per byte rather than bulk + CEL_LOW; + CEL_HIGH; + RWL_HIGH; + + } //go back for next address write + + RWL_HIGH; + 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 + + OEL_LOW; //OEL_ low in preparation for CEL_ + + + for (unsigned int address = addrStart; address < addrEnd; address++) { + PORTC = highByte(address); //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_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_HIGH; // deselect RAM chip as soon as read is done + ramBuffer[address] = dataByte; // load it into the buffer array to do printing later + + } + + OEL_HIGH; // disable the output +} // 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 +} + +// ***** 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, 1); //dataByte is recreated for each address of range + refreshBuffer(addrStart, addrCount); + compareBuffer(addrStart, addrCount); + } +} + + + +// ***** setup ***** ----------------------------------------------- +void setup() { + + // 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 + + DDRA = B11111111; // PortA 0 to 7 are the low address out for the 2Kx8 RAM + PORTA = B00000000; // Set low Address bits low + DDRC = B11111111; // PORTC 0 - 7 are the high byte of address output only 0,1,2 used + PORTC = B00000000; //Set high Address bits low + +// 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() { + bool received = getCommandLineFromSerialPort(CommandLine); //global CommandLine is defined in CommandLine.h + if (received) { + switch(inputMode){ + case CommandMode: + DoMyCommand(CommandLine); + break; + case DataMode: + DoMyHexLine(CommandLine); + break; + default: + break; + } + } +} + + +