OXYGENHUB
◆ main openmsx / src / fdc / YamahaFDC.cc
5.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "YamahaFDC.hh"
2
3
#include "DriveMultiplexer.hh"
4
#include "WD2793.hh"
5
6
#include "CacheLine.hh"
7
#include "MSXException.hh"
8
#include "Rom.hh"
9
#include "serialize.hh"
10
11
#include "one_of.hh"
12
13
// This is derived by disassembly of the Yamaha FD-03 disk rom
14
//   https://sourceforge.net/p/msxsyssrc/git/ci/master/tree/diskdrvs/fd-03/
15
//
16
// FDD interface:
17
// 7FC0   I/O            FDC STATUS/COMMAND
18
// 7FC1   I/O            FDC TRACK REGISTER
19
// 7FC2   I/O            FDC SECTOR REGISTER
20
// 7FC3   I/O            FDC DATA REGISTER
21
// 7FE0   O     bit 0    SELECT DRIVE A              "1" ON
22
//        I     bit 0    READY DRIVE A               "0" READY, "1" NOT READY
23
//        O     bit 1    SELECT DRIVE B              "1" ON
24
//        I     bit 1    READY DRIVE B               "0" READY, "1" NOT READY
25
//        O     bit 2    MOTOR                       "1" ON
26
//        I     bit 2    DISK CHANGE DRIVE A         "1" CHANGED
27
//        O     bit 3    UNKNOWN FUNCTION
28
//        I     bit 3    DISK CHANGE DRIVE B         "1" CHANGED
29
//        I     bit 6    FDC DATA REQUEST            "1" REQUEST
30
//        I     bit 7    FDC INTERRUPT REQUEST       "1" REQUEST
31
//
32
// 7FF0   O              RESET DISK CHANGE DRIVE A
33
//        I              RESET DISK CHANGE DRIVE B
34
35
namespace openmsx {
36
37
static constexpr int DRIVE_A_SELECT = 0x01;
38
static constexpr int DRIVE_B_SELECT = 0x02;
39
static constexpr int DRIVE_A_NOT_READY = 0x01;
40
static constexpr int DRIVE_B_NOT_READY = 0x02;
41
static constexpr int DISK_A_CHANGED = 0x04;
42
static constexpr int DISK_B_CHANGED = 0x08;
43
static constexpr int MOTOR_ON = 0x04;
44
static constexpr int DATA_REQUEST  = 0x40;
45
static constexpr int INTR_REQUEST  = 0x80;
46
47
48
YamahaFDC::YamahaFDC(DeviceConfig& config)
49
	: WD2793BasedFDC(config, "", true, DiskDrive::TrackMode::YAMAHA_FD_03)
50
{
51
	if (rom->size() != one_of(0x4000u, 0x8000u)) {
52
		throw MSXException("YamahaFDC ROM size must be 16kB or 32kB.");
53
	}
54
	reset(getCurrentTime());
55
}
56
57
void YamahaFDC::reset(EmuTime time)
58
{
59
	WD2793BasedFDC::reset(time);
60
	writeMem(0x7FE0, 0x00, time);
61
}
62
63
byte YamahaFDC::readMem(uint16_t address, EmuTime time)
64
{
65
	switch (address & 0x3FFF) {
66
	case 0x3FC0:
67
		return controller.getStatusReg(time);
68
	case 0x3FC1:
69
		return controller.getTrackReg(time);
70
	case 0x3FC2:
71
		return controller.getSectorReg(time);
72
	case 0x3FC3:
73
		return controller.getDataReg(time);
74
	case 0x3FE0: {
75
		byte value = 0;
76
		if (!multiplexer.isDiskInserted(DriveMultiplexer::Drive::A)) value |= DRIVE_A_NOT_READY;
77
		if (!multiplexer.isDiskInserted(DriveMultiplexer::Drive::B)) value |= DRIVE_B_NOT_READY;
78
		// note: peekDiskChanged() instead of diskChanged() because we don't want an implicit reset
79
		if (multiplexer.peekDiskChanged(DriveMultiplexer::Drive::A)) value |= DISK_A_CHANGED;
80
		if (multiplexer.peekDiskChanged(DriveMultiplexer::Drive::B)) value |= DISK_B_CHANGED;
81
		if (controller.getIRQ(time))  value |= INTR_REQUEST;
82
		if (controller.getDTRQ(time)) value |= DATA_REQUEST;
83
		return value;
84
	}
85
	case 0x3FF0:
86
		multiplexer.diskChanged(DriveMultiplexer::Drive::B); // clear
87
		[[fallthrough]];
88
	default:
89
		return peekMem(address, time);
90
	}
91
}
92
93
byte YamahaFDC::peekMem(uint16_t address, EmuTime time) const
94
{
95
	switch (address & 0x3FFF) {
96
	case 0x3FC0:
97
		return controller.peekStatusReg(time);
98
	case 0x3FC1:
99
		return controller.peekTrackReg(time);
100
	case 0x3FC2:
101
		return controller.peekSectorReg(time);
102
	case 0x3FC3:
103
		return controller.peekDataReg(time);
104
	case 0x3FE0: {
105
		byte value = 0;
106
		if (!multiplexer.isDiskInserted(DriveMultiplexer::Drive::A)) value |= DRIVE_A_NOT_READY;
107
		if (!multiplexer.isDiskInserted(DriveMultiplexer::Drive::B)) value |= DRIVE_B_NOT_READY;
108
		if (multiplexer.peekDiskChanged(DriveMultiplexer::Drive::A)) value |= DISK_A_CHANGED;
109
		if (multiplexer.peekDiskChanged(DriveMultiplexer::Drive::B)) value |= DISK_B_CHANGED;
110
		if (controller.peekIRQ(time))  value |= INTR_REQUEST;
111
		if (controller.peekDTRQ(time)) value |= DATA_REQUEST;
112
		return value;
113
	}
114
	case 0x3FF0:
115
		// don't clear on peek
116
		[[fallthrough]];
117
	default:
118
		if (unsigned(address - 0x4000) < rom->size()) {
119
			return (*rom)[address - 0x4000];
120
		} else {
121
			return 255;
122
		}
123
	}
124
}
125
126
const byte* YamahaFDC::getReadCacheLine(uint16_t start) const
127
{
128
	if ((start & 0x3FFF & CacheLine::HIGH) == (0x3FC0 & CacheLine::HIGH)) {
129
		// FDC at 0x7FC0-0x7FFF  or  0xBFC0-0xBFFF
130
		return nullptr;
131
	} else if (unsigned(start - 0x4000) < rom->size()) {
132
		return &(*rom)[start - 0x4000];
133
	} else {
134
		return unmappedRead.data();
135
	}
136
}
137
138
void YamahaFDC::writeMem(uint16_t address, byte value, EmuTime time)
139
{
140
	switch (address & 0x3fff) {
141
	case 0x3FC0:
142
		controller.setCommandReg(value, time);
143
		break;
144
	case 0x3FC1:
145
		controller.setTrackReg(value, time);
146
		break;
147
	case 0x3FC2:
148
		controller.setSectorReg(value, time);
149
		break;
150
	case 0x3FC3:
151
		controller.setDataReg(value, time);
152
		break;
153
	case 0x3FE0: {
154
		auto drive = [&] {
155
			switch (value & (DRIVE_A_SELECT | DRIVE_B_SELECT)) {
156
			case DRIVE_A_SELECT:
157
				return DriveMultiplexer::Drive::A;
158
			case DRIVE_B_SELECT:
159
				return DriveMultiplexer::Drive::B;
160
			default:
161
				// No drive selected or two drives at same time
162
				// The motor is enabled for all drives at the same time, so
163
				// in a real machine you must take care to do not select more
164
				// than one drive at the same time (you could get data
165
				// collision).
166
				return DriveMultiplexer::Drive::NONE;
167
			}
168
		}();
169
		multiplexer.selectDrive(drive, time);
170
		multiplexer.setSide(false);
171
		multiplexer.setMotor((value & MOTOR_ON) != 0, time);
172
		break;
173
	}
174
	case 0x3FF0:
175
		multiplexer.diskChanged(DriveMultiplexer::Drive::A);
176
		break;
177
	}
178
}
179
180
byte* YamahaFDC::getWriteCacheLine(uint16_t address)
181
{
182
	if ((address & 0x3FFF & CacheLine::HIGH) == (0x3FC0 & CacheLine::HIGH)) {
183
		// FDC at 0x7FC0-0x7FFF  or  0xBFC0-0xBFFF
184
		return nullptr;
185
	} else {
186
		return unmappedWrite.data();
187
	}
188
}
189
190
template<typename Archive>
191
void YamahaFDC::serialize(Archive& ar, unsigned /*version*/)
192
{
193
	ar.template serializeBase<WD2793BasedFDC>(*this);
194
}
195
INSTANTIATE_SERIALIZE_METHODS(YamahaFDC);
196
REGISTER_MSXDEVICE(YamahaFDC, "YamahaFDC");
197
198
} // namespace openmsx