openMSX
SunriseIDE.cc
Go to the documentation of this file.
1#include "SunriseIDE.hh"
2#include "IDEDeviceFactory.hh"
3#include "IDEDevice.hh"
4#include "Math.hh"
5#include "narrow.hh"
6#include "outer.hh"
7#include "serialize.hh"
8
9namespace openmsx {
10
12 : MSXDevice(config)
13 , romBlockDebug(*this)
14 , rom(getName() + " ROM", "rom", config)
15 , internalBank(subspan<0x4000>(rom)) // make valgrind happy
16{
17 device[0] = IDEDeviceFactory::create(
18 DeviceConfig(config, config.findChild("master")));
19 device[1] = IDEDeviceFactory::create(
20 DeviceConfig(config, config.findChild("slave")));
21
23}
24
25SunriseIDE::~SunriseIDE() = default;
26
27void SunriseIDE::powerUp(EmuTime::param time)
28{
29 writeControl(0xFF);
30 reset(time);
31}
32
33void SunriseIDE::reset(EmuTime::param time)
34{
35 selectedDevice = 0;
36 softReset = false;
37 device[0]->reset(time);
38 device[1]->reset(time);
39}
40
41byte SunriseIDE::readMem(word address, EmuTime::param time)
42{
43 if (ideRegsEnabled && ((address & 0x3E00) == 0x3C00)) {
44 // 0x7C00 - 0x7DFF ide data register
45 if ((address & 1) == 0) {
46 return readDataLow(time);
47 } else {
48 return readDataHigh(time);
49 }
50 }
51 if (ideRegsEnabled && ((address & 0x3F00) == 0x3E00)) {
52 // 0x7E00 - 0x7EFF ide registers
53 return readReg(address & 0xF, time);
54 }
55 if ((0x4000 <= address) && (address < 0x8000)) {
56 // read normal (flash) rom
57 return internalBank[address & 0x3FFF];
58 }
59 // read nothing
60 return 0xFF;
61}
62
63const byte* SunriseIDE::getReadCacheLine(word start) const
64{
65 if (ideRegsEnabled && ((start & 0x3E00) == 0x3C00)) {
66 return nullptr;
67 }
68 if (ideRegsEnabled && ((start & 0x3F00) == 0x3E00)) {
69 return nullptr;
70 }
71 if ((0x4000 <= start) && (start < 0x8000)) {
72 return &internalBank[start & 0x3FFF];
73 }
74 return unmappedRead.data();
75}
76
77void SunriseIDE::writeMem(word address, byte value, EmuTime::param time)
78{
79 if ((address & 0xBF04) == 0x0104) {
80 // control register
81 writeControl(value);
82 return;
83 }
84 if (ideRegsEnabled && ((address & 0x3E00) == 0x3C00)) {
85 // 0x7C00 - 0x7DFF ide data register
86 if ((address & 1) == 0) {
87 writeDataLow(value);
88 } else {
89 writeDataHigh(value, time);
90 }
91 return;
92 }
93 if (ideRegsEnabled && ((address & 0x3F00) == 0x3E00)) {
94 // 0x7E00 - 0x7EFF ide registers
95 writeReg(address & 0xF, value, time);
96 return;
97 }
98 // all other writes ignored
99}
100
101byte SunriseIDE::getBank() const
102{
103 byte bank = Math::reverseByte(control & 0xF8);
104 if (bank >= (rom.size() / 0x4000)) {
105 bank &= narrow_cast<byte>((rom.size() / 0x4000) - 1);
106 }
107 return bank;
108}
109
110void SunriseIDE::writeControl(byte value)
111{
112 control = value;
113 if (ideRegsEnabled != (control & 1)) {
114 ideRegsEnabled = control & 1;
115 invalidateDeviceRCache(0x3C00, 0x0300);
116 invalidateDeviceRCache(0x7C00, 0x0300);
117 invalidateDeviceRCache(0xBC00, 0x0300);
118 invalidateDeviceRCache(0xFC00, 0x0300);
119 }
120
121 size_t bank = getBank();
122 if (internalBank.data() != &rom[0x4000 * bank]) {
123 internalBank = subspan<0x4000>(rom, 0x4000 * bank);
124 invalidateDeviceRCache(0x4000, 0x4000);
125 }
126}
127
128byte SunriseIDE::readDataLow(EmuTime::param time)
129{
130 word temp = readData(time);
131 readLatch = narrow_cast<byte>(temp >> 8);
132 return narrow_cast<byte>(temp & 0xFF);
133}
134byte SunriseIDE::readDataHigh(EmuTime::param /*time*/) const
135{
136 return readLatch;
137}
138word SunriseIDE::readData(EmuTime::param time)
139{
140 return device[selectedDevice]->readData(time);
141}
142
143byte SunriseIDE::readReg(nibble reg, EmuTime::param time)
144{
145 if (reg == 14) {
146 // alternate status register
147 reg = 7;
148 }
149 if (softReset) {
150 if (reg == 7) {
151 // read status
152 return 0xFF; // BUSY
153 } else {
154 // all others
155 return 0x7F; // don't care
156 }
157 } else {
158 if (reg == 0) {
159 return narrow_cast<byte>(readData(time) & 0xFF);
160 } else {
161 byte result = device[selectedDevice]->readReg(reg, time);
162 if (reg == 6) {
163 result &= 0xEF;
164 result |= selectedDevice ? 0x10 : 0x00;
165 }
166 return result;
167 }
168 }
169}
170
171void SunriseIDE::writeDataLow(byte value)
172{
173 writeLatch = value;
174}
175void SunriseIDE::writeDataHigh(byte value, EmuTime::param time)
176{
177 auto temp = word((value << 8) | writeLatch);
178 writeData(temp, time);
179}
180void SunriseIDE::writeData(word value, EmuTime::param time)
181{
182 device[selectedDevice]->writeData(value, time);
183}
184
185void SunriseIDE::writeReg(nibble reg, byte value, EmuTime::param time)
186{
187 if (softReset) {
188 if ((reg == 14) && !(value & 0x04)) {
189 // clear SRST
190 softReset = false;
191 }
192 // ignore all other writes
193 } else {
194 if (reg == 0) {
195 writeData(narrow_cast<word>((value << 8) | value), time);
196 } else {
197 if ((reg == 14) && (value & 0x04)) {
198 // set SRST
199 softReset = true;
200 device[0]->reset(time);
201 device[1]->reset(time);
202 } else {
203 if (reg == 6) {
204 selectedDevice = (value & 0x10) ? 1 : 0;
205 }
206 device[selectedDevice]->writeReg(reg, value, time);
207 }
208 }
209 }
210}
211
212template<typename Archive>
213void SunriseIDE::serialize(Archive& ar, unsigned /*version*/)
214{
215 ar.template serializeBase<MSXDevice>(*this);
216 ar.serializePolymorphic("master", *device[0]);
217 ar.serializePolymorphic("slave", *device[1]);
218 ar.serialize("readLatch", readLatch,
219 "writeLatch", writeLatch,
220 "selectedDevice", selectedDevice,
221 "control", control,
222 "softReset", softReset);
223
224 if constexpr (Archive::IS_LOADER) {
225 // restore internalBank, ideRegsEnabled
226 writeControl(control);
227 }
228}
231
232
233SunriseIDE::Blocks::Blocks(const SunriseIDE& device)
234 : RomBlockDebuggableBase(device)
235{
236}
237
238byte SunriseIDE::Blocks::read(unsigned address)
239{
240 if ((address < 0x4000) || (address >= 0x8000)) return 255;
241 const auto& ide = OUTER(SunriseIDE, romBlockDebug);
242 return ide.getBank();
243}
244
245} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
const XMLElement * findChild(std::string_view name) const
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
void invalidateDeviceRCache()
Definition MSXDevice.hh:215
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
auto size() const
Definition Rom.hh:37
void reset(EmuTime::param time) override
This method is called on reset.
Definition SunriseIDE.cc:33
~SunriseIDE() override
SunriseIDE(const DeviceConfig &config)
Definition SunriseIDE.cc:11
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 SunriseIDE.cc:77
void serialize(Archive &ar, unsigned version)
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition SunriseIDE.cc:63
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition SunriseIDE.cc:27
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition SunriseIDE.cc:41
constexpr uint8_t reverseByte(uint8_t a)
Reverse the bits in a byte.
Definition Math.hh:125
std::unique_ptr< IDEDevice > create(const DeviceConfig &config)
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint8_t nibble
4 bit integer
Definition openmsx.hh:23
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
#define OUTER(type, member)
Definition outer.hh:42
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:481
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)