1 // license:BSD-3-Clause
2 // copyright-holders:MetalliC
3 /**********************************************************************
4 
5     Didaktik D40/D80 disk interface
6     (C) 1991 Didaktik Skalica
7 
8     useful commands:
9      RUN - boot disc
10      CAT - files list
11      LIST * - system information
12      LOAD *"filename" - load program
13 
14 **********************************************************************/
15 
16 
17 #include "emu.h"
18 #include "d40.h"
19 
20 
21 //**************************************************************************
22 //  DEVICE DEFINITIONS
23 //**************************************************************************
24 
25 DEFINE_DEVICE_TYPE(SPECTRUM_D40, spectrum_d40_device, "spectrum_d40", "Didaktik D40")
26 DEFINE_DEVICE_TYPE(SPECTRUM_D80, spectrum_d80_device, "spectrum_d80", "Didaktik D80 (MDOS 1, 2793 FDC)")
27 DEFINE_DEVICE_TYPE(SPECTRUM_D80V2, spectrum_d80v2_device, "spectrum_d80v2", "Didaktik D80 (MDOS 2, 8272 FDC)")
28 
29 //-------------------------------------------------
30 //  INPUT_PORTS
31 //-------------------------------------------------
32 
INPUT_PORTS_START(d40)33 INPUT_PORTS_START(d40)
34 	PORT_START("BUTTON")
35 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Snapshot Button") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_d40base_device, snapshot_button, 0)
36 
37 	PORT_START("JOY")
38 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_8WAY
39 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT)  PORT_8WAY
40 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN)  PORT_8WAY
41 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP)    PORT_8WAY
42 	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)
43 	PORT_BIT(0xe0, IP_ACTIVE_HIGH, IPT_UNUSED)
44 INPUT_PORTS_END
45 
46 //-------------------------------------------------
47 //  input_ports - device-specific input ports
48 //-------------------------------------------------
49 
50 ioport_constructor spectrum_d40base_device::device_input_ports() const
51 {
52 	return INPUT_PORTS_NAME(d40);
53 }
54 
55 //-------------------------------------------------
56 //  SLOT_INTERFACE( floppies )
57 //-------------------------------------------------
58 
didaktik_floppies(device_slot_interface & device)59 static void didaktik_floppies(device_slot_interface &device)
60 {
61 	device.option_add("35dd", FLOPPY_35_DD);
62 	device.option_add("525dd", FLOPPY_525_DD);
63 }
64 
65 //-------------------------------------------------
66 //  floppy_format_type floppy_formats
67 //-------------------------------------------------
68 
FLOPPY_FORMATS_MEMBER(spectrum_d40base_device::floppy_formats)69 FLOPPY_FORMATS_MEMBER(spectrum_d40base_device::floppy_formats)
70 	FLOPPY_PC_FORMAT
71 FLOPPY_FORMATS_END
72 
73 //-------------------------------------------------
74 //  ROM
75 //-------------------------------------------------
76 
77 ROM_START(d40)
78 	ROM_REGION(0x4000, "rom", 0)
79 	ROM_DEFAULT_BIOS("mdos11")
80 	ROM_SYSTEM_BIOS(0, "mdos11", "MDOS 1.0 (1991)")
81 	ROMX_LOAD("mdos10.bin", 0x0000, 0x4000, CRC(e6b70939) SHA1(308c4b5daf6bb1f05c68a447129d723da423326e), ROM_BIOS(0))
82 	ROM_SYSTEM_BIOS(1, "mdos12", "MDOS 1.0+ (1992)") // possible unofficial modification
83 	ROMX_LOAD("mdos10p.bin", 0x0000, 0x4000, CRC(92c45741) SHA1(b8473235feecff4eccbace56a90cf1d8c79506eb), ROM_BIOS(1))
84 ROM_END
85 
86 ROM_START(d80v2)
87 	ROM_REGION(0x4000, "rom", 0)
88 	ROM_DEFAULT_BIOS("mdos2")
89 	ROM_SYSTEM_BIOS(0, "mdos2", "MDOS 2.0 (1993)")
90 	ROMX_LOAD("mdos20.bin", 0x0000, 0x4000, CRC(9e79d022) SHA1(e8d3355051fb287dd0dda34ba8824442130c8254), ROM_BIOS(0))
91 ROM_END
92 
93 //-------------------------------------------------
94 //  device_add_mconfig - add device configuration
95 //-------------------------------------------------
96 
97 void spectrum_d40_device::device_add_mconfig(machine_config &config)
98 {
99 	WD2797(config, m_fdc, 4_MHz_XTAL / 4);
100 	m_fdc->intrq_wr_callback().set(FUNC(spectrum_d40_device::fdc_intrq_w));
101 	m_fdc->drq_wr_callback().set(FUNC(spectrum_d40_device::fdc_drq_w));
102 	FLOPPY_CONNECTOR(config, "fdc:0", didaktik_floppies, "525dd", spectrum_d40_device::floppy_formats).enable_sound(true);
103 	FLOPPY_CONNECTOR(config, "fdc:1", didaktik_floppies, "525dd", spectrum_d40_device::floppy_formats).enable_sound(true);
104 
105 	I8255(config, m_ppi);
106 	m_ppi->in_pa_callback().set_ioport("JOY");
107 
108 	/* software list */
109 	//SOFTWARE_LIST(config, "flop_list").set_original("spectrum_d40_flop");
110 }
111 
device_add_mconfig(machine_config & config)112 void spectrum_d80_device::device_add_mconfig(machine_config &config)
113 {
114 	WD2797(config, m_fdc, 4_MHz_XTAL / 4);
115 	m_fdc->intrq_wr_callback().set(FUNC(spectrum_d80_device::fdc_intrq_w));
116 	m_fdc->drq_wr_callback().set(FUNC(spectrum_d80_device::fdc_drq_w));
117 	FLOPPY_CONNECTOR(config, "fdc:0", didaktik_floppies, "35dd", spectrum_d80_device::floppy_formats).enable_sound(true);
118 	FLOPPY_CONNECTOR(config, "fdc:1", didaktik_floppies, "35dd", spectrum_d80_device::floppy_formats).enable_sound(true);
119 
120 	I8255(config, m_ppi);
121 
122 	/* software list */
123 	//SOFTWARE_LIST(config, "flop_list").set_original("spectrum_d40_flop");
124 }
125 
device_add_mconfig(machine_config & config)126 void spectrum_d80v2_device::device_add_mconfig(machine_config &config)
127 {
128 	WD37C65C(config, m_fdc, 16_MHz_XTAL); // actually GM82C765B
129 	FLOPPY_CONNECTOR(config, "fdc:0", didaktik_floppies, "35dd", spectrum_d80v2_device::floppy_formats).enable_sound(true);
130 	FLOPPY_CONNECTOR(config, "fdc:1", didaktik_floppies, "35dd", spectrum_d80v2_device::floppy_formats).enable_sound(true);
131 
132 	I8255(config, m_ppi);
133 
134 	/* software list */
135 	//SOFTWARE_LIST(config, "flop_list").set_original("spectrum_d40_flop");
136 }
137 
device_rom_region() const138 const tiny_rom_entry *spectrum_d40_device::device_rom_region() const
139 {
140 	return ROM_NAME(d40);
141 }
142 
device_rom_region() const143 const tiny_rom_entry *spectrum_d80v2_device::device_rom_region() const
144 {
145 	return ROM_NAME(d80v2);
146 }
147 
148 //**************************************************************************
149 //  LIVE DEVICE
150 //**************************************************************************
151 
152 //-------------------------------------------------
153 //  spectrum_d40base_device - constructor
154 //-------------------------------------------------
155 
spectrum_d40base_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)156 spectrum_d40base_device::spectrum_d40base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
157 	: device_t(mconfig, type, tag, owner, clock)
158 	, device_spectrum_expansion_interface(mconfig, *this)
159 	, m_rom(*this, "rom")
160 	, m_ppi(*this, "ppi")
161 	, m_snap(*this, "BUTTON")
162 {
163 }
164 
spectrum_d40_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)165 spectrum_d40_device::spectrum_d40_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
166 	: spectrum_d40base_device(mconfig, SPECTRUM_D40, tag, owner, clock)
167 	, m_fdc(*this, "fdc")
168 	, m_floppy(*this, "fdc:%u", 0)
169 {
170 }
171 
spectrum_d40_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)172 spectrum_d40_device::spectrum_d40_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
173 	:spectrum_d40base_device(mconfig, type, tag, owner, clock)
174 	, m_fdc(*this, "fdc")
175 	, m_floppy(*this, "fdc:%u", 0)
176 {
177 }
178 
spectrum_d80_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)179 spectrum_d80_device::spectrum_d80_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
180 	: spectrum_d40_device(mconfig, SPECTRUM_D80, tag, owner, clock)
181 {
182 }
183 
spectrum_d80v2_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)184 spectrum_d80v2_device::spectrum_d80v2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
185 	: spectrum_d40base_device(mconfig, SPECTRUM_D80V2, tag, owner, clock)
186 	, m_fdc(*this, "fdc")
187 	, m_floppy(*this, "fdc:%u", 0)
188 {
189 }
190 
191 //-------------------------------------------------
192 //  device_start - device-specific startup
193 //-------------------------------------------------
194 
device_start()195 void spectrum_d40base_device::device_start()
196 {
197 	std::fill(std::begin(m_ram), std::end(m_ram), 0);
198 	save_item(NAME(m_romcs));
199 	save_item(NAME(m_ram));
200 	save_item(NAME(m_snap_flag));
201 	save_item(NAME(m_8255_reset));
202 	save_item(NAME(m_8255_enable));
203 }
204 
205 //-------------------------------------------------
206 //  device_reset - device-specific reset
207 //-------------------------------------------------
208 
device_reset()209 void spectrum_d40base_device::device_reset()
210 {
211 	m_romcs = 0;
212 	m_snap_flag = 0;
213 	m_8255_reset = 0;
214 	m_8255_enable = 0;
215 }
216 
217 
218 //**************************************************************************
219 //  IMPLEMENTATION  spectrum_d40base_device
220 //**************************************************************************
221 
READ_LINE_MEMBER(spectrum_d40base_device::romcs)222 READ_LINE_MEMBER(spectrum_d40base_device::romcs)
223 {
224 	return m_romcs;
225 }
226 
pre_opcode_fetch(offs_t offset)227 void spectrum_d40base_device::pre_opcode_fetch(offs_t offset)
228 {
229 	if (!machine().side_effects_disabled())
230 	{
231 		switch (offset)
232 		{
233 		case 0x0000:
234 		case 0x0008:
235 			m_romcs = 1;
236 			nmi_check();
237 			break;
238 		case 0x0066:
239 			if (!m_romcs)
240 			{
241 				m_romcs = 1;
242 				m_snap_flag = 1;
243 				nmi_check();
244 			}
245 			break;
246 		case 0x1700:
247 			if (!m_snap_flag)
248 				m_romcs = 0;
249 			nmi_check();
250 			break;
251 		}
252 	}
253 }
254 
iorq_r(offs_t offset)255 uint8_t spectrum_d40base_device::iorq_r(offs_t offset)
256 {
257 	uint8_t data = 0xff;
258 
259 	switch (offset & 0xf9)
260 	{
261 	case 0x81: // FDC
262 		data = fdc0_r(offset);
263 		break;
264 	case 0x89: // FDC
265 		data = fdc1_r(offset);
266 		break;
267 	}
268 
269 	if (!BIT(offset, 7) && m_8255_enable) // 1F/3F/5F/7F
270 		data = m_ppi->read((offset >> 5) & 3);
271 
272 	return data;
273 }
274 
iorq_w(offs_t offset,uint8_t data)275 void spectrum_d40base_device::iorq_w(offs_t offset, uint8_t data)
276 {
277 	switch (offset & 0xf9)
278 	{
279 	case 0x81: // FDC
280 		fdc0_w(offset, data);
281 		break;
282 	case 0x89: // control port
283 		fdc1_w(offset, data);
284 		break;
285 	case 0x91: // PPI reset
286 		m_8255_reset = BIT(data, 5);
287 		if (!m_8255_reset)
288 		{
289 			m_ppi->reset();
290 			m_8255_enable = 0;
291 		}
292 		break;
293 	case 0x99: // PPI enable
294 		if (m_8255_reset)
295 			m_8255_enable = BIT(data, 4);
296 		break;
297 	}
298 
299 	if (!BIT(offset, 7) && m_8255_enable) // 1F/3F/5F/7F
300 		m_ppi->write((offset >> 5) & 3, data);
301 }
302 
mreq_r(offs_t offset)303 uint8_t spectrum_d40base_device::mreq_r(offs_t offset)
304 {
305 	if (m_snap_flag && offset == 0x66)
306 	{
307 		m_snap_flag = 0; // hacky, real hardware reset this latch by Z80 /WR line
308 		return 0xc7; // RST 0 inject
309 	}
310 
311 	uint8_t data = 0xff;
312 
313 	if (m_romcs)
314 	{
315 		if (offset < 0x3800)
316 			data = m_rom->base()[offset];
317 		else
318 			data = m_ram[offset & 0x7ff];
319 	}
320 
321 	return data;
322 }
323 
mreq_w(offs_t offset,uint8_t data)324 void spectrum_d40base_device::mreq_w(offs_t offset, uint8_t data)
325 {
326 	if (m_romcs)
327 	{
328 		if ((offset & 0xf800) == 0x3800)
329 			m_ram[offset & 0x7ff] = data;
330 	}
331 }
332 
INPUT_CHANGED_MEMBER(spectrum_d40base_device::snapshot_button)333 INPUT_CHANGED_MEMBER(spectrum_d40base_device::snapshot_button)
334 {
335 	nmi_check();
336 }
337 
nmi_check()338 void spectrum_d40base_device::nmi_check()
339 {
340 	int state = CLEAR_LINE;
341 
342 	if (!m_romcs && m_snap->read())
343 		state = ASSERT_LINE;
344 
345 	m_slot->nmi_w(state);
346 }
347 
348 //**************************************************************************
349 //  IMPLEMENTATION  spectrum_d40_device
350 //**************************************************************************
351 
device_start()352 void spectrum_d40_device::device_start()
353 {
354 	spectrum_d40base_device::device_start();
355 	save_item(NAME(m_control));
356 	save_item(NAME(m_intrq));
357 	save_item(NAME(m_drq));
358 }
359 
device_reset()360 void spectrum_d40_device::device_reset()
361 {
362 	spectrum_d40base_device::device_reset();
363 	m_control = 0;
364 	m_intrq = 0;
365 	m_drq = 0;
366 }
367 
fdc0_r(offs_t offset)368 uint8_t spectrum_d40_device::fdc0_r(offs_t offset)
369 {
370 	return m_fdc->read((offset >> 1) & 0x03);
371 }
372 
fdc0_w(offs_t offset,uint8_t data)373 void spectrum_d40_device::fdc0_w(offs_t offset, uint8_t data)
374 {
375 	m_fdc->write((offset >> 1) & 0x03, data);
376 }
377 
fdc1_w(offs_t offset,uint8_t data)378 void spectrum_d40_device::fdc1_w(offs_t offset, uint8_t data)
379 {
380 	m_control = data;
381 
382 	floppy_image_device* floppy = nullptr;
383 	if (BIT(data, 0))
384 		floppy = m_floppy[0]->get_device();
385 	else if (BIT(data, 1))
386 		floppy = m_floppy[1]->get_device();
387 	m_fdc->set_floppy(floppy);
388 
389 	m_floppy[0]->get_device()->mon_w(BIT(data, 2) ? 0 : 1);
390 	m_floppy[1]->get_device()->mon_w(BIT(data, 3) ? 0 : 1);
391 
392 	nmi_check();
393 }
394 
fdc_intrq_w(int state)395 void spectrum_d40_device::fdc_intrq_w(int state)
396 {
397 	m_intrq = state;
398 	nmi_check();
399 }
400 
fdc_drq_w(int state)401 void spectrum_d40_device::fdc_drq_w(int state)
402 {
403 	m_drq = state;
404 	nmi_check();
405 }
406 
nmi_check()407 void spectrum_d40_device::nmi_check()
408 {
409 	int state = CLEAR_LINE;
410 
411 	if (!m_romcs && m_snap->read())
412 		state = ASSERT_LINE;
413 
414 	if (m_intrq && BIT(m_control, 7))
415 		state = ASSERT_LINE;
416 
417 	if (m_drq && BIT(m_control, 6))
418 		state = ASSERT_LINE;
419 
420 	m_slot->nmi_w(state);
421 }
422 
423 //**************************************************************************
424 //  IMPLEMENTATION  spectrum_d80v2_device
425 //**************************************************************************
426 
pre_opcode_fetch(offs_t offset)427 void spectrum_d80v2_device::pre_opcode_fetch(offs_t offset)
428 {
429 	if (!machine().side_effects_disabled())
430 	{
431 		switch (offset)
432 		{
433 		case 0x0000:
434 		case 0x0008:
435 		case 0x0066:
436 			m_romcs = 1;
437 			nmi_check();
438 			break;
439 		case 0x1700:
440 			m_romcs = 0;
441 			nmi_check();
442 			break;
443 		}
444 	}
445 }
446 
fdc0_r(offs_t offset)447 uint8_t spectrum_d80v2_device::fdc0_r(offs_t offset)
448 {
449 	return BIT(offset, 2) ? m_fdc->fifo_r() : m_fdc->msr_r();
450 }
451 
fdc1_r(offs_t offset)452 uint8_t spectrum_d80v2_device::fdc1_r(offs_t offset)
453 {
454 	return m_fdc->dor_r();
455 }
456 
fdc0_w(offs_t offset,uint8_t data)457 void spectrum_d80v2_device::fdc0_w(offs_t offset, uint8_t data)
458 {
459 	if (BIT(offset, 2))
460 		m_fdc->fifo_w(data);
461 }
462 
fdc1_w(offs_t offset,uint8_t data)463 void spectrum_d80v2_device::fdc1_w(offs_t offset, uint8_t data)
464 {
465 	m_fdc->dor_w(data);
466 }
467 
mreq_r(offs_t offset)468 uint8_t spectrum_d80v2_device::mreq_r(offs_t offset)
469 {
470 	uint8_t data = 0xff;
471 
472 	if (m_romcs)
473 	{
474 		if (offset < 0x3800)
475 			data = m_rom->base()[offset];
476 		else
477 			data = m_ram[offset & 0x7ff];
478 	}
479 
480 	return data;
481 }
482