OXYGENHUB
◆ main openmsx / src / fdc / VictorFDC.cc
4.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "VictorFDC.hh"
2
3
#include "DriveMultiplexer.hh"
4
#include "WD2793.hh"
5
6
#include "CacheLine.hh"
7
#include "serialize.hh"
8
9
// This implementation is documented in the HC-95 service manual:
10
//
11
// FDD interface:
12
// 7FF8   I/O            FDC STATUS/COMMAND
13
// 7FF9   I/O            FDC TRACK REGISTER
14
// 7FFA   I/O            FDC SECTOR REGISTER
15
// 7FFB   I/O            FDC DATA REGISTER
16
// 7FFC   I/O   bit 0    A DRIVE MOTOR ON/OFF        "1" ON
17
//        I/O   bit 1    B DRIVE MOTOR ON/OFF        "1" ON
18
//        I/O   bit 2    DRIVE SELECT "0" A DRIVE    "1" B DRIVE
19
//        O     bit 3    SIDE SELECT "0" SIDE 0      "1" SIDE 1
20
//        I              SIDE SELECT "1" SIDE 0      "0" SIDE 1
21
//        I/O   bit 4    DRIVE ENABLE                "0" ENABLE
22
//              bit 5    unused
23
//        I     bit 6    FDC DATA REQUEST            "1" REQUEST
24
//        I     bit 7    FDC INTERRUPT REQUEST       "1" REQUEST
25
26
namespace openmsx {
27
28
static constexpr int DRIVE_A_MOTOR = 0x01;
29
static constexpr int DRIVE_B_MOTOR = 0x02;
30
static constexpr int DRIVE_SELECT  = 0x04;
31
static constexpr int SIDE_SELECT   = 0x08;
32
static constexpr int DRIVE_DISABLE = 0x10; // renamed due to inverse logic
33
static constexpr int DATA_REQUEST  = 0x40;
34
static constexpr int INTR_REQUEST  = 0x80;
35
36
37
VictorFDC::VictorFDC(DeviceConfig& config)
38
	: WD2793BasedFDC(config)
39
{
40
	// ROM only visible in 0x4000-0x7FFF by default
41
	parseRomVisibility(config, 0x4000, 0x4000);
42
43
	reset(getCurrentTime());
44
}
45
46
void VictorFDC::reset(EmuTime time)
47
{
48
	WD2793BasedFDC::reset(time);
49
	// initialize in such way that drives are disabled
50
	// (and motors off, etc.)
51
	// TODO: test on real machine (this is an assumption)
52
	writeMem(0x7FFC, DRIVE_DISABLE, time);
53
}
54
55
byte VictorFDC::readMem(uint16_t address, EmuTime time)
56
{
57
	switch (address) {
58
	case 0x7FF8:
59
		return controller.getStatusReg(time);
60
	case 0x7FF9:
61
		return controller.getTrackReg(time);
62
	case 0x7FFA:
63
		return controller.getSectorReg(time);
64
	case 0x7FFB:
65
		return controller.getDataReg(time);
66
	case 0x7FFC: {
67
		byte value = driveControls;
68
		if (controller.getIRQ(time))  value |= INTR_REQUEST;
69
		if (controller.getDTRQ(time)) value |= DATA_REQUEST;
70
		value ^= SIDE_SELECT; // inverted
71
		return value;
72
	}
73
	default:
74
		return VictorFDC::peekMem(address, time);
75
	}
76
}
77
78
byte VictorFDC::peekMem(uint16_t address, EmuTime time) const
79
{
80
	switch (address) {
81
	case 0x7FF8:
82
		return controller.peekStatusReg(time);
83
	case 0x7FF9:
84
		return controller.peekTrackReg(time);
85
	case 0x7FFA:
86
		return controller.peekSectorReg(time);
87
	case 0x7FFB:
88
		return controller.peekDataReg(time);
89
	case 0x7FFC: {
90
		byte value = driveControls;
91
		if (controller.peekIRQ(time))  value |= INTR_REQUEST;
92
		if (controller.peekDTRQ(time)) value |= DATA_REQUEST;
93
		value ^= SIDE_SELECT; // inverted
94
		return value;
95
	}
96
	default:
97
		return MSXFDC::peekMem(address, time);
98
	}
99
}
100
101
const byte* VictorFDC::getReadCacheLine(uint16_t start) const
102
{
103
	if ((start & CacheLine::HIGH) == (0x7FF8 & CacheLine::HIGH)) {
104
		// FDC at 0x7FF8-0x7FFC
105
		return nullptr;
106
	} else {
107
		return MSXFDC::getReadCacheLine(start);
108
	}
109
}
110
111
void VictorFDC::writeMem(uint16_t address, byte value, EmuTime time)
112
{
113
	switch (address) {
114
	case 0x7FF8:
115
		controller.setCommandReg(value, time);
116
		break;
117
	case 0x7FF9:
118
		controller.setTrackReg(value, time);
119
		break;
120
	case 0x7FFA:
121
		controller.setSectorReg(value, time);
122
		break;
123
	case 0x7FFB:
124
		controller.setDataReg(value, time);
125
		break;
126
	case 0x7FFC:
127
		auto drive
128
			= ((value & DRIVE_DISABLE) != 0)
129
			? DriveMultiplexer::Drive::NONE
130
			: (((value & DRIVE_SELECT) != 0) ? DriveMultiplexer::Drive::B
131
			                                 : DriveMultiplexer::Drive::A);
132
		multiplexer.selectDrive(drive, time);
133
		multiplexer.setSide((value & SIDE_SELECT) != 0);
134
		multiplexer.setMotor((drive == DriveMultiplexer::Drive::A) ? ((value & DRIVE_A_MOTOR) != 0) : ((value & DRIVE_B_MOTOR) != 0), time); // this is not 100% correct: the motors can be controlled independently via bit 0 and 1
135
		// back up for reading:
136
		driveControls = value & (DRIVE_A_MOTOR | DRIVE_B_MOTOR | DRIVE_SELECT | SIDE_SELECT | DRIVE_DISABLE);
137
		break;
138
	}
139
}
140
141
byte* VictorFDC::getWriteCacheLine(uint16_t address)
142
{
143
	if ((address & CacheLine::HIGH) == (0x7FF8 & CacheLine::HIGH)) {
144
		// FDC at 0x7FF8-0x7FFC
145
		return nullptr;
146
	} else {
147
		return unmappedWrite.data();
148
	}
149
}
150
151
bool VictorFDC::allowUnaligned() const
152
{
153
	// OK, because this device doesn't call any 'fillDeviceXXXCache()'functions.
154
	return true;
155
}
156
157
158
template<typename Archive>
159
void VictorFDC::serialize(Archive& ar, unsigned /*version*/)
160
{
161
	ar.template serializeBase<WD2793BasedFDC>(*this);
162
	ar.serialize("driveControls", driveControls);
163
}
164
INSTANTIATE_SERIALIZE_METHODS(VictorFDC);
165
REGISTER_MSXDEVICE(VictorFDC, "VictorFDC");
166
167
} // namespace openmsx