OXYGENHUB
◆ main openmsx / src / memory / RomFSA1FM.cc
7.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
// FS-A1FM mappers
2
//   based on info from:
3
//   http://sourceforge.net/tracker/index.php?func=detail&aid=672508&group_id=38274&atid=421864
4
//
5
// Slot 3-1
6
//   0x4000-0x5FFF  16 x 8kB ROM, same 16 pages as mapper in slot 3-3
7
//   0x6000-0x7FFF  8kB RAM   (mapped ???)
8
//   0x7FC0-0x7FCF  I/O area
9
//     0x7FC0 (R/W)   8251 (UART) data register
10
//     0x7FC1 (R/W)   8251 (UART) command/status register
11
//     0x7FC4 (?)     ROM switch address, lower 4 bits -> selected bank
12
//                                        upper 4 bits -> ??? (always preserved)
13
//     0x7FC5 (W)     ???
14
//     0x7FC6 (R)     ???
15
//     0x7FC8 (W)     ???
16
//     0x7FCC (W)     ???
17
//
18
// Slot 3-3
19
//   Very similar to other Panasonic-8kB rom mappers
20
//
21
//   0x0000-0x1FFF  switch address 0x6400, read-back 0x7FF0, initial value 0xA8
22
//   0x2000-0x3FFF  switch address 0x6C00, read-back 0x7FF1, initial value 0xA8
23
//   0x4000-0x5FFF  switch address 0x6000, read-back 0x7FF2, initial value 0xA8
24
//   0x6000-0x7FFF  switch address 0x6800, read-back 0x7FF3, initial value 0xA8
25
//   0x8000-0x9FFF  switch address 0x7000, read-back 0x7FF4, initial value 0xA8
26
//   0xA000-0xBFFF  switch address 0x7800, read-back 0x7FF5, initial value 0xA8
27
//
28
//   0x7FF6-0x7FF7  always read 0
29
//   0x7FF9         control byte, bit2 activates 0x7FF0-0x7FF7
30
//
31
//   mapper pages 0x10-0x7F            mirrored at 0x90-0xFF
32
//                0x80-0x83 0x88-0x8B  unmapped (read 0xFF)
33
//                0x84-0x87 0x8C-0x8F  contain (same) 8kB RAM
34
35
#include "RomFSA1FM.hh"
36
37
#include "SRAM.hh"
38
39
#include "CacheLine.hh"
40
#include "MSXException.hh"
41
#include "MSXMotherBoard.hh"
42
#include "serialize.hh"
43
44
#include "one_of.hh"
45
#include "strCat.hh"
46
#include "xrange.hh"
47
48
#include <memory>
49
50
namespace openmsx {
51
52
// 8kb shared sram //
53
54
[[nodiscard]] static std::shared_ptr<SRAM> getSram(const DeviceConfig& config)
55
{
56
	return config.getMotherBoard().getSharedStuff<SRAM>(
57
		"FSA1FM-sram",
58
		strCat(config.getAttributeValue("id"), " SRAM"), 0x2000, config);
59
}
60
61
62
// Mapper for slot 3-1 //
63
64
RomFSA1FM1::RomFSA1FM1(const DeviceConfig& config, Rom&& rom_)
65
	: MSXRom(config, std::move(rom_))
66
	, fsSram(getSram(config))
67
	, firmwareSwitch(config)
68
{
69
	if (rom.size() != one_of(0x100000u, 0x200000u)) {
70
		throw MSXException(
71
			"Rom for FSA1FM mapper must be 1MB in size "
72
			"(some dumps are 2MB, those can be used as well).");
73
	}
74
	reset(EmuTime::dummy());
75
}
76
77
void RomFSA1FM1::reset(EmuTime /*time*/)
78
{
79
	// initial rom bank is undefined
80
}
81
82
byte RomFSA1FM1::peekMem(uint16_t address, EmuTime /*time*/) const
83
{
84
	if ((0x4000 <= address) && (address < 0x6000)) {
85
		// read rom
86
		return rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
87
		              (address & 0x1FFF)];
88
	} else if ((0x7FC0 <= address) && (address < 0x7FD0)) {
89
		switch (address & 0x0F) {
90
		case 4:
91
			return (*fsSram)[address & 0x1FFF];
92
		case 6:
93
			return firmwareSwitch.getStatus() ? 0xFB : 0xFF;
94
		default:
95
			return 0xFF;
96
		}
97
	} else if ((0x6000 <= address) && (address < 0x8000)) {
98
		// read sram
99
		// TODO are there multiple sram blocks?
100
		return (*fsSram)[address & 0x1FFF];
101
	} else {
102
		return 0xFF;
103
	}
104
}
105
106
byte RomFSA1FM1::readMem(uint16_t address, EmuTime time)
107
{
108
	return RomFSA1FM1::peekMem(address, time);
109
}
110
111
const byte* RomFSA1FM1::getReadCacheLine(uint16_t address) const
112
{
113
	if (address == (0x7FC0 & CacheLine::HIGH)) {
114
		// don't cache IO area
115
		return nullptr;
116
	} else if ((0x4000 <= address) && (address < 0x6000)) {
117
		// read rom
118
		return &rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
119
		               (address & 0x1FFF)];
120
	} else if ((0x6000 <= address) && (address < 0x8000)) {
121
		// read sram
122
		return &(*fsSram)[address & 0x1FFF];
123
	} else {
124
		return unmappedRead.data();
125
	}
