openMSX
RomFSA1FM.cc
Go to the documentation of this file.
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#include "CacheLine.hh"
37#include "SRAM.hh"
38#include "MSXMotherBoard.hh"
39#include "MSXException.hh"
40#include "one_of.hh"
41#include "serialize.hh"
42#include "strCat.hh"
43#include "xrange.hh"
44#include <memory>
45
46namespace openmsx {
47
48// 8kb shared sram //
49
50[[nodiscard]] static std::shared_ptr<SRAM> getSram(const DeviceConfig& config)
51{
52 return config.getMotherBoard().getSharedStuff<SRAM>(
53 "FSA1FM-sram",
54 strCat(config.getAttributeValue("id"), " SRAM"), 0x2000, config);
55}
56
57
58// Mapper for slot 3-1 //
59
61 : MSXRom(config, std::move(rom_))
62 , fsSram(getSram(config))
63 , firmwareSwitch(config)
64{
65 if (rom.size() != one_of(0x100000u, 0x200000u)) {
66 throw MSXException(
67 "Rom for FSA1FM mapper must be 1MB in size "
68 "(some dumps are 2MB, those can be used as well).");
69 }
70}
71
72void RomFSA1FM1::reset(EmuTime::param /*time*/)
73{
74 // initial rom bank is undefined
75}
76
77byte RomFSA1FM1::peekMem(word address, EmuTime::param /*time*/) const
78{
79 if ((0x4000 <= address) && (address < 0x6000)) {
80 // read rom
81 return rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
82 (address & 0x1FFF)];
83 } else if ((0x7FC0 <= address) && (address < 0x7FD0)) {
84 switch (address & 0x0F) {
85 case 4:
86 return (*fsSram)[address & 0x1FFF];
87 case 6:
88 return firmwareSwitch.getStatus() ? 0xFB : 0xFF;
89 default:
90 return 0xFF;
91 }
92 } else if ((0x6000 <= address) && (address < 0x8000)) {
93 // read sram
94 // TODO are there multiple sram blocks?
95 return (*fsSram)[address & 0x1FFF];
96 } else {
97 return 0xFF;
98 }
99}
100
101byte RomFSA1FM1::readMem(word address, EmuTime::param time)
102{
103 return RomFSA1FM1::peekMem(address, time);
104}
105
106const byte* RomFSA1FM1::getReadCacheLine(word address) const
107{
108 if (address == (0x7FC0 & CacheLine::HIGH)) {
109 // don't cache IO area
110 return nullptr;
111 } else if ((0x4000 <= address) && (address < 0x6000)) {
112 // read rom
113 return &rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
114 (address & 0x1FFF)];
115 } else if ((0x6000 <= address) && (address < 0x8000)) {
116 // read sram
117 return &(*fsSram)[address & 0x1FFF];
118 } else {
119 return unmappedRead.data();
120 }
121}
122
123void RomFSA1FM1::writeMem(word address, byte value, EmuTime::param /*time*/)
124{
125 // TODO 0x7FC0 - 0x7FCF is modem IO area
126
127 if ((0x6000 <= address) && (address < 0x8000)) {
128 if (address == 0x7FC4) {
129 // switch rom bank
130 invalidateDeviceRCache(0x4000, 0x2000);
131 }
132 fsSram->write(address & 0x1FFF, value);
133 }
134}
135
137{
138 if (address == (0x7FC0 & CacheLine::HIGH)) {
139 // dont't cache IO area
140 return nullptr;
141 } else if ((0x6000 <= address) && (address < 0x8000)) {
142 // don't cache SRAM writes
143 return nullptr;
144 } else {
145 return unmappedWrite.data();
146 }
147}
148
149template<typename Archive>
150void RomFSA1FM1::serialize(Archive& ar, unsigned /*version*/)
151{
152 // skip MSXRom base class
153 ar.template serializeBase<MSXDevice>(*this);
154 // don't serialize (shared) sram here, rely on RomFSA1FM2 to do that
155}
158
159
160// Mapper for slot 3-3 //
161
163 : Rom8kBBlocks(config, std::move(rom_))
164 , fsSram(getSram(config))
165{
166 reset(EmuTime::dummy());
167}
168
169void RomFSA1FM2::reset(EmuTime::param /*time*/)
170{
171 control = 0;
172 for (auto region : xrange(6)) {
173 changeBank(region, 0xA8);
174 }
175 changeBank(6, 0); // for mapper-state read-back
176 changeBank(7, 0);
177 setUnmapped(6);
178 setUnmapped(7);
179}
180
181byte RomFSA1FM2::peekMem(word address, EmuTime::param time) const
182{
183 if (0xC000 <= address) {
184 return 0xFF;
185 } else if ((control & 0x04) && (0x7FF0 <= address) && (address < 0x7FF8)) {
186 // read mapper state
187 return bankSelect[address & 7];
188 } else if (isRam[address >> 13]) {
189 return (*fsSram)[address & 0x1FFF];
190 } else if (isEmpty[address >> 13]) {
191 return 0xFF;
192 } else {
193 return Rom8kBBlocks::peekMem(address, time);
194 }
195}
196
197byte RomFSA1FM2::readMem(word address, EmuTime::param time)
198{
199 return RomFSA1FM2::peekMem(address, time);
200}
201
202const byte* RomFSA1FM2::getReadCacheLine(word address) const
203{
204 if (0xC000 <= address) {
205 return unmappedRead.data();
206 } else if ((0x7FF0 & CacheLine::HIGH) == address) {
207 return nullptr;
208 } else if (isRam[address >> 13]) {
209 return &(*fsSram)[address & 0x1FFF];
210 } else if (isEmpty[address >> 13]) {
211 return unmappedRead.data();
212 } else {
213 return Rom8kBBlocks::getReadCacheLine(address);
214 }
215}
216
217void RomFSA1FM2::writeMem(word address, byte value,
218 EmuTime::param /*time*/)
219{
220 if ((0x6000 <= address) && (address < 0x7FF0)) {
221 // set mapper state
222 switch (address & 0x1C00) {
223 case 0x0000:
224 changeBank(2, value);
225 break;
226 case 0x0400:
227 changeBank(0, value);
228 break;
229 case 0x0800:
230 changeBank(3, value);
231 break;
232 case 0x0C00:
233 changeBank(1, value);
234 break;
235 case 0x1000:
236 changeBank(4, value);
237 break;
238 case 0x1400:
239 // nothing
240 break;
241 case 0x1800:
242 changeBank(5, value);
243 break;
244 case 0x1C00:
245 // nothing
246 break;
247 }
248 } else if (address == 0x7FF9) {
249 // write control byte
250 control = value;
251 } else if (isRam[address >> 13]) {
252 fsSram->write(address & 0x1FFF, value);
253 }
254}
255
257{
258 if ((0x6000 <= address) && (address < 0x8000)) {
259 return nullptr;
260 } else if (isRam[address >> 13]) {
261 return nullptr;
262 } else {
263 return unmappedWrite.data();
264 }
265}
266
267void RomFSA1FM2::changeBank(unsigned region, byte bank)
268{
269 bankSelect[region] = bank;
270 if ((0x80 <= bank) && (bank < 0x90)) {
271 if (bank & 0x04) {
272 isRam[region] = true;
273 isEmpty[region] = false;
274 } else {
275 isRam[region] = false;
276 isEmpty[region] = true;
277 }
278 invalidateDeviceRWCache(0x2000 * region, 0x2000);
279 } else {
280 isRam[region] = false;
281 isEmpty[region] = false;
282 setRom(region, bank & 0x7F);
283 if (region == 3) {
285 }
286 }
287}
288
289template<typename Archive>
290void RomFSA1FM2::serialize(Archive& ar, unsigned /*version*/)
291{
292 ar.template serializeBase<Rom8kBBlocks>(*this);
293 // note: SRAM can be serialized in this class (as opposed to
294 // Rom8kBBlocks), because we don't use setBank to map it
295 ar.serialize("SRAM", *fsSram,
296 "bankSelect", bankSelect,
297 "control", control);
298 if constexpr (Archive::IS_LOADER) {
299 // recalculate 'isRam' and 'isEmpty' from bankSelect
300 for (auto region : xrange(8)) {
301 changeBank(region, bankSelect[region]);
302 }
303 }
304}
307
308} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void invalidateDeviceRCache()
Definition MSXDevice.hh:215
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:307
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition MSXDevice.hh:214
void setUnmapped(unsigned region)
Select 'unmapped' memory for this region.
Definition RomBlocks.cc:92
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition RomBlocks.cc:60
void setRom(unsigned region, unsigned block)
Selects a block of the ROM image for reading in a certain region.
Definition RomBlocks.cc:104
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition RomBlocks.cc:72
void reset(EmuTime::param time) override
This method is called on reset.
Definition RomFSA1FM.cc:72
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
Definition RomFSA1FM.cc:123
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition RomFSA1FM.cc:101
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition RomFSA1FM.cc:77
void serialize(Archive &ar, unsigned version)
Definition RomFSA1FM.cc:150
RomFSA1FM1(const DeviceConfig &config, Rom &&rom)
Definition RomFSA1FM.cc:60
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition RomFSA1FM.cc:106
byte * getWriteCacheLine(word address) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition RomFSA1FM.cc:136
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition RomFSA1FM.cc:197
byte * getWriteCacheLine(word address) override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition RomFSA1FM.cc:256
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition RomFSA1FM.cc:202
void serialize(Archive &ar, unsigned version)
Definition RomFSA1FM.cc:290
void reset(EmuTime::param time) override
This method is called on reset.
Definition RomFSA1FM.cc:169
RomFSA1FM2(const DeviceConfig &config, Rom &&rom)
Definition RomFSA1FM.cc:162
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
Definition RomFSA1FM.cc:217
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition RomFSA1FM.cc:181
auto size() const
Definition Rom.hh:37
constexpr unsigned HIGH
Definition CacheLine.hh:10
constexpr unsigned SIZE
Definition CacheLine.hh:7
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
std::string strCat()
Definition strCat.hh:703
constexpr auto xrange(T e)
Definition xrange.hh:132