Mesen/Core/Eeprom24C01.h

131 lines
3.1 KiB
C++

#pragma once
#include "stdafx.h"
#include "BaseEeprom24C0X.h"
#include "BatteryManager.h"
class Eeprom24C01 : public BaseEeprom24C0X
{
private:
void WriteBit(uint8_t &dest, uint8_t value)
{
if(_counter < 8) {
uint8_t mask = ~(1 << _counter);
dest = (dest & mask) | (value << _counter);
_counter++;
}
}
void ReadBit()
{
if(_counter < 8) {
_output = (_data & (1 << _counter)) ? 1 : 0;
_counter++;
}
}
public:
Eeprom24C01(shared_ptr<Console> console)
{
_console = console;
_console->InitializeRam(_romData, 128);
_console->GetBatteryManager()->LoadBattery(".eeprom128", _romData, 128);
}
~Eeprom24C01()
{
SaveBattery();
}
void SaveBattery() override
{
_console->GetBatteryManager()->SaveBattery(".eeprom128", _romData, 128);
}
void Write(uint8_t scl, uint8_t sda) override
{
if(_prevScl && scl && sda < _prevSda) {
//"START is identified by a high to low transition of the SDA line while the clock SCL is *stable* in the high state"
_mode = Mode::Address;
_address = 0;
_counter = 0;
_output = 1;
} else if(_prevScl && scl && sda > _prevSda) {
//"STOP is identified by a low to high transition of the SDA line while the clock SCL is *stable* in the high state"
_mode = Mode::Idle;
_output = 1;
} else if(scl > _prevScl) {
//Clock rise
switch(_mode) {
case Mode::Address:
//To initiate a write operation, the master sends a start condition followed by a seven bit word address and a write bit.
if(_counter < 7) {
WriteBit(_address, sda);
} else if(_counter == 7) {
//8th bit to determine if we're in read or write mode
_counter = 8;
if(sda) {
//Read mode
_nextMode = Mode::Read;
_data = _romData[_address & 0x7F];
} else {
_nextMode = Mode::Write;
}
}
break;
case Mode::SendAck: _output = 0; break;
case Mode::Read: ReadBit(); break;
case Mode::Write: WriteBit(_data, sda); break;
case Mode::WaitAck:
if(!sda) {
//We expected an ack, but received something else, return to idle mode
_nextMode = Mode::Idle;
}
break;
default: break;
}
} else if(scl < _prevScl) {
//Clock fall
switch(_mode) {
case Mode::Address:
if(_counter == 8) {
//After receiving the address, "the X24C01 responds with an acknowledge, then waits for eight bits of data"
_mode = Mode::SendAck;
_output = 1;
}
break;
case Mode::SendAck:
//After sending an ack, move to the next mode of operation
_mode = _nextMode;
_counter = 0;
_output = 1;
break;
case Mode::Read:
if(_counter == 8) {
//After sending all 8 bits, wait for an ack
_mode = Mode::WaitAck;
_address = (_address + 1) & 0x7F;
}
break;
case Mode::Write:
if(_counter == 8) {
//After receiving all 8 bits, send an ack and then wait
_mode = Mode::SendAck;
_nextMode = Mode::Idle;
_romData[_address & 0x7F] = _data;
_address = (_address + 1) & 0x7F;
}
break;
default: break;
}
}
_prevScl = scl;
_prevSda = sda;
}
};