126
}
127
128
void RomFSA1FM1::writeMem(uint16_t address, byte value, EmuTime /*time*/)
129
{
130
	// TODO 0x7FC0 - 0x7FCF is modem IO area
131
132
	if ((0x6000 <= address) && (address < 0x8000)) {
133
		if (address == 0x7FC4) {
134
			// switch rom bank
135
			invalidateDeviceRCache(0x4000, 0x2000);
136
		}
137
		fsSram->write(address & 0x1FFF, value);
138
	}
139
}
140
141
byte* RomFSA1FM1::getWriteCacheLine(uint16_t address)
142
{
143
	if (address == (0x7FC0 & CacheLine::HIGH)) {
144
		// dont't cache IO area
145
		return nullptr;
146
	} else if ((0x6000 <= address) && (address < 0x8000)) {
147
		// don't cache SRAM writes
148
		return nullptr;
149
	} else {
150
		return unmappedWrite.data();
151
	}
152
}
153
154
template<typename Archive>
155
void RomFSA1FM1::serialize(Archive& ar, unsigned /*version*/)
156
{
157
	// skip MSXRom base class
158
	ar.template serializeBase<MSXDevice>(*this);
159
	// don't serialize (shared) sram here, rely on RomFSA1FM2 to do that
160
}
161
INSTANTIATE_SERIALIZE_METHODS(RomFSA1FM1);
162
REGISTER_MSXDEVICE(RomFSA1FM1, "RomFSA1FM1");
163
164
165
// Mapper for slot 3-3 //
166
167
RomFSA1FM2::RomFSA1FM2(const DeviceConfig& config, Rom&& rom_)
168
	: Rom8kBBlocks(config, std::move(rom_))
169
	, fsSram(getSram(config))
