openMSX
EEPROM_93C46.cc
Go to the documentation of this file.
1 #include "EEPROM_93C46.hh"
2 #include "serialize.hh"
3 
4 namespace openmsx {
5 
7  : sram(NUM_ADDRESSES, xml, SRAM::DontLoadTag{})
8 {
9 }
10 
11 EEPROM_93C46::EEPROM_93C46(const std::string& name, const DeviceConfig& config)
12  : sram(name + " EEPROM", NUM_ADDRESSES, config)
13 {
14 }
15 
17 {
18  completionTime = EmuTime::zero;
19  state = IN_RESET;
20  writeProtected = true;
21  bits = 0;
22  address = 0;
23  shiftRegister = 0;
24 }
25 
26 uint8_t EEPROM_93C46::read(unsigned addr)
27 {
28  return sram[addr];
29 }
30 
31 void EEPROM_93C46::write(unsigned addr, uint8_t value, EmuTime::param time)
32 {
33  // The ST M93C46 datasheet documents that erase before write is not
34  // needed.
35  sram.write(addr, value); // not: sram[addr] & value
36  completionTime = time + EmuDuration::usec(1750);
37 }
38 
39 void EEPROM_93C46::writeAll(uint8_t value, EmuTime::param time)
40 {
41  sram.memset(0, value, NUM_ADDRESSES);
42  //for (auto addr : xrange(NUM_ADDRESSES)) {
43  // sram.write(addr, sram[addr] & value);
44  //}
45  completionTime = time + EmuDuration::usec(8000);
46 }
47 
48 void EEPROM_93C46::erase(unsigned addr, EmuTime::param time)
49 {
50  sram.write(addr, 255);
51  completionTime = time + EmuDuration::usec(1000);
52 }
53 
54 void EEPROM_93C46::eraseAll(EmuTime::param time)
55 {
56  sram.memset(0, 255, NUM_ADDRESSES);
57  completionTime = time + EmuDuration::usec(8000);
58 }
59 
60 bool EEPROM_93C46::ready(EmuTime::param time) const
61 {
62  return time >= completionTime;
63 }
64 
65 bool EEPROM_93C46::read_DO(EmuTime::param time) const
66 {
67  if (state == READING_DATA) {
68  return shiftRegister & (1 << (SHIFT_REG_BITS - 1));
69  } else if (state == WAIT_FOR_START_BIT) {
70  return ready(time);
71  } else {
72  return true;
73  }
74 }
75 
76 void EEPROM_93C46::write_CS(bool value, EmuTime::param time)
77 {
78  if (pinCS == value) return;
79  pinCS = value;
80 
81  if (pinCS) {
82  csTime = time; // see clockEvent()
83 
84  assert(state == IN_RESET);
85  state = WAIT_FOR_START_BIT;
86  } else {
87  state = IN_RESET;
88  }
89 }
90 
91 void EEPROM_93C46::write_CLK(bool value, EmuTime::param time)
92 {
93  if (pinCLK == value) return;
94  pinCLK = value;
95 
96  if (pinCLK) {
97  clockEvent(time);
98  }
99 }
100 
101 void EEPROM_93C46::write_DI(bool value, EmuTime::param /*time*/)
102 {
103  pinDI = value;
104 }
105 
106 void EEPROM_93C46::clockEvent(EmuTime::param time)
107 {
108  switch (state) {
109  case IN_RESET:
110  // nothing
111  break;
112 
113  case WAIT_FOR_START_BIT:
114  // Ignore simultaneous rising CLK and CS edge.
115  if (pinDI && ready(time) && (time > csTime)) {
116  shiftRegister = 0;
117  bits = 0;
118  state = WAIT_FOR_COMMAND;
119  }
120  break;
121 
122  case WAIT_FOR_COMMAND:
123  shiftRegister = (shiftRegister << 1) | int(pinDI);
124  ++bits;
125  if (bits == (2 + ADDRESS_BITS)) {
126  execute_command(time);
127  }
128  break;
129 
130  case READING_DATA:
131  if ((bits % DATA_BITS) == 0) {
132  uint8_t value = read(address);
133  shiftRegister = value << (SHIFT_REG_BITS - DATA_BITS);
134  address = (address + 1) & ADDRESS_MASK;
135  } else {
136  shiftRegister <<= 1;
137  }
138  ++bits;
139  break;
140 
141  case WAIT_FOR_WRITE:
142  shiftRegister = (shiftRegister << 1) | int(pinDI);
143  ++bits;
144  if (bits == DATA_BITS) {
145  if (writeProtected) {
146  state = IN_RESET;
147  break;
148  }
149  write(address, shiftRegister, time);
150  state = IN_RESET;
151  }
152  break;
153 
154  case WAIT_FOR_WRITEALL:
155  shiftRegister = (shiftRegister << 1) | int(pinDI);
156  ++bits;
157  if (bits == DATA_BITS) {
158  if (writeProtected) {
159  state = IN_RESET;
160  break;
161  }
162  writeAll(shiftRegister, time);
163  state = IN_RESET;
164  }
165  break;
166  }
167 }
168 
169 void EEPROM_93C46::execute_command(EmuTime::param time)
170 {
171  bits = 0;
172  address = shiftRegister & ADDRESS_MASK;
173 
174  switch ((shiftRegister >> ADDRESS_BITS) & 3) {
175  case 0:
176  switch (address >> (ADDRESS_BITS - 2)) {
177  case 0: // LOCK
178  writeProtected = true;
179  state = IN_RESET;
180  break;
181 
182  case 1: // WRITEALL
183  shiftRegister = 0;
184  state = WAIT_FOR_WRITEALL;
185  break;
186 
187  case 2: // ERASEALL
188  if (writeProtected) {
189  state = IN_RESET;
190  break;
191  }
192  eraseAll(time);
193  state = IN_RESET;
194  break;
195 
196  case 3: // UNLOCK
197  writeProtected = false;
198  state = IN_RESET;
199  break;
200  }
201  break;
202 
203  case 1: // WRITE
204  shiftRegister = 0;
205  state = WAIT_FOR_WRITE;
206  break;
207 
208  case 2: // READ
209  // Data is fetched after first CLK. Reset the shift register to
210  // 0 to simulate the dummy 0 bit that happens prior to the
211  // first clock
212  shiftRegister = 0;
213  state = READING_DATA;
214  break;
215 
216  case 3: // ERASE
217  if (writeProtected) {
218  state = IN_RESET;
219  break;
220  }
221  erase(address, time);
222  state = IN_RESET;
223  break;
224  }
225 }
226 
227 static std::initializer_list<enum_string<EEPROM_93C46::State>> stateInfo = {
228  { "IN_RESET", EEPROM_93C46::IN_RESET },
229  { "WAIT_FOR_START_BIT", EEPROM_93C46::WAIT_FOR_START_BIT },
230  { "WAIT_FOR_COMMAND", EEPROM_93C46::WAIT_FOR_COMMAND },
231  { "READING_DATA", EEPROM_93C46::READING_DATA },
232  { "WAIT_FOR_WRITE", EEPROM_93C46::WAIT_FOR_WRITE },
233  { "WAIT_FOR_WRITEALL", EEPROM_93C46::WAIT_FOR_WRITEALL },
234 };
236 
237 template<typename Archive>
238 void EEPROM_93C46::serialize(Archive& ar, unsigned /*version*/)
239 {
240  ar.serialize("sram", sram);
241  ar.serialize("completionTime", completionTime);
242  ar.serialize("csTime", csTime);
243  ar.serialize("state", state);
244  ar.serialize("shiftRegister", shiftRegister);
245  ar.serialize("bits", bits);
246  ar.serialize("address", address);
247  ar.serialize("pinCS", pinCS);
248  ar.serialize("pinCLK", pinCLK);
249  ar.serialize("pinDI", pinDI);
250  ar.serialize("writeProtected", writeProtected);
251 }
253 
254 } // namespace openmsx
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
bool read_DO(EmuTime::param time) const
Definition: EEPROM_93C46.cc:65
static constexpr uint32_t NUM_ADDRESSES
Definition: EEPROM_93C46.hh:18
void serialize(Archive &ar, unsigned version)
void write(unsigned addr, byte value)
Definition: SRAM.cc:67
void memset(unsigned addr, byte c, unsigned size)
Definition: SRAM.cc:76
EEPROM_93C46(const XMLElement &xml)
Definition: EEPROM_93C46.cc:6
static constexpr uint32_t ADDRESS_MASK
Definition: EEPROM_93C46.hh:19
static constexpr uint8_t ADDRESS_BITS
Definition: EEPROM_93C46.hh:17
static constexpr uint8_t DATA_BITS
Definition: EEPROM_93C46.hh:20
void write_CLK(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:91
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
static EmuDuration usec(unsigned x)
Definition: EmuDuration.hh:40
void write_DI(bool value, EmuTime::param time)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
void write_CS(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:76