//============================================================================ // // SSSS tt lll lll // SS SS tt ll ll // SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // // Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. //============================================================================ #include #include "System.hxx" #include "Settings.hxx" #include "MT24LC256.hxx" #define DEBUG_EEPROM 0 #if DEBUG_EEPROM char jpee_msg[256]; #define JPEE_LOG0(msg) jpee_logproc(msg) #define JPEE_LOG1(msg,arg1) sprintf(jpee_msg,(msg),(arg1)), jpee_logproc(jpee_msg) #define JPEE_LOG2(msg,arg1,arg2) sprintf(jpee_msg,(msg),(arg1),(arg2)), jpee_logproc(jpee_msg) #else #define JPEE_LOG0(msg) { } #define JPEE_LOG1(msg,arg1) { } #define JPEE_LOG2(msg,arg1,arg2) { } #endif /* State values for I2C: 0 - Idle 1 - Byte going to chip (shift left until bit 8 is set) 2 - Chip outputting acknowledgement 3 - Byte coming in from chip (shift left until lower 8 bits are clear) 4 - Chip waiting for acknowledgement */ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MT24LC256::MT24LC256(const string& filename, const System& system) : mySystem(system), mySDA(false), mySCL(false), myTimerActive(false), myCyclesWhenTimerSet(0), myCyclesWhenSDASet(0), myCyclesWhenSCLSet(0), myDataFile(filename), myDataFileExists(false), myDataChanged(false), jpee_mdat(0), jpee_sdat(0), jpee_mclk(0), jpee_sizemask(0), jpee_pagemask(0), jpee_smallmode(0), jpee_logmode(0), jpee_pptr(0), jpee_state(0), jpee_nb(0), jpee_address(0), jpee_ad_known(0) { // Load the data from an external file (if it exists) ifstream in(myDataFile, std::ios_base::binary); if(in.is_open()) { // Get length of file; it must be 32768 in.seekg(0, std::ios::end); if(uInt32(in.tellg()) == FLASH_SIZE) { in.seekg(0, std::ios::beg); in.read(reinterpret_cast(myData), FLASH_SIZE); myDataFileExists = true; } } else myDataFileExists = false; // Then initialize the I2C state jpee_init(); systemReset(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MT24LC256::~MT24LC256() { // Save EEPROM data to external file only when necessary if(!myDataFileExists || myDataChanged) { ofstream out(myDataFile, std::ios_base::binary); if(out.is_open()) out.write(reinterpret_cast(myData), FLASH_SIZE); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::writeSDA(bool state) { mySDA = state; myCyclesWhenSDASet = mySystem.cycles(); update(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::writeSCL(bool state) { mySCL = state; myCyclesWhenSCLSet = mySystem.cycles(); update(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::update() { #define jpee_clock(x) ( (x) ? \ (jpee_mclk = 1) : \ (jpee_mclk && (jpee_clock_fall(),1), jpee_mclk = 0)) #define jpee_data(x) ( (x) ? \ (!jpee_mdat && jpee_sdat && jpee_mclk && (jpee_data_stop(),1), jpee_mdat = 1) : \ (jpee_mdat && jpee_sdat && jpee_mclk && (jpee_data_start(),1), jpee_mdat = 0)) // These pins have to be updated at the same time // However, there's no guarantee that the writeSDA() and writeSCL() // methods will be called at the same time or in the correct order, so // we only do the write when they have the same 'timestamp' if(myCyclesWhenSDASet == myCyclesWhenSCLSet) { #if DEBUG_EEPROM cerr << endl << " I2C_PIN_WRITE(SCL = " << mySCL << ", SDA = " << mySDA << ")" << " @ " << mySystem.cycles() << endl; #endif jpee_clock(mySCL); jpee_data(mySDA); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::systemReset() { std::fill(myPageHit, myPageHit + PAGE_NUM, false); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::eraseAll() { memset(myData, INIT_VALUE, FLASH_SIZE); myDataChanged = true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::eraseCurrent() { for(uInt32 page = 0; page < PAGE_NUM; ++page) { if(myPageHit[page]) { memset(myData + page * PAGE_SIZE, INIT_VALUE, PAGE_SIZE); myDataChanged = true; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool MT24LC256::isPageUsed(uInt32 page) const { if(page < PAGE_NUM) return myPageHit[page]; else return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::jpee_init() { jpee_sdat = 1; jpee_address = 0; jpee_state=0; jpee_sizemask = FLASH_SIZE - 1; jpee_pagemask = PAGE_SIZE - 1; jpee_smallmode = 0; jpee_logmode = -1; if(!myDataFileExists) memset(myData, INIT_VALUE, FLASH_SIZE); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::jpee_data_start() { /* We have a start condition */ if (jpee_state == 1 && (jpee_nb != 1 || jpee_pptr != 3)) { JPEE_LOG0("I2C_WARNING ABANDON WRITE"); jpee_ad_known = 0; } if (jpee_state == 3) { JPEE_LOG0("I2C_WARNING ABANDON READ"); } if (!jpee_timercheck(0)) { JPEE_LOG0("I2C_START"); jpee_state = 2; } else { JPEE_LOG0("I2C_BUSY"); jpee_state = 0; } jpee_pptr = 0; jpee_nb = 0; jpee_packet[0] = 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void MT24LC256::jpee_data_stop() { if (jpee_state == 1 && jpee_nb != 1) { JPEE_LOG0("I2C_WARNING ABANDON_WRITE"); jpee_ad_known = 0; } if (jpee_state == 3) { JPEE_LOG0("I2C_WARNING ABANDON_READ"); jpee_ad_known = 0; } /* We have a stop condition. */ if (jpee_state == 1 && jpee_nb == 1 && jpee_pptr > 3) { jpee_timercheck(1); JPEE_LOG2("I2C_STOP(Write %d bytes at %04X)",jpee_pptr-3,jpee_address); if (((jpee_address + jpee_pptr-4) ^ jpee_address) & ~jpee_pagemask) { jpee_pptr = 4+jpee_pagemask-(jpee_address & jpee_pagemask); JPEE_LOG1("I2C_WARNING PAGECROSSING!(Truncate to %d bytes)",jpee_pptr-3); } for (int i=3; i> 1) & 7; if (jpee_packet[1] != (jpee_address >> 8) && (jpee_packet[0] & 1)) JPEE_LOG0("I2C_WARNING ADDRESS MSB CHANGED"); jpee_nb &= 0x1A1; } if (jpee_nb == 0x1A0) { JPEE_LOG1("I2C_SENT(%02X--start write)",jpee_packet[0]); jpee_state = 2; jpee_sdat = 0; } else if (jpee_nb == 0x1A1) { jpee_state = 4; JPEE_LOG2("I2C_SENT(%02X--start read @%04X)", jpee_packet[0],jpee_address); if (!jpee_ad_known) JPEE_LOG0("I2C_WARNING ADDRESS IS UNKNOWN"); jpee_sdat = 0; } else { JPEE_LOG1("I2C_WARNING ODDBALL FIRST BYTE!(%02X)",jpee_nb & 0xFF); jpee_state = 0; } } else { jpee_state = 2; jpee_sdat = 0; } } break; case 2: if (jpee_nb) { if (!jpee_pptr) { jpee_packet[0] = uInt8(jpee_nb); if (jpee_smallmode) jpee_pptr=2; else jpee_pptr=1; } else if (jpee_pptr < 70) { JPEE_LOG1("I2C_SENT(%02X)",jpee_nb & 0xFF); jpee_packet[jpee_pptr++] = uInt8(jpee_nb); jpee_address = (jpee_packet[1] << 8) | jpee_packet[2]; if (jpee_pptr > 2) jpee_ad_known = 1; } else JPEE_LOG0("I2C_WARNING OUTPUT_OVERFLOW!"); } jpee_sdat = 1; jpee_nb = 1; jpee_state=1; break; case 4: if (jpee_mdat && jpee_sdat) { JPEE_LOG0("I2C_READ_NAK"); jpee_state=0; break; } jpee_state=3; myPageHit[jpee_address / PAGE_SIZE] = true; { bool devSettings = mySystem.oSystem().settings().getBool("dev.settings"); if(mySystem.oSystem().settings().getBool(devSettings ? "dev.eepromaccess" : "plr.eepromaccess")) mySystem.oSystem().frameBuffer().showMessage("AtariVox/SaveKey EEPROM read"); } jpee_nb = (myData[jpee_address & jpee_sizemask] << 1) | 1; /* Fall through */ JPEE_LOG2("I2C_READ(%04X=%02X)",jpee_address,jpee_nb/2); [[fallthrough]]; case 3: jpee_sdat = !!(jpee_nb & 256); jpee_nb <<= 1; if (!(jpee_nb & 510)) { jpee_state = 4; jpee_sdat = 1; ++jpee_address; } break; default: /* Do nothing */ break; } JPEE_LOG2("I2C_CLOCK (dat=%d/%d)",jpee_mdat,jpee_sdat); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool MT24LC256::jpee_timercheck(int mode) { /* Evaluate how long the EEPROM is busy. When invoked with an argument of 1, start a timer (probably about 5 milliseconds); when invoked with an argument of 0, return zero if the timer has expired or non-zero if it is still running. */ if(mode) // set timer { myCyclesWhenTimerSet = mySystem.cycles(); return myTimerActive = true; } else // read timer { if(myTimerActive) { uInt64 elapsed = mySystem.cycles() - myCyclesWhenTimerSet; myTimerActive = elapsed < uInt64(5000000.0 / 838.0); } return myTimerActive; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - int MT24LC256::jpee_logproc(char const *st) { cerr << " " << st << endl; return 0; }