openMSX
EEPROM_93C46.cc
Go to the documentation of this file.
1#include "EEPROM_93C46.hh"
2#include "serialize.hh"
3
4namespace openmsx {
5
7 : sram(NUM_ADDRESSES, xml, SRAM::DontLoadTag{})
8{
9}
10
11EEPROM_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
26uint8_t EEPROM_93C46::read(unsigned addr)
27{
28 return sram[addr];
29}
30
31void 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
39void 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
48void EEPROM_93C46::erase(unsigned addr, EmuTime::param time)
49{
50 sram.write(addr, 255);
51 completionTime = time + EmuDuration::usec(1000);
52}
53
54void EEPROM_93C46::eraseAll(EmuTime::param time)
55{
56 sram.memset(0, 255, NUM_ADDRESSES);
57 completionTime = time + EmuDuration::usec(8000);
58}
59
60bool EEPROM_93C46::ready(EmuTime::param time) const
61{
62 return time >= completionTime;
63}
64
65bool 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
76void 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
91void 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
101void EEPROM_93C46::write_DI(bool value, EmuTime::param /*time*/)
102{
103 pinDI = value;
104}
105
106void EEPROM_93C46::clockEvent(EmuTime::param time)
107{
108 switch (state) {
109 case IN_RESET:
110 // nothing
111 break;
112
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 = narrow_cast<uint16_t>((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 = uint16_t(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 = narrow_cast<uint16_t>((shiftRegister << 1) | int(pinDI));
143 ++bits;
144 if (bits == DATA_BITS) {
145 if (writeProtected) {
146 state = IN_RESET;
147 break;
148 }
149 write(address, narrow_cast<uint8_t>(shiftRegister), time);
150 state = IN_RESET;
151 }
152 break;
153
155 shiftRegister = narrow_cast<uint16_t>((shiftRegister << 1) | int(pinDI));
156 ++bits;
157 if (bits == DATA_BITS) {
158 if (writeProtected) {
159 state = IN_RESET;
160 break;
161 }
162 writeAll(narrow_cast<uint8_t>(shiftRegister), time);
163 state = IN_RESET;
164 }
165 break;
166 }
167}
168
169void 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: // WRITE ALL
183 shiftRegister = 0;
184 state = WAIT_FOR_WRITE_ALL;
185 break;
186
187 case 2: // ERASE ALL
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
227static constexpr 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_WRITE_ALL },
234};
236
237template<typename Archive>
238void 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
static constexpr uint32_t ADDRESS_MASK
Definition: EEPROM_93C46.hh:19
static constexpr uint8_t ADDRESS_BITS
Definition: EEPROM_93C46.hh:17
void write_CS(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:76
static constexpr uint32_t NUM_ADDRESSES
Definition: EEPROM_93C46.hh:18
static constexpr uint8_t DATA_BITS
Definition: EEPROM_93C46.hh:20
void serialize(Archive &ar, unsigned version)
EEPROM_93C46(const XMLElement &xml)
Definition: EEPROM_93C46.cc:6
void write_CLK(bool value, EmuTime::param time)
Definition: EEPROM_93C46.cc:91
bool read_DO(EmuTime::param time) const
Definition: EEPROM_93C46.cc:65
void write_DI(bool value, EmuTime::param time)
static constexpr EmuDuration usec(unsigned x)
Definition: EmuDuration.hh:45
void write(size_t addr, byte value)
Definition: SRAM.cc:63
void memset(size_t addr, byte c, size_t size)
Definition: SRAM.cc:72
This file implemented 3 utility functions:
Definition: Autofire.cc:9
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021