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 "serialize.hh"
41 #include <memory>
42 
43 namespace openmsx {
44 
45 // 8kb shared sram //
46 
47 static std::shared_ptr<SRAM> getSram(const DeviceConfig& config)
48 {
49  return config.getMotherBoard().getSharedStuff<SRAM>(
50  "FSA1FM-sram",
51  config.getAttribute("id") + " SRAM", 0x2000, config);
52 }
53 
54 
55 // Mapper for slot 3-1 //
56 
57 RomFSA1FM1::RomFSA1FM1(const DeviceConfig& config, Rom&& rom_)
58  : MSXRom(config, std::move(rom_))
59  , fsSram(getSram(config))
60  , firmwareSwitch(config)
61 {
62  if ((rom.getSize() != 0x100000) &&
63  (rom.getSize() != 0x200000)) {
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 
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  invalidateMemCache(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);
174  changeBank(7, 0);
175 }
176 
177 byte RomFSA1FM2::peekMem(word address, EmuTime::param time) const
178 {
179  byte result;
180  if (0xC000 <= address) {
181  result = 0xFF;
182  } else if ((control & 0x04) && (0x7FF0 <= address) && (address < 0x7FF8)) {
183  // read mapper state
184  result = bankSelect[address & 7];
185  } else if (isRam[address >> 13]) {
186  result = (*fsSram)[address & 0x1FFF];
187  } else if (isEmpty[address >> 13]) {
188  result = 0xFF;
189  } else {
190  result = Rom8kBBlocks::peekMem(address, time);
191  }
192  return result;
193 }
194 
195 byte RomFSA1FM2::readMem(word address, EmuTime::param time)
196 {
197  return RomFSA1FM2::peekMem(address, time);
198 }
199 
201 {
202  if (0xC000 <= address) {
203  return unmappedRead;
204  } else if ((0x7FF0 & CacheLine::HIGH) == address) {
205  return nullptr;
206  } else if (isRam[address >> 13]) {
207  return &(*fsSram)[address & 0x1FFF];
208  } else if (isEmpty[address >> 13]) {
209  return unmappedRead;
210  } else {
211  return Rom8kBBlocks::getReadCacheLine(address);
212  }
213 }
214 
215 void RomFSA1FM2::writeMem(word address, byte value,
216  EmuTime::param /*time*/)
217 {
218  if ((0x6000 <= address) && (address < 0x7FF0)) {
219  // set mapper state
220  switch (address & 0x1C00) {
221  case 0x0000:
222  changeBank(2, value);
223  break;
224  case 0x0400:
225  changeBank(0, value);
226  break;
227  case 0x0800:
228  changeBank(3, value);
229  break;
230  case 0x0C00:
231  changeBank(1, value);
232  break;
233  case 0x1000:
234  changeBank(4, value);
235  break;
236  case 0x1400:
237  // nothing
238  break;
239  case 0x1800:
240  changeBank(5, value);
241  break;
242  case 0x1C00:
243  // nothing
244  break;
245  }
246  } else if (address == 0x7FF9) {
247  // write control byte
248  control = value;
249  } else if (isRam[address >> 13]) {
250  fsSram->write(address & 0x1FFF, value);
251  }
252 }
253 
255 {
256  if ((0x6000 <= address) && (address < 0x8000)) {
257  return nullptr;
258  } else if (isRam[address >> 13]) {
259  return nullptr;
260  } else {
261  return unmappedWrite;
262  }
263 }
264 
265 void RomFSA1FM2::changeBank(byte region, byte bank)
266 {
267  bankSelect[region] = bank;
268  if ((0x80 <= bank) && (bank < 0x90)) {
269  if (bank & 0x04) {
270  isRam[region] = true;
271  isEmpty[region] = false;
272  } else {
273  isRam[region] = false;
274  isEmpty[region] = true;
275  }
276  invalidateMemCache(0x2000 * region, 0x2000);
277  } else {
278  isRam[region] = false;
279  isEmpty[region] = false;
280  setRom(region, bank & 0x7F);
281  }
282 }
283 
284 template<typename Archive>
285 void RomFSA1FM2::serialize(Archive& ar, unsigned /*version*/)
286 {
287  ar.template serializeBase<Rom8kBBlocks>(*this);
288  // note: SRAM can be serialized in this class (as opposed to
289  // Rom8kBBlocks), because we don't use setBank to map it
290  ar.serialize("SRAM", *fsSram);
291  ar.serialize("bankSelect", bankSelect);
292  ar.serialize("control", control);
293  if (ar.isLoader()) {
294  // recalculate 'isRam' and 'isEmpty' from bankSelect
295  for (int region = 0; region < 8; ++region) {
296  changeBank(region, bankSelect[region]);
297  }
298  }
299 }
301 REGISTER_MSXDEVICE(RomFSA1FM2, "RomFSA1FM2");
302 
303 } // namespace openmsx
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
Definition: RomFSA1FM.cc:254
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomFSA1FM.cc:75
RomFSA1FM1(const DeviceConfig &config, Rom &&rom)
Definition: RomFSA1FM.cc:57
void setRom(byte region, unsigned block)
Selects a block of the ROM image for reading in a certain region.
Definition: RomBlocks.cc:103
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void reset(EmuTime::param time) override
This method is called on reset.
Definition: RomFSA1FM.cc:70
STL namespace.
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
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
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
SRAM
Definition: SRAM.cc:150
void reset(EmuTime::param time) override
This method is called on reset.
Definition: RomFSA1FM.cc:167
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomFSA1FM.cc:177
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:215
void serialize(Archive &ar, unsigned version)
Definition: RomFSA1FM.cc:285
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
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: RomBlocks.cc:58
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
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:70
unsigned getSize() const
Definition: Rom.hh:32
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:274
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: RomFSA1FM.cc:195
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:200
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:275
RomFSA1FM2(const DeviceConfig &config, Rom &&rom)
Definition: RomFSA1FM.cc:160
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
void serialize(Archive &ar, unsigned version)
Definition: RomFSA1FM.cc:148
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:458