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