170
{
171
	reset(EmuTime::dummy());
172
}
173
174
void RomFSA1FM2::reset(EmuTime /*time*/)
175
{
176
	control = 0;
177
	for (auto region : xrange(6)) {
178
		changeBank(region, 0xA8);
179
	}
180
	changeBank(6, 0); // for mapper-state read-back
181
	changeBank(7, 0);
182
	setUnmapped(6);
183
	setUnmapped(7);
184
}
185
186
byte RomFSA1FM2::peekMem(uint16_t address, EmuTime time) const
187
{
188
	if (0xC000 <= address) {
189
		return 0xFF;
190
	} else if ((control & 0x04) && (0x7FF0 <= address) && (address < 0x7FF8)) {
191
		// read mapper state
192
		return bankSelect[address & 7];
193
	} else if (isRam[address >> 13]) {
194
		return (*fsSram)[address & 0x1FFF];
195
	} else if (isEmpty[address >> 13]) {
196
		return 0xFF;
197
	} else {
198
		return Rom8kBBlocks::peekMem(address, time);
199
	}
200
}
201
202
byte RomFSA1FM2::readMem(uint16_t address, EmuTime time)
203
{
204
	return RomFSA1FM2::peekMem(address, time);
205
}
206
207
const byte* RomFSA1FM2::getReadCacheLine(uint16_t address) const
208
{
209
	if (0xC000 <= address) {
210
		return unmappedRead.data();
211
	} else if ((0x7FF0 & CacheLine::HIGH) == address) {
212
		return nullptr;
213
	} else if (isRam[address >> 13]) {
214
		return &(*fsSram)[address & 0x1FFF];
215
	} else if (isEmpty[address >> 13]) {
216
		return unmappedRead.data();
217
	} else {
218
		return Rom8kBBlocks::getReadCacheLine(address);
219
	}
220
}
221
222
void RomFSA1FM2::writeMem(uint16_t address, byte value,
223
                          EmuTime /*time*/)
224
{
225
	if ((0x6000 <= address) && (address < 0x7FF0)) {
226
		// set mapper state
227
		switch (address & 0x1C00) {
228
		case 0x0000:
229
			changeBank(2, value);
230
			break;
231
		case 0x0400:
232
			changeBank(0, value);
233
			break;
234
		case 0x0800:
235
			changeBank(3, value);
236
			break;
237
		case 0x0C00:
238
			changeBank(1, value);
239
			break;
240
		case 0x1000:
241
			changeBank(4, value);
242
			break;
243
		case 0x1400:
244
			// nothing
245
			break;
246
		case 0x1800:
247
			changeBank(5, value);
248
			break;
249
		case 0x1C00:
250
			// nothing
251
			break;
252
		}
253
	} else if (address == 0x7FF9) {
254
		// write control byte
255
		control = value;
256
	} else if (isRam[address >> 13]) {
257
		fsSram->write(address & 0x1FFF, value);
258
	}
259
}
260
261
byte* RomFSA1FM2::getWriteCacheLine(uint16_t address)
262
{
263
	if ((0x6000 <= address) && (address < 0x8000)) {
264
		return nullptr;
265
	} else if (isRam[address >> 13]) {
266
		return nullptr;
267
	} else {
268
		return unmappedWrite.data();
269
	}
270
}
271
272
void RomFSA1FM2::changeBank(unsigned region, byte bank)
273
{
274
	bankSelect[region] = bank;
275
	if ((0x80 <= bank) && (bank < 0x90)) {
276
		if (bank & 0x04) {
277
			isRam[region]   = true;
278
			isEmpty[region] = false;
279
		} else {
280
			isRam[region]   = false;
281
			isEmpty[region] = true;
282
		}
283
		invalidateDeviceRWCache(0x2000 * region, 0x2000);
284
	} else {
285
		isRam[region]   = false;
286
		isEmpty[region] = false;
287
		setRom(region, bank & 0x7F);
288
		if (region == 3) {
289
			invalidateDeviceRCache(0x7FF0 & CacheLine::HIGH, CacheLine::SIZE);
290
		}
291
	}
292
}
293
294
template<typename Archive>
295
void RomFSA1FM2::serialize(Archive& ar, unsigned /*version*/)
296
{
297
	ar.template serializeBase<Rom8kBBlocks>(*this);
298
	// note: SRAM can be serialized in this class (as opposed to
299
	//       Rom8kBBlocks), because we don't use setBank to map it
300
	ar.serialize("SRAM",       *fsSram,
301
	             "bankSelect", bankSelect,
302
	             "control",    control);
303
	if constexpr (Archive::IS_LOADER) {
304
		// recalculate 'isRam' and 'isEmpty' from bankSelect
305
		for (auto region : xrange(8)) {
306
			changeBank(region, bankSelect[region]);
307
		}
308
	}
309
}
310
INSTANTIATE_SERIALIZE_METHODS(RomFSA1FM2);
311
REGISTER_MSXDEVICE(RomFSA1FM2, "RomFSA1FM2");
312
313
} // namespace openmsx