openMSX
TurboRFDC.cc
Go to the documentation of this file.
1 /*
2  * Based on code from NLMSX written by Frits Hilderink
3  *
4  * Despite the name, the class implements MSX connection to TC8566AF FDC for
5  * any MSX that uses it. To make reuse possible, it implements two sets of
6  * I/O configurations, one is used on turboR, the other on other machines with
7  * this FDC.
8  * There's also a mapper mechanism implemented. It's only used on the turboR,
9  * but all other machines only have a 16kB diskROM, so it's practically
10  * ineffective there. The mapper has 0x7FF0 as switch address. (Thanks to
11  * NYYRIKKI for this info.)
12  */
13 
14 #include "TurboRFDC.hh"
15 #include "MSXCPU.hh"
16 #include "CacheLine.hh"
17 #include "Rom.hh"
18 #include "MSXException.hh"
19 #include "serialize.hh"
20 
21 namespace openmsx {
22 
23 static TurboRFDC::Type parseType(const DeviceConfig& config)
24 {
25  auto ioregs = config.getChildData("io_regs", {});
26  if (ioregs == "7FF2") {
27  return TurboRFDC::R7FF2;
28  } else if (ioregs == "7FF8") {
29  return TurboRFDC::R7FF8;
30  } else if (ioregs.empty()) {
31  // for backwards compatibility
32  return TurboRFDC::BOTH;
33  } else {
34  throw MSXException(
35  "Invalid 'io_regs' specification: expected one of "
36  "'7FF2' or '7FF8', but got: ", ioregs);
37  }
38 }
39 
41  : MSXFDC(config)
42  , controller(getScheduler(), reinterpret_cast<DiskDrive**>(drives),
43  getCliComm(), getCurrentTime())
44  , romBlockDebug(*this, &bank, 0x4000, 0x4000, 14)
45  , blockMask((rom->getSize() / 0x4000) - 1)
46  , type(parseType(config))
47 {
49 }
50 
51 void TurboRFDC::reset(EmuTime::param time)
52 {
53  setBank(0);
54  controller.reset(time);
55 }
56 
57 byte TurboRFDC::readMem(word address, EmuTime::param time_)
58 {
59  EmuTime time = time_;
60  if (0x3FF0 <= (address & 0x3FFF)) {
61  // Reading or writing to this region takes 1 extra clock
62  // cycle. But only in R800 mode. Verified on a real turboR
63  // machine, it happens for all 16 positions in this region
64  // and both for reading and writing.
65  time = getCPU().waitCyclesR800(time, 1);
66  if (type != R7FF8) { // turboR or BOTH
67  switch (address & 0xF) {
68  case 0x1: {
69  byte result = 0x33;
70  if (controller.diskChanged(0)) result &= ~0x10;
71  if (controller.diskChanged(1)) result &= ~0x20;
72  return result;
73  }
74  case 0x4: return controller.readReg(4, time);
75  case 0x5: return controller.readReg(5, time);
76  }
77  }
78  if (type != R7FF2) { // non-turboR or BOTH
79  switch (address & 0xF) {
80  case 0xA: return controller.readReg(4, time);
81  case 0xB: return controller.readReg(5, time);
82  }
83 
84  }
85  }
86  // all other stuff is handled by peekMem()
87  return TurboRFDC::peekMem(address, time);
88 }
89 
90 
91 byte TurboRFDC::peekMem(word address, EmuTime::param time) const
92 {
93  if (0x3FF0 <= (address & 0x3FFF)) {
94  // note: this implementation requires that the handled
95  // addresses for the MSX2 and TURBOR variants don't overlap
96  if (type != R7FF8) { // turboR or BOTH
97  switch (address & 0xF) {
98  case 0x0: return bank;
99  case 0x1: {
100  // bit 0 FD2HD1 High Density detect drive 1
101  // bit 1 FD2HD2 High Density detect drive 2
102  // bit 4 FDCHG1 Disk Change detect on drive 1
103  // bit 5 FDCHG2 Disk Change detect on drive 2
104  // active low
105  byte result = 0x33;
106  if (controller.peekDiskChanged(0)) result &= ~0x10;
107  if (controller.peekDiskChanged(1)) result &= ~0x20;
108  return result;
109  }
110  case 0x4: return controller.peekReg(4, time);
111  case 0x5: return controller.peekReg(5, time);
112  }
113  }
114  if (type != R7FF2) { // non-turboR or BOTH
115  switch (address & 0xF) {
116  case 0xA: return controller.peekReg(4, time);
117  case 0xB: return controller.peekReg(5, time);
118  }
119  }
120  switch (address & 0xF) {
121  // TODO Any idea what these 4 are? I've confirmed that on a
122  // real FS-A1GT I get these values, though the ROM dumps
123  // contain 0xFF in these locations. When looking at the ROM
124  // content via the 'RomPanasonic' mapper in slot 3-3, you can
125  // see that the ROM dumps are correct (these 4 values are not
126  // part of the ROM).
127  // This MRC post indicates that also on non-turbor machines
128  // you see these 4 values (bluemsx' post of 10-03-2013, 10:15):
129  // http://www.msx.org/forum/msx-talk/development/finally-have-feature-request-openmsx?page=3
130  case 0xC: return 0xFC;
131  case 0xD: return 0xFC;
132  case 0xE: return 0xFF;
133  case 0xF: return 0x3F;
134  default: return 0xFF; // other regs in this region
135  }
136  } else if ((0x4000 <= address) && (address < 0x8000)) {
137  return memory[address & 0x3FFF];
138  } else {
139  return 0xFF;
140  }
141 }
142 
144 {
145  if ((start & 0x3FF0) == (0x3FF0 & CacheLine::HIGH)) {
146  return nullptr;
147  } else if ((0x4000 <= start) && (start < 0x8000)) {
148  return &memory[start & 0x3FFF];
149  } else {
150  return unmappedRead;
151  }
152 }
153 
154 void TurboRFDC::writeMem(word address, byte value, EmuTime::param time_)
155 {
156  EmuTime time = time_;
157  if (0x3FF0 <= (address & 0x3FFF)) {
158  // See comment in readMem().
159  time = getCPU().waitCyclesR800(time, 1);
160  }
161  if (address == 0x7FF0) {
162  setBank(value);
163  } else {
164  if (type != R7FF8) { // turboR or BOTH
165  switch (address & 0x3FFF) {
166  case 0x3FF2:
167  case 0x3FF3:
168  case 0x3FF4:
169  case 0x3FF5:
170  controller.writeReg(address & 0xF, value, time);
171  break;
172  }
173  }
174  if (type != R7FF2) { // non-turboR or BOTH
175  switch (address & 0x3FFF) {
176  case 0x3FF8:
177  case 0x3FF9:
178  case 0x3FFA:
179  case 0x3FFB:
180  controller.writeReg((address & 0xF) - 6, value, time);
181  break;
182  }
183  }
184  }
185 }
186 
187 void TurboRFDC::setBank(byte value)
188 {
189  invalidateMemCache(0x4000, 0x4000);
190  bank = value & blockMask;
191  memory = &(*rom)[0x4000 * bank];
192 }
193 
195 {
196  if ((address == (0x7FF0 & CacheLine::HIGH)) ||
197  ((address & 0x3FF0) == (0x3FF0 & CacheLine::HIGH))) {
198  return nullptr;
199  } else {
200  return unmappedWrite;
201  }
202 }
203 
204 
205 template<typename Archive>
206 void TurboRFDC::serialize(Archive& ar, unsigned /*version*/)
207 {
208  ar.template serializeBase<MSXFDC>(*this);
209  ar.serialize("TC8566AF", controller,
210  "bank", bank);
211  if (ar.isLoader()) {
212  setBank(bank);
213  }
214 }
216 REGISTER_MSXDEVICE(TurboRFDC, "TurboRFDC");
217 
218 } // namespace openmsx
EmuTime waitCyclesR800(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:197
byte peekReg(int reg, EmuTime::param time) const
Definition: TC8566AF.cc:116
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
void reset(EmuTime::param time) override
This method is called on reset.
Definition: TurboRFDC.cc:51
TurboRFDC(const DeviceConfig &config)
Definition: TurboRFDC.cc:40
byte readReg(int reg, EmuTime::param time)
Definition: TC8566AF.cc:127
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:133
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: TurboRFDC.cc:57
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void reset(EmuTime::param time)
Definition: TC8566AF.cc:81
void serialize(Archive &ar, unsigned version)
Definition: TurboRFDC.cc:206
MSXCPU & getCPU() const
Definition: MSXDevice.cc:137
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:274
bool peekDiskChanged(unsigned driveNum) const
Definition: TC8566AF.cc:796
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:275
bool diskChanged(unsigned driveNum)
Definition: TC8566AF.cc:790
This (abstract) class defines the DiskDrive interface.
Definition: DiskDrive.hh:12
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
Definition: TurboRFDC.cc:143
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: TurboRFDC.cc:154
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
Definition: TurboRFDC.cc:194
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:458
void writeReg(int reg, byte data, EmuTime::param time)
Definition: TC8566AF.cc:318
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: TurboRFDC.cc:91