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