OXYGENHUB
◆ main openmsx / src / fdc / SpectravideoFDC.cc
5.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "SpectravideoFDC.hh"
2
3
#include "DriveMultiplexer.hh"
4
#include "WD2793.hh"
5
6
#include "CacheLine.hh"
7
#include "MSXException.hh"
8
#include "serialize.hh"
9
10
// Note: although this implementation seems to work, it has not been checked on
11
// real hardware how the FDC registers are mirrored across the slot, nor how
12
// the ROM is visible in the slot. Currently FDC registers are implemented to
13
// be mirrored all over the slot (as it seems that the MSX-DOS that came with
14
// the SVI-707 needs that), and ROMs are implemented to be visible in page 1.
15
//
16
// This implementation is solely based on the MSX SVI-728 Service and Technical
17
// Manual [Vol.1], page 3-7 (memory mapping of registers) and page 3-1 (ROM).
18
// Thanks to Leonard Oliveira for interpreting some of the hardware schematics
19
// in that same manual.
20
// Thanks to Benoit Delvaux for providing some extra info and software to test
21
// with.
22
23
namespace openmsx {
24
25
SpectravideoFDC::SpectravideoFDC(DeviceConfig& config)
26
	: WD2793BasedFDC(config, "msxdos")
27
	, cpmRom(getName() + " CP/M ROM", "rom", config, "cpm")
28
{
29
	if (cpmRom.size() != 0x1000) {
30
		throw MSXException("CP/M ROM must be exactly 4kB in size.");
31
	}
32
	reset(getCurrentTime());
33
}
34
35
void SpectravideoFDC::reset(EmuTime /*time*/)
36
{
37
	cpmRomEnabled = true;
38
	invalidateDeviceRCache(0x4000, 0x4000);
39
}
40
41
byte SpectravideoFDC::readMem(uint16_t address, EmuTime time)
42
{
43
	switch (address & 0x3FFF) {
44
	case 0x3FB8:
45
		return controller.getStatusReg(time);
46
	case 0x3FB9:
47
		return controller.getTrackReg(time);
48
	case 0x3FBA:
49
		return controller.getSectorReg(time);
50
	case 0x3FBB:
51
		return controller.getDataReg(time);
52
	case 0x3FBC: {
53
		byte value = 0;
54
		if ( controller.getIRQ (time)) value |= 0x80;
55
		if (!controller.getDTRQ(time)) value |= 0x40;
56
		return value;
57
	}
58
	case 0x3FBE: // Software switch to turn on CP/M,
59
	             // boot ROM and turn off MSX DOS ROM.
60
		cpmRomEnabled = true;
61
		invalidateDeviceRCache(0x4000, 0x4000);
62
		return 0xFF;
63
	case 0x3FBF: // Software switch to turn off CP/M,
64
	             // boot ROM and turn on MSX DOS ROM.
65
		cpmRomEnabled = false;
66
		invalidateDeviceRCache(0x4000, 0x4000);
67
		return 0xFF;
68
	default:
69
		return SpectravideoFDC::peekMem(address, time);
70
	}
71
}
72
73
byte SpectravideoFDC::peekMem(uint16_t address, EmuTime time) const
74
{
75
	switch (address & 0x3FFF) {
76
	case 0x3FB8:
77
		return controller.peekStatusReg(time);
78
	case 0x3FB9:
79
		return controller.peekTrackReg(time);
80
	case 0x3FBA:
81
		return controller.peekSectorReg(time);
82
	case 0x3FBB:
83
		return controller.peekDataReg(time);
84
	case 0x3FBC: {
85
		// Drive control IRQ and DRQ lines are not connected to Z80 interrupt request
86
		// bit 7: interrupt of 1793 (1 for interrupt)
87
		// bit 6: data request of 1793 (0 for request)
88
		// TODO: other bits read 0?
89
		byte value = 0;
90
		if ( controller.peekIRQ (time)) value |= 0x80;
91
		if (!controller.peekDTRQ(time)) value |= 0x40;
92
		return value;
93
	}
94
	default:
95
		if ((0x4000 <= address) && (address < 0x8000) && !cpmRomEnabled) {
96
			// MSX-DOS ROM only visible in 0x4000-0x7FFF
97
			return MSXFDC::peekMem(address, time);
98
		} else if ((0x4000 <= address) && (address < 0x5000) && cpmRomEnabled) {
99
			// CP/M ROM only visible in 0x4000-0x4FFF
100
			return cpmRom[address & 0x0FFF];
101
		} else {
102
			return 0xFF;
103
		}
104
	}
105
}
106
107
const byte* SpectravideoFDC::getReadCacheLine(uint16_t start) const
108
{
109
	if ((start & 0x3FFF & CacheLine::HIGH) == (0x3FB8 & CacheLine::HIGH)) {
110
		// FDC at 0x7FB8-0x7FBF, and mirrored in other pages
111
		return nullptr;
112
	} else if ((0x4000 <= start) && (start < 0x8000) && !cpmRomEnabled) {
113
		// MSX-DOS ROM at 0x4000-0x7FFF
114
		return MSXFDC::getReadCacheLine(start);
115
	} else if ((0x4000 <= start) && (start < 0x5000) && cpmRomEnabled) {
116
		// CP/M ROM at 0x4000-0x4FFF
117
		return &cpmRom[start & 0x0FFF];
118
	} else {
119
		return unmappedRead.data();
120
	}
121
}
122
123
void SpectravideoFDC::writeMem(uint16_t address, byte value, EmuTime time)
124
{
125
	switch (address & 0x3FFF) {
126
	case 0x3FB8:
127
		controller.setCommandReg(value, time);
128
		break;
129
	case 0x3FB9:
130
		controller.setTrackReg(value, time);
131
		break;
132
	case 0x3FBA:
133
		controller.setSectorReg(value, time);
134
		break;
135
	case 0x3FBB:
136
		controller.setDataReg(value, time);
137
		break;
138
	case 0x3FBC:
139
		// bit 0 -> enable drive (1 for ENABLE)
140
		// bit 2 -> side select (0 for side 0)
141
		// bit 3 -> motor on (1 for ON)
142
		multiplexer.selectDrive((value & 0x01) != 0 ? DriveMultiplexer::Drive::A : DriveMultiplexer::Drive::NONE, time);
143
		multiplexer.setSide    ((value & 0x04) != 0);
144
		multiplexer.setMotor   ((value & 0x08) != 0, time);
145
		break;
146
	case 0x3FBE: // Software switch to turn on CP/M,
147
	             // boot ROM and turn off MSX DOS ROM.
148
		invalidateDeviceRCache(0x4000, 0x4000);
149
		cpmRomEnabled = true;
150
		break;
151
	case 0x3FBF: // Software switch to turn off CP/M,
152
	             // boot ROM and turn on MSX DOS ROM.
153
		invalidateDeviceRCache(0x4000, 0x4000);
154
		cpmRomEnabled = false;
155
		break;
156
	}
157
}
158
159
byte* SpectravideoFDC::getWriteCacheLine(uint16_t address)
160
{
161
	if ((address & 0x3FFF & CacheLine::HIGH) == (0x3FB8 & CacheLine::HIGH)) {
162
		// FDC at 0x7FB8-0x7FBF - mirrored
163
		return nullptr;
164
	} else {
165
		return unmappedWrite.data();
166
	}
167
}
168
169
template<typename Archive>
170
void SpectravideoFDC::serialize(Archive& ar, unsigned /*version*/)
171
{
172
	ar.template serializeBase<WD2793BasedFDC>(*this);
173
	ar.serialize("cpmRomEnabled", cpmRomEnabled);
174
}
175
INSTANTIATE_SERIALIZE_METHODS(SpectravideoFDC);
176
REGISTER_MSXDEVICE(SpectravideoFDC, "SpectravideoFDC");
177
178
} // namespace openmsx