1 // license:GPL-2.0+
2 // copyright-holders:Raphael Nabet
3 /*
4 990_hd.c: emulation of a generic ti990 hard disk controller, for use with
5 TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
6 and 300A).
7
8 This core will emulate the common feature set found in every disk controller.
9 Most controllers support additional features, but are still compatible with
10 the basic feature set. I have a little documentation on two specific
11 disk controllers (WD900 and WD800/WD800A), but I have not tried to emulate
12 controller-specific features.
13
14
15 Long description: see 2234398-9701 and 2306140-9701.
16
17
18 Raphael Nabet 2002-2003
19 */
20
21 #include "emu.h"
22
23 #include "990_hd.h"
24
25 #include "harddisk.h"
26 #include "imagedev/harddriv.h"
27
28 /* Max sector length is bytes. Generally 256, except for a few older disk
29 units which use 288-byte-long sectors, and SCSI units which generally use
30 standard 512-byte-long sectors. */
31 /* I chose a limit of 512. No need to use more until someone writes CD-ROMs
32 for TI990. */
33 #define MAX_SECTOR_SIZE 512
34
35 /* Description of custom format */
36 /* We can use MAME's harddisk.c image format instead. */
37
38 /* machine-independent big-endian 32-bit integer */
39 struct UINT32BE
40 {
41 uint8_t bytes[4];
42 };
43
get_UINT32BE(UINT32BE word)44 static inline uint32_t get_UINT32BE(UINT32BE word)
45 {
46 return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3];
47 }
48
49 #ifdef UNUSED_FUNCTION
set_UINT32BE(UINT32BE * word,uint32_t data)50 static inline void set_UINT32BE(UINT32BE *word, uint32_t data)
51 {
52 word->bytes[0] = (data >> 24) & 0xff;
53 word->bytes[1] = (data >> 16) & 0xff;
54 word->bytes[2] = (data >> 8) & 0xff;
55 word->bytes[3] = data & 0xff;
56 }
57 #endif
58
59 /* disk image header */
60 struct disk_image_header
61 {
62 UINT32BE cylinders; /* number of cylinders on hard disk (big-endian) */
63 UINT32BE heads; /* number of heads on hard disk (big-endian) */
64 UINT32BE sectors_per_track; /* number of sectors per track on hard disk (big-endian) */
65 UINT32BE bytes_per_sector; /* number of bytes of data per sector on hard disk (big-endian) */
66 };
67
68 enum
69 {
70 header_len = sizeof(disk_image_header)
71 };
72
73
74 /* masks for individual bits controller registers */
75 enum
76 {
77 w0_offline = 0x8000,
78 w0_not_ready = 0x4000,
79 w0_write_protect = 0x2000,
80 w0_unsafe = 0x1000,
81 w0_end_of_cylinder = 0x0800,
82 w0_seek_incomplete = 0x0400,
83 /*w0_offset_active = 0x0200,*/
84 w0_pack_change = 0x0100,
85
86 w0_attn_lines = 0x00f0,
87 w0_attn_mask = 0x000f,
88
89 w1_extended_command = 0xc000,
90 /*w1_strobe_early = 0x2000,
91 w1_strobe_late = 0x1000,*/
92 w1_transfer_inhibit = 0x0800,
93 w1_command = 0x0700,
94 w1_offset = 0x0080,
95 w1_offset_forward = 0x0040,
96 w1_head_address = 0x003f,
97
98 w6_unit0_sel = 0x0800,
99 w6_unit1_sel = 0x0400,
100 w6_unit2_sel = 0x0200,
101 w6_unit3_sel = 0x0100,
102
103 w7_idle = 0x8000,
104 w7_complete = 0x4000,
105 w7_error = 0x2000,
106 w7_int_enable = 0x1000,
107 /*w7_lock_out = 0x0800,*/
108 w7_retry = 0x0400,
109 w7_ecc = 0x0200,
110 w7_abnormal_completion = 0x0100,
111 w7_memory_error = 0x0080,
112 w7_data_error = 0x0040,
113 w7_tiline_timeout_err = 0x0020,
114 w7_header_err = 0x0010,
115 w7_rate_err = 0x0008,
116 w7_command_time_out_err = 0x0004,
117 w7_search_err = 0x0002,
118 w7_unit_err = 0x0001
119 };
120
121 /* masks for computer-controlled bit in each controller register */
122 static const uint16_t w_mask[8] =
123 {
124 0x000f, /* Controllers should prevent overwriting of w0 status bits, and I know
125 that some controllers do so. */
126 0xffff,
127 0xffff,
128 0xffff,
129 0xffff,
130 0xffff,
131 0xffff,
132 0xf7ff /* Don't overwrite reserved bits */
133 };
134
135
get_id_from_device(device_t * device)136 int ti990_hdc_device::get_id_from_device( device_t *device )
137 {
138 int id = -1;
139
140 if ( ! strcmp( ":harddisk1", device->tag() ) )
141 {
142 id = 0;
143 }
144 else if ( ! strcmp( ":harddisk2", device->tag() ) )
145 {
146 id = 1;
147 }
148 else if ( ! strcmp( ":harddisk3", device->tag() ) )
149 {
150 id = 2;
151 }
152 else if ( ! strcmp( ":harddisk4", device->tag() ) )
153 {
154 id = 3;
155 }
156 assert( id >= 0 );
157
158 return id;
159 }
160
161
162 /*
163 Initialize hard disk unit and open a hard disk image
164 */
DEVICE_IMAGE_LOAD_MEMBER(ti990_hdc_device::load_hd)165 DEVICE_IMAGE_LOAD_MEMBER( ti990_hdc_device::load_hd )
166 {
167 int id = get_id_from_device( &image.device() );
168 hd_unit_t *d;
169 hard_disk_file *hd_file;
170
171 d = &m_d[id];
172 d->img = ℑ
173
174 hd_file = dynamic_cast<harddisk_image_device *>(&image)->get_hard_disk_file();
175
176 if ( hd_file )
177 {
178 const hard_disk_info *standard_header;
179
180 d->format = format_mame;
181 d->hd_handle = hd_file;
182
183 /* use standard hard disk image header. */
184 standard_header = hard_disk_get_info(d->hd_handle);
185
186 d->cylinders = standard_header->cylinders;
187 d->heads = standard_header->heads;
188 d->sectors_per_track = standard_header->sectors;
189 d->bytes_per_sector = standard_header->sectorbytes;
190 }
191 else
192 {
193 /* older, custom format */
194 disk_image_header custom_header;
195 int bytes_read;
196
197 /* set file descriptor */
198 d->format = format_old;
199 d->hd_handle = nullptr;
200
201 /* use custom image header. */
202 /* to convert old header-less images to this format, insert a 16-byte
203 header as follow: 00 00 03 8f 00 00 00 05 00 00 00 21 00 00 01 00 */
204 d->img->fseek(0, SEEK_SET);
205 bytes_read = d->img->fread(&custom_header, sizeof(custom_header));
206 if (bytes_read != sizeof(custom_header))
207 {
208 d->format = format_mame; /* don't care */
209 d->wp = 1;
210 d->unsafe = 1;
211 return image_init_result::FAIL;
212 }
213
214 d->cylinders = get_UINT32BE(custom_header.cylinders);
215 d->heads = get_UINT32BE(custom_header.heads);
216 d->sectors_per_track = get_UINT32BE(custom_header.sectors_per_track);
217 d->bytes_per_sector = get_UINT32BE(custom_header.bytes_per_sector);
218 }
219
220 if (d->bytes_per_sector > MAX_SECTOR_SIZE)
221 {
222 d->format = format_mame;
223 d->hd_handle = nullptr;
224 d->wp = 1;
225 d->unsafe = 1;
226 return image_init_result::FAIL;
227 }
228
229 /* tell whether the image is writable */
230 d->wp = image.is_readonly();
231
232 d->unsafe = 1;
233 /* set attention line */
234 m_w[0] |= (0x80 >> id);
235
236 return image_init_result::PASS;
237 }
238
239 /*
240 close a hard disk image
241 */
DEVICE_IMAGE_UNLOAD_MEMBER(ti990_hdc_device::unload_hd)242 DEVICE_IMAGE_UNLOAD_MEMBER( ti990_hdc_device::unload_hd )
243 {
244 int id = get_id_from_device(&image.device());
245 hd_unit_t *d;
246
247 d = &m_d[id];
248
249 d->format = format_mame; /* don't care */
250 d->hd_handle = nullptr;
251 d->wp = 1;
252 d->unsafe = 1;
253
254 /* clear attention line */
255 m_w[0] &= ~ (0x80 >> id);
256 }
257
258 /*
259 Return true if a HD image has been loaded
260 */
is_unit_loaded(int unit)261 int ti990_hdc_device::is_unit_loaded(int unit)
262 {
263 int reply = 0;
264
265 switch (m_d[unit].format)
266 {
267 case format_mame:
268 reply = (m_d[unit].hd_handle != nullptr);
269 break;
270
271 case format_old:
272 reply = (m_d[unit].img->exists() ? 1 : 0);
273 break;
274 }
275
276 return reply;
277 }
278
279 /*
280 Parse the disk select lines, and return the corresponding tape unit.
281 (-1 if none)
282 */
cur_disk_unit()283 int ti990_hdc_device::cur_disk_unit()
284 {
285 int reply;
286
287
288 if (m_w[6] & w6_unit0_sel)
289 reply = 0;
290 else if (m_w[6] & w6_unit1_sel)
291 reply = 1;
292 else if (m_w[6] & w6_unit2_sel)
293 reply = 2;
294 else if (m_w[6] & w6_unit3_sel)
295 reply = 3;
296 else
297 reply = -1;
298
299 if (reply >= MAX_DISK_UNIT)
300 reply = -1;
301
302 return reply;
303 }
304
305 /*
306 Update interrupt state
307 */
update_interrupt()308 void ti990_hdc_device::update_interrupt()
309 {
310 if (!m_interrupt_callback.isnull())
311 m_interrupt_callback((m_w[7] & w7_idle)
312 && (((m_w[7] & w7_int_enable) && (m_w[7] & (w7_complete | w7_error)))
313 || ((m_w[0] & (m_w[0] >> 4)) & w0_attn_mask)));
314 }
315
316 /*
317 Check that a sector address is valid.
318
319 Terminate current command and return non-zero if the address is invalid.
320 */
check_sector_address(int unit,unsigned int cylinder,unsigned int head,unsigned int sector)321 int ti990_hdc_device::check_sector_address(int unit, unsigned int cylinder, unsigned int head, unsigned int sector)
322 {
323 if ((cylinder > m_d[unit].cylinders) || (head > m_d[unit].heads) || (sector > m_d[unit].sectors_per_track))
324 { /* invalid address */
325 if (cylinder > m_d[unit].cylinders)
326 {
327 m_w[0] |= w0_seek_incomplete;
328 m_w[7] |= w7_idle | w7_error | w7_unit_err;
329 }
330 else if (head > m_d[unit].heads)
331 {
332 m_w[0] |= w0_end_of_cylinder;
333 m_w[7] |= w7_idle | w7_error | w7_unit_err;
334 }
335 else if (sector > m_d[unit].sectors_per_track)
336 m_w[7] |= w7_idle | w7_error | w7_command_time_out_err;
337 update_interrupt();
338 return 1;
339 }
340
341 return 0;
342 }
343
344 /*
345 Seek to sector whose address is given
346 */
sector_to_lba(int unit,unsigned int cylinder,unsigned int head,unsigned int sector,unsigned int * lba)347 int ti990_hdc_device::sector_to_lba(int unit, unsigned int cylinder, unsigned int head, unsigned int sector, unsigned int *lba)
348 {
349 if (check_sector_address(unit, cylinder, head, sector))
350 return 1;
351
352 * lba = (cylinder*m_d[unit].heads + head)*m_d[unit].sectors_per_track + sector;
353
354 return 0;
355 }
356
357 /*
358 Read one given sector
359 */
read_sector(int unit,unsigned int lba,void * buffer,unsigned int bytes_to_read)360 int ti990_hdc_device::read_sector(int unit, unsigned int lba, void *buffer, unsigned int bytes_to_read)
361 {
362 unsigned long byte_position;
363 unsigned int bytes_read;
364
365 switch (m_d[unit].format)
366 {
367 case format_mame:
368 bytes_read = m_d[unit].bytes_per_sector * hard_disk_read(m_d[unit].hd_handle, lba, buffer);
369 if (bytes_read > bytes_to_read)
370 bytes_read = bytes_to_read;
371 break;
372
373 case format_old:
374 byte_position = lba*m_d[unit].bytes_per_sector + header_len;
375 m_d[unit].img->fseek(byte_position, SEEK_SET);
376 bytes_read = m_d[unit].img->fread(buffer, bytes_to_read);
377 break;
378
379 default:
380 bytes_read = 0;
381 break;
382 }
383
384 return bytes_read;
385 }
386
387 /*
388 Write one given sector
389 */
write_sector(int unit,unsigned int lba,const void * buffer,unsigned int bytes_to_write)390 int ti990_hdc_device::write_sector(int unit, unsigned int lba, const void *buffer, unsigned int bytes_to_write)
391 {
392 unsigned long byte_position;
393 unsigned int bytes_written;
394
395 switch (m_d[unit].format)
396 {
397 case format_mame:
398 bytes_written = m_d[unit].bytes_per_sector * hard_disk_write(m_d[unit].hd_handle, lba, buffer);
399 if (bytes_written > bytes_to_write)
400 bytes_written = bytes_to_write;
401 break;
402
403 case format_old:
404 byte_position = lba*m_d[unit].bytes_per_sector + header_len;
405 m_d[unit].img->fseek(byte_position, SEEK_SET);
406 bytes_written = m_d[unit].img->fwrite(buffer, bytes_to_write);
407 break;
408
409 default:
410 bytes_written = 0;
411 break;
412 }
413
414 return bytes_written;
415 }
416
417 /*
418 Handle the store registers command: read the drive geometry.
419 */
store_registers()420 void ti990_hdc_device::store_registers()
421 {
422 int dma_address;
423 int byte_count;
424
425 uint16_t buffer[3];
426 int i, real_word_count;
427
428 int dsk_sel = cur_disk_unit();
429
430
431 if (dsk_sel == -1)
432 {
433 /* No idea what to report... */
434 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
435 update_interrupt();
436 return;
437 }
438 else if (! is_unit_loaded(dsk_sel))
439 { /* offline */
440 m_w[0] |= w0_offline | w0_not_ready;
441 m_w[7] |= w7_idle | w7_error | w7_unit_err;
442 update_interrupt();
443 return;
444 }
445
446 m_d[dsk_sel].unsafe = 0; /* I think */
447
448 dma_address = ((((int) m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
449 byte_count = m_w[4] & 0xfffe;
450
451 /* formatted words per track */
452 buffer[0] = (m_d[dsk_sel].sectors_per_track*m_d[dsk_sel].bytes_per_sector) >> 1;
453 /* MSByte: sectors per track; LSByte: bytes of overhead per sector */
454 buffer[1] = (m_d[dsk_sel].sectors_per_track << 8) | 0;
455 /* bits 0-4: heads; bits 5-15: cylinders */
456 buffer[2] = (m_d[dsk_sel].heads << 11) | m_d[dsk_sel].cylinders;
457
458 real_word_count = byte_count >> 1;
459 if (real_word_count > 3)
460 real_word_count = 3;
461
462 /* DMA */
463 if (! (m_w[1] & w1_transfer_inhibit))
464 for (i=0; i<real_word_count; i++)
465 {
466 m_memory_space->write_word(dma_address, buffer[i]);
467 dma_address = (dma_address + 2) & 0x1ffffe;
468 }
469
470 m_w[7] |= w7_idle | w7_complete;
471 update_interrupt();
472 }
473
474 /*
475 Handle the write format command: format a complete track on disk.
476
477 The emulation just clears the track data in the disk image.
478 */
write_format()479 void ti990_hdc_device::write_format()
480 {
481 unsigned int cylinder, head, sector;
482 unsigned int lba;
483
484 uint8_t buffer[MAX_SECTOR_SIZE];
485 int bytes_written;
486
487 int dsk_sel = cur_disk_unit();
488
489
490 if (dsk_sel == -1)
491 {
492 /* No idea what to report... */
493 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
494 update_interrupt();
495 return;
496 }
497 else if (! is_unit_loaded(dsk_sel))
498 { /* offline */
499 m_w[0] |= w0_offline | w0_not_ready;
500 m_w[7] |= w7_idle | w7_error | w7_unit_err;
501 update_interrupt();
502 return;
503 }
504 else if (m_d[dsk_sel].unsafe)
505 { /* disk in unsafe condition */
506 m_w[0] |= w0_unsafe | w0_pack_change;
507 m_w[7] |= w7_idle | w7_error | w7_unit_err;
508 update_interrupt();
509 return;
510 }
511 else if (m_d[dsk_sel].wp)
512 { /* disk write-protected */
513 m_w[0] |= w0_write_protect;
514 m_w[7] |= w7_idle | w7_error | w7_unit_err;
515 update_interrupt();
516 return;
517 }
518
519 cylinder = m_w[3];
520 head = m_w[1] & w1_head_address;
521
522 if (sector_to_lba(dsk_sel, cylinder, head, 0, &lba))
523 return;
524
525 memset(buffer, 0, m_d[dsk_sel].bytes_per_sector);
526
527 for (sector=0; sector<m_d[dsk_sel].sectors_per_track; sector++)
528 {
529 bytes_written = write_sector(dsk_sel, lba, buffer, m_d[dsk_sel].bytes_per_sector);
530
531 if (bytes_written != m_d[dsk_sel].bytes_per_sector)
532 {
533 m_w[0] |= w0_offline | w0_not_ready;
534 m_w[7] |= w7_idle | w7_error | w7_unit_err;
535 update_interrupt();
536 return;
537 }
538
539 lba++;
540 }
541
542 m_w[7] |= w7_idle | w7_complete;
543 update_interrupt();
544 }
545
546 /*
547 Handle the read data command: read a variable number of sectors from disk.
548 */
read_data()549 void ti990_hdc_device::read_data()
550 {
551 int dma_address;
552 int byte_count;
553
554 unsigned int cylinder, head, sector;
555 unsigned int lba;
556
557 uint8_t buffer[MAX_SECTOR_SIZE];
558 int bytes_to_read;
559 int bytes_read;
560 int i;
561
562 int dsk_sel = cur_disk_unit();
563
564
565 if (dsk_sel == -1)
566 {
567 /* No idea what to report... */
568 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
569 update_interrupt();
570 return;
571 }
572 else if (! is_unit_loaded(dsk_sel))
573 { /* offline */
574 m_w[0] |= w0_offline | w0_not_ready;
575 m_w[7] |= w7_idle | w7_error | w7_unit_err;
576 update_interrupt();
577 return;
578 }
579 else if (m_d[dsk_sel].unsafe)
580 { /* disk in unsafe condition */
581 m_w[0] |= w0_unsafe | w0_pack_change;
582 m_w[7] |= w7_idle | w7_error | w7_unit_err;
583 update_interrupt();
584 return;
585 }
586
587 dma_address = ((((int) m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
588 byte_count = m_w[4] & 0xfffe;
589
590 cylinder = m_w[3];
591 head = m_w[1] & w1_head_address;
592 sector = m_w[2] & 0xff;
593
594 if (sector_to_lba(dsk_sel, cylinder, head, sector, &lba))
595 return;
596
597 while (byte_count)
598 { /* read data sector per sector */
599 if (cylinder > m_d[dsk_sel].cylinders)
600 {
601 m_w[0] |= w0_seek_incomplete;
602 m_w[7] |= w7_idle | w7_error | w7_unit_err;
603 update_interrupt();
604 return;
605 }
606
607 bytes_to_read = (byte_count < m_d[dsk_sel].bytes_per_sector) ? byte_count : m_d[dsk_sel].bytes_per_sector;
608 bytes_read = read_sector(dsk_sel, lba, buffer, bytes_to_read);
609
610 if (bytes_read != bytes_to_read)
611 { /* behave as if the controller could not found the sector ID mark */
612 m_w[7] |= w7_idle | w7_error | w7_command_time_out_err;
613 update_interrupt();
614 return;
615 }
616
617 /* DMA */
618 if (! (m_w[1] & w1_transfer_inhibit))
619 for (i=0; i<bytes_read; i+=2)
620 {
621 m_memory_space->write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
622 dma_address = (dma_address + 2) & 0x1ffffe;
623 }
624
625 byte_count -= bytes_read;
626
627 /* update sector address to point to next sector */
628 lba++;
629 sector++;
630 if (sector == m_d[dsk_sel].sectors_per_track)
631 {
632 sector = 0;
633 head++;
634 if (head == m_d[dsk_sel].heads)
635 {
636 head = 0;
637 cylinder++;
638 }
639 }
640 }
641
642 m_w[7] |= w7_idle | w7_complete;
643 update_interrupt();
644 }
645
646 /*
647 Handle the write data command: write a variable number of sectors from disk.
648 */
write_data()649 void ti990_hdc_device::write_data()
650 {
651 int dma_address;
652 int byte_count;
653
654 unsigned int cylinder, head, sector;
655 unsigned int lba;
656
657 uint8_t buffer[MAX_SECTOR_SIZE];
658 uint16_t word;
659 int bytes_written;
660 int i;
661
662 int dsk_sel = cur_disk_unit();
663
664
665 if (dsk_sel == -1)
666 {
667 /* No idea what to report... */
668 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
669 update_interrupt();
670 return;
671 }
672 else if (! is_unit_loaded(dsk_sel))
673 { /* offline */
674 m_w[0] |= w0_offline | w0_not_ready;
675 m_w[7] |= w7_idle | w7_error | w7_unit_err;
676 update_interrupt();
677 return;
678 }
679 else if (m_d[dsk_sel].unsafe)
680 { /* disk in unsafe condition */
681 m_w[0] |= w0_unsafe | w0_pack_change;
682 m_w[7] |= w7_idle | w7_error | w7_unit_err;
683 update_interrupt();
684 return;
685 }
686 else if (m_d[dsk_sel].wp)
687 { /* disk write-protected */
688 m_w[0] |= w0_write_protect;
689 m_w[7] |= w7_idle | w7_error | w7_unit_err;
690 update_interrupt();
691 return;
692 }
693
694 dma_address = ((((int) m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
695 byte_count = m_w[4] & 0xfffe;
696
697 cylinder = m_w[3];
698 head = m_w[1] & w1_head_address;
699 sector = m_w[2] & 0xff;
700
701 if (sector_to_lba(dsk_sel, cylinder, head, sector, &lba))
702 return;
703
704 while (byte_count > 0)
705 { /* write data sector per sector */
706 if (cylinder > m_d[dsk_sel].cylinders)
707 {
708 m_w[0] |= w0_seek_incomplete;
709 m_w[7] |= w7_idle | w7_error | w7_unit_err;
710 update_interrupt();
711 return;
712 }
713
714 /* DMA */
715 for (i=0; (i<byte_count) && (i<m_d[dsk_sel].bytes_per_sector); i+=2)
716 {
717 word = m_memory_space->read_word(dma_address);
718 buffer[i] = word >> 8;
719 buffer[i+1] = word & 0xff;
720
721 dma_address = (dma_address + 2) & 0x1ffffe;
722 }
723 /* fill with 0s if we did not reach sector end */
724 for (; i<m_d[dsk_sel].bytes_per_sector; i+=2)
725 buffer[i] = buffer[i+1] = 0;
726
727 bytes_written = write_sector(dsk_sel, lba, buffer, m_d[dsk_sel].bytes_per_sector);
728
729 if (bytes_written != m_d[dsk_sel].bytes_per_sector)
730 {
731 m_w[0] |= w0_offline | w0_not_ready;
732 m_w[7] |= w7_idle | w7_error | w7_unit_err;
733 update_interrupt();
734 return;
735 }
736
737 byte_count -= bytes_written;
738
739 /* update sector address to point to next sector */
740 lba++;
741 sector++;
742 if (sector == m_d[dsk_sel].sectors_per_track)
743 {
744 sector = 0;
745 head++;
746 if (head == m_d[dsk_sel].heads)
747 {
748 head = 0;
749 cylinder++;
750 }
751 }
752 }
753
754 m_w[7] |= w7_idle | w7_complete;
755 update_interrupt();
756 }
757
758 /*
759 Handle the unformatted read command: read drive geometry information.
760 */
unformatted_read()761 void ti990_hdc_device::unformatted_read()
762 {
763 int dma_address;
764 int byte_count;
765
766 unsigned int cylinder, head, sector;
767
768 uint16_t buffer[3];
769 int i, real_word_count;
770
771 int dsk_sel = cur_disk_unit();
772
773
774 if (dsk_sel == -1)
775 {
776 /* No idea what to report... */
777 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
778 update_interrupt();
779 return;
780 }
781 else if (! is_unit_loaded(dsk_sel))
782 { /* offline */
783 m_w[0] |= w0_offline | w0_not_ready;
784 m_w[7] |= w7_idle | w7_error | w7_unit_err;
785 update_interrupt();
786 return;
787 }
788 else if (m_d[dsk_sel].unsafe)
789 { /* disk in unsafe condition */
790 m_w[0] |= w0_unsafe | w0_pack_change;
791 m_w[7] |= w7_idle | w7_error | w7_unit_err;
792 update_interrupt();
793 return;
794 }
795
796 dma_address = ((((int) m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
797 byte_count = m_w[4] & 0xfffe;
798
799 cylinder = m_w[3];
800 head = m_w[1] & w1_head_address;
801 sector = m_w[2] & 0xff;
802
803 if (check_sector_address(dsk_sel, cylinder, head, sector))
804 return;
805
806 dma_address = ((((int) m_w[6]) << 16) | m_w[5]) & 0x1ffffe;
807 byte_count = m_w[4] & 0xfffe;
808
809 /* bits 0-4: head address; bits 5-15: cylinder address */
810 buffer[0] = (head << 11) | cylinder;
811 /* MSByte: sectors per record (1); LSByte: sector address */
812 buffer[1] = (1 << 8) | sector;
813 /* formatted words per record */
814 buffer[2] = m_d[dsk_sel].bytes_per_sector >> 1;
815
816 real_word_count = byte_count >> 1;
817 if (real_word_count > 3)
818 real_word_count = 3;
819
820 /* DMA */
821 if (! (m_w[1] & w1_transfer_inhibit))
822 for (i=0; i<real_word_count; i++)
823 {
824 m_memory_space->write_word(dma_address, buffer[i]);
825 dma_address = (dma_address + 2) & 0x1ffffe;
826 }
827
828 m_w[7] |= w7_idle | w7_complete;
829 update_interrupt();
830 }
831
832 /*
833 Handle the restore command: return to track 0.
834 */
restore()835 void ti990_hdc_device::restore()
836 {
837 int dsk_sel = cur_disk_unit();
838
839
840 if (dsk_sel == -1)
841 {
842 /* No idea what to report... */
843 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
844 update_interrupt();
845 return;
846 }
847 else if (! is_unit_loaded(dsk_sel))
848 { /* offline */
849 m_w[0] |= w0_offline | w0_not_ready;
850 m_w[7] |= w7_idle | w7_error | w7_unit_err;
851 update_interrupt();
852 return;
853 }
854
855 m_d[dsk_sel].unsafe = 0; /* I think */
856
857 /*if (seek_to_sector(dsk_sel, 0, 0, 0))
858 return;*/
859
860 m_w[7] |= w7_idle | w7_complete;
861 update_interrupt();
862 }
863
864 /*
865 Parse command code and execute the command.
866 */
execute_command()867 void ti990_hdc_device::execute_command()
868 {
869 /* hack */
870 m_w[0] &= 0xff;
871
872 if (m_w[1] & w1_extended_command)
873 logerror("extended commands not supported\n");
874
875 switch (/*((m_w[1] & w1_extended_command) >> 11) |*/ ((m_w[1] & w1_command) >> 8))
876 {
877 case 0x00: //0b000:
878 /* store registers */
879 logerror("store registers\n");
880 store_registers();
881 break;
882 case 0x01: //0b001:
883 /* write format */
884 logerror("write format\n");
885 write_format();
886 break;
887 case 0x02: //0b010:
888 /* read data */
889 logerror("read data\n");
890 read_data();
891 break;
892 case 0x03: //0b011:
893 /* write data */
894 logerror("write data\n");
895 write_data();
896 break;
897 case 0x04: //0b100:
898 /* unformatted read */
899 logerror("unformatted read\n");
900 unformatted_read();
901 break;
902 case 0x05: //0b101:
903 /* unformatted write */
904 logerror("unformatted write\n");
905 /* ... */
906 m_w[7] |= w7_idle | w7_error | w7_abnormal_completion;
907 update_interrupt();
908 break;
909 case 0x06: //0b110:
910 /* seek */
911 logerror("seek\n");
912 /* This command can (almost) safely be ignored */
913 m_w[7] |= w7_idle | w7_complete;
914 update_interrupt();
915 break;
916 case 0x07: //0b111:
917 /* restore */
918 logerror("restore\n");
919 restore();
920 break;
921 }
922 }
923
924 /*
925 Read one register in TPCS space
926 */
read(offs_t offset)927 uint16_t ti990_hdc_device::read(offs_t offset)
928 {
929 if (offset < 8)
930 return m_w[offset];
931 else
932 return 0;
933 }
934
935 /*
936 Write one register in TPCS space. Execute command if w7_idle is cleared.
937 */
write(offs_t offset,uint16_t data,uint16_t mem_mask)938 void ti990_hdc_device::write(offs_t offset, uint16_t data, uint16_t mem_mask)
939 {
940 if (offset < 8)
941 {
942 /* write protect if a command is in progress */
943 if (m_w[7] & w7_idle)
944 {
945 uint16_t old_data = m_w[offset];
946
947 /* Only write writable bits AND honor byte accesses (ha!) */
948 m_w[offset] = (m_w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
949
950 if ((offset == 0) || (offset == 7))
951 update_interrupt();
952
953 if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
954 { /* idle has been cleared: start command execution */
955 execute_command();
956 }
957 }
958 }
959 }
960
961
962 DEFINE_DEVICE_TYPE(TI990_HDC, ti990_hdc_device, "ti990_hdc", "Generic TI-990 Hard Disk Controller")
963
ti990_hdc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)964 ti990_hdc_device::ti990_hdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
965 : device_t(mconfig, TI990_HDC, tag, owner, clock)
966 , m_memory_space(*this, finder_base::DUMMY_TAG, -1)
967 , m_interrupt_callback(*this)
968 {
969 }
970
971 //-------------------------------------------------
972 // device_start - device-specific startup
973 //-------------------------------------------------
974
device_start()975 void ti990_hdc_device::device_start()
976 {
977 int i;
978
979 /* initialize harddisk information */
980 /* attention lines will be set by DEVICE_IMAGE_LOAD */
981 for (i=0; i<MAX_DISK_UNIT; i++)
982 {
983 m_d[i].format = format_mame;
984 m_d[i].hd_handle = nullptr;
985 m_d[i].wp = 1;
986 m_d[i].unsafe = 1;
987 }
988 memset(m_w, 0, sizeof(m_w));
989 m_w[7] = w7_idle;
990
991 /* get references to harddisk devices */
992 m_d[0].img = dynamic_cast<device_image_interface *>(subdevice("harddisk1"));
993 m_d[1].img = dynamic_cast<device_image_interface *>(subdevice("harddisk2"));
994 m_d[2].img = dynamic_cast<device_image_interface *>(subdevice("harddisk3"));
995 m_d[3].img = dynamic_cast<device_image_interface *>(subdevice("harddisk4"));
996
997 m_interrupt_callback.resolve_safe();
998
999 update_interrupt();
1000 }
1001
1002
1003 //-------------------------------------------------
1004 // device_add_mconfig - add device configuration
1005 //-------------------------------------------------
1006
device_add_mconfig(machine_config & config)1007 void ti990_hdc_device::device_add_mconfig(machine_config &config)
1008 {
1009 harddisk_image_device &harddisk1(HARDDISK(config, "harddisk1"));
1010 harddisk1.set_device_load(FUNC(ti990_hdc_device::load_hd));
1011 harddisk1.set_device_unload(FUNC(ti990_hdc_device::unload_hd));
1012
1013 harddisk_image_device &harddisk2(HARDDISK(config, "harddisk2"));
1014 harddisk2.set_device_load(FUNC(ti990_hdc_device::load_hd));
1015 harddisk2.set_device_unload(FUNC(ti990_hdc_device::unload_hd));
1016
1017 harddisk_image_device &harddisk3(HARDDISK(config, "harddisk3"));
1018 harddisk3.set_device_load(FUNC(ti990_hdc_device::load_hd));
1019 harddisk3.set_device_unload(FUNC(ti990_hdc_device::unload_hd));
1020
1021 harddisk_image_device &harddisk4(HARDDISK(config, "harddisk4"));
1022 harddisk4.set_device_load(FUNC(ti990_hdc_device::load_hd));
1023 harddisk4.set_device_unload(FUNC(ti990_hdc_device::unload_hd));
1024 }
1025