//*******************************************************************************
// ArduinoMega SRAM Tester for 
// TRS-80 Model 100 / Olivetti M10 / ...
// Author: Mauro Pintus
// Date  : 2018 12 21
// Ver   : 1.7
//*******************************************************************************

//   ___________SRAM PINs___________
//  |                               |
//  |          _MEGA GPIO_          |          TOP View           Bottom View
//  |         |           |         |         _____U______       _____U______  
// A07 [01] (D22)       (+5V) [28] VCC       |   __u___   |    .|   ______   |.
// A06 [02] (D24)       (D25) [27] A08       |  |      |  |    .|  |      |  |.
// A05 [03] (D26)       (D27) [26] A09       |  |      |  |    .|  |      |  |.
// A04 [04] (D28)       (D29) [25] _WE       |  | IC1  |  |    .|  | IC4  |  |.
// A03 [05] (D30)       (D31) [24] CE4       |  |      |  |    .|  |      |  |.
// A02 [06] (D32)       (D33) [23] A10       |  |______|  |    .|  |__n___|  |.
// A01 [07] (D34)       (D35) [22] CEX       |            |    .|            |.
// A00 [08] (D36)       (D37) [21] IO7       |   __u___   |    .|   ______   |.
// IO0 [09] (D38)       (D39) [20] IO6       |  |      |  |    .|  |      |  |.
// IO1 [10] (D40)       (D41) [19] IO5       |  |      |  |    .|  |      |  |.
// IO2 [11] (D42)       (D43) [18] IO4       |  | IC2  |  |    .|  | IC3  |  |.
// GND [12] (---)       (D45) [17] IO3       |  |      |  |    .|  |      |  |.
// CE1 [13] (D46)       (---) [16] GND       |  |______|  |    .|  |__n___|  |.
// CE2 [14] (D48)       (D49) [15] CE3       |____________|    .|____________|.
//


//If the test will FAIL some pin could be shorted to GND or to VCC
//so all pins will be set as input to prevent damage to the Arduino
//A small resistor (150 Ohm) between each Arduino GPIO and memory pin
//should prevent any damage to the Arduino.

#define pinCE1 46
#define pinCE2 48
#define pinCE3 49
#define pinCE4 31
#define pinCEX 35
#define pin_WE 29

#define pinA00 36
#define pinA01 34
#define pinA02 32
#define pinA03 30
#define pinA04 28
#define pinA05 26
#define pinA06 24
#define pinA07 22
#define pinA08 25
#define pinA09 27
#define pinA10 33

#define pinIO0 38
#define pinIO1 40
#define pinIO2 42
#define pinIO3 45
#define pinIO4 43
#define pinIO5 41
#define pinIO6 39
#define pinIO7 37

#define max_addr 0x7ff

// Configuration variables
// Fill = 0 the tester will read the memory without writing on it
// Fill = 1 the tester will test each cell of the memory
int Fill = 1;

// Verb = 0 the tester will print only the test results
// Verb = 1 the tester will print on the serial port all the memory content 
int Verb = 1;


uint8_t ctrl_pins[]  = { pinCE1, pinCE2, pinCE3, pinCE4, pinCEX, pin_WE };

uint8_t addr_pins[]  = { pinA00, pinA01, pinA02, pinA03, pinA04, pinA05, 
                         pinA06, pinA07, pinA08, pinA09, pinA10 };
                       
uint8_t inout_pins[] = { pinIO0, pinIO1, pinIO2, pinIO3, 
                         pinIO4, pinIO5, pinIO6, pinIO7 };

int  failed    = 0;
int  PassFail[]= {0,0,0,0};



