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
21namespace openmsx {
22
23[[nodiscard]] 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
51void TurboRFDC::reset(EmuTime::param time)
52{
53 setBank(0);
54 controller.reset(time);
55}
56
57byte 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.readStatus(time);
75 case 0x5: return controller.readDataPort(time);
76 }
77 }
78 if (type != R7FF2) { // non-turboR or BOTH
79 switch (address & 0xF) {
80 case 0xA: return controller.readStatus(time);
81 case 0xB: return controller.readDataPort(time);
82 }
83
84 }
85 }
86 // all other stuff is handled by peekMem()
87 return TurboRFDC::peekMem(address, time);
88}
89
90
91byte 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.peekStatus();
111 case 0x5: return controller.peekDataPort(time);
112 }
113 }
114 if (type != R7FF2) { // non-turboR or BOTH
115 switch (address & 0xF) {
116 case 0xA: return controller.peekStatus();
117 case 0xB: return controller.peekDataPort(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
143const byte* TurboRFDC::getReadCacheLine(word start) const
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
154void 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 controller.writeControlReg0(value, time);
168 break;
169 case 0x3ff3:
170 controller.writeControlReg1(value, time);
171 break;
172 case 0x3FF5:
173 controller.writeDataPort(value, time);
174 break;
175 }
176 }
177 if (type != R7FF2) { // non-turboR or BOTH
178 switch (address & 0x3FFF) {
179 case 0x3FF8:
180 controller.writeControlReg0(value, time);
181 break;
182 case 0x3FF9:
183 controller.writeControlReg1(value, time);
184 break;
185 case 0x3FFB:
186 controller.writeDataPort(value, time);
187 break;
188 }
189 }
190 }
191}
192
193void TurboRFDC::setBank(byte value)
194{
195 invalidateDeviceRCache(0x4000, 0x4000);
196 bank = value & blockMask;
197 memory = &(*rom)[0x4000 * bank];
198}
199
201{
202 if ((address == (0x7FF0 & CacheLine::HIGH)) ||
203 ((address & 0x3FF0) == (0x3FF0 & CacheLine::HIGH))) {
204 return nullptr;
205 } else {
206 return unmappedWrite;
207 }
208}
209
210
211template<typename Archive>
212void TurboRFDC::serialize(Archive& ar, unsigned /*version*/)
213{
214 ar.template serializeBase<MSXFDC>(*this);
215 ar.serialize("TC8566AF", controller,
216 "bank", bank);
217 if constexpr (Archive::IS_LOADER) {
218 setBank(bank);
219 }
220}
223
224} // namespace openmsx
This (abstract) class defines the DiskDrive interface.
Definition: DiskDrive.hh:13
EmuTime waitCyclesR800(EmuTime::param time, unsigned cycles)
Definition: MSXCPU.cc:329
void invalidateDeviceRCache()
Definition: MSXDevice.hh:210
MSXCPU & getCPU() const
Definition: MSXDevice.cc:130
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:302
void writeDataPort(byte value, EmuTime::param time)
Definition: TC8566AF.cc:334
byte peekDataPort(EmuTime::param time) const
Definition: TC8566AF.cc:142
byte readDataPort(EmuTime::param time)
Definition: TC8566AF.cc:154
byte readStatus(EmuTime::param time)
Definition: TC8566AF.cc:129
void writeControlReg1(byte value, EmuTime::param time)
Definition: TC8566AF.cc:325
void reset(EmuTime::param time)
Definition: TC8566AF.cc:82
void writeControlReg0(byte value, EmuTime::param time)
Definition: TC8566AF.cc:314
bool peekDiskChanged(unsigned driveNum) const
Definition: TC8566AF.cc:846
byte peekStatus() const
Definition: TC8566AF.cc:122
bool diskChanged(unsigned driveNum)
Definition: TC8566AF.cc:840
void reset(EmuTime::param time) override
This method is called on reset.
Definition: TurboRFDC.cc:51
void serialize(Archive &ar, unsigned version)
Definition: TurboRFDC.cc:212
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: TurboRFDC.cc:91
TurboRFDC(const DeviceConfig &config)
Definition: TurboRFDC.cc:40
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
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
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: TurboRFDC.cc:200
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
constexpr unsigned HIGH
Definition: CacheLine.hh:10
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
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009