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