void setup() 
{
  Serial.begin(115200);

  Serial.println(F("\n*****************************************")); 
  Serial.println(F("ArduinoMega SRAM Tester for")); 
  Serial.println(F("TRS-80 Model 100 / Olivetti M10 / ...")); 
  Serial.println(F("Author: Mauro Pintus")); 
  Serial.println(F("Date  : 2018 12 21"));
  Serial.println(F("Ver   : 1.7")); 
  Serial.println(F("*****************************************\n"));

  Serial.println(F("  ___________SRAM PINs___________ "));
  Serial.println(F(" |                               |"));
  Serial.println(F(" |          _MEGA GPIO_          |          TOP View           Bottom View  "));
  Serial.println(F(" |         |           |         |         _____U______       _____U______  ")); 
  Serial.println(F("A07 [01] (D22)       (+5V) [28] VCC       |   __u___   |    .|   ______   |."));
  Serial.println(F("A06 [02] (D24)       (D25) [27] A08       |  |      |  |    .|  |      |  |."));
  Serial.println(F("A05 [03] (D26)       (D27) [26] A09       |  |      |  |    .|  |      |  |."));
  Serial.println(F("A04 [04] (D28)       (D29) [25] _WE       |  | IC1  |  |    .|  | IC3  |  |."));
  Serial.println(F("A03 [05] (D30)       (D31) [24] CE4       |  |      |  |    .|  |      |  |."));
  Serial.println(F("A02 [06] (D32)       (D33) [23] A10       |  |______|  |    .|  |__n___|  |."));
  Serial.println(F("A01 [07] (D34)       (D35) [22] CEX       |            |    .|            |."));
  Serial.println(F("A00 [08] (D36)       (D37) [21] IO7       |   __u___   |    .|   ______   |."));
  Serial.println(F("IO0 [09] (D38)       (D39) [20] IO6       |  |      |  |    .|  |      |  |."));
  Serial.println(F("IO1 [10] (D40)       (D41) [19] IO5       |  |      |  |    .|  |      |  |."));
  Serial.println(F("IO2 [11] (D42)       (D43) [18] IO4       |  | IC2  |  |    .|  | IC4  |  |."));
  Serial.println(F("GND [12] (---)       (D45) [17] IO3       |  |      |  |    .|  |      |  |."));
  Serial.println(F("CE1 [13] (D46)       (---) [16] GND       |  |______|  |    .|  |__n___|  |."));
  Serial.println(F("CE2 [14] (D48)       (D49) [15] CE3       |____________|    .|____________|."));
  Serial.println();
    
}


void checkPins()
{
  int     readback,index,i;
  String  text;
  
  for (index = 0; index <= 1; index++)
  {
    if (index == 0) text="HIGH "; else text="LOW  ";
    
    for (i = 0; i < sizeof(ctrl_pins); i++)
    {
      if (index == 0) pinMode(ctrl_pins[i], INPUT_PULLUP);
      else          { pinMode(ctrl_pins[i], OUTPUT);
                      digitalWrite(ctrl_pins[i], LOW);
      }                
      readback = digitalRead(ctrl_pins[i]);
      if (readback != index) {
        if (Verb==1) Serial.println("Check: Ctrl pin  " + String(i, DEC) + " " + text +"PASS");
        pinMode(ctrl_pins[i], OUTPUT);
        digitalWrite(ctrl_pins[i], HIGH);
      } else {
        if (Verb==1) Serial.println("Check: Ctrl pin  " + String(i, DEC) + " " + text +"FAIL");
        pinMode(ctrl_pins[i], INPUT);
        failed++;
      }
    }
    if (Verb==1) Serial.println();
  
    //set address pins as output
    for (i = 0; i < sizeof(addr_pins); i++)
    {
      if (index == 0) pinMode(addr_pins[i], INPUT_PULLUP);
      else          { pinMode(addr_pins[i], OUTPUT);
                      digitalWrite(addr_pins[i], LOW);
      }  
      if (readback != index) {
        if (Verb==1){
          if (i<10)Serial.println("Check: Addr pin  " + String(i, DEC) + " " + text +"PASS");
          else     Serial.println("Check: Addr pin "  + String(i, DEC) + " " + text +"PASS");
        }
      } else {
        if (Verb==1){
          if (i<10)Serial.println("Check: Addr pin  " + String(i, DEC) + " " + text +"FAIL");
          else     Serial.println("Check: Addr pin "  + String(i, DEC) + " " + text +"FAIL");
        }   
        pinMode(addr_pins[i], INPUT);
        failed++;
      }
    }
    if (Verb==1) Serial.println();
    
    //set I/O pins as input
    for (i = 0; i < sizeof(inout_pins); i++)
    {
      if (index == 0) pinMode(inout_pins[i], INPUT_PULLUP);
      else          { pinMode(inout_pins[i], OUTPUT);
                      digitalWrite(inout_pins[i], LOW);
      }  
      if (readback != index) {
        if (Verb==1)Serial.println("Check: I/O  pin  " + String(i, DEC) + " " + text +"PASS");
        pinMode(inout_pins[i], INPUT_PULLUP);
      } else {
        if (Verb==1)Serial.println("Check: I/O  pin  " + String(i, DEC) + " " + text +"FAIL");
        pinMode(inout_pins[i], INPUT);
        failed++;
      }
    }
    if   (Verb==1) Serial.println();
  }
  if   (Verb==0) 
  {
    if (failed == 0) Serial.println(F("Check pins PASS\n"));
    else             Serial.println(F("Check pins FAIL\n"));
  } 
}



void setAddress(int address)
{        
  for (int i = 0; i < sizeof(addr_pins); i++) 
  {
    digitalWrite(addr_pins[i], ((address >> i) & 1));
  }

}



