1 // license:BSD-3-Clause
2 // copyright-holders:MetalliC
3 /*********************************************************************
4 
5     Kempston Disc Interface
6     (c) Abbeydale Designers ltd 1985
7 
8     Known clones:
9      Sandy Disco Vers. 3
10 
11     Few useful K-DOS commands (should be preceded with PRINT #4: ):
12      CAT - disc file list
13      LOAD "filename" - load and run program
14      COPY - tape to disc transfer utility
15      FORMAT "discname": PRINT drive#, tracks#, sides#, steprate - format disc
16 
17     Manual https://archive.org/download/World_of_Spectrum_June_2017_Mirror/World%20of%20Spectrum%20June%202017%20Mirror.zip/World%20of%20Spectrum%20June%202017%20Mirror/sinclair/hardware-info/k/KempstonDiscInterface_Manual.pdf
18 
19     Notes/TODO:
20      - schematics is missing, actual I/O ports decode might be not right
21      - find out port 0xDF 3 MSB bits wiring, probably FDC /DDEN, /MR
22      - implement onboard Kempston Centronics E interace and joystick port
23      - add more docs/information
24 
25 *********************************************************************
26 
27     Watford SP-DOS Interface
28     (c) Abbeydale Designers ltd 1984
29 
30     Earlier version of Kempston Disc, uses 2Kbyte ROM with small boot loader, same FDC ports but slightly different paging.
31     Require "System disk" with DOS to function, which is not dumped / missing at the moment.
32 
33 *********************************************************************/
34 
35 #include "emu.h"
36 #include "kempdisc.h"
37 
38 
39 /***************************************************************************
40     DEVICE DEFINITIONS
41 ***************************************************************************/
42 
43 DEFINE_DEVICE_TYPE(SPECTRUM_KEMPDISC, spectrum_kempdisc_device, "spectrum_kempdisc", "Kempston Disc Interface")
44 DEFINE_DEVICE_TYPE(SPECTRUM_SPDOS, spectrum_spdos_device, "spectrum_spdos", "Watford SP-DOS Interface")
45 
46 
47 //-------------------------------------------------
48 //  SLOT_INTERFACE( floppies )
49 //-------------------------------------------------
50 
kempdisc_floppies(device_slot_interface & device)51 static void kempdisc_floppies(device_slot_interface &device)
52 {
53 	device.option_add("525dd", FLOPPY_525_DD);
54 	device.option_add("525qd", FLOPPY_525_QD);
55 	device.option_add("35dd", FLOPPY_35_DD);
56 	device.option_add("3dsdd", FLOPPY_3_DSDD);
57 }
58 
59 //-------------------------------------------------
60 //  floppy_format_type floppy_formats
61 //-------------------------------------------------
62 
FLOPPY_FORMATS_MEMBER(spectrum_kempdisc_device::floppy_formats)63 FLOPPY_FORMATS_MEMBER(spectrum_kempdisc_device::floppy_formats)
64 	FLOPPY_DSK_FORMAT
65 FLOPPY_FORMATS_END
66 
67 //-------------------------------------------------
68 //  ROM( kempdisc )
69 //-------------------------------------------------
70 
71 ROM_START(kempdisc)
72 	ROM_REGION(0x2000, "rom", 0)
73 	ROM_DEFAULT_BIOS("kd21")
74 
75 	// original
76 	ROM_SYSTEM_BIOS(0, "kd20", "K-DOS v2.0")
77 	ROMX_LOAD("kd20.rom", 0x0000, 0x2000, CRC(244816a7) SHA1(b08e0e30f1db4f57d38b112be0115256528c6621), ROM_BIOS(0))
78 	ROM_SYSTEM_BIOS(1, "kd21", "K-DOS v2.1")
79 	ROMX_LOAD("kd21.rom", 0x0000, 0x2000, CRC(3a0705eb) SHA1(adebc46c1b6718eaed7e2844506011787117cb05), ROM_BIOS(1))
80 	//ROMX_LOAD("kd21.rom", 0x0000, 0x2000, CRC(d18fb812) SHA1(b3e00bc4111ef6311789159024fb4cd25c32a72f), ROM_BIOS(1)) // bad dump
81 	ROM_SYSTEM_BIOS(2, "kd21it", "K-DOS v2.1 Italian") // copyrights and texts changed and translated
82 	ROMX_LOAD("kd21it.rom", 0x0000, 0x2000, CRC(f8ccdf8a) SHA1(4a75fb4951d74e254c230d56b5566c924b3c38f6), ROM_BIOS(2))
83 ROM_END
84 
85 ROM_START(spdos)
86 	ROM_REGION(0x0800, "rom", 0)
87 	ROM_LOAD("spdos.rom", 0x0000, 0x0800, CRC(87b954a3) SHA1(bb0706cf1538da8acd12b8c9cd2cd75ca689ec44))
88 ROM_END
89 
90 //-------------------------------------------------
91 //  device_add_mconfig - add device configuration
92 //-------------------------------------------------
93 
94 void spectrum_kempdisc_device::device_add_mconfig(machine_config &config)
95 {
96 	WD1770(config, m_fdc, 16_MHz_XTAL / 2);
97 
98 	FLOPPY_CONNECTOR(config, "fdc:0", kempdisc_floppies, "525qd", spectrum_kempdisc_device::floppy_formats).enable_sound(true);
99 	FLOPPY_CONNECTOR(config, "fdc:1", kempdisc_floppies, "525qd", spectrum_kempdisc_device::floppy_formats).enable_sound(true);
100 	FLOPPY_CONNECTOR(config, "fdc:2", kempdisc_floppies, nullptr, spectrum_kempdisc_device::floppy_formats).enable_sound(true);
101 	FLOPPY_CONNECTOR(config, "fdc:3", kempdisc_floppies, nullptr, spectrum_kempdisc_device::floppy_formats).enable_sound(true);
102 
103 	// passthru
104 	SPECTRUM_EXPANSION_SLOT(config, m_exp, spectrum_expansion_devices, nullptr);
105 	m_exp->irq_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::irq_w));
106 	m_exp->nmi_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::nmi_w));
107 }
108 
device_rom_region() const109 const tiny_rom_entry *spectrum_kempdisc_device::device_rom_region() const
110 {
111 	return ROM_NAME(kempdisc);
112 }
113 
device_rom_region() const114 const tiny_rom_entry *spectrum_spdos_device::device_rom_region() const
115 {
116 	return ROM_NAME(spdos);
117 }
118 
119 
120 //**************************************************************************
121 //  LIVE DEVICE
122 //**************************************************************************
123 
124 //-------------------------------------------------
125 //  spectrum_kempdisc_device - constructor
126 //-------------------------------------------------
127 
spectrum_kempdisc_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)128 spectrum_kempdisc_device::spectrum_kempdisc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
129 	: device_t(mconfig, type, tag, owner, clock)
130 	, device_spectrum_expansion_interface(mconfig, *this)
131 	, m_rom(*this, "rom")
132 	, m_fdc(*this, "fdc")
133 	, m_floppy(*this, "fdc:%u", 0)
134 	, m_exp(*this, "exp")
135 //  , m_control(0)
136 {
137 }
138 
spectrum_kempdisc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)139 spectrum_kempdisc_device::spectrum_kempdisc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
140 	: spectrum_kempdisc_device(mconfig, SPECTRUM_KEMPDISC, tag, owner, clock)
141 {
142 }
143 
spectrum_spdos_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)144 spectrum_spdos_device::spectrum_spdos_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
145 	: spectrum_kempdisc_device(mconfig, SPECTRUM_SPDOS, tag, owner, clock)
146 {
147 }
148 
149 //-------------------------------------------------
150 //  device_start - device-specific startup
151 //-------------------------------------------------
152 
device_start()153 void spectrum_kempdisc_device::device_start()
154 {
155 	save_item(NAME(m_romcs));
156 //  save_item(NAME(m_control));
157 }
158 
159 //-------------------------------------------------
160 //  device_reset - device-specific reset
161 //-------------------------------------------------
162 
device_reset()163 void spectrum_kempdisc_device::device_reset()
164 {
165 	m_romcs = 1;
166 }
167 
168 //**************************************************************************
169 //  IMPLEMENTATION
170 //**************************************************************************
171 
READ_LINE_MEMBER(spectrum_kempdisc_device::romcs)172 READ_LINE_MEMBER(spectrum_kempdisc_device::romcs)
173 {
174 	return m_romcs | m_exp->romcs();
175 }
176 
177 
iorq_r(offs_t offset)178 uint8_t spectrum_kempdisc_device::iorq_r(offs_t offset)
179 {
180 	uint8_t data = m_exp->iorq_r(offset);
181 
182 	switch (offset & 0xff)
183 	{
184 	case 0xe5: case 0xe7: case 0xed: case 0xef:
185 		data &= m_fdc->read(BIT(offset, 1) | (BIT(offset, 3) << 1));
186 		break;
187 	}
188 
189 	return data;
190 }
191 
iorq_w(offs_t offset,uint8_t data)192 void spectrum_kempdisc_device::iorq_w(offs_t offset, uint8_t data)
193 {
194 	switch (offset & 0xff)
195 	{
196 	case 0xe5: case 0xe7: case 0xed: case 0xef:
197 		m_fdc->write(BIT(offset, 1) | (BIT(offset, 3) << 1), data);
198 		break;
199 	case 0xdf:
200 	{
201 		floppy_image_device* floppy = nullptr;
202 		for (int i = 1; i < 5; i++)
203 			if (BIT(data, i))
204 			{
205 				floppy = m_floppy[i - 1]->get_device();
206 				break;
207 			}
208 
209 //      m_control = data;
210 		m_fdc->set_floppy(floppy);
211 		if (floppy) floppy->ss_w(BIT(data, 0));
212 		if (data & 0xe0)
213 			logerror("port DF unhandled %02X\n", data);
214 	}
215 	break;
216 	case 0xfd:
217 		m_romcs = 1;
218 		break;
219 	case 0xd7:
220 		m_romcs = 0;
221 		break;
222 	}
223 
224 	m_exp->iorq_w(offset, data);
225 }
226 
mreq_r(offs_t offset)227 uint8_t spectrum_kempdisc_device::mreq_r(offs_t offset)
228 {
229 	uint8_t data = 0xff;
230 
231 	if (m_romcs)
232 		data = m_rom->base()[offset & 0x1fff];
233 
234 	if (m_exp->romcs())
235 		data &= m_exp->mreq_r(offset);
236 
237 	return data;
238 }
239 
240 
iorq_w(offs_t offset,uint8_t data)241 void spectrum_spdos_device::iorq_w(offs_t offset, uint8_t data)
242 {
243 	switch (offset & 0xff)
244 	{
245 	case 0xe5: case 0xe7: case 0xed: case 0xef:
246 		m_fdc->write(BIT(offset, 1) | (BIT(offset, 3) << 1), data);
247 		break;
248 	case 0xdf:
249 	{
250 		floppy_image_device* floppy = nullptr;
251 		for (int i = 1; i < 5; i++)
252 			if (BIT(data, i))
253 			{
254 				floppy = m_floppy[i - 1]->get_device();
255 				break;
256 			}
257 
258 //      m_control = data;
259 		m_fdc->set_floppy(floppy);
260 		if (floppy) floppy->ss_w(BIT(data, 0));
261 		if (data & 0xe0)
262 			logerror("port DF unhandled %02X\n", data);
263 		m_romcs = 0;
264 	}
265 	break;
266 	// below probably is wrong/different for this device
267 	case 0xfd:
268 		m_romcs = 1;
269 		break;
270 	case 0xd7:
271 		m_romcs = 0;
272 		break;
273 	}
274 
275 	m_exp->iorq_w(offset, data);
276 }
277 
mreq_r(offs_t offset)278 uint8_t spectrum_spdos_device::mreq_r(offs_t offset)
279 {
280 	uint8_t data = 0xff;
281 
282 	if (m_romcs)
283 		data = m_rom->base()[offset & 0x7ff];
284 
285 	if (m_exp->romcs())
286 		data &= m_exp->mreq_r(offset);
287 
288 	return data;
289 }
290