summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/28fxxx.cpp
blob: 9ef279dcd44f89bb0d63fc6fdd4d7d39439f9c76 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * An implementation of the 28Fxxx flash memory devices, fabricated by many
 * suppliers including Intel, Atmel, AMD, SGS, TI and Toshiba. Range includes:
 *
 *   Part     Bits     Org.
 *   ------  -----   ------
 *   28F256   256K    32Kx8
 *   28F512   512K    64Kx8
 *   28F010  1024K   128Kx8
 *   28F020  2048K   256Kx8
 *   28F040  4096K   512Kx8
 *
 * These devices use a JEDEC-standard pinout allowing them to be fitted to a
 * standard EPROM socket, but have their own command set which is not
 * compatible with the set implemented by the intelfsh devices. It appears the
 * command set used here is a variation on the one defined by JEDEC standard
 * 21-C, as "dual power supply eeprom command set", in figure 3.5.1-11 on page
 * 23, here: https://www.jedec.org/system/files/docs/3_05_01R14.pdf
 *
 * This implementation has been developed primarily against Intel's datasheet,
 * with some minor adjustments to match details in the AMD equivalent.
 *
 * TODO
 *  - implement more variants
 *  - testing in systems other than InterPro
 *
 */

#include "emu.h"
#include "28fxxx.h"

#define VERBOSE 0
#include "logmacro.h"

// manufacturer codes defined by JEDEC: https://www.jedec.org/system/files/docs/JEP106AV.pdf
enum manufacturer_codes
{
	MFG_AMD   = 0x01,
	MFG_INTEL = 0x89,
};

DEFINE_DEVICE_TYPE(INTEL_28F010, intel_28f010_device, "intel_28f010", "Intel 28F010 1024K (128K x 8) CMOS Flash Memory")
DEFINE_DEVICE_TYPE(AMD_28F010, amd_28f010_device, "amd_28f010", "Am28F010 1 Megabit (128K x 8-Bit) CMOS 12.0 Volt, Bulk Erase Flash Memory")

ALLOW_SAVE_TYPE(base_28fxxx_device::state);

base_28fxxx_device::base_28fxxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 size, u8 manufacturer_code, u8 device_code)
	: device_t(mconfig, type, tag, owner, clock)
	, device_nvram_interface(mconfig, *this)
	, m_region(*this, DEVICE_SELF)
	, m_size(size)
	, m_manufacturer_code(manufacturer_code)
	, m_device_code(device_code)
	, m_program_power(CLEAR_LINE)
	, m_state(STATE_READ_MEMORY)
{
	assert_always((m_size & (m_size - 1)) == 0, "memory size must be an exact power of two");
}

intel_28f010_device::intel_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: base_28fxxx_device(mconfig, INTEL_28F010, tag, owner, clock, 0x20000, MFG_INTEL, 0xb4)
{
}

amd_28f010_device::amd_28f010_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: base_28fxxx_device(mconfig, AMD_28F010, tag, owner, clock, 0x20000, MFG_AMD, 0xa7)
{
}

void base_28fxxx_device::device_start()
{
	m_data = std::make_unique<u8[]>(m_size);

	save_item(NAME(m_program_power));
	save_pointer(NAME(m_data.get()), m_size);

	save_item(NAME(m_state));
	save_item(NAME(m_address_latch));
}

void base_28fxxx_device::nvram_default()
{
	if (m_region.found())
	{
		u32 bytes = m_region->bytes();
		if (bytes > m_size)
			bytes = m_size;

		for (offs_t offs = 0; offs < bytes; offs++)
			m_data[offs] = m_region->as_u8(offs);
	}
	else
		erase();
}

void base_28fxxx_device::nvram_read(emu_file &file)
{
	file.read(m_data.get(), m_size);
}

void base_28fxxx_device::nvram_write(emu_file &file)
{
	file.write(m_data.get(), m_size);
}

void base_28fxxx_device::erase()
{
	memset(m_data.get(), 0xff, m_size);
}

READ8_MEMBER(base_28fxxx_device::read)
{
	switch (m_state)
	{
	case STATE_READ_MEMORY:
		return m_data.get()[offset & (m_size - 1)];

	case STATE_READ_IDENTIFIER:
		switch (offset)
		{
		case 0: return m_manufacturer_code;
		case 1: return m_device_code;
		}
		LOG("unknown read identifier offset 0x%08x (%s)\n", offset, machine().describe_context());
		return space.unmap();

	case STATE_ERASE_VERIFY:
		return m_data.get()[m_address_latch];

	case STATE_PROGRAM_VERIFY:
		return m_data.get()[m_address_latch];

	default:
		LOG("unexpected read offset 0x%08x mask 0x%08x state %d (%s)\n", offset, mem_mask, m_state, machine().describe_context());
		return space.unmap();
	}
}

WRITE8_MEMBER(base_28fxxx_device::write)
{
	// writes are ignored unless Vpp is asserted
	if (!m_program_power)
		return;

	switch (m_state)
	{
	case STATE_ERASE_SETUP:
		if (data == ERASE)
		{
			LOG("erase command initiated (%s)\n", machine().describe_context());
			erase();
			m_state = STATE_ERASE;
		}
		else if (data == RESET)
		{
			LOG("erase command reset (%s)\n", machine().describe_context());
			m_state = STATE_READ_MEMORY;
		}
		else
			LOG("unexpected command 0x%02x in erase setup state (%s)\n", data, machine().describe_context());
		break;

	case STATE_ERASE:
		if (data == ERASE_VERIFY)
		{
			m_address_latch = offset & (m_size - 1);
			m_state = STATE_ERASE_VERIFY;
		}
		else
			LOG("unexpected command 0x%02x in erase state (%s)\n", data, machine().describe_context());
		break;

	case STATE_PROGRAM_SETUP:
		m_address_latch = offset & (m_size - 1);

		LOG("programming address 0x%08x data 0x%02x\n", m_address_latch, data);

		// bits can only be written from uncharged (logical 1) to charged (logical 0) state
		m_data.get()[m_address_latch] &= data;

		m_state = STATE_PROGRAM;
		break;

	case STATE_PROGRAM:
		if (data == PROGRAM_VERIFY)
			m_state = STATE_PROGRAM_VERIFY;
		else if (data == RESET)
		{
			LOG("program command reset (%s)\n", machine().describe_context());

			m_state = STATE_READ_MEMORY;
		}
		else
			LOG("unexpected command 0x%02x in program state (%s)\n", data, machine().describe_context());
		break;

	default:
		// start a new command
		switch (data)
		{
		case READ_MEMORY:
			m_state = STATE_READ_MEMORY;
			break;

		case READ_IDENTIFIER:
		case READ_IDENTIFIER_ALT:
			m_state = STATE_READ_IDENTIFIER;
			break;

		case ERASE:
			m_state = STATE_ERASE_SETUP;
			break;

		case ERASE_VERIFY:
			m_address_latch = offset & (m_size - 1);
			m_state = STATE_ERASE_VERIFY;
			break;

		case PROGRAM:
			m_state = STATE_PROGRAM_SETUP;
			break;

		case RESET:
			m_state = STATE_READ_MEMORY;
			break;

		default:
			LOG("unexpected command 0x%02x in state %d (%s)\n", data, m_state, machine().describe_context());
			break;
		}
		break;
	}
}