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 <memory>
43 
44 namespace openmsx {
45 
46 // 8kb shared sram //
47 
48 static std::shared_ptr<SRAM> getSram(const DeviceConfig& config)
49 {
50  return config.getMotherBoard().getSharedStuff<SRAM>(
51  "FSA1FM-sram",
52  config.getAttribute("id") + " SRAM", 0x2000, config);
53 }
54 
55 
56 // Mapper for slot 3-1 //
57 
58 RomFSA1FM1::RomFSA1FM1(const DeviceConfig& config, Rom&& rom_)
59  : MSXRom(config, std::move(rom_))
60  , fsSram(getSram(config))
61  , firmwareSwitch(config)
62 {
63  if (rom.getSize() != one_of(0x100000u, 0x200000u)) {
64  throw MSXException(
65  "Rom for FSA1FM mapper must be 1MB in size "
66  "(some dumps are 2MB, those can be used as well).");
67  }
68 }
69 
70 void RomFSA1FM1::reset(EmuTime::param /*time*/)
71 {
72  // initial rom bank is undefined
73 }
74 
75 byte RomFSA1FM1::peekMem(word address, EmuTime::param /*time*/) const
76 {
77  if ((0x4000 <= address) && (address < 0x6000)) {
78  // read rom
79  return rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
80  (address & 0x1FFF)];
81  } else if ((0x7FC0 <= address) && (address < 0x7FD0)) {
82  switch (address & 0x0F) {
83  case 4:
84  return (*fsSram)[address & 0x1FFF];
85  case 6:
86  return firmwareSwitch.getStatus() ? 0xFB : 0xFF;
87  default:
88  return 0xFF;
89  }
90  } else if ((0x6000 <= address) && (address < 0x8000)) {
91  // read sram
92  // TODO are there multiple sram blocks?
93  return (*fsSram)[address & 0x1FFF];
94  } else {
95  return 0xFF;
96  }
97 }
98 
99 byte RomFSA1FM1::readMem(word address, EmuTime::param time)
100 {
101  return RomFSA1FM1::peekMem(address, time);
102 }
103 
104 const byte* RomFSA1FM1::getReadCacheLine(word address) const
105 {
106  if (address == (0x7FC0 & CacheLine::HIGH)) {
107  // dont't cache IO area
108  return nullptr;
109  } else if ((0x4000 <= address) && (address < 0x6000)) {
110  // read rom
111  return &rom[(0x2000 * ((*fsSram)[0x1FC4] & 0x0F)) +
112  (address & 0x1FFF)];
113  } else if ((0x6000 <= address) && (address < 0x8000)) {
114  // read sram
115  return &(*fsSram)[address & 0x1FFF];
116  } else {
117  return unmappedRead;
118  }
119 }
120 
121 void RomFSA1FM1::writeMem(word address, byte value, EmuTime::param /*time*/)
122 {
123  // TODO 0x7FC0 - 0x7FCF is modem IO area
124 
125  if ((0x6000 <= address) && (address < 0x8000)) {
126  if (address == 0x7FC4) {
127  // switch rom bank
128  invalidateDeviceRCache(0x4000, 0x2000);
129  }
130  fsSram->write(address & 0x1FFF, value);
131  }
132 }
133 
135 {
136  if (address == (0x7FC0 & CacheLine::HIGH)) {
137  // dont't cache IO area
138  return nullptr;
139  } else if ((0x6000 <= address) && (address < 0x8000)) {
140  // don't cache SRAM writes
141  return nullptr;
142  } else {
143  return unmappedWrite;
144  }
145 }
146 
147 template<typename Archive>
148 void RomFSA1FM1::serialize(Archive& ar, unsigned /*version*/)
149 {
150  // skip MSXRom base class
151  ar.template serializeBase<MSXDevice>(*this);
152  // don't serialize (shared) sram here, rely on RomFSA1FM2 to do that
153 }
155 REGISTER_MSXDEVICE(RomFSA1FM1, "RomFSA1FM1");
156 
157 
158 // Mapper for slot 3-3 //
159 
161  : Rom8kBBlocks(config, std::move(rom_))
162  , fsSram(getSram(config))
163 {
164  reset(EmuTime::dummy());
165 }
166 
167 void RomFSA1FM2::reset(EmuTime::param /*time*/)
168 {
169  control = 0;
170  for (int region = 0; region < 6; ++region) {
171  changeBank(region, 0xA8);
172  }
173  changeBank(6, 0); // for mapper-state read-back
174  changeBank(7, 0);
175  setUnmapped(6);
176  setUnmapped(7);
177 }
178 
179 byte RomFSA1FM2::peekMem(word address, EmuTime::param time) const
180 {
181  byte result;
182  if (0xC000 <= address) {
183  result = 0xFF;
184  } else if ((control & 0x04) && (0x7FF0 <= address) && (address < 0x7FF8)) {
185  // read mapper state
186  result = bankSelect[address & 7];
187  } else if (isRam[address >> 13]) {
188  result = (*fsSram)[address & 0x1FFF];
189  } else if (isEmpty[address >> 13]) {
190  result = 0xFF;
191  } else {
192  result = Rom8kBBlocks::peekMem(address, time);
193  }
194  return result;
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 (ar.isLoader()) {
299  // recalculate 'isRam' and 'isEmpty' from bankSelect
300  for (int region = 0; region < 8; ++region) {
301  changeBank(region, bankSelect[region]);
302  }
303  }
304 }
306 REGISTER_MSXDEVICE(RomFSA1FM2, "RomFSA1FM2");
307 
308 } // 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:99
openmsx::RomBlocks::peekMem
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomBlocks.cc:59
openmsx::RomFSA1FM2
Definition: RomFSA1FM.hh:33
serialize.hh
RomFSA1FM.hh
openmsx::DeviceConfig
Definition: DeviceConfig.hh:19
openmsx::MSXDevice::unmappedWrite
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:301
openmsx::RomFSA1FM2::serialize
void serialize(Archive &ar, unsigned version)
Definition: RomFSA1FM.cc:290
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:197
MSXException.hh
openmsx::MSXException
Definition: MSXException.hh:9
openmsx::CacheLine::HIGH
constexpr unsigned HIGH
Definition: CacheLine.hh:10
openmsx::RomFSA1FM1
Definition: RomFSA1FM.hh:12
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:104
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:217
openmsx::MSXRom
Definition: MSXRom.hh:9
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:202
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:256
openmsx::SRAM
SRAM
Definition: SRAM.cc:150
openmsx::RomFSA1FM2::RomFSA1FM2
RomFSA1FM2(const DeviceConfig &config, Rom &&rom)
Definition: RomFSA1FM.cc:160
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:121
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:71
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:104
one_of
Definition: one_of.hh:7
openmsx::RomBlocks::setUnmapped
void setUnmapped(byte region)
Select 'unmapped' memory for this region.
Definition: RomBlocks.cc:91
openmsx::RomBlocks
Definition: RomBlocks.hh:13
openmsx::RomFSA1FM2::peekMem
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomFSA1FM.cc:179
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::RomFSA1FM1::RomFSA1FM1
RomFSA1FM1(const DeviceConfig &config, Rom &&rom)
Definition: RomFSA1FM.cc:58
openmsx::CacheLine::SIZE
constexpr unsigned SIZE
Definition: CacheLine.hh:7
CacheLine.hh
openmsx::MSXRom::rom
Rom rom
Definition: MSXRom.hh:20
openmsx::Rom
Definition: Rom.hh:20
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:148
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:134
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:70
openmsx::RomFSA1FM2::reset
void reset(EmuTime::param time) override
This method is called on reset.
Definition: RomFSA1FM.cc:167
openmsx::RomFSA1FM1::peekMem
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomFSA1FM.cc:75
openmsx::FirmwareSwitch::getStatus
bool getStatus() const
Definition: FirmwareSwitch.hh:15
openmsx::Rom::getSize
unsigned getSize() const
Definition: Rom.hh:32
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
MSXMotherBoard.hh