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  "completionTime", completionTime,
242  "csTime", csTime,
243  "state", state,
244  "shiftRegister", shiftRegister,
245  "bits", bits,
246  "address", address,
247  "pinCS", pinCS,
248  "pinCLK", pinCLK,
249  "pinDI", pinDI,
250  "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:41
void write_DI(bool value, EmuTime::param time)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
void write_CS(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:76