1 // license:LGPL-2.1+
2 // copyright-holders:Michael Zapf
3 /****************************************************************************
4
5 Myarc Hard and Floppy Disk Controller ("HFDC")
6
7 The HFDC is based on the HDC9234 controller chip from Standard
8 Microsystems Corporation (SMC). It can work with up to three MFM hard disk
9 drives and up to four floppy disk drives.
10
11 Data flow is detached from the main CPU. The HDC transfers data to/from
12 the drives using direct memory access to attached memory circuits. That
13 is, to write a sector on a drive the CPU must set up the contents in the
14 memory, then initiate a sector write operation.
15
16 The advantage is a much higher data rate (in particular important when
17 working with hard disks) with less load for the main CPU. Also, we do not
18 need a READY line control (as seen with the WD17xx-based controllers).
19 Any kinds of asynchronous events are propagated via INTA* (configurable
20 to INTB*).
21
22 Most of the control logic is hidden in the custom Gate Array chip. We do
23 not have details on its contents, but the specifications in the HFDC manual
24 and in the schematics are sufficient to create a (functionally) proper
25 emulation.
26
27 The HDC9234 can also control tape drives. In early HFDC controller card
28 layouts, a socket for connecting a drive is available. However, there
29 never was a support from the DSR (firmware), so this feature was eliminated
30 in later releases.
31
32 DIP switches
33 - Settings for step rate and track count for each floppy drive (DSK1-DSK4)
34 - CRU base address. Note that only on all other addresses than 1100, the
35 floppy drives are labeled DSK5-DSK8 by the card software.
36
37
38 Components
39
40 HDC 9234 - Universal Disk Controller
41 FDC 9216 - Floppy disk data separator (8 MHz, divider is set by CD0 and CD1)
42 HDC 92C26 - MFM hard disk data separator (10 MHz, also used for 9234)
43 HDC 9223 - Analog data separator support
44 DS 1000-50 - Delay circuit
45 MM 58274BN - Real time clock
46 HM 6264-LP15 - SRAM 8K x 8 (may also be 32K x 8)
47 27C128 - EPROM 16K x 8
48
49 References:
50 [1] Myarc Inc.: Hard and Floppy Disk Controller / Users Manual
51
52 Michael Zapf
53 July 2015
54
55 *****************************************************************************/
56
57 #include "emu.h"
58 #include "hfdc.h"
59 #include "formats/mfm_hd.h"
60 #include "formats/ti99_dsk.h" // Format
61
62 #define LOG_WARN (1U<<1) // Warnings
63 #define LOG_EMU (1U<<2)
64 #define LOG_COMP (1U<<3)
65 #define LOG_RAM (1U<<4)
66 #define LOG_ROM (1U<<5)
67 #define LOG_LINES (1U<<6)
68 #define LOG_DMA (1U<<7)
69 #define LOG_MOTOR (1U<<8)
70 #define LOG_INT (1U<<9)
71 #define LOG_CRU (1U<<10)
72 #define LOG_CONFIG (1U<<15) // Configuration
73
74 #define VERBOSE ( LOG_CONFIG | LOG_WARN )
75 #include "logmacro.h"
76
77 DEFINE_DEVICE_TYPE_NS(TI99_HFDC, bus::ti99::peb, myarc_hfdc_device, "ti99_hfdc", "Myarc Hard and Floppy Disk Controller")
78
79 namespace bus { namespace ti99 { namespace peb {
80
81 #define BUFFER "ram"
82 #define FDC_TAG "hdc9234"
83 #define CLOCK_TAG "mm58274c"
84
85 #define MOTOR_TIMER 1
86
87 #define TAPE_ADDR 0x0fc0
88 #define HDC_R_ADDR 0x0fd0
89 #define HDC_W_ADDR 0x0fd2
90 #define CLK_ADDR 0x0fe0
91 #define RAM_ADDR 0x1000
92
93 // =========================================================================
94
95 /*
96 Constructor for the HFDC card.
97 */
myarc_hfdc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)98 myarc_hfdc_device::myarc_hfdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
99 device_t(mconfig, TI99_HFDC, tag, owner, clock),
100 device_ti99_peribox_card_interface(mconfig, *this),
101 m_motor_on_timer(nullptr),
102 m_hdc9234(*this, FDC_TAG),
103 m_clock(*this, CLOCK_TAG), m_current_floppy(nullptr),
104 m_current_harddisk(nullptr), m_see_switches(false),
105 m_irq(), m_dip(), m_motor_running(false),
106 m_inDsrArea(false), m_HDCsel(false), m_RTCsel(false),
107 m_tapesel(false), m_RAMsel(false), m_ROMsel(false), m_address(0),
108 m_wait_for_hd1(false), m_dsrrom(nullptr), m_rom_page(0),
109 m_buffer_ram(*this, BUFFER), m_status_latch(0), m_dma_address(0),
110 m_output1_latch(0), m_output2_latch(0), m_lastval(0), m_MOTOR_ON(), m_readyflags(0)
111 {
112 }
113
setaddress_dbin(offs_t offset,int state)114 void myarc_hfdc_device::setaddress_dbin(offs_t offset, int state)
115 {
116 // Do not allow setaddress for the debugger. It will mess up the
117 // setaddress/memory access pairs when the CPU enters wait states.
118 if (machine().side_effects_disabled()) return;
119
120 // Selection login in the PAL and some circuits on the board
121
122 // Is the card being selected?
123 // Area = 4000-5fff
124 // 010x xxxx xxxx xxxx
125 m_address = offset;
126
127 m_inDsrArea = in_dsr_space(m_address, true);
128
129 if (!m_inDsrArea) return;
130
131 // Is the HDC chip on the card being selected?
132 // HDC9234: read: 4fd0,4 (mirror 8,c)
133 // HDC9234: write: 4fd2,6 (mirror a,e)
134 // read: ...0 1111 1101 xx00
135 // write: ...0 1111 1101 xx10
136
137 m_HDCsel = m_inDsrArea &&
138 ((state==ASSERT_LINE && ((m_address & 0x1ff3)==HDC_R_ADDR)) // read
139 || (state==CLEAR_LINE && ((m_address & 0x1ff3)==HDC_W_ADDR))); // write
140
141 // Is the tape selected?
142 // ...0 1111 1100 xxxx
143 m_tapesel = m_inDsrArea && ((m_address & 0x1ff0)==TAPE_ADDR);
144
145 // Is the RTC selected on the card? (even addr)
146 // ...0 1111 111x xxx0
147 m_RTCsel = m_inDsrArea && ((m_address & 0x1fe1)==CLK_ADDR);
148
149 // Is RAM selected?
150 // ...1 xxxx xxxx xxxx
151 m_RAMsel = m_inDsrArea && ((m_address & 0x1000)==RAM_ADDR);
152
153 // Is ROM selected?
154 // not 0100 1111 11xx xxxx
155 m_ROMsel = m_inDsrArea && !m_RAMsel && !((m_address & 0x0fc0)==0x0fc0);
156 }
157
158 /*
159 Access for debugger. This is a stripped-down version of the
160 main methods below. We only allow ROM and RAM access.
161 */
debug_read(offs_t offset,uint8_t * value)162 void myarc_hfdc_device::debug_read(offs_t offset, uint8_t* value)
163 {
164 if (in_dsr_space(offset, true) && m_selected)
165 {
166 if ((offset & 0x1000)==RAM_ADDR)
167 {
168 int bank = (offset & 0x0c00) >> 10;
169 *value = m_buffer_ram->pointer()[(m_ram_page[bank]<<10) | (offset & 0x03ff)];
170 }
171 else
172 {
173 if ((offset & 0x0fc0)!=0x0fc0)
174 {
175 *value = m_dsrrom[(m_rom_page << 12) | (offset & 0x0fff)];
176 }
177 }
178 }
179 }
180
debug_write(offs_t offset,uint8_t data)181 void myarc_hfdc_device::debug_write(offs_t offset, uint8_t data)
182 {
183 if (in_dsr_space(offset, true) && m_selected)
184 {
185 if ((offset & 0x1000)==RAM_ADDR)
186 {
187 int bank = (offset & 0x0c00) >> 10;
188 m_buffer_ram->pointer()[(m_ram_page[bank]<<10) | (m_address & 0x03ff)] = data;
189 }
190 }
191 }
192
193 /*
194 Read a byte from the memory address space of the HFDC
195
196 0x4000 - 0x4fbf one of four possible ROM pages
197 0x4fc0 - 0x4fcf Tape control (only available in prototype HFDC models)
198 0x4fd0 - 0x4fdf HDC 9234 ports
199 0x4fe0 - 0x4fff RTC chip ports
200
201 0x5000 - 0x53ff static RAM page 0x08
202 0x5400 - 0x57ff static RAM page any of 32 pages
203 0x5800 - 0x5bff static RAM page any of 32 pages
204 0x5c00 - 0x5fff static RAM page any of 32 pages
205
206 HFDC manual, p. 44
207 */
readz(offs_t offset,uint8_t * value)208 void myarc_hfdc_device::readz(offs_t offset, uint8_t *value)
209 {
210 if (machine().side_effects_disabled())
211 {
212 debug_read(offset, value);
213 return;
214 }
215
216 if (m_inDsrArea && m_selected)
217 {
218 if (m_tapesel)
219 {
220 LOGMASKED(LOG_WARN, "Tape support not available on this HFDC version (access to address %04x)\n", m_address & 0xffff);
221 return;
222 }
223
224 if (m_HDCsel)
225 {
226 *value = m_hdc9234->read((m_address>>2)&1);
227 LOGMASKED(LOG_COMP, "%04x[HDC] -> %02x\n", m_address & 0xffff, *value);
228 return;
229 }
230
231 if (m_RTCsel)
232 {
233 *value = m_clock->read((m_address & 0x001e) >> 1);
234 LOGMASKED(LOG_COMP, "%04x[CLK] -> %02x\n", m_address & 0xffff, *value);
235 return;
236 }
237
238 if (m_RAMsel)
239 {
240 // 0101 00xx xxxx xxxx static 0x08
241 // 0101 01xx xxxx xxxx bank 1
242 // 0101 10xx xxxx xxxx bank 2
243 // 0101 11xx xxxx xxxx bank 3
244 int bank = (m_address & 0x0c00) >> 10;
245
246 // If a DMA is in progress, do not respond
247 if (m_dip == CLEAR_LINE) *value = m_buffer_ram->pointer()[(m_ram_page[bank]<<10) | (m_address & 0x03ff)];
248 if (WORD_ALIGNED(m_address))
249 {
250 int valword = (((*value) << 8) | m_buffer_ram->pointer()[(m_ram_page[bank]<<10) | ((m_address+1) & 0x03ff)])&0xffff;
251 LOGMASKED(LOG_RAM, "%04x[%02x] -> %04x\n", m_address & 0xffff, m_ram_page[bank], valword);
252 }
253 return;
254 }
255
256 if (m_ROMsel)
257 {
258 *value = m_dsrrom[(m_rom_page << 12) | (m_address & 0x0fff)];
259 if (WORD_ALIGNED(m_address))
260 {
261 int valword = (((*value) << 8) | m_dsrrom[(m_rom_page << 12) | ((m_address + 1) & 0x0fff)])&0xffff;
262 LOGMASKED(LOG_ROM, "%04x[%02x] -> %04x\n", m_address & 0xffff, m_rom_page, valword);
263 }
264 return;
265 }
266 }
267 }
268
269 /*
270 Write a byte to the memory address space of the HFDC
271
272 0x4fc0 - 0x4fcf Tape control (only available in prototype HFDC models)
273 0x4fd0 - 0x4fdf HDC 9234 ports
274 0x4fe0 - 0x4fff RTC chip ports
275
276 0x5000 - 0x53ff static RAM page 0x08
277 0x5400 - 0x57ff static RAM page any of 32 pages
278 0x5800 - 0x5bff static RAM page any of 32 pages
279 0x5c00 - 0x5fff static RAM page any of 32 pages
280 */
write(offs_t offset,uint8_t data)281 void myarc_hfdc_device::write(offs_t offset, uint8_t data)
282 {
283 if (machine().side_effects_disabled())
284 {
285 debug_write(offset, data);
286 return;
287 }
288
289 if (m_inDsrArea && m_selected)
290 {
291 if (m_tapesel)
292 {
293 LOGMASKED(LOG_WARN, "Tape support not available on this HFDC version (write access to address %04x: %02x)\n", m_address & 0xffff, data);
294 return;
295 }
296
297 if (m_HDCsel)
298 {
299 LOGMASKED(LOG_COMP, "%04x[HDC] <- %02x\n", m_address & 0xffff, data);
300 m_hdc9234->write((m_address>>2)&1, data);
301 return;
302 }
303
304 if (m_RTCsel)
305 {
306 LOGMASKED(LOG_COMP, "%04x[CLK] <- %02x\n", m_address & 0xffff, data);
307 m_clock->write((m_address & 0x001e) >> 1, data);
308 return;
309 }
310
311 if (m_RAMsel)
312 {
313 // 0101 00xx xxxx xxxx static 0x08
314 // 0101 01xx xxxx xxxx bank 1
315 // 0101 10xx xxxx xxxx bank 2
316 // 0101 11xx xxxx xxxx bank 3
317 int bank = (m_address & 0x0c00) >> 10;
318 LOGMASKED(LOG_RAM, "%04x[%02x] <- %02x\n", m_address & 0xffff, m_ram_page[bank], data);
319
320 // When a DMA is in progress, do not change anything
321 if (m_dip == CLEAR_LINE) m_buffer_ram->pointer()[(m_ram_page[bank]<<10) | (m_address & 0x03ff)] = data;
322 return;
323 }
324 // The rest is ROM
325 if (m_ROMsel)
326 {
327 LOGMASKED(LOG_ROM, "Ignoring write ROM %04x[%02x]: %02x\n", m_address & 0xffff, m_rom_page, data);
328 }
329 }
330 }
331
332 /*
333 Read a set of 8 bits in the CRU space of the HFDC
334 There are two banks, according to the state of m_see_switches
335
336 m_see_switches == true:
337
338 7 6 5 4 3 2 1 0 CRU bit
339 +-----+-----+-----+-----+-----+-----+-----+-----+
340 |DIP5*|DIP6*|DIP7*|DIP8*|DIP1*|DIP2*|DIP3*|DIP4*|
341 +-----+-----+-----+-----+-----+-----+-----+-----+
342 | DSK3 | DSK4 | DSK1 | DSK2 |
343 +-----+-----+-----+-----+-----+-----+-----+-----+
344
345 Settings for DSKn: (n=1..4)
346
347 DIP(2n-1) DIP(2n) Tracks Step(ms) Sectors (256 byte)
348 off off 40 16 18/16/9
349 on off 40 8 18/16/9
350 off on 80/40 2 18/16/9
351 on on 80 2 36
352
353 Inverted logic: switch=on means a 0 bit, off is a 1 bit when read by the CRU
354
355 Caution: The last setting is declared as "future expansion" and is
356 locked to a 1.44 MiB capacity. No lower formats can be used.
357
358 ---
359
360 m_see_switches == false:
361
362 7 6 5 4 3 2 1 0
363 +-----+-----+-----+-----+-----+-----+-----+-----+
364 | 0 | 0 | 0 | 0 | WAIT| MON*| DIP | IRQ |
365 +-----+-----+-----+-----+-----+-----+-----+-----+
366
367 WAIT = Wait for WDS1 to become ready
368 MON* = Motor on
369 DIP = DMA in progress
370 IRQ = Interrupt request
371 ---
372 0 on all other locations
373 */
crureadz(offs_t offset,uint8_t * value)374 void myarc_hfdc_device::crureadz(offs_t offset, uint8_t *value)
375 {
376 uint8_t reply;
377 if ((offset & 0xff00)==m_cru_base)
378 {
379 if ((offset & 0x00f0)==0) // CRU bits 0-7
380 {
381 if (m_see_switches)
382 {
383 reply = ~(ioport("HFDCDIP")->read());
384 }
385 else
386 {
387 reply = 0;
388 if (m_irq == ASSERT_LINE) reply |= 0x01;
389 if (m_dip == ASSERT_LINE) reply |= 0x02;
390 if (!m_motor_running) reply |= 0x04;
391 if (m_wait_for_hd1) reply |= 0x08;
392 }
393 *value = BIT(reply, (offset >> 1) & 7);
394 }
395 else // CRU bits 8+
396 {
397 *value = 0;
398 }
399
400 LOGMASKED(LOG_CRU, "CRU %04x -> %02x\n", offset & 0xffff, *value);
401 }
402 }
403
404 /*
405 Set a bit in the CRU space of the HFDC
406
407 7 6 5 4 3 2 1 0
408 +-----+-----+-----+-----+-----+-----+-----+-----+
409 | - | - | - | ROM1| ROM0| MON | RES*| SEL |
410 | | | | CSEL| CD1 | CD0 | | |
411 +-----+-----+-----+-----+-----+-----+-----+-----+
412
413 17 16 15 14 13 12 11 10 F E D C B A 9 8
414 +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
415 | RAM page select @5C00 | RAM page select @5800 | RAM page select @5400 | - |
416 +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
417
418 SEL = Select card (and map ROM into address space)
419 RES* = Reset controller
420 MON = Motor on / same line goes to CD0 input of floppy separator
421 ROM bank select: bank 0..3; bit 3 = MSB, 4 = LSB
422 RAM bank select: bank 0..31; bit 9 = LSB (accordingly for other two areas)
423 CD0 and CD1 are Clock Divider selections for the Floppy Data Separator (FDC9216)
424 CSEL = CRU input select (m_see_switches)
425
426 HFDC manual p. 43
427 */
cruwrite(offs_t offset,uint8_t data)428 void myarc_hfdc_device::cruwrite(offs_t offset, uint8_t data)
429 {
430 if ((offset & 0xff00)==m_cru_base)
431 {
432 LOGMASKED(LOG_CRU, "CRU %04x <- %d\n", offset & 0xffff, data);
433
434 int bit = (offset >> 1) & 0x1f;
435
436 // Handle the page selects right here
437 if (bit >= 0x09 && bit < 0x18)
438 {
439 if (data)
440 // we leave index 0 unchanged; modify indices 1-3
441 m_ram_page[(bit-4)/5] |= 1 << ((bit-9)%5);
442 else
443 m_ram_page[(bit-4)/5] &= ~(1 << ((bit-9)%5));
444
445 if (bit==0x0d) LOGMASKED(LOG_CRU, "RAM page @5400 = %d\n", m_ram_page[1]);
446 if (bit==0x12) LOGMASKED(LOG_CRU, "RAM page @5800 = %d\n", m_ram_page[2]);
447 if (bit==0x17) LOGMASKED(LOG_CRU, "RAM page @5C00 = %d\n", m_ram_page[3]);
448 return;
449 }
450
451 switch (bit)
452 {
453 case 0:
454 {
455 bool turnOn = (data!=0);
456 // Avoid too many meaningless log outputs
457 if (m_selected != turnOn) LOGMASKED(LOG_CRU, "card %s\n", turnOn? "selected" : "unselected");
458 m_selected = turnOn;
459 break;
460 }
461 case 1:
462 if (data==0) LOGMASKED(LOG_CRU, "trigger HDC reset\n");
463 m_hdc9234->reset((data == 0)? ASSERT_LINE : CLEAR_LINE);
464 break;
465
466 case 2:
467 m_hdc9234->set_clock_divider(0, data);
468
469 // Activate motor
470 // When 1, let motor run continuously. When 0, a simple monoflop circuit keeps the line active for another 4 sec
471 if (data==1)
472 {
473 m_motor_on_timer->reset();
474 set_floppy_motors_running(true);
475 }
476 else
477 {
478 m_motor_on_timer->adjust(attotime::from_msec(4230));
479 }
480 m_lastval = data;
481 break;
482
483 case 3:
484 m_hdc9234->set_clock_divider(1, data);
485 m_rom_page = (data != 0)? (m_rom_page | 2) : (m_rom_page & 0xfd);
486 LOGMASKED(LOG_CRU, "ROM page = %d\n", m_rom_page);
487 break;
488
489 case 4:
490 m_see_switches = (data != 0);
491 m_rom_page = (data != 0)? (m_rom_page | 1) : (m_rom_page & 0xfe);
492 LOGMASKED(LOG_CRU, "ROM page = %d, see_switches = %d\n", m_rom_page, m_see_switches);
493 break;
494
495 default:
496 LOGMASKED(LOG_WARN, "Attempt to set undefined CRU bit %d\n", bit);
497 }
498 }
499 }
500
501 /*
502 Monoflop has gone back to the OFF state.
503 */
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)504 void myarc_hfdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
505 {
506 set_floppy_motors_running(false);
507 }
508
509 /*
510 This is called back from the floppy when an index hole is passing by.
511 */
floppy_index_callback(floppy_image_device * floppy,int state)512 void myarc_hfdc_device::floppy_index_callback(floppy_image_device *floppy, int state)
513 {
514 if (state==1) LOGMASKED(LOG_LINES, "Floppy index pulse\n");
515 // m_status_latch = (state==ASSERT_LINE)? (m_status_latch | hdc92x4_device::DS_INDEX) : (m_status_latch & ~hdc92x4_device::DS_INDEX);
516 set_bits(m_status_latch, hdc92x4_device::DS_INDEX, (state==ASSERT_LINE));
517 signal_drive_status();
518 }
519
520 /*
521 This is called back from the hard disk when an index hole is passing by.
522 */
harddisk_index_callback(mfm_harddisk_device * harddisk,int state)523 void myarc_hfdc_device::harddisk_index_callback(mfm_harddisk_device *harddisk, int state)
524 {
525 if (state==1) LOGMASKED(LOG_LINES, "HD index pulse\n");
526 set_bits(m_status_latch, hdc92x4_device::DS_INDEX, (state==ASSERT_LINE));
527 signal_drive_status();
528 }
529
530 /*
531 This is called back from the hard disk when READY becomes asserted.
532 */
harddisk_ready_callback(mfm_harddisk_device * harddisk,int state)533 void myarc_hfdc_device::harddisk_ready_callback(mfm_harddisk_device *harddisk, int state)
534 {
535 LOGMASKED(LOG_LINES, "HD READY = %d\n", state);
536 set_bits(m_status_latch, hdc92x4_device::DS_READY, (state==ASSERT_LINE));
537 signal_drive_status();
538 }
539
540 /*
541 This is called back from the hard disk when seek_complete becomes asserted.
542 */
harddisk_skcom_callback(mfm_harddisk_device * harddisk,int state)543 void myarc_hfdc_device::harddisk_skcom_callback(mfm_harddisk_device *harddisk, int state)
544 {
545 LOGMASKED(LOG_LINES, "HD seek complete = %d\n", state);
546 set_bits(m_status_latch, hdc92x4_device::DS_SKCOM, (state==ASSERT_LINE));
547 signal_drive_status();
548 }
549
set_bits(uint8_t & byte,int mask,bool set)550 void myarc_hfdc_device::set_bits(uint8_t& byte, int mask, bool set)
551 {
552 if (set) byte |= mask;
553 else byte &= ~mask;
554 }
555
556 /*
557 Maps the set bit to an index. The rightmost 1 bit is significant. When no
558 bit is set, returns -1.
559 */
bit_to_index(int value)560 int myarc_hfdc_device::bit_to_index(int value)
561 {
562 if (value & 0x01) return 0;
563 if (value & 0x02) return 1;
564 if (value & 0x04) return 2;
565 if (value & 0x08) return 3;
566 return -1;
567 }
568
569 /*
570 Notify the controller about the status change
571 */
signal_drive_status()572 void myarc_hfdc_device::signal_drive_status()
573 {
574 uint8_t reply = 0;
575 // Status byte as defined by HDC9234
576 // +------+------+------+------+------+------+------+------+
577 // | ECC |Index | SeekC| Tr00 | User | WrPrt| Ready|Fault |
578 // +------+------+------+------+------+------+------+------+
579 //
580 // Set by HFDC
581 // 74LS240 is used for driving the lines; it also inverts the inputs
582 // If no hard drive or floppy is connected, all lines are 0
583 // +------+------+------+------+------+------+------+------+
584 // | 0 | Index| SeekC| Tr00 | 0 | WrPrt| Ready|Fault |
585 // +------+------+------+------+------+------+------+------+
586 //
587 // Ready = /WDS.ready* | DSK
588 // SeekComplete = /WDS.seekComplete* | DSK
589
590 // If DSK is selected, set Ready and SeekComplete to 1
591 if ((m_output1_latch & 0x10)!=0)
592 {
593 reply |= 0x22;
594
595 // Check for TRK00*
596 if ((m_current_floppy != nullptr) && (!m_current_floppy->trk00_r()))
597 reply |= hdc92x4_device::DS_TRK00;
598 }
599 else
600 {
601 if ((m_output1_latch & 0xe0)!=0)
602 {
603 if (m_current_harddisk != nullptr)
604 {
605 if (m_current_harddisk->ready_r()==ASSERT_LINE)
606 {
607 m_status_latch |= hdc92x4_device::DS_READY;
608 set_bits(m_status_latch, hdc92x4_device::DS_SKCOM, m_current_harddisk->seek_complete_r()==ASSERT_LINE);
609 set_bits(m_status_latch, hdc92x4_device::DS_TRK00, m_current_harddisk->trk00_r()==ASSERT_LINE);
610 }
611 }
612 // If WDS is selected but not connected, WDS.ready* and WDS.seekComplete* are 1, so Ready=SeekComplete=0
613 else set_bits(m_status_latch, hdc92x4_device::DS_READY | hdc92x4_device::DS_SKCOM, false);
614 }
615 }
616
617 reply |= m_status_latch;
618
619 m_hdc9234->auxbus_in(reply);
620 }
621
622 /*
623 When the HDC outputs a byte via its AB (auxiliary bus), we need to latch it.
624 The target of the transfer is determined by two control lines (S1,S0).
625 (0,0) = input drive status
626 (0,1) = DMA address
627 (1,0) = OUTPUT1
628 (1,1) = OUTPUT2
629 */
auxbus_out(offs_t offset,uint8_t data)630 void myarc_hfdc_device::auxbus_out(offs_t offset, uint8_t data)
631 {
632 int index;
633 switch (offset)
634 {
635 case hdc92x4_device::INPUT_STATUS:
636 LOGMASKED(LOG_WARN, "Invalid operation: S0=S1=0, but tried to write (expected: read drive status)\n");
637 break;
638
639 case hdc92x4_device::OUTPUT_DMA_ADDR:
640 // Value is dma address byte. Shift previous contents to the left.
641 // The value is latched inside the Gate Array.
642 m_dma_address = ((m_dma_address << 8) + (data&0xff))&0xffffff;
643 LOGMASKED(LOG_DMA, "Setting DMA address; current value = %06x\n", m_dma_address);
644 break;
645
646 case hdc92x4_device::OUTPUT_1:
647 // value is output1
648 // The HFDC interprets the value as follows:
649 // WDS = Winchester Drive System, old name for hard disk
650 // +------+------+------+------+------+------+------+------+
651 // | WDS3 | WDS2 | WDS1 | DSKx | x=4 | x=3 | x=2 | x=1 |
652 // +------+------+------+------+------+------+------+------+
653 // Accordingly, drive 0 is always the floppy; selected by the low nibble
654
655 m_output1_latch = data;
656
657 if ((data & 0x10) != 0) connect_floppy_unit(bit_to_index(data & 0x0f)); // Floppy selected
658 else
659 {
660 index = bit_to_index((data>>4) & 0x0f);
661
662 if (index > 0) connect_harddisk_unit(index-1); // HD selected; index >= 1
663 else
664 {
665 disconnect_floppy_drives();
666 disconnect_hard_drives();
667
668 // Turn off READY and SEEK COMPLETE
669 set_bits(m_status_latch, hdc92x4_device::DS_READY | hdc92x4_device::DS_SKCOM, false);
670 }
671 }
672 break;
673
674 case hdc92x4_device::OUTPUT_2:
675 // value is output2
676 // DS3* = /WDS3
677 // WCur = Reduced Write Current
678 // Dir = Step direction
679 // Step = Step pulse
680 // Head = Selected head number (floppy: 0000 or 0001)
681 // +------+------+------+------+------+------+------+------+
682 // | DS3* | WCur | Dir | Step | Head |
683 // +------+------+------+------+------+------+------+------+
684 m_output2_latch = data;
685
686 // Output the step pulse to the selected floppy drive
687 if (m_current_floppy != nullptr)
688 {
689 m_current_floppy->ss_w(data & 0x01);
690 m_current_floppy->dir_w((data & 0x20)==0);
691 m_current_floppy->stp_w((data & 0x10)==0);
692 }
693
694 if (m_current_harddisk != nullptr)
695 {
696 // Dir = 0 -> outward
697 m_current_harddisk->direction_in_w((data & 0x20)? ASSERT_LINE : CLEAR_LINE);
698 m_current_harddisk->step_w((data & 0x10)? ASSERT_LINE : CLEAR_LINE);
699 m_current_harddisk->headsel_w(data & 0x0f);
700 }
701
702 // We are pushing the drive status after OUTPUT2
703 signal_drive_status();
704 break;
705 }
706 }
707
708 enum
709 {
710 HFDC_FLOPPY = 1,
711 HFDC_HARDDISK = 2
712 };
713
connect_floppy_unit(int index)714 void myarc_hfdc_device::connect_floppy_unit(int index)
715 {
716 // Check if we have a new floppy
717 if (m_floppy_unit[index] != m_current_floppy)
718 {
719 // Clear all latched flags from other drives
720 m_status_latch = 0;
721 disconnect_floppy_drives();
722 LOGMASKED(LOG_LINES, "Select floppy drive DSK%d\n", index+1);
723
724 // Connect new drive
725 m_current_floppy = m_floppy_unit[index];
726
727 // We don't use the READY line of floppy drives.
728 // READY is asserted when DSKx = 1
729 // The controller fetches the state with the auxbus access
730 LOGMASKED(LOG_LINES, "Connect index callback DSK%d\n", index+1);
731 if (m_current_floppy != nullptr)
732 m_current_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&myarc_hfdc_device::floppy_index_callback, this));
733 else
734 LOGMASKED(LOG_WARN, "Connection to DSK%d failed because no drive is connected\n", index+1);
735 m_hdc9234->connect_floppy_drive(m_floppy_unit[index]);
736 }
737
738 // We can only run a floppy or a harddisk at a time, not both
739 disconnect_hard_drives();
740 }
741
connect_harddisk_unit(int index)742 void myarc_hfdc_device::connect_harddisk_unit(int index)
743 {
744 if (m_harddisk_unit[index] != m_current_harddisk)
745 {
746 // Clear all latched flags form other drives
747 m_status_latch = 0;
748 disconnect_hard_drives();
749 LOGMASKED(LOG_LINES, "Select hard disk WDS%d\n", index+1);
750
751 // Connect new drive
752 m_current_harddisk = m_harddisk_unit[index];
753
754 LOGMASKED(LOG_LINES, "Connect index callback WDS%d\n", index+1);
755 if (m_current_harddisk != nullptr)
756 {
757 m_current_harddisk->setup_index_pulse_cb(mfm_harddisk_device::index_pulse_cb(&myarc_hfdc_device::harddisk_index_callback, this));
758 m_current_harddisk->setup_ready_cb(mfm_harddisk_device::ready_cb(&myarc_hfdc_device::harddisk_ready_callback, this));
759 m_current_harddisk->setup_seek_complete_cb(mfm_harddisk_device::seek_complete_cb(&myarc_hfdc_device::harddisk_skcom_callback, this));
760 }
761 else
762 LOGMASKED(LOG_WARN, "Connection to WDS%d failed because no drive is connected\n", index+1);
763 m_hdc9234->connect_hard_drive(m_current_harddisk);
764 }
765
766 // We can only run a floppy or a harddisk at a time, not both
767 disconnect_floppy_drives();
768 }
769
disconnect_floppy_drives()770 void myarc_hfdc_device::disconnect_floppy_drives()
771 {
772 LOGMASKED(LOG_LINES, "Unselect floppy drives\n");
773 // Disconnect current floppy
774 if (m_current_floppy != nullptr)
775 {
776 m_current_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
777 m_current_floppy = nullptr;
778 }
779 }
780
disconnect_hard_drives()781 void myarc_hfdc_device::disconnect_hard_drives()
782 {
783 LOGMASKED(LOG_LINES, "Unselect hard drives\n");
784 if (m_current_harddisk != nullptr)
785 {
786 m_current_harddisk->setup_index_pulse_cb(mfm_harddisk_device::index_pulse_cb());
787 m_current_harddisk->setup_seek_complete_cb(mfm_harddisk_device::seek_complete_cb());
788 m_current_harddisk = nullptr;
789 }
790 }
791
792 /*
793 All floppy motors are operated by the same line.
794 */
set_floppy_motors_running(bool run)795 void myarc_hfdc_device::set_floppy_motors_running(bool run)
796 {
797 if (run)
798 {
799 if (m_MOTOR_ON==CLEAR_LINE) LOGMASKED(LOG_MOTOR, "Motor START\n");
800 m_MOTOR_ON = ASSERT_LINE;
801 }
802 else
803 {
804 if (m_MOTOR_ON==ASSERT_LINE) LOGMASKED(LOG_MOTOR, "Motor STOP\n");
805 m_MOTOR_ON = CLEAR_LINE;
806 }
807
808 // Set all motors
809 for (auto & elem : m_floppy_unit)
810 if (elem != nullptr) elem->mon_w((run)? 0 : 1);
811 }
812
813 /*
814 Called whenever the state of the HDC9234 interrupt pin changes.
815 */
WRITE_LINE_MEMBER(myarc_hfdc_device::intrq_w)816 WRITE_LINE_MEMBER( myarc_hfdc_device::intrq_w )
817 {
818 m_irq = (line_state)state;
819 LOGMASKED(LOG_INT, "INT pin from controller = %d, propagating to INTA*\n", state);
820
821 // Set INTA*
822 // Signal from SMC is active high, INTA* is active low; board inverts signal
823 // Anyway, we stay with ASSERT_LINE and CLEAR_LINE
824 m_slot->set_inta(state);
825 }
826
827 /*
828 Called whenever the HDC9234 desires bus access to the buffer RAM. The
829 controller expects a call to dmarq in 1 byte time.
830 */
WRITE_LINE_MEMBER(myarc_hfdc_device::dmarq_w)831 WRITE_LINE_MEMBER( myarc_hfdc_device::dmarq_w )
832 {
833 LOGMASKED(LOG_DMA, "DMARQ pin from controller = %d\n", state);
834 if (state == ASSERT_LINE)
835 {
836 m_hdc9234->dmaack(ASSERT_LINE);
837 }
838 }
839
840 /*
841 Called whenever the state of the HDC9234 DMA in progress changes.
842 */
WRITE_LINE_MEMBER(myarc_hfdc_device::dip_w)843 WRITE_LINE_MEMBER( myarc_hfdc_device::dip_w )
844 {
845 m_dip = (line_state)state;
846 }
847
848 /*
849 Read a byte from the onboard SRAM. This is called from the HDC9234.
850 */
read_buffer()851 uint8_t myarc_hfdc_device::read_buffer()
852 {
853 LOGMASKED(LOG_DMA, "Read access to onboard SRAM at %04x\n", m_dma_address);
854 if (m_dma_address > 0x8000) LOGMASKED(LOG_WARN, "Read access beyond RAM size: %06x\n", m_dma_address);
855 uint8_t value = m_buffer_ram->pointer()[m_dma_address & 0x7fff];
856 m_dma_address = (m_dma_address+1) & 0x7fff;
857 return value;
858 }
859
860 /*
861 Write a byte to the onboard SRAM. This is called from the HDC9234.
862 */
write_buffer(uint8_t data)863 void myarc_hfdc_device::write_buffer(uint8_t data)
864 {
865 LOGMASKED(LOG_DMA, "Write access to onboard SRAM at %04x: %02x\n", m_dma_address, data);
866 if (m_dma_address > 0x8000) LOGMASKED(LOG_WARN, "Write access beyond RAM size: %06x\n", m_dma_address);
867 m_buffer_ram->pointer()[m_dma_address & 0x7fff] = data;
868 m_dma_address = (m_dma_address+1) & 0x7fff;
869 }
870
device_start()871 void myarc_hfdc_device::device_start()
872 {
873 m_dsrrom = memregion(TI99_DSRROM)->base();
874 m_motor_on_timer = timer_alloc(MOTOR_TIMER);
875 // The HFDC does not use READY; it has on-board RAM for DMA
876 m_current_floppy = nullptr;
877 m_current_harddisk = nullptr;
878
879 // Parent class members
880 save_item(NAME(m_senila));
881 save_item(NAME(m_senilb));
882 save_item(NAME(m_selected));
883 save_item(NAME(m_cru_base));
884
885 // Own members
886 save_item(NAME(m_see_switches));
887 save_item(NAME(m_irq));
888 save_item(NAME(m_dip));
889 save_item(NAME(m_motor_running));
890 save_item(NAME(m_inDsrArea));
891 save_item(NAME(m_HDCsel));
892 save_item(NAME(m_RTCsel));
893 save_item(NAME(m_tapesel));
894 save_item(NAME(m_RAMsel));
895 save_item(NAME(m_ROMsel));
896 save_item(NAME(m_address));
897 save_item(NAME(m_wait_for_hd1));
898 save_item(NAME(m_rom_page));
899 save_pointer(NAME(m_ram_page),4);
900 save_item(NAME(m_status_latch));
901 save_item(NAME(m_dma_address));
902 save_item(NAME(m_output1_latch));
903 save_item(NAME(m_output2_latch));
904 save_item(NAME(m_lastval));
905 save_item(NAME(m_MOTOR_ON));
906 save_item(NAME(m_readyflags));
907 }
908
device_reset()909 void myarc_hfdc_device::device_reset()
910 {
911 m_cru_base = ioport("CRUHFDC")->read();
912 m_wait_for_hd1 = ioport("WAITHD1")->read();
913
914 // Resetting values
915 m_rom_page = 0;
916
917 m_ram_page[0] = 0x08; // static page 0x08
918 for (int i=1; i < 4; i++) m_ram_page[i] = 0;
919
920 m_output1_latch = m_output2_latch = 0;
921
922 m_status_latch = 0x00;
923
924 m_dip = m_irq = CLEAR_LINE;
925 m_see_switches = false;
926 m_motor_running = false;
927 m_selected = false;
928 m_lastval = 0;
929 m_readyflags = 0;
930
931 for (int i=0; i < 4; i++)
932 {
933 if (m_floppy_unit[i] != nullptr)
934 LOGMASKED(LOG_CONFIG, "FD connector %d with %s\n", i+1, m_floppy_unit[i]->name());
935 else
936 LOGMASKED(LOG_CONFIG, "FD connector %d has no floppy attached\n", i+1);
937 }
938
939 for (int i=0; i < 3; i++)
940 {
941 if (m_harddisk_unit[i] != nullptr)
942 LOGMASKED(LOG_CONFIG, "HD connector %d with %s\n", i+1, m_harddisk_unit[i]->name());
943 else
944 LOGMASKED(LOG_CONFIG, "HD connector %d has no drive attached\n", i+1);
945 }
946
947 // Disconnect all units
948 disconnect_floppy_drives();
949 disconnect_hard_drives();
950 }
951
device_config_complete()952 void myarc_hfdc_device::device_config_complete()
953 {
954 for (int i=0; i < 3; i++)
955 {
956 m_floppy_unit[i] = nullptr;
957 m_harddisk_unit[i] = nullptr;
958 }
959 m_floppy_unit[3] = nullptr;
960
961 // Seems to be null when doing a "-listslots"
962 if (subdevice("f1")!=nullptr)
963 {
964 m_floppy_unit[0] = static_cast<floppy_connector*>(subdevice("f1"))->get_device();
965 m_floppy_unit[1] = static_cast<floppy_connector*>(subdevice("f2"))->get_device();
966 m_floppy_unit[2] = static_cast<floppy_connector*>(subdevice("f3"))->get_device();
967 m_floppy_unit[3] = static_cast<floppy_connector*>(subdevice("f4"))->get_device();
968
969 m_harddisk_unit[0] = static_cast<mfm_harddisk_connector*>(subdevice("h1"))->get_device();
970 m_harddisk_unit[1] = static_cast<mfm_harddisk_connector*>(subdevice("h2"))->get_device();
971 m_harddisk_unit[2] = static_cast<mfm_harddisk_connector*>(subdevice("h3"))->get_device();
972 }
973 }
974
975 /*
976 The HFDC controller can be configured for different CRU base addresses,
977 but DSK1-DSK4 are only available for CRU 1100. For all other addresses,
978 the drives 1 to 4 are renamed to DSK5-DSK8 (see [1] p. 7).
979 */
980 INPUT_PORTS_START( ti99_hfdc )
981 PORT_START( "WAITHD1" )
982 PORT_DIPNAME( 0x01, 0x00, "HFDC Wait for HD1" )
DEF_STR(Off)983 PORT_DIPSETTING( 0x00, DEF_STR( Off ) )
984 PORT_DIPSETTING( 0x01, DEF_STR( On ) )
985
986 PORT_START( "CRUHFDC" )
987 PORT_DIPNAME( 0x1f00, 0x1100, "HFDC CRU base" )
988 PORT_DIPSETTING( 0x1000, "1000" )
989 PORT_DIPSETTING( 0x1100, "1100" )
990 PORT_DIPSETTING( 0x1200, "1200" )
991 PORT_DIPSETTING( 0x1300, "1300" )
992 PORT_DIPSETTING( 0x1400, "1400" )
993 PORT_DIPSETTING( 0x1500, "1500" )
994 PORT_DIPSETTING( 0x1600, "1600" )
995 PORT_DIPSETTING( 0x1700, "1700" )
996 PORT_DIPSETTING( 0x1800, "1800" )
997 PORT_DIPSETTING( 0x1900, "1900" )
998 PORT_DIPSETTING( 0x1a00, "1A00" )
999 PORT_DIPSETTING( 0x1b00, "1B00" )
1000 PORT_DIPSETTING( 0x1c00, "1C00" )
1001 PORT_DIPSETTING( 0x1d00, "1D00" )
1002 PORT_DIPSETTING( 0x1e00, "1E00" )
1003 PORT_DIPSETTING( 0x1f00, "1F00" )
1004
1005 PORT_START( "HFDCDIP" )
1006 PORT_DIPNAME( 0x0c, 0x08, "HFDC drive 1 config" )
1007 PORT_DIPSETTING( 0x00, "40 track, 16 ms")
1008 PORT_DIPSETTING( 0x08, "40 track, 8 ms")
1009 PORT_DIPSETTING( 0x04, "80 track, 2 ms")
1010 PORT_DIPSETTING( 0x0c, "80 track HD, 2 ms")
1011 PORT_DIPNAME( 0x03, 0x02, "HFDC drive 2 config" )
1012 PORT_DIPSETTING( 0x00, "40 track, 16 ms")
1013 PORT_DIPSETTING( 0x02, "40 track, 8 ms")
1014 PORT_DIPSETTING( 0x01, "80 track, 2 ms")
1015 PORT_DIPSETTING( 0x03, "80 track HD, 2 ms")
1016 PORT_DIPNAME( 0xc0, 0x80, "HFDC drive 3 config" )
1017 PORT_DIPSETTING( 0x00, "40 track, 16 ms")
1018 PORT_DIPSETTING( 0x80, "40 track, 8 ms")
1019 PORT_DIPSETTING( 0x40, "80 track, 2 ms")
1020 PORT_DIPSETTING( 0xc0, "80 track HD, 2 ms")
1021 PORT_DIPNAME( 0x30, 0x20, "HFDC drive 4 config" )
1022 PORT_DIPSETTING( 0x00, "40 track, 16 ms")
1023 PORT_DIPSETTING( 0x20, "40 track, 8 ms")
1024 PORT_DIPSETTING( 0x10, "80 track, 2 ms")
1025 PORT_DIPSETTING( 0x30, "80 track HD, 2 ms")
1026 INPUT_PORTS_END
1027
1028 FLOPPY_FORMATS_MEMBER(myarc_hfdc_device::floppy_formats)
1029 FLOPPY_TI99_SDF_FORMAT,
1030 FLOPPY_TI99_TDF_FORMAT
1031 FLOPPY_FORMATS_END
1032
1033 static void hfdc_floppies(device_slot_interface &device)
1034 {
1035 device.option_add("525dd", FLOPPY_525_DD); // 40 tracks
1036 device.option_add("525qd", FLOPPY_525_QD); // 80 tracks
1037 device.option_add("35dd", FLOPPY_35_DD); // 80 tracks
1038 device.option_add("35hd", FLOPPY_35_HD); // 80 tracks 1.4 MiB
1039 }
1040
hfdc_harddisks(device_slot_interface & device)1041 static void hfdc_harddisks(device_slot_interface &device)
1042 {
1043 device.option_add("generic", MFMHD_GENERIC); // Generic hard disk (self-adapting to image)
1044 device.option_add("st213", MFMHD_ST213); // Seagate ST-213 (10 MB)
1045 device.option_add("st225", MFMHD_ST225); // Seagate ST-225 (20 MB)
1046 device.option_add("st251", MFMHD_ST251); // Seagate ST-251 (40 MB)
1047 }
1048
1049 ROM_START( ti99_hfdc )
1050 ROM_REGION(0x4000, TI99_DSRROM, 0)
1051 ROM_LOAD("hfdc_dsr.u34", 0x0000, 0x4000, CRC(66fbe0ed) SHA1(11df2ecef51de6f543e4eaf8b2529d3e65d0bd59)) /* HFDC disk DSR ROM */
1052 ROM_END
1053
1054
device_add_mconfig(machine_config & config)1055 void myarc_hfdc_device::device_add_mconfig(machine_config& config)
1056 {
1057 HDC9234(config, m_hdc9234, 0);
1058 m_hdc9234->intrq_cb().set(FUNC(myarc_hfdc_device::intrq_w));
1059 m_hdc9234->dmarq_cb().set(FUNC(myarc_hfdc_device::dmarq_w));
1060 m_hdc9234->dip_cb().set(FUNC(myarc_hfdc_device::dip_w));
1061 m_hdc9234->auxbus_cb().set(FUNC(myarc_hfdc_device::auxbus_out));
1062 m_hdc9234->dmain_cb().set(FUNC(myarc_hfdc_device::read_buffer));
1063 m_hdc9234->dmaout_cb().set(FUNC(myarc_hfdc_device::write_buffer));
1064
1065 // First two floppy drives shall be connected by default
1066 FLOPPY_CONNECTOR(config, "f1", hfdc_floppies, "525dd", myarc_hfdc_device::floppy_formats).enable_sound(true);
1067 FLOPPY_CONNECTOR(config, "f2", hfdc_floppies, "525dd", myarc_hfdc_device::floppy_formats).enable_sound(true);
1068 FLOPPY_CONNECTOR(config, "f3", hfdc_floppies, nullptr, myarc_hfdc_device::floppy_formats).enable_sound(true);
1069 FLOPPY_CONNECTOR(config, "f4", hfdc_floppies, nullptr, myarc_hfdc_device::floppy_formats).enable_sound(true);
1070
1071 // Hard disks don't go without image (other than floppy drives)
1072 MFM_HD_CONNECTOR(config, "h1", hfdc_harddisks, nullptr, MFM_BYTE, 3000, 20, MFMHD_GEN_FORMAT);
1073 MFM_HD_CONNECTOR(config, "h2", hfdc_harddisks, nullptr, MFM_BYTE, 3000, 20, MFMHD_GEN_FORMAT);
1074 MFM_HD_CONNECTOR(config, "h3", hfdc_harddisks, nullptr, MFM_BYTE, 3000, 20, MFMHD_GEN_FORMAT);
1075
1076 MM58274C(config, CLOCK_TAG, 0).set_mode_and_day(1, 0); // 24h, sunday
1077
1078 RAM(config, BUFFER).set_default_size("32K").set_default_value(0);
1079 }
1080
device_rom_region() const1081 const tiny_rom_entry *myarc_hfdc_device::device_rom_region() const
1082 {
1083 return ROM_NAME( ti99_hfdc );
1084 }
1085
device_input_ports() const1086 ioport_constructor myarc_hfdc_device::device_input_ports() const
1087 {
1088 return INPUT_PORTS_NAME( ti99_hfdc );
1089 }
1090
1091 } } } // end namespace bus::ti99::peb
1092