add .ino and .h file
This commit is contained in:
commit
e01c48c50e
537
CommandLine.h
Normal file
537
CommandLine.h
Normal file
|
@ -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 <string.h> 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h> // https://stackoverflow.com/questions/26080829/detecting-strtol-failure when strtol fails it returns zero and an errno
|
||||
#include <limits.h> // 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;
|
||||
}
|
||||
|
||||
|
806
PinBallMemoryPort20230205.ino
Normal file
806
PinBallMemoryPort20230205.ino
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user