// parts of this sketch were adapted from https://github.com/bienata/CA80/tree/master/ca80loader_ardu , credits to Natasza // // this sketch controls the EPROM memory Emulator hardware described on https://mygeekyhobby.com/2020/07/05/eprom-emulator/ // // #include #include #define DATA 3 // data pin for shift register handling D0-D7 #define ADLO 4 // data pin for shift register handling A0-A7 #define ADHI 5 // data pin for shift register handling A8-A15 #define SCLK 7 // clock ping for all shift registers #define DAT_LD 6 // latch pin for all shift registers #define EN_RST 2 // internal / external bus control pin #define WE 8 // SRAM Write Enable Pin #define LD_BTN 9 // load data from SPI button #define LED_G A0 // RED LED // Gating signals - ignores selected A11 - A15 address pins besed on EPROM Type #define EN_A11 A1 // A11 data pin gating signal #define EN_A12 A2 // A12 data pin gating signal #define EN_A13 A3 // A13 data pin gating signal #define EN_A14 A4 // A14 data pin gating signal #define EN_A15 A5 // A15 data pin gating signal // SPI EEPROM stuff #define DATAOUT 11 //MOSI #define DATAIN 12 //MISO #define SPICLOCK 13 //sck #define SLAVESELECT 10 //ss // SPI opcodes - taken from Arduino SPI examples #define WREN 6 #define WRDI 4 #define RDSR 5 #define WRSR 1 #define READ 3 #define WRITE 2 // utility byte byte clr; // spi_address tracking unsigned int spi_address = 0; // SPI_EEPROM address unsigned int page_ctn = 0; //data buffer for SPI write char buffer [128] = ""; // configuration addresses in internal EEPROM int mem_cfg = 0; // EEPROM address where we store last used EPROM information (value 1 = 2716, 2 = 2732, 3 = 2764, 4 = 27128, 5 = 27256, 6 = 27512) int save_cfg = 1; // EEPROM address where we store settings for "SAVE to SPI EEPROM option" (value 1 = yes, 2 = no) int auto_cfg = 2; // EEPROM address where we store settings for "Auto load from SPI EEPROM" option (value 1 = yes, 2 = no) // global config settings bool saveSPI; bool autoLoad; unsigned int lastEPROM; const char HW_ver[] = "1.0b"; const char FW_ver[] = "1.5"; // void setup() { pinMode( EN_RST, OUTPUT ); digitalWrite( EN_RST, HIGH ); // disable outputs to start with AD0-15 from outside pinMode( DATA, OUTPUT ); digitalWrite( DATA, LOW ); pinMode( ADLO, OUTPUT ); digitalWrite( ADLO, LOW ); pinMode( ADHI, OUTPUT ); digitalWrite( ADHI, LOW ); pinMode( DAT_LD, OUTPUT ); digitalWrite( DAT_LD, LOW ); pinMode( SCLK, OUTPUT ); digitalWrite( SCLK, LOW ); pinMode( WE, OUTPUT ); digitalWrite( WE, HIGH ); pinMode( LED_G, OUTPUT ); digitalWrite( LED_G, LOW ); // address lines gate signals as outputs pinMode( EN_A11, OUTPUT ); pinMode( EN_A12, OUTPUT ); pinMode( EN_A13, OUTPUT ); pinMode( EN_A14, OUTPUT ); pinMode( EN_A15, OUTPUT ); setBus(6); // default settings to enable all address lines 6 = 27512 memory code // SPI EEPROM pins pinMode(DATAOUT, OUTPUT); pinMode(DATAIN, INPUT); pinMode(SPICLOCK,OUTPUT); pinMode(SLAVESELECT,OUTPUT); digitalWrite(SLAVESELECT,HIGH); //disable device // SPCR = 01010000 //interrupt disabled,spi enabled,msb 1st,master,clk low when idle, //sample on leading edge of clk,system clock/4 rate (fastest) SPCR = (1< 6) || (EEPROM.read(mem_cfg) < 1)) { EEPROM.write(mem_cfg,6); // set default to 27512 Serial.println("Setting default EPROM Memory to 27512"); } else { lastEPROM = EEPROM.read(mem_cfg) ; Serial.println(String("Config: Last EPROM used is ") + lastEPROM); } switch (EEPROM.read(save_cfg)) { case 0: saveSPI = false; Serial.println("Option: Save to SPI EEPROM set to disable"); break; case 1: saveSPI = true; Serial.println("Option: Save to SPI EEPROM set to enable"); break; default: EEPROM.write(save_cfg,0); Serial.println("Default: disabling save to spi"); break; } switch (EEPROM.read(auto_cfg)) { case 0: autoLoad = false; Serial.println("Option: Auto load from SPI EEPROM set to disable"); break; case 1: autoLoad = true; Serial.println("Option: Auto load from SPI EEPROM set to enable"); break; default: EEPROM.write(auto_cfg,0); Serial.println("Default: Disabling Auto load from SPI EEPROM "); break; } // load data from SPI EEPROM if configured if (autoLoad == true ) { load_SPIEEPROM(lastEPROM); } // Serial.println(""); Serial.println("Emulator Started, waiting for computer contol"); printabout(); } void load_SPIEEPROM (int eprom) { setBus(6); // allows access to enire 64kb SRAM Memory - equivlet of emulating 27512 eprom digitalWrite( EN_RST, HIGH ); // enable writing to SRAM / RESET traget digitalWrite( LED_G, LOW ); unsigned int max_size; switch (eprom) { case 1: Serial.println( "-> Loading 2716 EPROM from SPI" ); max_size = 16; // pages of 128 bytes break; case 2: Serial.println( "-> Loading 2732 EPROM from SPI" ); max_size = 32; // pages of 128 bytes break; case 3: Serial.println( "-> Loading 2764 EPROM from SPI" ); max_size = 64; // pages of 128 bytes break; case 4: Serial.println( "-> Loading 27128 EPROM from SPI" ); max_size = 128; // pages of 128 bytes break; case 5: Serial.println( "-> Loading 27256 EPROM from SPI" ); max_size = 256; // pages of 128 bytes break; case 6: Serial.println( "-> Loading 27512 EPROM from SPI" ); max_size = 512; // pages of 128 bytes break; default: Serial.println( "-> Default: Loading 27512 EPROM from SPI" ); max_size = 512; // pages of 128 bytes break; } // now load this many pages from SPI EEPROM //int data; byte data; int clmn = 0; for ( int page = 0; page <= max_size; page++) { //Serial.println(String("Reading SPI Page: ")+page); //Serial.println(String("Address: ")+spi_address); digitalWrite(LED_G, !digitalRead(LED_G)); // flash LED while loading digitalWrite(SLAVESELECT,LOW); // select SPI EEPROM spi_transfer(READ); //transmit read opcode spi_transfer((char)(spi_address>>8)); //send MSByte address first spi_transfer((char)(spi_address)); //send LSByte address for (int i=0; i<128; i++) { data = spi_transfer(0xFF); //get data byte by trasfering dummy 0xff data to spi eeprom writeMemoryLocation((short)(spi_address),(char)(data)); spi_address++; //if (data < 16) { // Serial.print("0"); //} //Serial.print(data, HEX); //Serial.print(" "); } digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer Serial.print("."); clmn ++; if (clmn > 64) { Serial.println(""); clmn = 0; } } Serial.println(""); setBus(eprom); // now that SRAM is loaded with data, we can enable correct address lines digitalWrite( EN_RST, LOW ); digitalWrite( LED_G, HIGH ); Serial.println("Done loading from SPI EEPROM to SRAM" ); } // // saves byte to 128 page buffer, saves page to EEPROM after each 128 bytes // // can't selectivly write data, only blocks of 128 bytes starting from address 0x0000h and up to 0xFFFFh void saveToSPI (char data) { // write to SPI buffer buffer[page_ctn] = data; page_ctn++; if(page_ctn == 128) { // send write enable command to unlock writting to EEPROM digitalWrite(SLAVESELECT,LOW); spi_transfer(WREN); digitalWrite(SLAVESELECT,HIGH); // write command digitalWrite(SLAVESELECT,LOW); spi_transfer(WRITE); //write instruction // set start address spi_transfer((char)(spi_address>>8)); spi_transfer((char)(spi_address)); // send all 128 bytes for (int k=0;k<128;k++) { spi_transfer(buffer[k]); } digitalWrite(SLAVESELECT,HIGH); // wait for write to finish, for 25LC512 Twc = 5ms delay(6); // page_ctn = 0; spi_address = spi_address + 128; } return; } // SPI EEPROM transfer routine char spi_transfer(volatile char data) { SPDR = data; // Start the transmission while (!(SPSR & (1< HW: "); Serial.print(HW_ver); Serial.print(" @ 115200, FW: "); Serial.print(FW_ver); Serial.print(", SPI: "); Serial.print(saveSPI); Serial.print(", Auto: "); Serial.print(autoLoad); Serial.print(", Last EPROM: "); Serial.print(lastEPROM); Serial.println(", mygeekyhobby.com 2020 :)"); } // Adjusts address lanes A11 - A15, based on imput EPROM type is 2716 = 1 ... 27512 = 6 void setBus (int eprom){ switch (eprom) { case 1: Serial.println( "-> Bus set to 2716 EPROM Memory" ); digitalWrite( EN_A11, LOW ); digitalWrite( EN_A12, LOW ); digitalWrite( EN_A13, LOW ); digitalWrite( EN_A14, LOW ); digitalWrite( EN_A15, LOW ); break; case 2: Serial.println( "-> Bus set to 2732 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, LOW ); digitalWrite( EN_A13, LOW ); digitalWrite( EN_A14, LOW ); digitalWrite( EN_A15, LOW ); break; case 3: Serial.println( "-> Bus set to 2764 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, HIGH ); digitalWrite( EN_A13, LOW ); digitalWrite( EN_A14, LOW ); digitalWrite( EN_A15, LOW ); break; case 4: Serial.println( "-> Bus set to 27128 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, HIGH ); digitalWrite( EN_A13, HIGH ); digitalWrite( EN_A14, LOW ); digitalWrite( EN_A15, LOW ); break; case 5: Serial.println( "-> Bus set to 27256 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, HIGH ); digitalWrite( EN_A13, HIGH ); digitalWrite( EN_A14, HIGH ); digitalWrite( EN_A15, LOW ); break; case 6: Serial.println( "-> Bus set to 27512 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, HIGH ); digitalWrite( EN_A13, HIGH ); digitalWrite( EN_A14, HIGH ); digitalWrite( EN_A15, HIGH ); break; default: Serial.println( "-> Bus set to 27512 EPROM Memory" ); digitalWrite( EN_A11, HIGH ); digitalWrite( EN_A12, HIGH ); digitalWrite( EN_A13, HIGH ); digitalWrite( EN_A14, HIGH ); digitalWrite( EN_A15, HIGH ); break; } return; } // Write single byte of data to SRAM and optionally to SPI_EEPROM void writeMemoryLocation (unsigned short address, unsigned char data ) { unsigned char addressHi = address >> 8 ; unsigned char addressLo = address & 0xFF ; unsigned char memData = data ; // send data for (int i = 0; i < 8 ; i++ ) { digitalWrite( DATA, 0x80 & (memData << i) ); digitalWrite( ADLO, 0x80 & (addressLo << i)); digitalWrite( ADHI, 0x80 & (addressHi << i) ); digitalWrite( SCLK, HIGH ); digitalWrite( SCLK, LOW ); } digitalWrite( DAT_LD, HIGH ); // latch data and address out digitalWrite( DAT_LD, LOW ); // at this point we have data and address loaded into the shift registers // now we can request a write to SRAM memory digitalWrite( WE, LOW ); digitalWrite( WE, HIGH ); } // process recieved data frame from PC char ch = 0; String rxFrame = ""; void processFrame(const char *pS ) { char szTemp[5] = ""; int recLen = 0; unsigned char data = 0; unsigned short address = 0; strncpy(szTemp, pS+1, 2 ); szTemp[ 2 ] = 0x00; recLen = (int)strtol( szTemp, 0, 16 ); strncpy(szTemp, pS+3, 4 ); szTemp[ 4 ] = 0x00; address = (unsigned short)strtol( szTemp, 0, 16 ); for (int i = 0; i < recLen; i++ ) { strncpy(szTemp, pS+9 + i*2, 2 ); szTemp[ 2 ] = 0x00; data = (unsigned char)strtol( szTemp, 0, 16 ); writeMemoryLocation( address + i , data ); // optionally write to SPI buffer if (saveSPI) {saveToSPI(data);} } } int isHexFrame ( const char *f ) { return (f[0] == ':' ) && (f[7] == '0') && (f[8] == '0'); // ":" iHEX, record type 00 = Data } // main program loop void loop() { if (digitalRead(LD_BTN) == LOW) { delay(500); if (digitalRead(LD_BTN) == HIGH) { // short press will load from SPI_EEPROM Serial.println("Load from SPI EEPROM triggered by pushbutton"); load_SPIEEPROM(lastEPROM); delay(5000); } else { // long press disable AutoLoad, otherwise you can't upload due to auto-reset when arduino port opens on computer EEPROM.write(auto_cfg,0); autoLoad = false; Serial.println("Auto load disabled by pushbutton"); delay(5000); } } if (Serial.available() > 0) { char ch = Serial.read(); if ( ch == 0x0D /*CR*/) { return; } if ( ch == 0x0A /*LF*/) { // start emulation if ( rxFrame == ":00000001FF" ) { digitalWrite( EN_RST, LOW ); digitalWrite( LED_G, HIGH ); Serial.println("-> Emulator Running."); spi_address = 0; } // get sync if ( rxFrame == ":dml" ) { digitalWrite( EN_RST, HIGH ); printabout(); } if ( rxFrame == ":ini16" ) { setBus(1); EEPROM.update(mem_cfg,1); } if ( rxFrame == ":ini32" ) { setBus(2); EEPROM.update(mem_cfg,2); } if ( rxFrame == ":ini64" ) { setBus(3); EEPROM.update(mem_cfg,3); } if ( rxFrame == ":ini128" ) { setBus(4); EEPROM.update(mem_cfg,4); } if ( rxFrame == ":ini256" ) { setBus(5); EEPROM.update(mem_cfg,5); } if ( rxFrame == ":ini512" ) { setBus(6); EEPROM.update(mem_cfg,6); } // data will be also written to SPI if ( rxFrame == ":iniSPI1" ) { saveSPI = true; EEPROM.update(save_cfg,1); Serial.println("-> SPI save ON"); } // data will be also written to SPI if ( rxFrame == ":iniSPI0" ) { saveSPI = false; EEPROM.update(save_cfg,0); Serial.println("-> SPI save OFF"); } // // data will be also written to SPI if ( rxFrame == ":iniAuto1" ) { autoLoad = true; EEPROM.update(auto_cfg,1); Serial.println("-> Auto Load ON"); } // data will be also written to SPI if ( rxFrame == ":iniAuto0" ) { autoLoad = false; EEPROM.update(auto_cfg,0); Serial.println("-> Auto Load OFF"); } if (isHexFrame( rxFrame.c_str() ) ) { processFrame( rxFrame.c_str() ); Serial.println( "> " + rxFrame.substring(3, 7) ); } rxFrame = ""; } else { rxFrame += ch; } } }