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 
46 namespace 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 
60 RomFSA1FM1::RomFSA1FM1(const DeviceConfig& config, Rom&& rom_)
61  : MSXRom(config, std::move(rom_))
62  , fsSram(getSram(config))
63  , firmwareSwitch(config)
64 {
65  if (rom.getSize() != 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 
72 void RomFSA1FM1::reset(EmuTime::param /*time*/)
73 {
74  // initial rom bank is undefined
75 }
76 
77 byte 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 
101 byte RomFSA1FM1::readMem(word address, EmuTime::param time)
102 {
103  return RomFSA1FM1::peekMem(address, time);
104 }
105 
106 const 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;
120  }
121 }
122 
123 void 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;
146  }
147 }
148 
149 template<typename Archive>
150 void 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 
169 void 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 
181 byte 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 
197 byte RomFSA1FM2::readMem(word address, EmuTime::param time)
198 {
199  return RomFSA1FM2::peekMem(address, time);
200 }
201 
202 const byte* RomFSA1FM2::getReadCacheLine(word address) const
203 {
204  if (0xC000 <= address) {
205  return unmappedRead;
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;
212  } else {
213  return Rom8kBBlocks::getReadCacheLine(address);
214  }
215 }
216 
217 void 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;
264  }
265 }
266 
267 void RomFSA1FM2::changeBank(byte 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 
289 template<typename Archive>
290 void 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
Definition: one_of.hh:7
void invalidateDeviceRCache()
Definition: MSXDevice.hh:210
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:209
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:302
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomBlocks.cc:60
void setRom(byte region, unsigned block)
Selects a block of the ROM image for reading in a certain region.
Definition: RomBlocks.cc:105
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 setUnmapped(byte region)
Select 'unmapped' memory for this region.
Definition: RomBlocks.cc:92
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 * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: RomFSA1FM.cc:136
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 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) const 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
unsigned getSize() const
Definition: Rom.hh:34
constexpr unsigned HIGH
Definition: CacheLine.hh:10
constexpr unsigned SIZE
Definition: CacheLine.hh:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
SRAM
Definition: SRAM.cc:147
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
std::string strCat(Ts &&...ts)
Definition: strCat.hh:591
constexpr auto xrange(T e)
Definition: xrange.hh:155