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