void writeData(uint8_t data, int IC)
{

  int pinCEn;

  switch (IC) {
    case 1:
      pinCEn=pinCE1;
      break;
    case 2:
      pinCEn=pinCE2;
      break;
    case 3:
      pinCEn=pinCE3;
      break;
    case 4:
      pinCEn=pinCE4;
      break;
  }

  digitalWrite(pin_WE, LOW);
  digitalWrite(pinCEn, LOW);
  digitalWrite(pinCEX, LOW);
  
  for (int i = 0; i < sizeof(inout_pins); i++) 
  {
    pinMode(inout_pins[i], OUTPUT);
    digitalWrite(inout_pins[i], ((data >> i) & 1));
  }

  digitalWrite(pin_WE, HIGH);
  digitalWrite(pinCEn, HIGH);
  digitalWrite(pinCEX, HIGH);
    
}



int readData(int IC)
{
  int pinCEn;

  switch (IC) {
    case 1:
      pinCEn=pinCE1;
      break;
    case 2:
      pinCEn=pinCE2;
      break;
    case 3:
      pinCEn=pinCE3;
      break;
    case 4:
      pinCEn=pinCE4;
      break;
  }

  digitalWrite(pin_WE, HIGH);
  digitalWrite(pinCEn, LOW);
  digitalWrite(pinCEX, LOW);
  delayMicroseconds(1);
  uint8_t readback=0;
  
  for (int i = 0; i < sizeof(inout_pins); i++) 
  {
    pinMode(inout_pins[i], INPUT_PULLUP);
    readback = readback | ((digitalRead(inout_pins[i]) << i));
  }
  
  digitalWrite(pinCEn, HIGH);
  digitalWrite(pinCEX, HIGH);
  return readback;
}



void test(int Fill, int Verb) 
{
  if (Fill==0) Serial.println(F("Dump Entire Memory without writing on it"));
  else         Serial.println(F("Test Entire Memory"));
  
  int     Addr,index;
  uint8_t Data[]={0x55,0xAA};   //Data Fill 01010101,10101010
  uint8_t readback;
  int     tab=0;
  int     IC;
  
  PassFail[0] = 1;
  PassFail[1] = 1;
  PassFail[2] = 1;
  PassFail[3] = 1;

  for (IC=1; IC <= 4; IC++){
    for (index=0; index <= 1; index++){
      if ((Fill==0)&&(index==1)) break;
      Serial.print("IC"+String(IC)+" Content");
      if  (Fill==1) Serial.println(" after filling it with 0x" + String(Data[index],HEX));
      else          Serial.println();
      for (Addr=0; Addr <= max_addr; Addr++)
      {
        if((tab==0)&&(Verb==1)){
          if(Addr<=0xF) Serial.print("00");
          else if(Addr<=0xFF) Serial.print("0");
          Serial.print(Addr,HEX);
          Serial.print(" ");
        }
        setAddress(Addr);
        if (Fill==1) writeData(Data[index],IC);
        readback = readData(IC);

        if ((Fill==1)&&(Verb==1)){
          if (readback == Data[index]) Serial.print(" ");
          else
          { 
            Serial.print("x"); 
            failed++;
            PassFail[IC-1]=0;
          }
        }
        else if ((Fill==1)&&(Verb==0)){
          if (readback != Data[index])
          { 
            failed++;
            PassFail[IC-1]=0;
          }
        }  
        else if ((Fill==0)&&(Verb==1)) Serial.print(" ");
        
        if (Verb==1){
          if(readback<=0xF) Serial.print("0");
          Serial.print(readback,HEX);
          tab++;
          if(tab==32){
            tab=0;
            Serial.println();
          }
        }

        
      }
      if (Verb==1) Serial.println();
    }
  }
  if (Verb==0) Serial.println();
}




void loop()
{
  checkPins();
    
  if (failed == 0) 
  {
    digitalWrite(LED_BUILTIN, HIGH);
    unsigned long duration = millis();
    test(Fill,Verb);  
    duration = millis() - duration;
    Serial.println("Duration: " + String(duration/1000, DEC)+"S");
  }

  for (int IC=1; IC <= 4; IC++){
    if (PassFail[IC-1]) Serial.println("IC"+String(IC)+" PASS");
    else                Serial.println("IC"+String(IC)+" FAIL");
  }
  
  if (failed > 0) 
  {
    Serial.println("Result: FAIL (" + String(failed, DEC) + ")");
    digitalWrite(LED_BUILTIN, LOW);
  } 
  else 
  {
    Serial.println("Result: PASS");
  }

  while(1);
}