1 // license:LGPL-2.1+
2 // copyright-holders:Michael Zapf
3 /*******************************************************************************
4     CorComp Disk Controller
5     Based on WD2793/WD1773
6     Double Density, Double-sided
7 
8     Two flavors:
9 
10     * Original controller
11       Named "PEB-DCC"
12       Single 16K EPROM or two 8K EPROMs (selectable by jumper; only 2x8K emulated)
13       Two PALs
14       WD2793
15 
16     * Modified controller with redesigned PCB
17       Named "CorComp FDC Rev A"
18       Two 8K EPROMs (by Millers Graphics, 3rd party HW/SW contributor)
19       Two PALs
20       WD1773
21 
22     Michael Zapf
23     March 2020
24 
25 *******************************************************************************/
26 
27 #include "emu.h"
28 #include "cc_fdc.h"
29 #include "formats/ti99_dsk.h"
30 #include "machine/rescap.h"
31 
32 // ----------------------------------
33 // Flags for debugging
34 
35 #define LOG_WARN        (1U<<1)    // Warnings
36 #define LOG_CONFIG      (1U<<2)    // Configuration
37 #define LOG_EPROM       (1U<<3)    // Access to EPROM
38 #define LOG_CONTR       (1U<<4)    // Access to controller
39 #define LOG_RAM         (1U<<5)    // Access to SRAM
40 #define LOG_READY       (1U<<6)    // READY line
41 #define LOG_SIGNALS     (1U<<7)    // IRQ and HLD lines
42 #define LOG_DRQ         (1U<<8)    // DRQ line (too noisy in SIGNALS)
43 #define LOG_DRIVE       (1U<<9)    // Drive operations
44 #define LOG_CRU         (1U<<10)   // CRU operations
45 
46 #define VERBOSE ( LOG_GENERAL | LOG_WARN | LOG_CONFIG )
47 #include "logmacro.h"
48 
49 #define CCDCC_TAG "ti99_ccdcc"
50 #define CCFDC_TAG "ti99_ccfdc"
51 #define WDC_TAG "wdfdc"
52 #define MOTORMF_TAG "motor_mf"
53 #define TMS9901_TAG "tms9901"
54 
55 #define CCDCC_PALU2_TAG "palu2"
56 #define CCDCC_PALU1_TAG "palu1"
57 #define CCFDC_PALU12_TAG "palu12"
58 #define CCFDC_PALU6_TAG "palu6"
59 
60 #define BUFFER "ram"
61 
62 DEFINE_DEVICE_TYPE_NS(TI99_CCDCC, bus::ti99::peb, corcomp_dcc_device, CCDCC_TAG, "CorComp Disk Controller Card")
63 DEFINE_DEVICE_TYPE_NS(TI99_CCFDC, bus::ti99::peb, corcomp_fdca_device, CCFDC_TAG, "CorComp Floppy Disk Controller Card Rev A")
64 
65 DEFINE_DEVICE_TYPE_NS(CCDCC_PALU2, bus::ti99::peb, ccdcc_palu2_device, CCDCC_PALU2_TAG, "CorComp DCC PAL u2")
66 DEFINE_DEVICE_TYPE_NS(CCDCC_PALU1, bus::ti99::peb, ccdcc_palu1_device, CCDCC_PALU1_TAG, "CorComp DCC PAL u1")
67 
68 DEFINE_DEVICE_TYPE_NS(CCFDC_PALU12, bus::ti99::peb, ccfdc_palu12_device, CCFDC_PALU12_TAG, "CorComp FDC PAL u12")
69 DEFINE_DEVICE_TYPE_NS(CCFDC_PALU6, bus::ti99::peb, ccfdc_palu6_device, CCFDC_PALU6_TAG, "CorComp FDC PAL u6")
70 
71 namespace bus { namespace ti99 { namespace peb {
72 
73 // ----------------------------------
74 
corcomp_fdc_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)75 corcomp_fdc_device::corcomp_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock):
76 	  device_t(mconfig, type, tag, owner, clock),
77 	  device_ti99_peribox_card_interface(mconfig, *this),
78 	  m_wdc(*this, WDC_TAG),
79 	  m_decpal(nullptr),
80 	  m_ctrlpal(nullptr),
81 	  m_motormf(*this, MOTORMF_TAG),
82 	  m_tms9901(*this, TMS9901_TAG),
83 	  m_buffer_ram(*this, BUFFER),
84 	  m_dsrrom(nullptr),
85 	  m_cardsel(false),
86 	  m_banksel(false),
87 	  m_selected_drive(0),
88 	  m_address(0),
89 	  m_writing(false)
90 {
91 }
92 
setaddress_dbin(offs_t offset,int state)93 void corcomp_fdc_device::setaddress_dbin(offs_t offset, int state)
94 {
95 	// Do not allow setaddress for debugger
96 	if (machine().side_effects_disabled()) return;
97 	m_address = offset;
98 	m_writing = (state==CLEAR_LINE);
99 	operate_ready_line();
100 }
101 
102 /*
103     Provides the current address to the PALs.
104 */
get_address()105 uint16_t corcomp_fdc_device::get_address()
106 {
107 	return m_address;
108 }
109 
110 /*
111     Before the 9901 configures the P11 pin as output, it delivers Z output,
112     which is pulled down by R10 on the board. We implement this by using a
113     variable.
114 */
upper_bank()115 bool corcomp_fdc_device::upper_bank()
116 {
117 	return m_banksel;
118 }
119 
card_selected()120 bool corcomp_fdc_device::card_selected()
121 {
122 	return m_cardsel;
123 }
124 
write_access()125 bool corcomp_fdc_device::write_access()
126 {
127 	return m_writing;
128 }
129 
130 /*
131     The Debugging access must not have any side effect on the controller.
132     We only allow access to the EPROM and RAM.
133 */
debug_read(offs_t offset,uint8_t * value)134 void corcomp_fdc_device::debug_read(offs_t offset, uint8_t* value)
135 {
136 	uint16_t saveaddress = m_address;
137 
138 	m_address = offset;
139 	*value = 0x00;
140 
141 	if (m_ctrlpal->selectram())
142 	{
143 		// SRAM selected
144 		*value = m_buffer_ram->pointer()[m_address & 0x7f] & 0xf0;  // only the first 4 bits
145 	}
146 
147 	if (m_ctrlpal->selectdsr())
148 	{
149 		// EPROM selected
150 		uint16_t base = m_banksel? 0x2000 : 0;
151 		uint8_t* rom = &m_dsrrom[base | (m_address & 0x1fff)];
152 		*value = *rom;
153 	}
154 	m_address = saveaddress;
155 }
156 
debug_write(offs_t offset,uint8_t data)157 void corcomp_fdc_device::debug_write(offs_t offset, uint8_t data)
158 {
159 	uint16_t saveaddress = m_address;
160 	m_address = offset;
161 	if (m_ctrlpal->selectram())
162 	{
163 		m_buffer_ram->pointer()[m_address & 0x7f] = data & 0xf0;  // only the first 4 bits
164 	}
165 	m_address = saveaddress;
166 }
167 
168 /*
169     Operate the wait state logic.
170 */
operate_ready_line()171 void corcomp_fdc_device::operate_ready_line()
172 {
173 	line_state ready = (line_state)m_ctrlpal->ready_out();
174 	m_slot->set_ready(ready);
175 }
176 
177 /*
178     Callbacks from the WDC chip
179 */
WRITE_LINE_MEMBER(corcomp_fdc_device::fdc_irq_w)180 WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_irq_w )
181 {
182 	LOGMASKED(LOG_SIGNALS, "INTRQ callback = %d\n", state);
183 	operate_ready_line();
184 }
185 
WRITE_LINE_MEMBER(corcomp_fdc_device::fdc_drq_w)186 WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_drq_w )
187 {
188 	LOGMASKED(LOG_DRQ, "DRQ callback = %d\n", state);
189 	operate_ready_line();
190 }
191 
WRITE_LINE_MEMBER(corcomp_fdc_device::fdc_hld_w)192 WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_hld_w )
193 {
194 	LOGMASKED(LOG_SIGNALS, "HLD callback = %d\n", state);
195 }
196 
readz(offs_t offset,uint8_t * value)197 void corcomp_fdc_device::readz(offs_t offset, uint8_t *value)
198 {
199 	if (machine().side_effects_disabled())
200 	{
201 		debug_read(offset, value);
202 		return;
203 	}
204 
205 	if (m_ctrlpal->selectram())
206 	{
207 		// SRAM selected
208 		*value = m_buffer_ram->pointer()[m_address & 0x7f] & 0xf0;  // only the first 4 bits
209 		LOGMASKED(LOG_RAM, "Read RAM: %04x -> %02x\n", m_address & 0xffff, *value);
210 	}
211 
212 	if (m_ctrlpal->selectwdc())
213 	{
214 		// WDC selected
215 		*value = m_wdc->read((m_address >> 1)&0x03);
216 		LOGMASKED(LOG_CONTR, "Read FDC: %04x -> %02x\n", m_address & 0xffff, *value);
217 	}
218 
219 	if (m_ctrlpal->selectdsr())
220 	{
221 		// EPROM selected
222 		uint16_t base = m_banksel? 0x2000 : 0;
223 		uint8_t* rom = &m_dsrrom[base | (m_address & 0x1fff)];
224 		*value = *rom;
225 
226 		if (WORD_ALIGNED(m_address))
227 		{
228 			uint16_t val = (*rom << 8) | (*(rom+1));
229 			LOGMASKED(LOG_EPROM, "Read DSR: %04x (page %d)-> %04x\n", m_address & 0xffff, base>>13, val);
230 		}
231 	}
232 }
233 
write(offs_t offset,uint8_t data)234 void corcomp_fdc_device::write(offs_t offset, uint8_t data)
235 {
236 	if (machine().side_effects_disabled())
237 	{
238 		debug_write(offset, data);
239 		return;
240 	}
241 	if (m_ctrlpal->selectram())
242 	{
243 		// SRAM selected
244 		LOGMASKED(LOG_RAM, "Write RAM: %04x <- %02x\n", m_address & 0xffff, data&0xf0);
245 		m_buffer_ram->pointer()[m_address & 0x7f] = data & 0xf0;  // only the first 4 bits
246 	}
247 
248 	if (m_ctrlpal->selectwdc())
249 	{
250 		// WDC selected
251 		LOGMASKED(LOG_CONTR, "Write FDC: %04x <- %02x\n", m_address & 0xffff, data);
252 		m_wdc->write((m_address >> 1)&0x03, data);
253 	}
254 }
255 
crureadz(offs_t offset,uint8_t * value)256 void corcomp_fdc_device::crureadz(offs_t offset, uint8_t *value)
257 {
258 	m_address = offset; // Copy the CRU address on the address variable
259 	if (m_decpal->address9901())
260 	{
261 		// The S0 select line is inverted, which means that
262 		// a CRU address of 0x1120 is actually address 0x00 for the 9901,
263 		// while the CRU address 0x1100 is 0x20 for the 9901.
264 		// This trick is necessary to relocate the P0 line to the
265 		// first CRU address, which by convention turns on the EPROM
266 		// of the card.
267 		int bitno = ((offset & 0x3e)^0x20)>>1;
268 		*value = m_tms9901->read_bit(bitno)? 0x01 : 0x00;
269 		LOGMASKED(LOG_CRU, "cru %04x (bit %d) -> %d\n", offset, bitno, *value);
270 	}
271 }
272 
cruwrite(offs_t offset,uint8_t data)273 void corcomp_fdc_device::cruwrite(offs_t offset, uint8_t data)
274 {
275 	m_address = offset; // Copy the CRU address on the address variable
276 	if (m_decpal->address9901())
277 	{
278 		int bitno = ((offset & 0x3e)^0x20)>>1;
279 		LOGMASKED(LOG_CRU, "cru %04x (bit %d) <- %d\n", offset, bitno, data);
280 		m_tms9901->write_bit(bitno, data!=0);
281 	}
282 }
283 
WRITE_LINE_MEMBER(corcomp_fdc_device::clock_in)284 WRITE_LINE_MEMBER( corcomp_fdc_device::clock_in )
285 {
286 	m_tms9901->phi_line(state);
287 }
288 
tms9901_input(offs_t offset)289 uint8_t corcomp_fdc_device::tms9901_input(offs_t offset)
290 {
291 	// Inputs
292 	// INT1: Switch 8
293 	// INT2: Switch 1
294 	// INT3: Switch 7
295 	// INT4: Switch 5
296 	// INT5: Switch 3
297 	// INT6: Switch 2
298 	// INT7: Switch 4
299 	// INT8: Switch 6
300 	// INT9: -
301 	// P9: MotorMF
302 	// P12: WDCu11.28 (HLD)
303 	const uint8_t dipswitch[] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
304 
305 	switch (offset)
306 	{
307 	case tms9901_device::INT1:
308 		return ((ioport("HEADSTEP")->read() & dipswitch[8])!=0)? 0:1;
309 	case tms9901_device::INT2:
310 		return ((ioport("HEADSTEP")->read() & dipswitch[1])!=0)? 0:1;
311 	case tms9901_device::INT3:
312 		return ((ioport("HEADSTEP")->read() & dipswitch[7])!=0)? 0:1;
313 	case tms9901_device::INT4:
314 		return ((ioport("HEADSTEP")->read() & dipswitch[5])!=0)? 0:1;
315 	case tms9901_device::INT5:
316 		return ((ioport("HEADSTEP")->read() & dipswitch[3])!=0)? 0:1;
317 	case tms9901_device::INT6:
318 		return ((ioport("HEADSTEP")->read() & dipswitch[2])!=0)? 0:1;
319 	case tms9901_device::INT7_P15:
320 		return ((ioport("HEADSTEP")->read() & dipswitch[4])!=0)? 0:1;
321 	case tms9901_device::INT8_P14:
322 		return ((ioport("HEADSTEP")->read() & dipswitch[6])!=0)? 0:1;
323 	case tms9901_device::INT10_P12:
324 		return (m_wdc->hld_r()==ASSERT_LINE)? 1:0;
325 	case tms9901_device::INT13_P9:
326 		return (m_motormf->q_r()==0)? 1:0;
327 	default:
328 		return 1;
329 	}
330 }
331 
WRITE_LINE_MEMBER(corcomp_fdc_device::select_dsk)332 WRITE_LINE_MEMBER( corcomp_fdc_device::select_dsk )
333 {
334 	if (state == CLEAR_LINE)
335 	{
336 		if (   (!m_tms9901->read_bit(tms9901_device::P4))
337 			&& (!m_tms9901->read_bit(tms9901_device::P5))
338 			&& (!m_tms9901->read_bit(tms9901_device::P6))
339 			&& (!m_tms9901->read_bit(tms9901_device::INT14_P8)))
340 		{
341 			LOGMASKED(LOG_DRIVE, "Unselect all drives\n");
342 			m_wdc->set_floppy(nullptr);
343 			m_selected_drive = 0;
344 		}
345 	}
346 	else
347 	{
348 		if (m_tms9901->read_bit(tms9901_device::P4))
349 		{
350 			m_selected_drive = 1;
351 		}
352 		else if (m_tms9901->read_bit(tms9901_device::P5))
353 		{
354 			m_selected_drive = 2;
355 		}
356 		else if (m_tms9901->read_bit(tms9901_device::P6))
357 		{
358 			m_selected_drive = 3;
359 		}
360 		else if (m_tms9901->read_bit(tms9901_device::INT14_P8))
361 		{
362 			m_selected_drive = 4;
363 		}
364 		LOGMASKED(LOG_DRIVE, "Select drive DSK%d\n", m_selected_drive);
365 
366 		if (m_floppy[m_selected_drive-1] != nullptr)
367 		{
368 			m_wdc->set_floppy(m_floppy[m_selected_drive-1]);
369 			m_floppy[m_selected_drive-1]->ss_w(m_tms9901->read_bit(tms9901_device::INT15_P7));
370 		}
371 	}
372 }
373 
WRITE_LINE_MEMBER(corcomp_fdc_device::side_select)374 WRITE_LINE_MEMBER( corcomp_fdc_device::side_select )
375 {
376 	// Select side of disk (bit 7)
377 	if (m_selected_drive != 0)
378 	{
379 		LOGMASKED(LOG_DRIVE, "Set side (bit 7) = %d on DSK%d\n", state, m_selected_drive);
380 		m_floppy[m_selected_drive-1]->ss_w(state);
381 	}
382 }
383 
384 /*
385     All floppy motors are operated by the same line.
386 */
WRITE_LINE_MEMBER(corcomp_fdc_device::motor_w)387 WRITE_LINE_MEMBER( corcomp_fdc_device::motor_w )
388 {
389 	LOGMASKED(LOG_DRIVE, "Motor %s\n", state? "on" : "off");
390 	m_wdc->set_force_ready(state==ASSERT_LINE);
391 
392 	// Set all motors
393 	for (auto & elem : m_floppy)
394 		if (elem != nullptr) elem->mon_w((state==ASSERT_LINE)? 0 : 1);
395 	operate_ready_line();
396 }
397 
398 /*
399     Push the P11 state to the variable.
400 */
WRITE_LINE_MEMBER(corcomp_fdc_device::select_bank)401 WRITE_LINE_MEMBER( corcomp_fdc_device::select_bank )
402 {
403 	LOGMASKED(LOG_CRU, "Set bank %d\n", state);
404 	m_banksel = (state==ASSERT_LINE);
405 	operate_ready_line();
406 }
407 
WRITE_LINE_MEMBER(corcomp_fdc_device::select_card)408 WRITE_LINE_MEMBER( corcomp_fdc_device::select_card )
409 {
410 	LOGMASKED(LOG_CRU, "Select card = %d\n", state);
411 	m_cardsel = (state==ASSERT_LINE);
412 	operate_ready_line();
413 }
414 
415 // =========================================================================
416 
device_start()417 void corcomp_fdc_device::device_start()
418 {
419 	m_dsrrom = memregion(TI99_DSRROM)->base();
420 	save_item(NAME(m_address));
421 	save_item(NAME(m_writing));
422 	save_item(NAME(m_cardsel));
423 	save_item(NAME(m_banksel));
424 	save_item(NAME(m_selected_drive));
425 }
426 
device_reset()427 void corcomp_fdc_device::device_reset()
428 {
429 	for (int i=0; i < 4; i++)
430 	{
431 		if (m_floppy[i] != nullptr)
432 			LOGMASKED(LOG_CONFIG, "Connector %d with %s\n", i, m_floppy[i]->name());
433 		else
434 			LOGMASKED(LOG_CONFIG, "Connector %d has no floppy attached\n", i);
435 	}
436 }
437 
connect_drives()438 void corcomp_fdc_device::connect_drives()
439 {
440 	for (auto & elem : m_floppy)
441 		elem = nullptr;
442 
443 	if (subdevice("0")!=nullptr) m_floppy[0] = static_cast<floppy_image_device*>(subdevice("0")->subdevices().first());
444 	if (subdevice("1")!=nullptr) m_floppy[1] = static_cast<floppy_image_device*>(subdevice("1")->subdevices().first());
445 	if (subdevice("2")!=nullptr) m_floppy[2] = static_cast<floppy_image_device*>(subdevice("2")->subdevices().first());
446 	if (subdevice("3")!=nullptr) m_floppy[3] = static_cast<floppy_image_device*>(subdevice("3")->subdevices().first());
447 }
448 
449 INPUT_PORTS_START( cc_fdc )
450 	PORT_START( "HEADSTEP" )
451 	PORT_DIPNAME( 0x03, 0x00, "DSK1 head step time" )
452 		PORT_DIPSETTING( 0x00, "15 ms")
453 		PORT_DIPSETTING( 0x01, "10 ms")
454 		PORT_DIPSETTING( 0x02, "6 ms")
455 		PORT_DIPSETTING( 0x03, "3 ms")
456 	PORT_DIPNAME( 0x0c, 0x00, "DSK2 head step time" )
457 		PORT_DIPSETTING( 0x00, "15 ms")
458 		PORT_DIPSETTING( 0x04, "10 ms")
459 		PORT_DIPSETTING( 0x08, "6 ms")
460 		PORT_DIPSETTING( 0x0c, "3 ms")
461 	PORT_DIPNAME( 0x30, 0x00, "DSK3 head step time" )
462 		PORT_DIPSETTING( 0x00, "15 ms")
463 		PORT_DIPSETTING( 0x10, "10 ms")
464 		PORT_DIPSETTING( 0x20, "6 ms")
465 		PORT_DIPSETTING( 0x30, "3 ms")
466 	PORT_DIPNAME( 0xc0, 0x00, "DSK4 head step time" )
467 		PORT_DIPSETTING( 0x00, "15 ms")
468 		PORT_DIPSETTING( 0x40, "10 ms")
469 		PORT_DIPSETTING( 0x80, "6 ms")
470 		PORT_DIPSETTING( 0xc0, "3 ms")
471 INPUT_PORTS_END
472 
FLOPPY_FORMATS_MEMBER(corcomp_fdc_device::floppy_formats)473 FLOPPY_FORMATS_MEMBER(corcomp_fdc_device::floppy_formats)
474 	FLOPPY_TI99_SDF_FORMAT,
475 	FLOPPY_TI99_TDF_FORMAT
476 FLOPPY_FORMATS_END
477 
478 static void ccfdc_floppies(device_slot_interface &device)
479 {
480 	device.option_add("525dd", FLOPPY_525_DD);  // 40 tracks
481 	device.option_add("525qd", FLOPPY_525_QD);  // 80 tracks
482 	device.option_add("35dd", FLOPPY_35_DD);    // 80 tracks
483 }
484 
common_config(machine_config & config)485 void corcomp_fdc_device::common_config(machine_config& config)
486 {
487 	m_wdc->intrq_wr_callback().set(FUNC(corcomp_fdc_device::fdc_irq_w));
488 	m_wdc->drq_wr_callback().set(FUNC(corcomp_fdc_device::fdc_drq_w));
489 	m_wdc->hld_wr_callback().set(FUNC(corcomp_fdc_device::fdc_hld_w));
490 
491 	TMS9901(config, m_tms9901, 0);
492 	m_tms9901->read_cb().set(FUNC(corcomp_fdc_device::tms9901_input));
493 
494 	// Outputs
495 	// P0: LED (DSR?), PALu2.1
496 	// P1: MFu6.clk (Motor)
497 	// P2: PALu1.5 (WATEN)
498 	// P3: WDCu11.50 (HLT)
499 	// P4: DSK1 select
500 	// P5: DSK2 select
501 	// P6: DSK3 select
502 	// P7: SIDSEL
503 	// P8: DSK4 select
504 	// P10: WDCu11.37 (DDEN)
505 	// P11: ROMBNK
506 	// P13: -
507 	// P14: -
508 	// P15: -
509 	m_tms9901->p_out_cb(0).set(FUNC(corcomp_fdc_device::select_card));
510 	m_tms9901->p_out_cb(1).set(MOTORMF_TAG, FUNC(ttl74123_device::b_w));
511 	m_tms9901->p_out_cb(4).set(FUNC(corcomp_fdc_device::select_dsk));
512 	m_tms9901->p_out_cb(5).set(FUNC(corcomp_fdc_device::select_dsk));
513 	m_tms9901->p_out_cb(6).set(FUNC(corcomp_fdc_device::select_dsk));
514 	m_tms9901->p_out_cb(7).set(FUNC(corcomp_fdc_device::side_select));
515 	m_tms9901->p_out_cb(8).set(FUNC(corcomp_fdc_device::select_dsk));
516 	m_tms9901->p_out_cb(10).set(WDC_TAG, FUNC(wd_fdc_device_base::dden_w));
517 	m_tms9901->p_out_cb(11).set(FUNC(corcomp_fdc_device::select_bank));
518 
519 	// Motor monoflop
520 	TTL74123(config, m_motormf, 0);
521 	m_motormf->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
522 	m_motormf->set_resistor_value(RES_K(100));
523 	m_motormf->set_capacitor_value(CAP_U(47));
524 	m_motormf->set_a_pin_value(0);
525 	m_motormf->set_b_pin_value(1);
526 	m_motormf->set_clear_pin_value(1);
527 	m_motormf->out_cb().set(FUNC(corcomp_fdc_device::motor_w));
528 
529 	FLOPPY_CONNECTOR(config, "0", ccfdc_floppies, "525dd", corcomp_fdc_device::floppy_formats).enable_sound(true);
530 	FLOPPY_CONNECTOR(config, "1", ccfdc_floppies, "525dd", corcomp_fdc_device::floppy_formats).enable_sound(true);
531 	FLOPPY_CONNECTOR(config, "2", ccfdc_floppies, nullptr, corcomp_fdc_device::floppy_formats).enable_sound(true);
532 	FLOPPY_CONNECTOR(config, "3", ccfdc_floppies, nullptr, corcomp_fdc_device::floppy_formats).enable_sound(true);
533 
534 	// SRAM 2114 1Kx4
535 	RAM(config, BUFFER).set_default_size("1k").set_default_value(0);
536 }
537 
538 // ============================================================================
539 // Original CorComp Disk Controller Card (PEB-DCC)
540 // ============================================================================
541 
corcomp_dcc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)542 corcomp_dcc_device::corcomp_dcc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
543 	  corcomp_fdc_device(mconfig, TI99_CCDCC, tag, owner, clock)
544 {
545 }
546 
device_add_mconfig(machine_config & config)547 void corcomp_dcc_device::device_add_mconfig(machine_config& config)
548 {
549 	WD2793(config, m_wdc, 4_MHz_XTAL / 4);
550 	common_config(config);
551 
552 	// For the 2793, attach the HLT line
553 	m_tms9901->p_out_cb(3).set(WDC_TAG, FUNC(wd_fdc_device_base::hlt_w));
554 
555 	// PAL circuits are connected in device_config_complete
556 	CCDCC_PALU2(config, CCDCC_PALU2_TAG, 0);
557 	CCDCC_PALU1(config, CCDCC_PALU1_TAG, 0);
558 }
559 
560 ROM_START( cc_dcc )
561 	ROM_REGION(0x4000, TI99_DSRROM, 0)
CRC(de3f9476)562 	ROM_LOAD("ccdcc_v89.u3", 0x0000, 0x2000, CRC(de3f9476) SHA1(b88aea1141769dad4e4bea5f93ac4f63a627cc82)) /* 8K single ROM bank 1*/
563 	ROM_LOAD("ccdcc_v89.u4", 0x2000, 0x2000, CRC(9c4e5c08) SHA1(26f8096ae60f3839902b4e8764c5fde283ad4ba2)) /* 8K single ROM bank 2*/
564 ROM_END
565 
566 void corcomp_dcc_device::device_config_complete()
567 {
568 	m_decpal = static_cast<ccfdc_dec_pal_device*>(subdevice(CCDCC_PALU2_TAG));
569 	m_ctrlpal = static_cast<ccfdc_sel_pal_device*>(subdevice(CCDCC_PALU1_TAG));
570 	connect_drives();
571 }
572 
device_input_ports() const573 ioport_constructor corcomp_fdc_device::device_input_ports() const
574 {
575 	return INPUT_PORTS_NAME( cc_fdc );
576 }
577 
device_rom_region() const578 const tiny_rom_entry *corcomp_dcc_device::device_rom_region() const
579 {
580 	return ROM_NAME( cc_dcc );
581 }
582 
583 // ========================================================================
584 //    PAL circuits on the CorComp board
585 // ========================================================================
586 
ccfdc_dec_pal_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)587 ccfdc_dec_pal_device::ccfdc_dec_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
588 	:  device_t(mconfig, type, tag, owner, clock),
589 	   m_board(nullptr),
590 	   m_tms9901(*owner, TMS9901_TAG)
591 {
592 }
593 
ccfdc_sel_pal_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)594 ccfdc_sel_pal_device::ccfdc_sel_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
595 	:  device_t(mconfig, type, tag, owner, clock),
596 	   m_board(nullptr),
597 	   m_decpal(nullptr),
598 	   m_motormf(*owner, MOTORMF_TAG),
599 	   m_tms9901(*owner, TMS9901_TAG),
600 	   m_wdc(*owner, WDC_TAG)
601 {
602 }
603 
device_config_complete()604 void ccfdc_dec_pal_device::device_config_complete()
605 {
606 	m_board = static_cast<corcomp_fdc_device*>(owner());
607 }
608 
609 /*
610     Indicates 9901 addressing.
611 */
READ_LINE_MEMBER(ccfdc_dec_pal_device::address9901)612 READ_LINE_MEMBER( ccfdc_dec_pal_device::address9901 )
613 {
614 	return ((m_board->get_address() & 0xff80)==0x1100)? ASSERT_LINE : CLEAR_LINE;
615 }
616 
617 /*
618     Indicates SRAM addressing.
619 */
READ_LINE_MEMBER(ccfdc_dec_pal_device::addressram)620 READ_LINE_MEMBER( ccfdc_dec_pal_device::addressram )
621 {
622 	return ((m_board->card_selected()) &&
623 		(m_board->get_address() & 0xff80)==0x4000)? ASSERT_LINE : CLEAR_LINE;
624 }
625 
626 /*
627     Indicates WDC addressing.
628 */
READ_LINE_MEMBER(ccfdc_dec_pal_device::addresswdc)629 READ_LINE_MEMBER( ccfdc_dec_pal_device::addresswdc )
630 {
631 	return ((m_board->card_selected()) &&
632 		(m_board->get_address() & 0xff80)==0x5f80)? ASSERT_LINE : CLEAR_LINE;
633 }
634 
635 /*
636     Indicates DSR addressing.
637 */
READ_LINE_MEMBER(ccfdc_dec_pal_device::address4)638 READ_LINE_MEMBER( ccfdc_dec_pal_device::address4 )
639 {
640 	return ((m_board->card_selected()) &&
641 		(m_board->get_address() & 0xe000)==0x4000)? ASSERT_LINE : CLEAR_LINE;
642 }
643 
644 /*
645     Indicates SRAM selection.
646 */
READ_LINE_MEMBER(ccfdc_sel_pal_device::selectram)647 READ_LINE_MEMBER( ccfdc_sel_pal_device::selectram )
648 {
649 	return (m_decpal->addressram() && (m_board->upper_bank()))
650 		? ASSERT_LINE : CLEAR_LINE;
651 }
652 
653 /*
654     Indicates WDC selection.
655 */
READ_LINE_MEMBER(ccfdc_sel_pal_device::selectwdc)656 READ_LINE_MEMBER( ccfdc_sel_pal_device::selectwdc )
657 {
658 	return (m_decpal->addresswdc() && ((m_board->get_address()&1)==0))? ASSERT_LINE : CLEAR_LINE;
659 }
660 
661 /*
662     Indicates EPROM selection.
663 */
READ_LINE_MEMBER(ccfdc_sel_pal_device::selectdsr)664 READ_LINE_MEMBER( ccfdc_sel_pal_device::selectdsr )
665 {
666 	return (m_decpal->address4()
667 		&& !m_decpal->addresswdc()
668 		&& !(m_decpal->addressram() && (m_board->upper_bank())))
669 		? ASSERT_LINE : CLEAR_LINE;
670 }
671 
672 // ========================================================================
673 //    PAL circuits on the original CorComp board
674 //    PAL u2 is the address decoder, delivering its results to the
675 //    selector PAL u1.
676 // ========================================================================
677 
ccdcc_palu2_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)678 ccdcc_palu2_device::ccdcc_palu2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
679 	: ccfdc_dec_pal_device(mconfig, CCDCC_PALU2, tag, owner, clock)
680 {
681 }
682 
ccdcc_palu1_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)683 ccdcc_palu1_device::ccdcc_palu1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
684 	: ccfdc_sel_pal_device(mconfig, CCDCC_PALU1, tag, owner, clock)
685 {
686 }
687 
688 /*
689     Wait state logic
690 */
READ_LINE_MEMBER(ccdcc_palu1_device::ready_out)691 READ_LINE_MEMBER( ccdcc_palu1_device::ready_out )
692 {
693 	bool wdc = m_decpal->addresswdc();                         // Addressing the WDC
694 	bool lastdig = (m_board->get_address()&7)==6;              // Address ends with 6 or e (5ff6, 5ffe)
695 	bool trap = m_tms9901->read_bit(tms9901_device::P2);       // Wait state generation is active (SBO 2)
696 	bool waitbyte = m_wdc->drq_r()==CLEAR_LINE;                // We are waiting for a byte
697 	bool noterm = m_wdc->intrq_r()==CLEAR_LINE;                // There is no interrupt yet
698 	bool motor = (m_motormf->q_r()==1);                        // The disk is spinning
699 
700 	line_state ready = (wdc && lastdig && trap && waitbyte && noterm && motor)? CLEAR_LINE : ASSERT_LINE; // then clear READY and thus trigger wait states
701 
702 	LOGMASKED(LOG_READY, "READY = %d (%d,%d,%d,%d,%d,%d)\n", ready, wdc, lastdig, trap, waitbyte, noterm, motor);
703 	return ready;
704 }
705 
device_config_complete()706 void ccdcc_palu1_device::device_config_complete()
707 {
708 	m_board = static_cast<corcomp_fdc_device*>(owner());
709 	m_decpal = static_cast<ccfdc_dec_pal_device*>(owner()->subdevice(CCDCC_PALU2_TAG));
710 }
711 
712 // ============================================================================
713 // Revised CorComp floppy disk controller card REV A
714 // ============================================================================
715 
corcomp_fdca_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)716 corcomp_fdca_device::corcomp_fdca_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
717 	  corcomp_fdc_device(mconfig, TI99_CCFDC, tag, owner, clock)
718 {
719 }
720 
device_add_mconfig(machine_config & config)721 void corcomp_fdca_device::device_add_mconfig(machine_config& config)
722 {
723 	WD1773(config, m_wdc, 8_MHz_XTAL);
724 	common_config(config);
725 
726 	// PAL circuits are connected in device_config_complete
727 	CCFDC_PALU12(config, CCFDC_PALU12_TAG, 0);
728 	CCFDC_PALU6(config, CCFDC_PALU6_TAG, 0);
729 }
730 
731 /*
732     READY trap circuitry on the revised board (U10)
733 */
ready_trap_active()734 bool corcomp_fdca_device::ready_trap_active()
735 {
736 	return m_tms9901->read_bit(tms9901_device::P2)
737 		&& ((m_address & 6)==6)
738 		&& (m_motormf->q_r()==1);
739 }
740 
741 ROM_START( cc_fdcmg )
742 	ROM_REGION(0x8000, TI99_DSRROM, 0)
CRC(f010e273)743 	ROM_LOAD("ccfdc_v89mg.u1", 0x0000, 0x2000, CRC(f010e273) SHA1(bd30103d80c43d4b35e0669145cef7b5c6b9813b)) /* 16K single ROM */
744 	ROM_LOAD("ccfdc_v89mg.u2", 0x2000, 0x2000, CRC(0cad8f5b) SHA1(7744f777b51eedf614f766576bbc3f8c2c2e0042)) /* 16K single ROM */
745 ROM_END
746 
747 void corcomp_fdca_device::device_config_complete()
748 {
749 	m_decpal = static_cast<ccfdc_dec_pal_device*>(subdevice(CCFDC_PALU12_TAG));
750 	m_ctrlpal = static_cast<ccfdc_sel_pal_device*>(subdevice(CCFDC_PALU6_TAG));
751 	connect_drives();
752 }
753 
device_rom_region() const754 const tiny_rom_entry *corcomp_fdca_device::device_rom_region() const
755 {
756 	return ROM_NAME( cc_fdcmg );
757 }
758 
759 // ========================================================================
760 //    PAL circuits on the revised CorComp board
761 //    PAL u12 is the address decoder, delivering its results to the
762 //    selector PAL u6.
763 // ========================================================================
764 
ccfdc_palu12_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)765 ccfdc_palu12_device::ccfdc_palu12_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
766 	: ccfdc_dec_pal_device(mconfig, CCFDC_PALU12, tag, owner, clock)
767 {
768 }
769 
770 /*
771     Indicates 9901 addressing. In this PAL version, the A9 address line is
772     also used.
773 */
READ_LINE_MEMBER(ccfdc_palu12_device::address9901)774 READ_LINE_MEMBER( ccfdc_palu12_device::address9901 )
775 {
776 	return ((m_board->get_address() & 0xffc0)==0x1100)? ASSERT_LINE : CLEAR_LINE;
777 }
778 
ccfdc_palu6_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)779 ccfdc_palu6_device::ccfdc_palu6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
780 	: ccfdc_sel_pal_device(mconfig, CCFDC_PALU6, tag, owner, clock)
781 {
782 }
783 
784 /*
785     Indicates WDC selection. Also checks whether A12 and /WE match.
786     That is, when writing (/WE=0), A12 must be 1 (addresses 5ff8..e),
787     otherwise (/WE=1), A12 must be 0 (addresses 5ff0..6)
788 */
READ_LINE_MEMBER(ccfdc_palu6_device::selectwdc)789 READ_LINE_MEMBER( ccfdc_palu6_device::selectwdc )
790 {
791 	return (m_decpal->addresswdc()
792 		&& ((m_board->get_address()&1)==0)
793 		&& (((m_board->get_address()&8)!=0)==(m_board->write_access())))? ASSERT_LINE : CLEAR_LINE;
794 }
795 
796 /*
797     Indicates EPROM selection. The Rev A selector PAL leads back some of
798     its outputs for this calculation.
799 */
READ_LINE_MEMBER(ccfdc_palu6_device::selectdsr)800 READ_LINE_MEMBER( ccfdc_palu6_device::selectdsr )
801 {
802 	return (m_decpal->address4() && !selectwdc() && !selectram())? ASSERT_LINE : CLEAR_LINE;
803 }
804 
805 /*
806     Wait state logic. The Rev A selector relies on an AND circuit on the
807     board which evaluates whether the trap is active.
808 */
809 
READ_LINE_MEMBER(ccfdc_palu6_device::ready_out)810 READ_LINE_MEMBER( ccfdc_palu6_device::ready_out )
811 {
812 	bool wdc = m_decpal->addresswdc();                   // Addressing the WDC
813 	bool even = (m_board->get_address()&1)==0;          // A15 = 0
814 	bool trap = static_cast<corcomp_fdca_device*>(m_board)->ready_trap_active();   // READY trap active
815 	bool waitbyte = m_wdc->drq_r()==CLEAR_LINE;          // We are waiting for a byte
816 	bool noterm = m_wdc->intrq_r()==CLEAR_LINE;          // There is no interrupt yet
817 
818 	line_state ready = (wdc && even && trap && waitbyte && noterm)? CLEAR_LINE : ASSERT_LINE; // then clear READY and thus trigger wait states
819 
820 	LOGMASKED(LOG_READY, "READY = %d (%d,%d,%d,%d,%d)\n", ready, wdc, even, trap, waitbyte, noterm);
821 	return ready;
822 }
823 
device_config_complete()824 void ccfdc_palu6_device::device_config_complete()
825 {
826 	m_board = static_cast<corcomp_fdca_device*>(owner());
827 	m_decpal = static_cast<ccfdc_dec_pal_device*>(owner()->subdevice(CCFDC_PALU12_TAG));
828 }
829 } } } // end namespace bus::ti99::peb
830