Previous 199869 Revisions Next

r26123 Tuesday 12th November, 2013 at 19:51:18 UTC by Jürgen Buchmüller
Tried to move the Diablo hard drive code to diablo_hd_device in src/mess/machine, but now linking is broken.
[/branches/alto2/src/emu/cpu]cpu.mak
[/branches/alto2/src/emu/cpu/alto2]a2disk.c a2drive.c a2mem.c alto2.c alto2.h
[/branches/alto2/src/emu/imagedev]diablo.c
[/branches/alto2/src/mess]mess.mak
[/branches/alto2/src/mess/machine]diablo_hd.c* diablo_hd.h*

branches/alto2/src/emu/cpu/alto2/a2drive.c
r26122r26123
1/*****************************************************************************
2 *
3 *   Portable Xerox AltoII disk drive DIABLO31
4 *
5 *   Copyright: Juergen Buchmueller <pullmoll@t-online.de>
6 *
7 *   Licenses: MAME, GPLv2
8 *
9 *****************************************************************************/
10#include "alto2.h"
11
12#define   MFROBL         34      //!< from the microcode: disk header preamble is 34 words
13#define   MFRRDL         21      //!< from the microcode: disk header read delay is 21 words
14#define   MIRRDL         4      //!< from the microcode: interrecord read delay is 4 words
15#define   MIROBL         3      //!< from the microcode: disk interrecord preamble is 3 words
16#define   MRPAL         3      //!< from the microcode: disk read postamble length is 3 words
17#define   MWPAL         5      //!< from the microcode: disk write postamble length is 5 words
18
19/** @brief end of the guard zone at the beginning of a sector (wild guess!) */
20#define   GUARD_ZONE_BITS   (16*32)
21
22/** @brief write a bit into an array of UINT32 */
23static inline size_t WRBIT(UINT32* bits, size_t dst, int bit)
24{
25   if (bit) {
26      bits[(dst)/32] |= 1 << ((dst) % 32);
27   } else {
28      bits[(dst)/32] &= ~(1 << ((dst) % 32));
29   }
30   return ++dst;
31}
32
33/** @brief read a bit from an array of UINT32 */
34static inline size_t RDBIT(UINT32* bits, size_t src, int& bit)
35{
36   bit = (bits[src/32] >> (src % 32)) & 1;
37   return ++src;
38}
39
40/**
41 * @brief calculate the sector from the logical block address
42 *
43 * Modifies drive's page by calculating the logical
44 * block address from cylinder, head, and sector.
45 *
46 * @param unit unit number
47 */
48void alto2_cpu_device::drive_get_sector(int unit)
49{
50   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
51      fatal(1, "invalid unit %d in call to drive_get_sector()\n", unit);
52
53   /* uninitialized drive? */
54   diablo_drive_t *d = m_drive[unit];
55   if (!d)
56      return;
57
58   /* If there's no image, just reset the page number */
59   if (!d->image) {
60      d->page = -1;
61      return;
62   }
63   if (d->cylinder < 0 || d->cylinder >= DIABLO_DRIVE_CYLINDERS) {
64      LOG((LOG_DRIVE,9,"   DRIVE C/H/S:%d/%d/%d => invalid cylinder\n", d->cylinder, d->head, d->sector));
65      d->page = -1;
66      return;
67   }
68   /* calculate the new disk relative sector offset */
69   d->page = DRIVE_PAGE(d->cylinder, d->head, d->sector);
70   LOG((LOG_DRIVE,9,"   DRIVE C/H/S:%d/%d/%d => page:%d\n", d->cylinder, d->head, d->sector, d->page));
71}
72
73/**
74 * @brief compute the checksum of a record
75 *
76 * @param src pointer to a record (header, label, data)
77 * @param size size of the record in bytes
78 * @param start start value for the checksum
79 * @result returns the checksum of the record
80 */
81static int cksum(UINT8 *src, size_t size, int start)
82{
83   int sum = start;
84   /* compute XOR of all words */
85   for (size_t offs = 0; offs < size; offs += 2) {
86      int word = src[size - 2 - offs] + 256 * src[size - 2 - offs + 1];
87      sum ^= word;
88   }
89   return sum;
90}
91
92/**
93 * @brief expand a series of clock bits and 0 data bits
94 *
95 * @param bits pointer to the sector bits
96 * @param dst destination offset into bits (bit number)
97 * @param size number of words to write
98 * @result pointer to next destination bit
99 */
100static size_t expand_zeroes(UINT32 *bits, size_t dst, size_t size)
101{
102   for (size_t offs = 0; offs < 32 * size; offs += 2) {
103      dst = WRBIT(bits, dst, 1);      // write the clock bit
104      dst = WRBIT(bits, dst, 0);      // write the 0 data bit
105   }
106   return dst;
107}
108
109/**
110 * @brief expand a series of 0 words and write a final sync bit
111 *
112 * @param bits pointer to the sector bits
113 * @param dst destination offset into bits (bit number)
114 * @param size number of words to write
115 * @result pointer to next destination bit
116 */
117static size_t expand_sync(UINT32 *bits, size_t dst, size_t size)
118{
119   for (size_t offs = 0; offs < 32 * size - 2; offs += 2) {
120      dst = WRBIT(bits, dst, 1);      // write the clock bit
121      dst = WRBIT(bits, dst, 0);      // write the 0 data bit
122   }
123   dst = WRBIT(bits, dst, 1);   // write the final clock bit
124   dst = WRBIT(bits, dst, 1);   // write the 1 data bit
125   return dst;
126}
127
128/**
129 * @brief expand a record of words into a array of bits at dst
130 *
131 * @param bits pointer to the sector bits
132 * @param dst destination offset into bits (bit number)
133 * @param field pointer to the record data (bytes)
134 * @param size size of the record in bytes
135 * @result pointer to next destination bit
136 */
137static size_t expand_record(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
138{
139   for (size_t offs = 0; offs < size; offs += 2) {
140      int word = field[size - 2 - offs] + 256 * field[size - 2 - offs + 1];
141      for (size_t bit = 0; bit < 16; bit++) {
142         dst = WRBIT(bits, dst, 1);               // write the clock bit
143         dst = WRBIT(bits, dst, (word >> 15) & 1);   // write the data bit
144         word <<= 1;
145      }
146   }
147   return dst;
148}
149
150/**
151 * @brief Expand a record's checksum word to 32 bits
152 *
153 * @param bits pointer to the sector bits
154 * @param dst destination offset into bits (bit number)
155 * @param field pointer to the record data (bytes)
156 * @param size size of the record in bytes
157 * @result pointer to next destination bit
158 */
159static size_t expand_cksum(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
160{
161   int word = cksum(field, size, 0521);
162   for (size_t bit = 0; bit < 32; bit += 2) {
163      dst = WRBIT(bits, dst, 1);            // write the clock bit
164      dst = WRBIT(bits, dst, (word >> 15) & 1);   // write the data bit
165      word <<= 1;
166   }
167   return dst;
168}
169
170/**
171 * @brief Expand a sector into an array of clock and data bits
172 *
173 * @param unit drive unit number (0 or 1)
174 * @param page page number (0 to DRIVE_PAGES-1)
175 */
176void alto2_cpu_device::expand_sector(int unit, int page)
177{
178   diablo_sector_t *s;
179   UINT32 *bits;
180   size_t dst;
181
182   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
183      fatal(1, "invalid unit %d in call to expand_sector()\n", unit);
184
185   if (page < 0 || page >= DIABLO_DRIVE_PAGES)
186      return;
187
188   /* uninitialized drive? */
189   diablo_drive_t *d = m_drive[unit];
190   if (!d)
191      return;
192
193   /* already expanded this sector? */
194   if (d->bits[page])
195      return;
196
197   if (-1 == page || !d->image) {
198      LOG((LOG_DRIVE,0,"   no sector for #%d: %d/%d/%d\n", d->unit, d->cylinder, d->head, d->sector));
199      return;
200   }
201
202   /* get the sector pointer */
203   s = &d->image[page];
204
205   /* allocate a bits image */
206   bits = (UINT32 *)auto_alloc_array(machine(), UINT32, 400);
207   if (!bits) {
208      fatal(1, "failed to malloc(%d) bytes bits for drive #%d page #%d\n",
209         sizeof(bits), unit, page);
210   }
211
212#if   DIABLO31
213   /* write sync bit after 31 words - 1 bit */
214   dst = expand_sync(bits, 0, 31);
215   dst = expand_record(bits, dst, s->header, sizeof(s->header));
216   dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
217
218   /* write sync bit after 2 * 5 + 1 words - 1 bit */
219   dst = expand_sync(bits, dst, 2 * MWPAL);
220   dst = expand_record(bits, dst, s->label, sizeof(s->label));
221   dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
222
223   /* write sync bit after 2 * 5 + 1 words - 1 bit */
224   dst = expand_sync(bits, dst, 2 * MWPAL);
225   dst = expand_record(bits, dst, s->data, sizeof(s->data));
226   dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
227
228   /* fill MWPAL words of clock and 0 data bits */
229   dst = expand_zeroes(bits, dst, MWPAL);
230#else
231   /* write sync bit after 31 words - 1 bit */
232   dst = expand_sync(bits, 0, 31);
233   dst = expand_record(bits, dst, s->header, sizeof(s->header));
234   dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
235
236   /* write sync bit after 2 * 5 words - 1 bit */
237   dst = expand_sync(bits, dst, 2 * MWPAL);
238   dst = expand_record(bits, dst, s->label, sizeof(s->label));
239   dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
240
241   /* write sync bit after 2 * 5 words - 1 bit */
242   dst = expand_sync(bits, dst, 2 * MWPAL);
243   dst = expand_record(bits, dst, s->data, sizeof(s->data));
244   dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
245
246   /* fill MWPAL words of clock and 0 data bits */
247   dst = expand_zeroes(bits, dst, MWPAL);
248#endif
249   d->bits[page] = bits;
250
251   LOG((LOG_DRIVE,0,"   BITS #%d: %03d/%d/%02d #%-5d bits (@%03d.%02d)\n",
252      d->unit, d->cylinder, d->head, d->sector,
253      dst, dst / 32, dst % 32));
254
255}
256
257#if   ALTO2_DEBUG
258void alto2_cpu_device::drive_dump_ascii(UINT8 *src, size_t size)
259{
260   size_t offs;
261   LOG((LOG_DRIVE,0," ["));
262   for (offs = 0; offs < size; offs++) {
263      char ch = (char)src[offs ^ 1];
264      LOG((LOG_DRIVE,0, "%c", ch < 32 || ch > 126 ? '.' : ch));
265   }
266   LOG((LOG_DRIVE,0,"]\n"));
267}
268
269
270/**
271 * @brief Dump a record's contents
272 *
273 * @param src pointer to a record (header, label, data)
274 * @param size size of the record in bytes
275 * @param name name to print before the dump
276 */
277size_t alto2_cpu_device::dump_record(UINT8 *src, size_t addr, size_t size, const char *name, int cr)
278{
279   size_t offs;
280   LOG((LOG_DRIVE,0,"%s:", name));
281   for (offs = 0; offs < size; offs += 2) {
282      int word = src[offs] + 256 * src[offs + 1];
283      if (offs % 16) {
284         LOG((LOG_DRIVE,0," %06o", word));
285      } else {
286         if (offs > 0)
287            drive_dump_ascii(&src[offs-16], 16);
288         LOG((LOG_DRIVE,0,"\t%05o: %06o", (addr + offs) / 2, word));
289      }
290   }
291   if (offs % 16) {
292      drive_dump_ascii(&src[offs - (offs % 16)], offs % 16);
293   } else {
294      drive_dump_ascii(&src[offs-16], 16);
295   }
296   if (cr) {
297      LOG((LOG_DRIVE,0,"\n"));
298   }
299   return size;
300}
301#endif
302
303/**
304 * @brief find a sync bit in an array of clock and data bits
305 *
306 * @param bits pointer to the sector's bits
307 * @param src source offset into bits (bit number)
308 * @param size number of words to scan for a sync word
309 * @result next src pointer
310 */
311size_t alto2_cpu_device::squeeze_sync(UINT32 *bits, size_t src, size_t size)
312{
313   UINT32 accu = 0;
314   /* hunt for the first 0x0001 word */
315   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
316      /*
317       * accumulate clock and data bits until we are
318       * on the clock bit boundary
319       */
320      int bit;
321      src = RDBIT(bits,src,bit);
322      accu = (accu << 1) | bit;
323      /*
324       * look for 15 alternating clocks and 0-bits
325       * and the 16th clock with a 1-bit
326       */
327      if (accu == 0xaaaaaaab)
328         return src;
329      if (++bitcount == 32) {
330         bitcount = 0;
331         offs++;
332      }
333   }
334   /* return if no sync found within size*32 clock and data bits */
335   LOG((LOG_DRIVE,0,"   no sync within %d words\n", size));
336   return src;
337}
338
339/**
340 * @brief find a 16 x 0 bits sequence in an array of clock and data bits
341 *
342 * @param bits pointer to the sector's bits
343 * @param src source offset into bits (bit number)
344 * @param size number of words to scan for a sync word
345 * @result next src pointer
346 */
347size_t alto2_cpu_device::squeeze_unsync(UINT32 *bits, size_t src, size_t size)
348{
349   UINT32 accu = 0;
350   /* hunt for the first 0 word (16 x 0 bits) */
351   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
352      /*
353       * accumulate clock and data bits until we are
354       * on the clock bit boundary
355       */
356      int bit;
357      src = RDBIT(bits,src,bit);
358      accu = (accu << 1) | bit;
359      /*
360       * look for 16 alternating clocks and 0 data bits
361       */
362      if (accu == 0xaaaaaaaa)
363         return src;
364      if (++bitcount == 32) {
365         bitcount = 0;
366         offs++;
367      }
368   }
369   /* return if no sync found within size*32 clock and data bits */
370   LOG((LOG_DRIVE,0,"   no unsync within %d words\n", size));
371   return src;
372}
373
374/**
375 * @brief squeeze an array of clock and data bits into a sector's record
376 *
377 * @param bits pointer to the sector's bits
378 * @param src source offset into bits (bit number)
379 * @param field pointer to the record data (bytes)
380 * @param size size of the record in bytes
381 * @result next src pointer
382 */
383size_t alto2_cpu_device::squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size)
384{
385   UINT32 accu = 0;
386   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
387      int bit;
388      src = RDBIT(bits,src,bit);      // skip clock
389      assert(bit == 1);
390      src = RDBIT(bits,src,bit);      // get data bit
391      accu = (accu << 1) | bit;
392      bitcount += 2;
393      if (bitcount == 32) {
394         /* collected a word */
395         field[size - 2 - offs + 0] = accu % 256;
396         field[size - 2 - offs + 1] = accu / 256;
397         offs += 2;
398         bitcount = 0;
399      }
400   }
401   return src;
402}
403
404/**
405 * @brief squeeze an array of 32 clock and data bits into a checksum word
406 *
407 * @param bits pointer to the sector's bits
408 * @param src source offset into bits (bit number)
409 * @param cksum pointer to an int to receive the checksum word
410 * @result next src pointer
411 */
412size_t alto2_cpu_device::squeeze_cksum(UINT32 *bits, size_t src, int *cksum)
413{
414   UINT32 accu = 0;
415
416   for (size_t bitcount = 0; bitcount < 32; bitcount += 2) {
417      int bit;
418      src = RDBIT(bits,src,bit);      // skip clock
419      assert(bit == 1);
420      src = RDBIT(bits,src,bit);      // get data bit
421      accu = (accu << 1) | bit;
422   }
423
424   /* set the cksum to the extracted word */
425   *cksum = accu;
426   return src;
427}
428
429/**
430 * @brief Squeeze a array of clock and data bits into a sector's data
431 */
432void alto2_cpu_device::squeeze_sector(int unit)
433{
434   diablo_sector_t *s;
435   UINT32 *bits;
436   size_t src;
437   int cksum_header, cksum_label, cksum_data;
438
439   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
440      fatal(1, "invalid unit %d in call to squeeze_sector()\n", unit);
441
442   /* uninitialized drive? */
443   diablo_drive_t *d = m_drive[unit];
444   if (!d)
445      return;
446
447   if (d->rdfirst >= 0) {
448      LOG((LOG_DRIVE,0,
449         "   RD #%d %03d/%d/%02d bit#%-5d (@%03d.%02d) ... bit#%-5d (@%03d.%02d)\n",
450         d->unit, d->cylinder, d->head, d->sector,
451         d->rdfirst, d->rdfirst / 32, d->rdfirst % 32,
452         d->rdlast, d->rdlast / 32, d->rdlast % 32));
453   }
454   d->rdfirst = -1;
455   d->rdlast = -1;
456
457   /* not written to, just drop it now */
458   if (d->wrfirst < 0) {
459      d->wrfirst = -1;
460      d->wrlast = -1;
461      return;
462   }
463
464   /* did write into the next sector (?) */
465   if (d->wrlast > d->wrfirst && d->wrlast < 256) {
466      d->wrfirst = -1;
467      d->wrlast = -1;
468      return;
469   }
470
471   if (d->wrfirst >= 0) {
472      LOG((LOG_DRIVE,0,
473         "   WR #%d %03d/%d/%02d bit#%-5d (@%03d.%02d) ... bit#%-5d (@%03d.%02d)\n",
474         d->unit, d->cylinder, d->head, d->sector,
475         d->wrfirst, d->wrfirst / 32, d->wrfirst % 32,
476         d->wrlast, d->wrlast / 32, d->wrlast % 32));
477   }
478   d->wrfirst = -1;
479   d->wrlast = -1;
480
481   if (d->page < 0 || d->page >= DIABLO_DRIVE_PAGES) {
482      LOG((LOG_DRIVE,0,"   no sector for #%d: %d/%d/%d\n", d->unit, d->cylinder, d->head, d->sector));
483      return;
484   }
485
486   /* get the sector pointer */
487   s = &d->image[d->page];
488
489   /* get the bits pointer */
490   bits = d->bits[d->page];
491
492   /* no bits to write? */
493   if (!bits) {
494      LOG((LOG_DRIVE,0,"   no sector for #%d: %d/%d/%d\n", d->unit, d->cylinder, d->head, d->sector));
495      return;
496   }
497
498   /* zap the sector first */
499   memset(s->header, 0, sizeof(s->header));
500   memset(s->label, 0, sizeof(s->label));
501   memset(s->data, 0, sizeof(s->data));
502
503   src = MFRRDL * 32;
504   /* skip first words and garbage until 0 bits are coming in */
505   src = squeeze_unsync(bits, src, 40);
506   /* sync on header preamble */
507   src = squeeze_sync(bits, src, 40);
508   LOG((LOG_DRIVE,0,"   header sync bit #%d (@%03d.%02d)\n",
509      src, src / 32, src % 32));
510   src = squeeze_record(bits, src, s->header, sizeof(s->header));
511   src = squeeze_cksum(bits, src, &cksum_header);
512#if   ALTO2_DEBUG
513   dump_record(s->header, 0, sizeof(s->header), "header", 0);
514#endif
515
516   /* skip garbage until 0 bits are coming in */
517   src = squeeze_unsync(bits, src, 40);
518   /* sync on label preamble */
519   src = squeeze_sync(bits, src, 40);
520   LOG((LOG_DRIVE,0,"   label  sync bit #%d (@%03d.%02d)\n",
521      src, src / 32, src % 32));
522   src = squeeze_record(bits, src, s->label, sizeof(s->label));
523   src = squeeze_cksum(bits, src, &cksum_label);
524#if   ALTO2_DEBUG
525   dump_record(s->label, 0, sizeof(s->label), "label", 0);
526#endif
527
528   /* skip garbage until 0 bits are coming in */
529   src = squeeze_unsync(bits, src, 40);
530   /* sync on data preamble */
531   src = squeeze_sync(bits, src, 40);
532   LOG((LOG_DRIVE,0,"   data   sync bit #%d (@%03d.%02d)\n",
533      src, src / 32, src % 32));
534   src = squeeze_record(bits, src, s->data, sizeof(s->data));
535   src = squeeze_cksum(bits, src, &cksum_data);
536#if   ALTO2_DEBUG
537   dump_record(s->data, 0, sizeof(s->data), "data", 1);
538#endif
539
540   /* TODO: what is the cksum start value for the data record? */
541   cksum_header ^= cksum(s->header, sizeof(s->header), 0521);
542   cksum_label ^= cksum(s->label, sizeof(s->label), 0521);
543   cksum_data ^= cksum(s->data, sizeof(s->data), 0521);
544
545   if (cksum_header || cksum_label || cksum_data) {
546#if   ALTO2_DEBUG
547      LOG((LOG_DRIVE,0,"   cksum check - header:%06o label:%06o data:%06o\n", cksum_header, cksum_label, cksum_data));
548#else
549      printf("   cksum check - header:%06o label:%06o data:%06o\n", cksum_header, cksum_label, cksum_data);
550#endif
551   }
552}
553
554/**
555 * @brief return number of bitclk edges for a sector
556 *
557 * @result number of bitclk edges for a sector
558 */
559int alto2_cpu_device::drive_bits_per_sector() const
560{
561   return DIABLO_SECTOR_WORDS * 32;
562}
563
564/**
565 * @brief return a pointer to a drive's description
566 *
567 * @param unit unit number
568 * @result a pointer to the string description
569 */
570const char* alto2_cpu_device::drive_description(int unit)
571{
572   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
573      fatal(1, "invalid unit %d in call to drive_description()\n", unit);
574
575   /* uninitialized drive? */
576   diablo_drive_t *d = m_drive[unit];
577   if (!d)
578      return "";
579
580   return d->description;
581}
582
583/**
584 * @brief return a pointer to a drive's image basename
585 *
586 * @param unit unit number
587 * @result a pointer to the string basename
588 */
589const char* alto2_cpu_device::drive_basename(int unit)
590{
591   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
592      fatal(1, "invalid unit %d in call to drive_description()\n", unit);
593
594   /* uninitialized drive? */
595   diablo_drive_t *d = m_drive[unit];
596   if (!d)
597      return "";
598
599   return d->basename;
600}
601
602/**
603 * @brief return the number of a drive unit
604 *
605 * This is usually a no-op, but drives may be swapped?
606 *
607 * @param unit unit number
608 * @result the unit number
609 */
610int alto2_cpu_device::drive_unit(int unit)
611{
612   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
613      fatal(1, "invalid unit %d in call to drive_unit()\n", unit);
614
615   /* uninitialized drive? */
616   diablo_drive_t *d = m_drive[unit];
617   if (!d)
618      return 0;
619
620   return d->unit;
621}
622
623/**
624 * @brief return the time for a full rotation
625 *
626 * @param unit unit number
627 * @result the time for a full track rotation
628 */
629attotime alto2_cpu_device::drive_rotation_time(int unit)
630{
631   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
632      fatal(1, "invalid unit %d in call to drive_rotation_time()\n", unit);
633
634   /* uninitialized drive? */
635   diablo_drive_t *d = m_drive[unit];
636   if (!d)
637      return attotime::never;
638
639   return d->rotation_time;
640}
641
642/**
643 * @brief return the time for a data bit
644 *
645 * @param unit unit number
646 * @result the time in nano seconds per bit clock
647 */
648attotime alto2_cpu_device::drive_bit_time(int unit)
649{
650   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
651      fatal(1, "invalid unit %d in call to drive_bit_time()\n", unit);
652
653   /* uninitialized drive? */
654   diablo_drive_t *d = m_drive[unit];
655   if (!d)
656      return attotime::never;
657
658   return d->bit_time;
659}
660
661/**
662 * @brief return the seek/read/write status of a drive
663 *
664 * @param unit unit number
665 * @result the seek/read/write status for the drive unit (0: active, 1: inactive)
666 */
667int alto2_cpu_device::drive_seek_read_write_0(int unit)
668{
669   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
670      fatal(1, "invalid unit %d in call to drive_seek_read_write_0()\n", unit);
671
672   /* uninitialized drive? */
673   diablo_drive_t *d = m_drive[unit];
674   if (!d)
675      return 0;
676
677   return d->s_r_w_0;
678}
679
680/**
681 * @brief return the ready status of a drive
682 *
683 * @param unit unit number
684 * @result the ready status for the drive unit
685 */
686int alto2_cpu_device::drive_ready_0(int unit)
687{
688   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
689      fatal(1, "invalid unit %d in call to drive_ready_0()\n", unit);
690
691   /* uninitialized drive? */
692   diablo_drive_t *d = m_drive[unit];
693   if (!d)
694      return 0;
695
696   return d->ready_0;
697}
698
699/**
700 * @brief return the current sector mark status of a drive
701 *
702 * The sector mark is derived from the offset into the current sector.
703 *
704 * @param unit unit number
705 * @result the current sector for the drive unit
706 */
707int alto2_cpu_device::drive_sector_mark_0(int unit)
708{
709   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
710      fatal(1, "invalid unit %d in call to drive_sector_mark_0()\n", unit);
711
712   /* uninitialized drive? */
713   diablo_drive_t *d = m_drive[unit];
714   if (!d)
715      return 0;
716
717   /* no sector marks while seeking (?) */
718   if (d->s_r_w_0)
719      return 1;
720
721   /* return the sector mark */
722   return d->sector_mark_0;
723}
724
725/**
726 * @brief return the address acknowledge state
727 *
728 * @param unit unit number
729 * @result the address acknowledge state
730 */
731int alto2_cpu_device::drive_addx_acknowledge_0(int unit)
732{
733   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
734      fatal(1, "invalid unit %d in call to drive_addx_acknowledge_0()\n", unit);
735
736   /* uninitialized drive? */
737   diablo_drive_t *d = m_drive[unit];
738   if (!d)
739      return 0;
740
741   return d->addx_acknowledge_0;
742}
743
744/**
745 * @brief return the log address interlock state
746 *
747 * @param unit unit number
748 * @result the log address interlock state
749 */
750int alto2_cpu_device::drive_log_addx_interlock_0(int unit)
751{
752   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
753      fatal(1, "invalid unit %d in call to drive_log_addx_interlock_0()\n", unit);
754
755   /* uninitialized drive? */
756   diablo_drive_t *d = m_drive[unit];
757   if (!d)
758      return 0;
759
760   return d->log_addx_interlock_0;
761}
762
763/**
764 * @brief return the seek incomplete state
765 *
766 * @param unit unit number
767 * @result the address acknowledge state
768 */
769int alto2_cpu_device::drive_seek_incomplete_0(int unit)
770{
771   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
772      fatal(1, "invalid unit %d in call to drive_addx_acknowledge_0()\n", unit);
773
774   /* uninitialized drive? */
775   diablo_drive_t *d = m_drive[unit];
776   if (!d)
777      return 0;
778
779   return d->seek_incomplete_0;
780}
781
782/**
783 * @brief return the current cylinder of a drive unit
784 *
785 * Note: the bus lines are active low, thus the XOR with DRIVE_CYLINDER_MASK.
786 *
787 * @param unit unit number
788 * @result the current cylinder number for the drive unit
789 */
790int alto2_cpu_device::drive_cylinder(int unit)
791{
792   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
793      fatal(1, "invalid unit %d in call to drive_cylinder()\n", unit);
794
795   /* uninitialized drive? */
796   diablo_drive_t *d = m_drive[unit];
797   if (!d)
798      return DIABLO_DRIVE_CYLINDER_MASK;
799
800   return d->cylinder ^ DIABLO_DRIVE_CYLINDER_MASK;
801}
802
803/**
804 * @brief return the current head of a drive unit
805 *
806 * Note: the bus lines are active low, thus the XOR with DRIVE_HEAD_MASK.
807 *
808 * @param unit unit number
809 * @result the currently selected head for the drive unit
810 */
811int alto2_cpu_device::drive_head(int unit)
812{
813   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
814      fatal(1, "invalid unit %d in call to drive_head()\n", unit);
815
816   /* uninitialized drive? */
817   diablo_drive_t *d = m_drive[unit];
818   if (!d)
819      return DIABLO_DRIVE_HEAD_MASK;
820
821   return d->head ^ DIABLO_DRIVE_HEAD_MASK;
822}
823
824/**
825 * @brief return the current sector of a drive unit
826 *
827 * The current sector number is derived from the time since the
828 * most recent track rotation started.
829 *
830 * Note: the bus lines are active low, thus the XOR with DRIVE_SECTOR_MASK.
831 *
832 * @param unit unit number
833 * @result the current sector for the drive unit
834 */
835int alto2_cpu_device::drive_sector(int unit)
836{
837   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
838      fatal(1, "invalid unit %d in call to drive_sector()\n", unit);
839
840   /* uninitialized drive? */
841   diablo_drive_t *d = m_drive[unit];
842   if (!d)
843      return DIABLO_DRIVE_SECTOR_MASK;
844
845   return d->sector ^ DIABLO_DRIVE_SECTOR_MASK;
846}
847
848/**
849 * @brief return the current page of a drive unit
850 *
851 * The current page number is derived from the cylinder, head,
852 * and sector numbers.
853 *
854 * @param unit unit number
855 * @result the current page for the drive unit
856 */
857int alto2_cpu_device::drive_page(int unit)
858{
859   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
860      fatal(1, "invalid unit %d in call to drive_page()\n", unit);
861
862   /* uninitialized drive? */
863   diablo_drive_t *d = m_drive[unit];
864   if (!d)
865      return 0;
866
867   return d->page;
868}
869
870/**
871 * @brief select a drive unit and head
872 *
873 * @param unit unit number
874 * @param head head number
875 */
876void alto2_cpu_device::drive_select(int unit, int head)
877{
878
879   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
880      fatal(1, "invalid unit %d in call to drive_select()\n", unit);
881
882   if (unit != m_unit_selected || head != m_head_selected) {
883      /* this drive is selected */
884      m_unit_selected = unit;
885      m_head_selected = head;
886      printf("select unit:%d head:%d\n", unit, head);
887   }
888
889   /* uninitialized drive? */
890   diablo_drive_t *d = m_drive[unit];
891   if (!d)
892      return;
893
894   if (d->image) {
895      /* it is ready */
896      d->ready_0 = 0;
897      /* and can take seek/read/write commands */
898      d->s_r_w_0 = 0;
899      /* address acknowledge (?) */
900      d->addx_acknowledge_0 = 0;
901      /* clear log address interlock (?) */
902      d->log_addx_interlock_0 = 1;
903      LOG((LOG_DRIVE,1,"   UNIT select %d ready\n", unit));
904   } else {
905      /* it is not ready (?) */
906      d->ready_0 = 1;
907      /* can't take seek/read/write commands (?) */
908      d->s_r_w_0 = 1;
909      /* address acknowledge (?) */
910      d->addx_acknowledge_0 = 0;
911      LOG((LOG_DRIVE,1,"   UNIT select %d not ready (no image)\n", unit));
912   }
913
914   /* Note: head select input is active low (0: selects head 1, 1: selects head 0) */
915   head = head & DIABLO_DRIVE_HEAD_MASK;
916   if (head != d->head) {
917      d->head = head;
918      LOG((LOG_DRIVE,1,"   HEAD %d select on unit %d\n", head, unit));
919   }
920   drive_get_sector(unit);
921}
922
923/**
924 * @brief strobe a seek operation
925 *
926 * Seek to the cylinder cylinder, or restore.
927 *
928 * @param unit unit number
929 * @param cylinder cylinder number to seek to
930 * @param restore flag if the drive should restore to cylinder 0
931 * @param strobe current level of the strobe signal (for edge detection)
932 */
933void alto2_cpu_device::drive_strobe(int unit, int cylinder, int restore, int strobe)
934{
935   int seekto = restore ? 0 : cylinder;
936
937   if (unit < 0 || unit >= DIABLO_DRIVE_MAX)
938      fatal(1, "invalid unit %d in call to drive_strobe()\n", unit);
939
940   /* uninitialized drive? */
941   diablo_drive_t *d = m_drive[unit];
942   if (!d)
943      return;
944
945   if (strobe == 1) {
946      LOG((LOG_DRIVE,1,"   STROBE end of interlock\n", seekto));
947      /* deassert the log address interlock */
948      d->log_addx_interlock_0 = 1;
949      return;
950   }
951
952   /* assert the log address interlock */
953   d->log_addx_interlock_0 = 0;
954
955   if (seekto == d->cylinder) {
956      LOG((LOG_DRIVE,1,"   STROBE to cylinder %d acknowledge\n", seekto));
957      d->addx_acknowledge_0 = 0;   /* address acknowledge, if cylinder is reached */
958      d->seek_incomplete_0 = 1;   /* reset seek incomplete */
959      return;
960   }
961
962   d->s_r_w_0 = 0;
963
964   if (seekto < d->cylinder) {
965      /* decrement cylinder */
966      d->cylinder -= 1;
967      if (d->cylinder < 0) {
968         d->cylinder = 0;
969         d->log_addx_interlock_0 = 1;   /* deassert the log address interlock */
970         d->seek_incomplete_0 = 1;   /* deassert seek incomplete */
971         d->addx_acknowledge_0 = 0;   /* assert address acknowledge  */
972         LOG((LOG_DRIVE,1,"   STROBE to cylinder %d incomplete\n", seekto));
973         return;
974      }
975   } else {
976      /* increment cylinder */
977      d->cylinder += 1;
978      if (d->cylinder >= DIABLO_DRIVE_CYLINDERS) {
979         d->cylinder = DIABLO_DRIVE_CYLINDERS - 1;
980         d->log_addx_interlock_0 = 1;   /* deassert the log address interlock */
981         d->seek_incomplete_0 = 1;   /* deassert seek incomplete */
982         d->addx_acknowledge_0 = 0;   /* assert address acknowledge  */
983         LOG((LOG_DRIVE,1,"   STROBE to cylinder %d incomplete\n", seekto));
984         return;
985      }
986   }
987   LOG((LOG_DRIVE,1,"   STROBE to cylinder %d (now %d) - interlock\n", seekto, d->cylinder));
988
989   d->addx_acknowledge_0 = 1;   /* deassert address acknowledge  */
990   d->seek_incomplete_0 = 1;   /* deassert seek incomplete */
991   drive_get_sector(unit);
992}
993
994/**
995 * @brief install a callback to be called whenever a drive sector starts
996 *
997 * @param callback function to call when a new sector begins
998 */
999void alto2_cpu_device::drive_sector_callback(a2cb callback)
1000{
1001   m_sector_callback = callback;
1002}
1003
1004
1005/**
1006 * @brief set the drive erase gate
1007 *
1008 * @param unit drive unit number
1009 * @param gate value of erase gate
1010 */
1011void alto2_cpu_device::drive_egate(int unit, int gate)
1012{
1013   /* uninitialized drive? */
1014   diablo_drive_t *d = m_drive[unit];
1015   if (!d)
1016      return;
1017
1018   d->egate_0 = gate;
1019}
1020
1021/**
1022 * @brief set the drive write gate
1023 *
1024 * @param unit drive unit number
1025 * @param gate value of write gate
1026 */
1027void alto2_cpu_device::drive_wrgate(int unit, int gate)
1028{
1029   /* uninitialized drive? */
1030   diablo_drive_t *d = m_drive[unit];
1031   if (!d)
1032      return;
1033
1034   d->wrgate_0 = gate;
1035}
1036
1037/**
1038 * @brief set the drive read gate
1039 *
1040 * @param unit drive unit number
1041 * @param gate value of read gate
1042 */
1043void alto2_cpu_device::drive_rdgate(int unit, int gate)
1044{
1045   /* uninitialized drive? */
1046   diablo_drive_t *d = m_drive[unit];
1047   if (!d)
1048      return;
1049
1050   d->rdgate_0 = gate;
1051}
1052
1053/**
1054 * @brief write the sector relative bit at index
1055 *
1056 * The disk controller writes a combined clock and data pulse to one output
1057 * <PRE>
1058 * Encoding of binary 01011
1059 *
1060 *   clk   data  clk   data  clk   data  clk   data  clk   data
1061 *   0     1     2     3     4     5     6     7     8     9
1062 *   +--+        +--+  +--+  +--+        +--+  +--+  +--+  +--+  +--
1063 *   |  |        |  |  |  |  |  |        |  |  |  |  |  |  |  |  |
1064 * --+  +--------+  +--+  +--+  +--------+  +--+  +--+  +--+  +--+
1065 * </PRE>
1066 *
1067 * @param unit drive unit number
1068 * @param index relative index of bit/clock into sector
1069 * @param wrdata write data clock or bit
1070 */
1071void alto2_cpu_device::drive_wrdata(int unit, int index, int wrdata)
1072{
1073   /* uninitialized drive? */
1074   diablo_drive_t *d = m_drive[unit];
1075   if (!d)
1076      return;
1077
1078   if (d->wrgate_0) {
1079      /* write gate is not asserted (active 0) */
1080      return;
1081   }
1082
1083   /* don't write before or beyond the sector */
1084   if (index < 0 || index >= drive_bits_per_sector())
1085      return;
1086
1087   if (-1 == d->page) {
1088      /* invalid page */
1089      return;
1090   }
1091
1092   UINT32 *bits = d->bits[d->page];
1093   if (!bits) {
1094      /* expand the sector to bits */
1095      expand_sector(unit, d->page);
1096      bits = d->bits[d->page];
1097      /* just to be safe */
1098      if (!bits)
1099         fatal(1, "No bits for drive #%d page #%d\n", unit, d->page);
1100   }
1101   if (-1 == d->wrfirst)
1102      d->wrfirst = index;
1103
1104   LOG((LOG_DRIVE,7,"   write #%d %d/%d/%d bit #%d bit:%d\n", unit, d->cylinder, d->head, d->sector, index, wrdata));
1105
1106   if (index < GUARD_ZONE_BITS) {
1107      /* don't write in the guard zone (?) */
1108   } else {
1109      WRBIT(bits,index,wrdata);
1110   }
1111   d->wrlast = index;
1112}
1113
1114/**
1115 * @brief read the sector relative bit at index
1116 *
1117 * Note: this is a gross hack to allow the controller pulling bits
1118 * at its will, rather than clocking them with the drive's RDCLK-
1119 *
1120 * @param unit is the drive index
1121 * @param index is the sector relative bit index
1122 * @result returns the sector's bit by index
1123 */
1124int alto2_cpu_device::drive_rddata(int unit, int index)
1125{
1126   int bit = 0;
1127
1128   /* uninitialized drive? */
1129   diablo_drive_t *d = m_drive[unit];
1130   if (!d)
1131      return bit;
1132
1133   if (d->rdgate_0) {
1134      /* read gate is not asserted (active 0) */
1135      return 0;
1136   }
1137
1138   /* don't read before or beyond the sector */
1139   if (index < 0 || index >= drive_bits_per_sector())
1140      return 1;
1141
1142   /* no data while sector mark is low (?) */
1143   if (0 == d->sector_mark_0)
1144      return 1;
1145
1146   if (-1 == d->page) {
1147      /* invalid page */
1148      return 1;
1149   }
1150
1151   UINT32 *bits = d->bits[d->page];
1152   if (!bits) {
1153      /* expand the sector to bits */
1154      expand_sector(unit, d->page);
1155      bits = d->bits[d->page];
1156      /* just to be safe */
1157      if (!bits)
1158         fatal(1, "No bits for drive #%d page #%d\n", unit, d->page);
1159   }
1160   if (-1 == d->rdfirst)
1161      d->rdfirst = index;
1162
1163   RDBIT(bits,index,bit);
1164   LOG((LOG_DRIVE,7,"   read #%d %d/%d/%d bit #%d:%d\n", unit, d->cylinder, d->head, d->sector, index, bit));
1165   d->rdlast = index;
1166   return bit;
1167}
1168
1169/**
1170 * @brief get the sector relative clock at index
1171 *
1172 * Note: this is a gross hack to allow the controller pulling bits
1173 * at its will, rather than clocking them with the drive's RDCLK-
1174 *
1175 * @param unit is the drive index
1176 * @param index is the sector relative bit index
1177 * @result returns the sector's bit by index
1178 */
1179int alto2_cpu_device::drive_rdclk(int unit, int index)
1180{
1181   int clk = 0;
1182
1183   /* don't read before or beyond the sector */
1184   if (index < 0 || index >= drive_bits_per_sector())
1185      return 1;
1186
1187   /* uninitialized drive? */
1188   diablo_drive_t *d = m_drive[unit];
1189   if (!d)
1190      return clk;
1191
1192   /* no clock while sector mark is low (?) */
1193   if (0 == d->sector_mark_0)
1194      return 1;
1195
1196   if (-1 == d->page) {
1197      /* invalid page */
1198      return 1;
1199   }
1200
1201   UINT32 *bits = d->bits[d->page];
1202   if (!bits) {
1203      /* expand the sector to bits */
1204      expand_sector(unit, d->page);
1205      bits = d->bits[d->page];
1206      /* just to be safe */
1207      if (!bits)
1208         fatal(1, "No bits for drive #%d page #%d\n", unit, d->page);
1209   }
1210   if (-1 == d->rdfirst)
1211      d->rdfirst = index;
1212
1213   if (index & 1) {
1214      clk = 0;
1215   } else {
1216      RDBIT(bits,index,clk);
1217   }
1218   LOG((LOG_DRIVE,7,   "   read #%d %d/%d/%d clk #%d:%d\n", unit, d->cylinder, d->head, d->sector, index, clk));
1219   d->rdlast = index;
1220   return clk ^ 1;
1221}
1222
1223/**
1224 * @brief debug function to read sync bit position from a sector
1225 *
1226 * @param unit is the drive index
1227 * @param page is the page number to read
1228 * @param offs is the first bit offset
1229 * @result returns bit offset after the next sync bit
1230 */
1231int alto2_cpu_device::debug_read_sync(int unit, int page, int offs)
1232{
1233   if (unit < 0 || unit > 1)
1234      return 0;
1235
1236   /* don't read before or beyond the sector */
1237   if (offs < 0 || offs >= 400*32)
1238      return 0;
1239
1240   /* check for invalid page */
1241   if (page < 0 || page >= DIABLO_DRIVE_CYLINDERS * DIABLO_DRIVE_HEADS * DIABLO_DRIVE_SPT)
1242      return 0;
1243
1244   /* uninitialized drive? */
1245   diablo_drive_t *d = m_drive[unit];
1246   if (!d)
1247      return 0;
1248
1249   UINT32 *bits = d->bits[page];
1250   if (!bits) {
1251      /* expand the sector to bits */
1252      expand_sector(unit, page);
1253      bits = d->bits[page];
1254      /* just to be safe */
1255      if (!bits)
1256         return 0;
1257   }
1258
1259   UINT32 accu = 0;
1260   while (offs < 400 * 32) {
1261      int bit;
1262      offs = RDBIT(bits,offs,bit);
1263      accu = (accu << 1) | bit;
1264      if (accu == 0xaaaaaaab)
1265         break;
1266   }
1267
1268   return offs;
1269}
1270
1271/**
1272 * @brief debug function to read bits from a drive sector
1273 *
1274 * @param unit is the drive index
1275 * @param page is the page number to read
1276 * @param offs is the first bit offset
1277 * @result returns a word starting at the bit offset
1278 */
1279int alto2_cpu_device::debug_read_sec(int unit, int page, int offs)
1280{
1281   int i, clks, word;
1282
1283   if (unit < 0 || unit > 1)
1284      return 0177777;
1285
1286   /* don't read before or beyond the sector */
1287   if (offs < 0 || offs >= 400*32)
1288      return 0177777;
1289
1290   /* check for invalid page */
1291   if (page < 0 || page >= DIABLO_DRIVE_CYLINDERS * DIABLO_DRIVE_HEADS * DIABLO_DRIVE_SPT)
1292      return 0177777;
1293
1294   /* uninitialized drive? */
1295   diablo_drive_t *d = m_drive[unit];
1296   if (!d)
1297      return 0;
1298
1299   UINT32 *bits = d->bits[page];
1300   if (!bits) {
1301      /* expand the sector to bits */
1302      expand_sector(unit, page);
1303      bits = d->bits[page];
1304      /* just to be safe */
1305      if (!bits)
1306         return 0177777;
1307   }
1308
1309   for (i = 0, clks = 0, word = 0; i < 16; i++) {
1310      int bit;
1311      offs = RDBIT(bits,offs,bit);
1312      clks = (clks << 1) | bit;
1313      offs = RDBIT(bits,offs,bit);
1314      word = (word << 1) | bit;
1315   }
1316
1317   return word;
1318}
1319
1320/**
1321 * @brief timer callback that is called once per sector in the rotation
1322 *
1323 * @param id timer id
1324 * @param arg argument supplied to timer_insert (unused)
1325 */
1326void alto2_cpu_device::drive_next_sector(void* ptr, int arg)
1327{
1328   (void)ptr;
1329   int unit = m_unit_selected;
1330
1331   switch (arg) {
1332   case 0:
1333      m_sector_timer->adjust(DIABLO_SECTOR_MARK_PULSE_PRE, 1);
1334      /* deassert sector mark */
1335      sector_mark_1(unit);
1336      break;
1337   case 1:
1338      m_sector_timer->adjust(DIABLO_SECTOR_MARK_PULSE_POST, 2);
1339      /* assert sector mark */
1340      sector_mark_0(unit);
1341      break;
1342   case 2:
1343      /* next sector starting soon now */
1344      m_sector_timer->adjust(DIABLO_SECTOR_TIME - DIABLO_SECTOR_MARK_PULSE_PRE, 0);
1345      /* call the sector_callback, if any */
1346      if (m_sector_callback)
1347         ((*this).*m_sector_callback)(unit);
1348   }
1349}
1350
1351/**
1352 * @brief timer callback that deasserts the sector mark
1353 *
1354 * @param unit drive unit number
1355 */
1356void alto2_cpu_device::sector_mark_1(int unit)
1357{
1358   /* uninitialized drive? */
1359   diablo_drive_t *d = m_drive[unit];
1360   if (!d)
1361      return;
1362
1363   LOG((LOG_DRIVE,5, "   sector mark 1 (unit #%d sector %d)\n", unit, d->sector));
1364   /* set sector mark to 1 */
1365   d->sector_mark_0 = 1;
1366}
1367
1368/**
1369 * @brief timer callback that asserts the sector mark
1370 *
1371 * @param unit drive unit number
1372 */
1373void alto2_cpu_device::sector_mark_0(int unit)
1374{
1375   /* uninitialized drive? */
1376   diablo_drive_t *d = m_drive[unit];
1377   if (!d)
1378      return;
1379
1380   LOG((LOG_DRIVE,5,"   sector mark 0 (unit #%d sector %d)\n", unit, d->sector));
1381
1382   /* squeeze previous sector, if it was written to */
1383   squeeze_sector(unit);
1384
1385   d->sector_mark_0 = 0;
1386
1387   /* reset read and write bit locations */
1388   d->rdfirst = -1;
1389   d->rdlast = -1;
1390   d->wrfirst = -1;
1391   d->wrlast = -1;
1392
1393   /* count sectors */
1394   d->sector = (d->sector + 1) % DIABLO_DRIVE_SPT;
1395   drive_get_sector(unit);
1396}
1397
1398#if   0   // FIXME: unused loading of the disk image
1399/**
1400 * @brief pass down command line arguments to the drive emulation
1401 *
1402 * @param arg command line argument
1403 * @result returns 0 if this was a disk image, -1 on error
1404 */
1405int drive_args(const char *arg)
1406{
1407   const char *basename;
1408   int unit;
1409   int hdr, c, h, s, chs;
1410   diablo_drive_t *d;
1411   int zcat;
1412   FILE *fp;
1413   size_t size;      /* size in sectors */
1414   size_t done;      /* read loops */
1415   size_t csize;      /* size of cooked image */
1416   UINT8 *image;      /* file image (either cooked, or compressed) */
1417   diablo_sector_t *cooked;   /* cooked sector data */
1418   UINT8 *ip, *cp;
1419   char *p;
1420
1421   basename = strrchr(arg, '/');
1422   if (basename) {
1423      basename++;
1424   } else {
1425      basename = strrchr(arg, '\\');
1426      if (basename)
1427         basename++;
1428      else
1429         basename = arg;
1430   }
1431
1432   /* find trailing dot */
1433   p = strrchr(basename, '.');
1434   if (!p)
1435      return -1;
1436
1437   /* check for .Z extension */
1438   if (!strcmp(p, ".z") || !strcmp(p, ".Z") ||
1439      !strcmp(p, ".gz") || !strcmp(p, ".GZ")) {
1440      /* compress (LZW) or gzip compressed image */
1441      LOG((LOG_DRIVE,0,"loading compressed disk image %s\n", arg));
1442      zcat = 1;
1443   } else if (!strcmp(p, ".dsk") || !strcmp(p, ".DSK")) {
1444      /* uncompressed .dsk extension */
1445      LOG((LOG_DRIVE,0,"loading uncompressed disk image %s\n", arg));
1446      zcat = 0;
1447   } else {
1448      return -1;
1449   }
1450
1451   for (unit = 0; unit < DRIVE_MAX; unit++)
1452      if (!drive[unit].image)
1453         break;
1454
1455   /* all drive slots filled? */
1456   if (unit == DRIVE_MAX)
1457      return -1;
1458
1459   d = m_drive[unit];
1460
1461   snprintf(d->basename, sizeof(d->basename), "%s", basename);
1462
1463   fp = fopen(arg, "rb");
1464   if (!fp)
1465      fatal(1, "failed to fopen(%s,\"rb\") (%s)\n",
1466         arg, strerror(errno));
1467
1468   /* size in sectors */
1469   size = DRIVE_CYLINDERS * DRIVE_HEADS * DRIVE_SPT;
1470
1471   csize = size * sizeof(diablo_sector_t);
1472   image = (UINT8 *)malloc(csize + 1);
1473   if (!image)
1474      fatal(1, "failed to malloc(%d) bytes\n", csize);
1475   ip = (UINT8 *)image;
1476   for (done = 0; done < size * sizeof(diablo_sector_t); /* */) {
1477      int got = fread(ip + done, 1, size * sizeof(diablo_sector_t) - done, fp);
1478      if (got < 0) {
1479         LOG((LOG_DRIVE,0,"fread() returned error (%s)\n",
1480            strerror(errno)));
1481         break;
1482      }
1483      if (got == 0)
1484         break;
1485      done += got;
1486   }
1487   fclose(fp);
1488
1489   LOG((LOG_DRIVE,0, "got %d (%#x) bytes\n", done, done));
1490
1491   cooked = (diablo_sector_t *)malloc(csize);
1492   if (!cooked)
1493      fatal(1, "failed to malloc(%d) bytes\n", csize);
1494   cp = (UINT8 *)cooked;
1495
1496   if (zcat) {
1497      size_t skip;
1498      int type = z_header(ip, done, &skip);
1499
1500      switch (type) {
1501      case 0:
1502         LOG((log_MISC,0, "compression type unknown, skip:%d\n", skip));
1503         csize = gz_copy(cp, csize, ip, done);
1504         break;
1505      case 1:
1506         LOG((log_MISC,0, "compression type compress (LZW), skip:%d\n", skip));
1507         csize = z_copy(cp, csize, ip + skip, done - skip);
1508         break;
1509      case 2:
1510         LOG((log_MISC,0, "compression type gzip, skip:%d\n", skip));
1511         csize = gz_copy(cp, csize, ip, done);
1512         break;
1513      }
1514   } else {
1515      memcpy(cp, ip, done);
1516      csize = done;
1517   }
1518   free(image);
1519   image = NULL;
1520   ip = NULL;
1521
1522   if (csize != size * sizeof(diablo_sector_t)) {
1523      LOG((LOG_DRIVE,0,"disk image %s size mismatch (%d bytes)\n", arg, csize));
1524      free(cooked);
1525      return -1;
1526   }
1527
1528   /* reset cylinder, head, sector counters */
1529   c = h = s = 0;
1530
1531   /* check image sectors for sanity */
1532   for (done = 0; done < size; done++) {
1533      /* copy the available records in their place */
1534
1535      /* verify the chs with the header and log any mismatches */
1536      hdr = cooked->header[2] + 256 * cooked->header[3];
1537      chs = (s << 12) | (c << 3) | (h << 2) | (unit << 1);
1538      if (chs != hdr) {
1539         LOG((LOG_DRIVE,0,"WARNING: header mismatch C/H/S: %3d/%d/%2d chs:%06o hdr:%06o\n",
1540            c, h, s, chs, hdr));
1541      }
1542      if (++s == DRIVE_SPT) {
1543         s = 0;
1544         if (++h == DRIVE_HEADS) {
1545            h = 0;
1546            c++;
1547         }
1548      }
1549      cooked++;
1550   }
1551
1552   /* set drive image */
1553   d->image = (diablo_sector_t *)cp;
1554
1555   LOG((LOG_DRIVE,0,"drive #%d successfully created image for %s\n", unit, arg));
1556
1557   drive_select(unit, 0);
1558
1559   /* drive address acknowledge is active now */
1560   d->s_r_w_0 = 0;
1561   /* drive address acknowledge is active now */
1562   d->addx_acknowledge_0 = 0;
1563
1564   LOG((LOG_DRIVE,0,"possible %s sector size is %#o (%d) words\n", d->description, SECTOR_WORDS, SECTOR_WORDS));
1565
1566   LOG((LOG_DRIVE,0,"sector mark pulse length is %lldns\n", SECTOR_MARK_PULSE_PRE + SECTOR_MARK_PULSE_POST));
1567
1568   return 0;
1569}
1570#endif
1571
1572/**
1573 * @brief initialize the disk drive context and insert a disk word timer
1574 *
1575 * @result returns 0 on success, fatal() on error
1576 */
1577void alto2_cpu_device::drive_init()
1578{
1579   int i;
1580
1581   if (sizeof(diablo_sector_t) != 267 * 2)
1582      fatal(1, "sizeof(sector_t) is not %d (%d)\n",
1583         267 * 2, sizeof(diablo_sector_t));
1584
1585   for (i = 0; i < DIABLO_DRIVE_MAX; i++) {
1586      diablo_drive_t *d = reinterpret_cast<diablo_drive_t *>(auto_alloc_clear(machine(), diablo_drive_t));
1587      m_drive[i] = d;
1588
1589      d->unit = i;               /* set the unit number */
1590      snprintf(d->description, sizeof(d->description), "DIABLO31");
1591      d->rotation_time = DIABLO_ROTATION_TIME;
1592      d->bit_time = DIABLO_BIT_TIME(1);
1593      d->s_r_w_0 = 1;               /* seek/read/write not ready */
1594      d->ready_0 = 1;               /* drive is not ready */
1595      d->sector_mark_0 = 1;         /* sector mark clear */
1596      d->addx_acknowledge_0 = 1;      /* drive address acknowledge is not active */
1597      d->log_addx_interlock_0 = 1;   /* drive log address interlock is not active */
1598      d->seek_incomplete_0 = 1;      /* drive seek incomplete is not active */
1599
1600      /* reset the disk drive's address */
1601      d->cylinder = 0;
1602      d->head = 0;
1603      d->sector = 0;
1604
1605      d->egate_0 = 1;
1606      d->wrgate_0 = 1;
1607      d->rdgate_0 = 1;
1608
1609      /* clocks + bits for the expanded sector + some slack */
1610      d->wrfirst = -1;
1611      d->wrlast = -1;
1612      d->rdfirst = -1;
1613      d->rdlast = -1;
1614   }
1615
1616   m_sector_callback = 0;
1617   m_sector_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(alto2_cpu_device::drive_next_sector),this));
1618   m_sector_timer->adjust(DIABLO_SECTOR_TIME - DIABLO_SECTOR_MARK_PULSE_PRE, 0);
1619}
branches/alto2/src/emu/cpu/alto2/a2mem.c
r26122r26123
789789UINT16 alto2_cpu_device::debug_read_mem(UINT32 addr)
790790{
791791   int base_addr = addr & 0177777;
792   int data;
792   int data = 0177777;
793793   if (base_addr >= ALTO2_IO_PAGE_BASE) {
794794      offs_t offset = base_addr - ALTO2_IO_PAGE_BASE;
795795      if (mmio_read_fn[offset])
r26122r26123
830830 */
831831void alto2_cpu_device::init_memory()
832832{
833   int addr;
834833   memset(&m_mem, 0, sizeof(m_mem));
835834
836   for (addr = 0; addr < ALTO2_IO_PAGE_SIZE; addr++) {
835   for (UINT32 addr = 0; addr < ALTO2_IO_PAGE_SIZE; addr++) {
837836      mmio_read_fn[addr] = &alto2_cpu_device::bad_mmio_read_fn;
838837      mmio_write_fn[addr] = &alto2_cpu_device::bad_mmio_write_fn;
839838   }
r26122r26123
886885   install_mmio_fn(0177024, 0177024, &alto2_cpu_device::mear_r, 0);
887886   install_mmio_fn(0177025, 0177025, &alto2_cpu_device::mesr_r, &alto2_cpu_device::mesr_w);
888887   install_mmio_fn(0177026, 0177026, &alto2_cpu_device::mecr_r, &alto2_cpu_device::mecr_w);
888
889#if   ALTO2_HAMMING_CHECK
890   // Initialize the hamming codes and parity bit
891   for (UINT32 addr = 0; addr < ALTO2_IO_PAGE_BASE; addr++)
892      hamming_code(1, addr, 0);
893#endif
889894}
branches/alto2/src/emu/cpu/alto2/a2disk.c
r26122r26123
1212/** @brief 1 to debug the JK flip-flops, 0 to use a lookup table */
1313#define   JKFF_FUNCTION   0
1414
15/**
16 *
17 * Just for completeness' sake:
18 * The mapping of disk controller connector P2 pins to the
19 * Winchester disk drive signals (see drive.h)
20 * <PRE>
21 * Alto Controller     Winchester
22 * P2 signal           disk bus
23 * -----------------------------------------------
24 *  1 GND              D_GROUND
25 *  2 RDCLK'           A_READ_CLOCK
26 *  3 WRDATA'          B_WRITE_DATA_AND_CLOCK
27 *  4 SRWRDY'          F_S_R_W
28 *  5 DISK             L_SELECT_LINE_UNIT_1
29 *  6 CYL(7)'          N_CYL_7
30 *  7 DISK'            R_SELECT_LINE_UNIT_2
31 *  8 CYL(2)'          T_CYL_2
32 *  9 ???              V_SELECT_LINE_UNIT_3
33 * 10 CYL(4)'          X_CYL_4
34 * 11 CYL(0)'          Z_CYL_0
35 * 12 CYL(1)'          BB_CYL_1
36 * 13 CYL(3)'          FF_CYL_3
37 * 14 ???              KK_BIT_2
38 * 15 CYL(8)'          LL_CYL_8
39 * 16 ADRACK'          NN_ADDX_ACKNOWLEDGE
40 * 17 SKINC'           TT_SEEK_INCOMPLETE
41 * 18 LAI'             XX_LOG_ADDX_INTERLOCK
42 * 19 CYL(6)'          RR_CYL_6
43 * 20 RESTOR'          VV_RESTORE
44 * 21 ???              UU_BIT_16
45 * 22 STROBE'          SS_STROBE
46 * 23 ???              MM_BIT_8
47 * 24 ???              KK_BIT_4
48 * 25 ???              HH_WRITE_CHK
49 * 26 WRTGATE'         EE_WRITE_GATE
50 * 27 ???              CC_BIT_SECTOR_ADDX
51 * 28 HEAD'            AA_HEAD_SELECT
52 * 29 ???              Y_INDEX_MARK
53 * 30 SECT(4)'         W_SECTOR_MARK
54 * 31 READY'           U_FILE_READY
55 * 32 ???              S_PSEUDO_SECTOR_MARK
56 * 33 ???              P_WRITE_PROTECT_IND
57 * 34 ???              H_WRITE_PROTECT_INPUT_ATTENTION
58 * 35 ERGATE'          K_ERASE_GATE
59 * 36 ???              M_HIGH_DENSITY
60 * 37 CYL(5)'          J_CYL_5
61 * 38 RDDATA'          C_READ_DATA
62 * 39 RDGATE'          E_READ_GATE
63 * 40 GND              ??
64 * </PRE>
65 */
66
6715#define   GET_KADDR_SECTOR(kaddr)         A2_GET16(kaddr,16, 0, 3)         //!< get sector number from address register
6816#define   PUT_KADDR_SECTOR(kaddr,val)      A2_PUT16(kaddr,16, 0, 3,val)      //!< put sector number into address register
6917#define   GET_KADDR_CYLINDER(kaddr)      A2_GET16(kaddr,16, 4,12)         //!< get cylinder number from address register
r26122r26123
10871035   int i;
10881036   UINT8 s0, s1;
10891037
1090   LOG((LOG_DISK,5,"   >>> KWD timing bitclk:%d datin:%d sect4:%d\n", bitclk, datin, drive_sector_mark_0(m_dsk.drive)));
1038   diablo_hd_device* dhd = m_drive[m_dsk.drive];
1039   LOG((LOG_DISK,5,"   >>> KWD timing bitclk:%d datin:%d sect4:%d\n", bitclk, datin, dhd->get_sector_mark_0()));
10911040
10921041   if (0 == m_dsk.seclate) {
10931042      /* If SECLATE is 0, WDDONE' never goes low (counter's clear has precedence). */
r26122r26123
12761225      DEBUG_NAME("      RDYLAT 45a");
12771226      s0 = m_dsk.ff_45a;
12781227      s1 = m_sysclka1[i];
1279      if (drive_ready_0(m_dsk.drive))
1228      if (dhd->get_ready_0())
12801229         s1 |= JKFF_J;
12811230      s1 |= JKFF_K;
12821231      s1 |= JKFF_S;
r26122r26123
14521401    */
14531402   DEBUG_NAME("      KSEC 21a");
14541403   s0 = m_dsk.ff_21a;
1455   s1 = drive_sector_mark_0(m_dsk.drive) ? 0 : JKFF_CLK;
1404   s1 = dhd->get_sector_mark_0() ? 0 : JKFF_CLK;
14561405   if (!(m_dsk.ff_22b & JKFF_Q))
14571406      s1 |= JKFF_J;
14581407   s1 |= JKFF_K;
r26122r26123
14801429    * check if write and erase gate, or read gate are changed
14811430    */
14821431   if ((m_task_wakeup & (1 << task_ksec)) || GET_KCOM_XFEROFF(m_dsk.kcom) || m_dsk.kfer) {
1483      drive_egate(m_dsk.drive, 1);
1484      drive_wrgate(m_dsk.drive, 1);
1485      drive_rdgate(m_dsk.drive, 1);
1432      dhd->set_egate(1);
1433      dhd->set_wrgate(1);
1434      dhd->set_rdgate(1);
14861435   } else {
14871436      /* enable either read or write gates depending on current record R/W */
14881437      if (m_dsk.krwc & RWC_WRITE) {
14891438         /* enable erase and write gates only if OKTORUN is high */
14901439         if (m_dsk.ok_to_run) {
1491            drive_egate(m_dsk.drive, 0);
1492            drive_wrgate(m_dsk.drive, 0);
1440            dhd->set_egate(0);
1441            dhd->set_wrgate(0);
14931442         }
14941443      } else {
14951444         /* enable read gate */
1496         drive_rdgate(m_dsk.drive, 0);
1445         dhd->set_rdgate(0);
14971446      }
14981447   }
14991448
r26122r26123
15521501   int lai;
15531502   int strobe;
15541503
1504   diablo_hd_device* dhd = m_drive[unit];
15551505   LOG((LOG_DISK,2,"   STROBE #%d restore:%d cylinder:%d\n", unit, restore, cylinder));
15561506
15571507   /* This is really monoflop 52a generating a very short 0 pulse */
15581508   for (strobe = 0; strobe < 2; strobe++) {
15591509      UINT8 s0, s1;
15601510      /* pulse the strobe signal to the unit */
1561      drive_strobe(unit, cylinder, restore, strobe);
1511      dhd->set_strobe(cylinder, restore, strobe);
15621512
1563      lai = drive_log_addx_interlock_0(unit);
1513      lai = dhd->get_log_addx_interlock_0();
15641514      LOG((LOG_DISK,6,"      LAI':%d\n", lai));
15651515      /**
15661516       * JK flip-flop 44a (LAI' clocked)
r26122r26123
15811531      s1 |= JKFF_S;
15821532      s1 |= JKFF_C;
15831533      m_dsk.ff_44a = update_jkff(s0, s1);
1584      if (drive_addx_acknowledge_0(unit) == 0 && (m_dsk.ff_44a & JKFF_Q)) {
1534      if (dhd->get_addx_acknowledge_0() == 0 && (m_dsk.ff_44a & JKFF_Q)) {
15851535         /* if address is acknowledged, and Q' of FF 44a, clear the strobe */
15861536         m_dsk.strobe = 0;
15871537      }
15881538   }
15891539
1590   if (drive_addx_acknowledge_0(unit)) {
1540   if (dhd->get_addx_acknowledge_0()) {
15911541
15921542      /* no acknowledge yet */
15931543
r26122r26123
15961546      LOG((LOG_DISK,2,"      STROBON:%d\n", m_dsk.strobe));
15971547
15981548      /* update the seekok status: SKINC' && LAI' && Q' of FF 44a */
1599      seekok = drive_seek_incomplete_0(unit);
1549      seekok = dhd->get_seek_incomplete_0();
16001550      if (seekok != m_dsk.seekok) {
16011551         m_dsk.seekok = seekok;
16021552         LOG((LOG_DISK,2,"      SEEKOK:%d\n", m_dsk.seekok));
16031553      }
16041554   }
16051555
1606   LOG((LOG_DISK,2,"   current cylinder:%d\n", drive_cylinder(unit) ^ DIABLO_DRIVE_CYLINDER_MASK));
1556   LOG((LOG_DISK,2,"   current cylinder:%d\n", dhd->get_cylinder()));
16071557
16081558   /* if the strobe is still set, restart the timer */
16091559   if (m_dsk.strobe) {
r26122r26123
16171567/** @brief timer callback to change the READY monoflop 31a */
16181568void alto2_cpu_device::disk_ready_mf31a(void* ptr, INT32 arg)
16191569{
1620   m_dsk.ready_mf31a = arg & drive_ready_0(m_dsk.drive);
1570   diablo_hd_device* dhd = m_drive[m_dsk.drive];
1571   m_dsk.ready_mf31a = arg & dhd->get_ready_0();
16211572   /* log the not ready result with level 0, else 2 */
16221573   LOG((LOG_DISK,m_dsk.ready_mf31a ? 0 : 2,"   ready mf31a:%d\n", m_dsk.ready_mf31a));
16231574}
r26122r26123
16501601{
16511602   UINT16 r;
16521603   int unit = m_dsk.drive;
1604   diablo_hd_device* dhd = m_drive[unit];
16531605
16541606   /* KSTAT[4-7] bus is open */
16551607   PUT_KSTAT_DONE(m_dsk.kstat, 017);
r26122r26123
16581610   PUT_KSTAT_SEEKFAIL(m_dsk.kstat, m_dsk.seekok ? 0 : 1);
16591611
16601612   /* KSTAT[9] latch the drive seek/read/write status */
1661   PUT_KSTAT_SEEK(m_dsk.kstat, drive_seek_read_write_0(unit));
1613   PUT_KSTAT_SEEK(m_dsk.kstat, dhd->get_seek_read_write_0());
16621614
16631615   /* KSTAT[10] latch the latched (FF 45a at CLRSTAT) ready status */
16641616   PUT_KSTAT_NOTRDY(m_dsk.kstat, m_dsk.ff_45a & JKFF_Q ? 1 : 0);
r26122r26123
17871739      PUT_KADDR_DRIVE(m_dsk.kaddr, GET_KADDR_DRIVE(m_bus));
17881740      m_dsk.drive = GET_KADDR_DRIVE(m_dsk.kaddr);
17891741
1790      LOG((LOG_DISK,1,"   KDATA<-; BUS (%#o) (drive:%d restore:%d %d/%d/%02d page:%d)\n",
1742      LOG((LOG_DISK,1,"   KDATA<-; BUS (%#o) (drive:%d restore:%d %d/%d/%02d)\n",
17911743         m_bus,
17921744         GET_KADDR_DRIVE(m_dsk.kaddr),
17931745         GET_KADDR_RESTORE(m_dsk.kaddr),
17941746         GET_KADDR_CYLINDER(m_dsk.kaddr),
17951747         GET_KADDR_HEAD(m_dsk.kaddr),
1796         GET_KADDR_SECTOR(m_dsk.kaddr),
1797         DRIVE_PAGE(GET_KADDR_CYLINDER(m_dsk.kaddr),GET_KADDR_HEAD(m_dsk.kaddr),GET_KADDR_SECTOR(m_dsk.kaddr))));
1748         GET_KADDR_SECTOR(m_dsk.kaddr)));
17981749#if   0
17991750      /* printing changes in the disk address */
18001751      {
r26122r26123
18901841 */
18911842void alto2_cpu_device::f1_clrstat_1()
18921843{
1844   diablo_hd_device* dhd = m_drive[m_dsk.drive];
18931845   LOG((LOG_DISK,1,"   CLRSTAT\n"));
18941846
18951847   /* clears the LAI clocked flip-flop 44a
r26122r26123
19381890   DEBUG_NAME("      RDYLAT 45a");
19391891   m_dsk.ff_45a = update_jkff(m_dsk.ff_45a,
19401892      (m_dsk.ff_45a & JKFF_CLK) |
1941      drive_ready_0(m_dsk.drive) |
1893      dhd->get_ready_0() |
19421894      JKFF_K |
19431895      JKFF_S |
19441896      0);
r26122r26123
19611913      JKFF_C);
19621914
19631915   /* set or reset monoflop 31a, depending on drive READY' */
1964   m_dsk.ready_mf31a = drive_ready_0(m_dsk.drive);
1916   m_dsk.ready_mf31a = dhd->get_ready_0();
19651917
19661918   /* start monoflop 31a, which resets ready_mf31a */
19671919   if (!m_dsk.ready_timer)
r26122r26123
20241976
20251977   /* get selected drive from DATA[14] output (FF 67a really) */
20261978   unit = GET_KADDR_DRIVE(m_dsk.kaddr);
2027
2028   /* get the selected head, and select drive unit and head */
20291979   /* latch head from DATA[13] */
20301980   head = GET_KADDR_HEAD(m_dsk.dataout);
20311981   PUT_KADDR_HEAD(m_dsk.kaddr, head);
2032   drive_select(unit, head);
1982   /* get the selected head, and select drive unit and head */
1983   diablo_hd_device* dhd = m_drive[unit];
1984   dhd->select(unit, head);
20331985
20341986   /* On KDAR<- bit 0 of parts #36 and #37 is reset to 0, i.e. recno = 0 */
20351987   m_dsk.krecno = 0;
r26122r26123
21562108 */
21572109void alto2_cpu_device::f2_swrnrdy_1()
21582110{
2159   UINT16 r = drive_seek_read_write_0(m_dsk.drive);
2111   diablo_hd_device* dhd = m_drive[m_dsk.drive];
2112   UINT16 r = dhd->get_seek_read_write_0();
21602113   UINT16 init = INIT ? 037 : 0;
21612114
21622115   LOG((LOG_DISK,1,"   SWRNRDY; %sbranch (%#o|%#o|%#o)\n", (r | init) ? "" : "no ", m_next2, r, init));
r26122r26123
22172170void alto2_cpu_device::disk_bitclk(void* ptr, INT32 arg)
22182171{
22192172   (void)ptr;
2220   int bits = drive_bits_per_sector();
2173   diablo_hd_device* dhd = m_drive[m_dsk.drive];
2174   int bits = dhd->bits_per_sector();
22212175   int clk = arg & 1;
22222176   int bit = 0;
22232177
r26122r26123
22402194      } else {
22412195         LOG((LOG_DISK,7,"   BITCLK#%d bit:%d (write) @%lldns\n", arg, bit, ntime()));
22422196         if (clk)
2243            drive_wrdata(m_dsk.drive, arg, bit);
2197            dhd->wr_data(arg, bit);
22442198         else
2245            drive_wrdata(m_dsk.drive, arg, 1);
2199            dhd->wr_data(arg, 1);
22462200      }
22472201   } else if (GET_KCOM_BCLKSRC(m_dsk.kcom)) {
22482202      /* always select the crystal clock */
2249      bit = drive_rddata(m_dsk.drive, arg);
2203      bit = dhd->rd_data(arg);
22502204      LOG((LOG_DISK,7,"   BITCLK#%d bit:%d (read, crystal) @%lldns\n", arg, bit, ntime()));
22512205      kwd_timing(clk, bit, 0);
22522206   } else {
r26122r26123
22542208      if (GET_KCOM_XFEROFF(m_dsk.kcom)) {
22552209         bit = 1;
22562210      } else {
2257         clk = drive_rdclk(m_dsk.drive, arg);
2258         bit = drive_rddata(m_dsk.drive, arg);
2211         clk = dhd->rd_clock(arg & ~1);
2212         bit = dhd->rd_data(arg | 1);
22592213         LOG((LOG_DISK,7,"   BITCLK#%d bit:%d (read, driveclk) @%lldns\n", arg, bit, ntime()));
22602214      }
22612215      kwd_timing(clk, bit, 0);
r26122r26123
22632217
22642218   /* more bits to clock? */
22652219   if (++arg < bits) {
2266      m_dsk.bitclk_timer->adjust(drive_bit_time(m_dsk.drive), arg);
2220      m_dsk.bitclk_timer->adjust(dhd->bit_time(), arg);
22672221      m_dsk.bitclk_timer->enable();
22682222   }
22692223}
r26122r26123
22752229 */
22762230void alto2_cpu_device::disk_sector_start(int unit)
22772231{
2232   diablo_hd_device* dhd = m_drive[unit];
22782233   if (m_dsk.bitclk_timer) {
22792234      LOG((LOG_DISK,0,"   unit #%d stop bitclk\n", m_dsk.bitclk));
22802235      m_dsk.bitclk_timer->enable(false);
22812236   }
22822237
22832238   /* KSTAT[0-3] update the current sector in the kstat field */
2284   PUT_KSTAT_SECTOR(m_dsk.kstat, drive_sector(unit) ^ DIABLO_DRIVE_SECTOR_MASK);
2239   PUT_KSTAT_SECTOR(m_dsk.kstat, dhd->get_sector());
22852240
22862241   /* clear input and output shift registers (?) */
22872242   m_dsk.shiftin = 0;
r26122r26123
23442299   m_dsk.ok_to_run_timer->adjust(attotime::from_nsec(15 * ALTO2_UCYCLE), 1);
23452300
23462301   /* install a callback to be called whenever a drive sector starts */
2347   drive_sector_callback(&alto2_cpu_device::disk_sector_start);
2302//   diablo_hd_device* dhd = m_drive[m_dsk.drive];
2303//   dhd->sector_callback(&alto2_cpu_device::disk_sector_start);
23482304}
23492305
branches/alto2/src/emu/cpu/alto2/alto2.c
r26122r26123
9595   m_hw(),
9696   m_mouse(),
9797   m_drive(),
98   m_unit_selected(0),
99   m_head_selected(0),
100   m_sector_callback(),
101   m_sector_timer(0),
10298   m_dsk(),
10399   m_sysclka0(),
104100   m_sysclka1(),
r26122r26123
934930   save_item(NAME(m_mouse.dx));
935931   save_item(NAME(m_mouse.dy));
936932   save_item(NAME(m_mouse.latch));
937   save_item(NAME(m_unit_selected));
938   save_item(NAME(m_head_selected));
939933   save_item(NAME(m_dsk.drive));
940934   save_item(NAME(m_dsk.kaddr));
941935   save_item(NAME(m_dsk.kadr));
branches/alto2/src/emu/cpu/alto2/alto2.h
r26122r26123
1515#ifndef _CPU_ALTO2_H_
1616#define _CPU_ALTO2_H
1717
18#include "machine/diablo_hd.h"
19
1820#define   ALTO2_TAG "alto2"
1921
2022#ifndef   ALTO2_DEBUG
r26122r26123
13241326   // ************************************************
13251327   // diablo31 drive stuff
13261328   // ************************************************
1327   #define   DIABLO31 1
1328   static const int DIABLO_DRIVE_MAX = 2;               //!< max number of drive units
1329   static const int DIABLO_DRIVE_CYLINDERS = 203;         //!< number of cylinders per drive
1330   static const int DIABLO_DRIVE_CYLINDER_MASK = 0777;      //!< bit maks for cylinder number (9 bits)
1331   static const int DIABLO_DRIVE_SPT = 12;               //!< number of sectors per track
1332   static const int DIABLO_DRIVE_SECTOR_MASK = 017;      //!< bit maks for cylinder number (4 bits)
1333   static const int DIABLO_DRIVE_HEADS = 2;            //!< number of heads per drive
1334   static const int DIABLO_DRIVE_PAGES = 203*2*12;         //!< number of pages per drive
1335   static const int DIABLO_DRIVE_HEAD_MASK = 1;         //!< bit maks for cylinder number (4 bits)
1336   //! convert a cylinder/head/sector to a logical block address (page)
1337   static inline int DRIVE_PAGE(int c,int h,int s)   { return (c * DIABLO_DRIVE_HEADS + h) * DIABLO_DRIVE_SPT + s; }
1338   /**
1339    * @brief description of the sector layout
1340    * <PRE>
1341    *
1342    *                                   xx.x msec sector mark pulses
1343    * -+   +-------------------------------------------------------------------------------+   +--
1344    *  |   |                                                                               |   |
1345    *  +---+                                                                               +---+
1346    *
1347    *    |                                                                                   |
1348    *
1349    *    +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
1350    *    | PRE- |SYNC|HEADER|CKSUM| PRE- |SYNC| LABEL |CKSUM| PRE- |SYNC| DATA  |CKSUM| POST |
1351    *    |AMBLE1|  1 |      |  1  |AMBLE2|  2 |       |  2  |AMBLE3|  3 |       |  3  |AMBLE |
1352    *    +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
1353    *
1354    *    |                                                                                   |
1355    *
1356    *    +-----------------------------------------------------------------------------------+
1357    *    |                                                                                   |
1358    * ---+                                                                                   +----
1359    *      FORMAT WRITE GATE FOR INITIALIZING
1360    *    |                                                                                   |
1361    *
1362    *    |                                                    +------------------------------+
1363    *                                                         |                              |
1364    * ---|----------------------------------------------------+                              +----
1365    *      WRITE GATE FOR DATA XFER (*)
1366    *    |                                                                                   |
1367    *
1368    *    |                          +-----------------------+-+------------------------------+
1369    *                               |                       | | may be continuous (?)        |
1370    * ------------------------------+                       +-+                              +----
1371    * ???  WRITE GATE FOR LABEL AND DATA XFER (*)
1372    *    |                                                                                   |
1373    *
1374    *    |   +--------------------+   +---------------------+   +----------------------------+
1375    *        |                    |   |                     |   |                            |
1376    * -------+                    +---+                     +---+                            +----
1377    *      READ GATE FOR INITIALIZING OR DATA XFER (**)
1378    *
1379    *
1380    *  (*) Enable should be delayed 1 byte/word time from last bit of checks sum.
1381    *  (**) Read Gate should be enabled half way through the preamble area. This
1382    *       ensures reading a zero field for data separator synchronization.
1383    *
1384    * </PRE>
1385    */
1386   static const int DIABLO_PAGENO_WORDS = 1;      //!< number of words in a page number (this doesn't really belong here)
1387   static const int DIABLO_HEADER_WORDS = 2;      //!< number of words in a header (this doesn't really belong here)
1388   static const int DIABLO_LABEL_WORDS = 8;      //!< number of words in a label (this doesn't really belong here)
1389   static const int DIABLO_DATA_WORDS = 256;      //!< number of data words (this doesn't really belong here)
1390   static const int DIABLO_CKSUM_WORDS = 1;      //!< number of words for a checksum (this doesn't really belong here)
1329   diablo_hd_device* m_drive[2];               //!< two diablo drives
1330   void init_drive();                        //!< initialize the disk drives context
13911331
1392   #if   DIABLO31
1393   /** @brief DIABLO 31 rotation time is approx. 40ms */
1394   #define   DIABLO_ROTATION_TIME attotime::from_msec(39.9999)
1395
1396   /** @brief DIABLO 31 sector time */
1397   #define   DIABLO_SECTOR_TIME attotime::from_msec(39.9999/DIABLO_DRIVE_SPT)
1398
1399   /** @brief DIABLO 31 bit clock is 3330kHz ~= 300ns per bit
1400    * ~= 133333 bits/track (?)
1401    * ~= 11111 bits/sector
1402    * ~= 347 words/sector
1403    */
1404   #define   DIABLO_BIT_TIME(bits) attotime::from_nsec(300*(bits))
1405
1406   /** @brief DIABLO 31 possible sector words */
1407   #define   DIABLO_SECTOR_WORDS 347
1408
1409   /** @brief pulse width of sector mark before the next sector begins */
1410   #define   DIABLO_SECTOR_MARK_PULSE_PRE DIABLO_BIT_TIME(16)
1411
1412   /** @brief pulse width of sector mark after the next sector began */
1413   #define   DIABLO_SECTOR_MARK_PULSE_POST DIABLO_BIT_TIME(16)
1414
1415   #else   /* DIABLO31 */
1416
1417   /** @brief DIABLO 44 rotation time is approx. 25ms */
1418   #define   DIABLO_ROTATION_TIME attotime::from_msec(25)
1419
1420   /** @brief DIABLO 44 sector time */
1421   #define   DIABLO_SECTOR_TIME   attotime::from_msec(25/DIABLO_DRIVE_SPT)
1422
1423   /** @brief DIABLO 44 bit clock is 5000kHz ~= 200ns per bit
1424    * ~= 125184 bits/track (?)
1425    * ~= 10432 bits/sector
1426    * ~= 325 words/sector
1427    */
1428   #define   DIABLO_BIT_TIME(bits) attotime::from_nsec(200*(bits))
1429
1430   /** @brief DIABLO 44 possible sector words */
1431   #define   DIABLO_SECTOR_WORDS   325
1432
1433   /** @brief pulse width of sector mark before the next sector begins */
1434   #define   DIABLO_SECTOR_MARK_PULSE_PRE DIABLO_BIT_TIME(16)
1435
1436   /** @brief pulse width of sector mark after the next sector began */
1437   #define   DIABLO_SECTOR_MARK_PULSE_POST DIABLO_BIT_TIME(16)
1438   #endif
1439
1440   /**
1441    * @brief format of the cooked disk image sectors, i.e. pure data
1442    *
1443    * The available images are a multiple of 267 words per sector,
1444    * 1 word page number
1445    * 2 words header
1446    * 8 words label
1447    * 256 words data
1448    */
1449   typedef struct {
1450      UINT8 pageno[2*DIABLO_PAGENO_WORDS];   //!< sector page number
1451      UINT8 header[2*DIABLO_HEADER_WORDS];   //!< sector header words
1452      UINT8 label[2*DIABLO_LABEL_WORDS];      //!< sector label words
1453      UINT8 data[2*DIABLO_DATA_WORDS];      //!< sector data words
1454   }   diablo_sector_t;
1455
1456   /**
1457    * @brief Structure of the disk drive context (2 drives or packs per system)
1458    */
1459   typedef struct {
1460      diablo_sector_t *image;      //!< disk image, made up of 203 x 2 x 12 sectors
1461      int unit;               //!< drive unit number (0 or 1)
1462      char description[32];      //!< description of the drive(s)
1463      char basename[80];         //!< basename of the drive image
1464      int packs;               //!< number of packs in drive (1 or 2)
1465      attotime rotation_time;      //!< rotation time
1466      attotime bit_time;         //!< bit time in atto seconds
1467      int s_r_w_0;            //!< drive seek/read/write signal (active 0)
1468      int ready_0;            //!< drive ready signal (active 0)
1469      int sector_mark_0;         //!< sector mark (0 if new sector)
1470      int addx_acknowledge_0;      //!< address acknowledge, i.e. seek successful (active 0)
1471      int log_addx_interlock_0;   //!< log address interlock, i.e. seek in progress (active 0)
1472      int seek_incomplete_0;      //!< seek incomplete, i.e. seek in progress (active 0)
1473      int egate_0;            //!< erase gate
1474      int wrgate_0;            //!< write gate
1475      int rdgate_0;            //!< read gate
1476      int cylinder;            //!< current cylinder number
1477      int head;               //!< current head (track) number on cylinder
1478      int sector;               //!< current sector number in track
1479      UINT32 *bits[DIABLO_DRIVE_CYLINDERS * DIABLO_DRIVE_HEADS * DIABLO_DRIVE_SPT];      //!< sectors expanded to bits
1480      int page;               //!< current page = (cylinder * HEADS + head) * SPT + sector
1481      int rdfirst;            //!< set to first bit of a sector that is read from
1482      int rdlast;               //!< set to last bit of a sector that was read from
1483      int wrfirst;            //!< set to non-zero if a sector is written to
1484      int wrlast;               //!< set to last bit of a sector that was written to
1485   }   diablo_drive_t;
1486
1487   diablo_drive_t* m_drive[2];                     //!< per drive data
1488   int m_unit_selected;                        //!< selected drive unit
1489   int m_head_selected;                        //!< selected drive head
1490   a2cb m_sector_callback;                        //!< callback to call at the start of each sector
1491   emu_timer* m_sector_timer;                     //!< sector timer
1492#if   ALTO2_DEBUG
1493   void drive_dump_ascii(UINT8 *src, size_t size);
1494   size_t dump_record(UINT8 *src, size_t addr, size_t size, const char *name, int cr);
1495#endif
1496   void drive_get_sector(int unit);               //!< calculate the sector from the logical block address
1497   void expand_sector(int unit, int page);            //!< Expand a sector into an array of clock and data bits
1498   size_t squeeze_sync(UINT32 *bits, size_t src, size_t size);
1499   size_t squeeze_unsync(UINT32 *bits, size_t src, size_t size);
1500   size_t squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size);
1501   size_t squeeze_cksum(UINT32 *bits, size_t src, int *cksum);
1502   void squeeze_sector(int unit);                  //!< Squeeze a array of clock and data bits into a sector's data
1503   int drive_bits_per_sector() const;               //!< return number of bitclk edges for a sector
1504   const char* drive_description(int unit);         //!< return a pointer to a drive's description
1505   const char* drive_basename(int unit);            //!< return a pointer to a drive's image basename
1506   int drive_unit(int unit);                     //!< return the number of a drive unit
1507   attotime drive_rotation_time(int unit);            //!< return the atto time for a full rotation
1508   attotime drive_bit_time(int unit);               //!< return the atto time for a data bit
1509   int drive_seek_read_write_0(int unit);            //!< return the seek/read/write status of a drive
1510   int drive_ready_0(int unit);                  //!< return the ready status of a drive
1511   int drive_sector_mark_0(int unit);               //!< return the current sector mark status of a drive
1512   int drive_addx_acknowledge_0(int unit);            //!< return the address acknowledge state
1513   int drive_log_addx_interlock_0(int unit);         //!< return the log address interlock state
1514   int drive_seek_incomplete_0(int unit);            //!< return the seek incomplete state
1515   int drive_cylinder(int unit);                  //!< return the current cylinder of a drive unit
1516   int drive_head(int unit);                     //!< return the current head of a drive unit
1517   int drive_sector(int unit);                     //!< return the current sector of a drive unit
1518   int drive_page(int unit);                     //!< return the current page of a drive unit
1519   void drive_select(int unit, int head);            //!< select a drive unit and head
1520   void drive_sector_callback(a2cb callback);         //!< set a new drive sector callback
1521   void drive_strobe(int unit, int cylinder, int restore, int strobe);   //!< strobe a seek operation
1522   void drive_egate(int unit, int gate);            //!< set the drive erase gate
1523   void drive_wrgate(int unit, int gate);            //!< set the drive write gate
1524   void drive_rdgate(int unit, int gate);            //!< set the drive read gate
1525   void drive_wrdata(int unit, int index, int wrdata);   //!< write the sector relative bit at index
1526   int drive_rddata(int unit, int index);            //!< read the sector relative bit at index
1527   int drive_rdclk(int unit, int index);            //!< get the sector relative clock at index
1528   int debug_read_sync(int unit, int page, int offs);   //!< debug function to read sync bit position from a sector
1529   int debug_read_sec(int unit, int page, int offs);   //!< debug function to read bits from a drive sector
1530   TIMER_CALLBACK_MEMBER( drive_next_sector );         //!< timer callback that is called once per sector in the rotation
1531   void sector_mark_1(int unit);                  //!< deasserts the sector mark
1532   void sector_mark_0(int unit);                  //!< asserts the sector mark
1533   void drive_init();                           //!< initialize the disk drive context and insert a disk word timer
1534
15351332   // ************************************************
15361333   // disk controller stuff
15371334   // ************************************************
branches/alto2/src/emu/cpu/cpu.mak
r26122r26123
22772277ifneq ($(filter ALTO2,$(CPUS)),)
22782278OBJDIRS += $(CPUOBJ)/alto2
22792279CPUOBJS += $(CPUOBJ)/alto2/alto2.o \
2280   $(CPUOBJ)/alto2/a2drive.o \
22812280   $(CPUOBJ)/alto2/a2disk.o \
22822281   $(CPUOBJ)/alto2/a2disp.o \
22832282   $(CPUOBJ)/alto2/a2curt.o \
r26122r26123
23012300$(CPUOBJ)/alto2/alto2.o:    $(CPUSRC)/alto2/alto2.c \
23022301                     $(CPUSRC)/alto2/alto2.h
23032302
2304$(CPUOBJ)/alto2/a2drive.o:  $(CPUSRC)/alto2/a2drive.c \
2305                     $(CPUSRC)/alto2/alto2.h
2306
23072303$(CPUOBJ)/alto2/a2disk.o:   $(CPUSRC)/alto2/a2disk.c \
23082304                     $(CPUSRC)/alto2/alto2.h
23092305
r26122r26123
23552351
23562352$(CPUOBJ)/alto2/alto2dsm.o: $(CPUSRC)/alto2/alto2dsm.c \
23572353                     $(CPUSRC)/alto2/alto2.h
2354
branches/alto2/src/emu/imagedev/diablo.c
r26122r26123
1/*********************************************************************
1/**********************************************************
2 *   Portable DIABLO drive image to hard disk interface
3 *
4 *   Copyright: Juergen Buchmueller <pullmoll@t-online.de>
5 *
6 *   Licenses: MAME, GPLv2
7 **********************************************************/
28
3   Code to interface the image code with harddisk core.
4
5   Raphael Nabet 2003
6
7   Update: 23-Feb-2004 - Unlike floppy disks, for which we support
8   myriad formats on many systems, it is my intention for MESS to
9   standardize on the CHD file format for hard drives so I made a few
10   changes to support this
11
12*********************************************************************/
13
149#include "emu.h"
1510#include "emuopts.h"
1611#include "harddisk.h"
branches/alto2/src/mess/mess.mak
r26122r26123
21012101   $(MESS_DRIVERS)/xerox820.o  \
21022102   $(MESS_DRIVERS)/bigbord2.o  \
21032103   $(MESS_DRIVERS)/alto2.o     \
2104   $(MESS_MACHINE)/diablo_hd.o   \
21042105
21052106$(MESSOBJ)/yamaha.a:            \
21062107   $(MESS_DRIVERS)/ymmu100.o   \
branches/alto2/src/mess/machine/diablo_hd.c
r0r26123
1/**********************************************************
2 *   DIABLO31 and DIABLO44 hard drive support
3 *
4 *   Copyright: Juergen Buchmueller <pullmoll@t-online.de>
5 *
6 *   Licenses: MAME, GPLv2
7 **********************************************************/
8
9#include "diablo_hd.h"
10
11/**
12 *
13 * Just for completeness' sake:
14 * The mapping of disk controller connector P2 pins to the
15 * Winchester disk drive signals (see drive.h)
16 * <PRE>
17 * Alto Controller     Winchester
18 * P2 signal           disk bus
19 * -----------------------------------------------
20 *  1 GND              D_GROUND
21 *  2 RDCLK'           A_READ_CLOCK
22 *  3 WRDATA'          B_WRITE_DATA_AND_CLOCK
23 *  4 SRWRDY'          F_S_R_W
24 *  5 DISK             L_SELECT_LINE_UNIT_1
25 *  6 CYL(7)'          N_CYL_7
26 *  7 DISK'            R_SELECT_LINE_UNIT_2
27 *  8 CYL(2)'          T_CYL_2
28 *  9 ???              V_SELECT_LINE_UNIT_3
29 * 10 CYL(4)'          X_CYL_4
30 * 11 CYL(0)'          Z_CYL_0
31 * 12 CYL(1)'          BB_CYL_1
32 * 13 CYL(3)'          FF_CYL_3
33 * 14 ???              KK_BIT_2
34 * 15 CYL(8)'          LL_CYL_8
35 * 16 ADRACK'          NN_ADDX_ACKNOWLEDGE
36 * 17 SKINC'           TT_SEEK_INCOMPLETE
37 * 18 LAI'             XX_LOG_ADDX_INTERLOCK
38 * 19 CYL(6)'          RR_CYL_6
39 * 20 RESTOR'          VV_RESTORE
40 * 21 ???              UU_BIT_16
41 * 22 STROBE'          SS_STROBE
42 * 23 ???              MM_BIT_8
43 * 24 ???              KK_BIT_4
44 * 25 ???              HH_WRITE_CHK
45 * 26 WRTGATE'         EE_WRITE_GATE
46 * 27 ???              CC_BIT_SECTOR_ADDX
47 * 28 HEAD'            AA_HEAD_SELECT
48 * 29 ???              Y_INDEX_MARK
49 * 30 SECT(4)'         W_SECTOR_MARK
50 * 31 READY'           U_FILE_READY
51 * 32 ???              S_PSEUDO_SECTOR_MARK
52 * 33 ???              P_WRITE_PROTECT_IND
53 * 34 ???              H_WRITE_PROTECT_INPUT_ATTENTION
54 * 35 ERGATE'          K_ERASE_GATE
55 * 36 ???              M_HIGH_DENSITY
56 * 37 CYL(5)'          J_CYL_5
57 * 38 RDDATA'          C_READ_DATA
58 * 39 RDGATE'          E_READ_GATE
59 * 40 GND              ??
60 * </PRE>
61 */
62
63diablo_hd_device::diablo_hd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
64   device_t(mconfig, DIABLO_HD, "Diablo Disk", tag, owner, clock, "diablo_hd", __FILE__),
65#if   DIABLO_DEBUG
66   m_log_level(9),
67#endif
68   m_diablo31(true),
69   m_unit(0),
70   m_description(),
71   m_packs(1),
72   m_rotation_time(),
73   m_sector_time(),
74   m_sector_mark_0_time(),
75   m_sector_mark_1_time(),
76   m_bit_time(),
77   m_s_r_w_0(1),
78   m_ready_0(1),
79   m_sector_mark_0(1),
80   m_addx_acknowledge_0(1),
81   m_log_addx_interlock_0(1),
82   m_seek_incomplete_0(1),
83   m_egate_0(1),
84   m_wrgate_0(1),
85   m_rdgate_0(1),
86   m_cylinder(-1),
87   m_head(-1),
88   m_sector(-1),
89   m_page(-1),
90   m_image(),
91   m_bits(),
92   m_rdfirst(-1),
93   m_rdlast(-1),
94   m_wrfirst(-1),
95   m_wrlast(-1),
96   m_sector_callback(0),
97   m_sector_timer(0),
98   m_drive(0)
99{
100}
101
102#if   DIABLO_DEBUG
103void alto2_cpu_device::logprintf(int level, const char* format, ...)
104{
105   if (level > m_log_level)
106      return;
107   va_list ap;
108   va_start(ap, format);
109   debug_console_vprintf(machine(), format, ap);
110   va_end(ap);
111}
112#endif
113
114#define   DIABLO31_ROTATION_TIME attotime::from_msec(39.9999)      //!< DIABLO 31 rotation time is approx. 40ms
115#define   DIABLO31_SECTOR_TIME attotime::from_msec(39.9999/12)   //!< DIABLO 31 sector time
116/**
117 * @brief DIABLO 31 bit clock is 3330kHz ~= 300ns per bit
118 * ~= 133333 bits/track (?)
119 * ~= 11111 bits/sector
120 * ~= 347 words/sector
121 */
122#define   DIABLO31_BIT_TIME(bits) attotime::from_nsec(300*(bits))
123#define   DIABLO31_SECTOR_WORDS 347                        //!< DIABLO 31 possible sector words
124#define   DIABLO31_SECTOR_MARK_PULSE_PRE DIABLO31_BIT_TIME(16)   //!< pulse width of sector mark before the next sector begins
125#define   DIABLO31_SECTOR_MARK_PULSE_POST DIABLO31_BIT_TIME(16)   //!< pulse width of sector mark after the next sector began
126
127#define   DIABLO44_ROTATION_TIME attotime::from_msec(25)         //!< DIABLO 44 rotation time is approx. 25ms
128#define   DIABLO44_SECTOR_TIME   attotime::from_msec(25/12)      //!< DIABLO 44 sector time
129
130/**
131 * @brief DIABLO 44 bit clock is 5000kHz ~= 200ns per bit
132 * ~= 125184 bits/track (?)
133 * ~= 10432 bits/sector
134 * ~= 325 words/sector
135 */
136#define   DIABLO44_BIT_TIME(bits) attotime::from_nsec(200*(bits))
137#define   DIABLO44_SECTOR_WORDS   325                        //!< DIABLO 44 possible sector words
138#define   DIABLO44_SECTOR_MARK_PULSE_PRE DIABLO44_BIT_TIME(16)   //!< pulse width of sector mark before the next sector begins
139#define   DIABLO44_SECTOR_MARK_PULSE_POST DIABLO44_BIT_TIME(16)   //!< pulse width of sector mark after the next sector began
140
141#define   MFROBL         34      //!< from the microcode: disk header preamble is 34 words
142#define   MFRRDL         21      //!< from the microcode: disk header read delay is 21 words
143#define   MIRRDL         4      //!< from the microcode: interrecord read delay is 4 words
144#define   MIROBL         3      //!< from the microcode: disk interrecord preamble is 3 words
145#define   MRPAL         3      //!< from the microcode: disk read postamble length is 3 words
146#define   MWPAL         5      //!< from the microcode: disk write postamble length is 5 words
147
148#define   GUARD_ZONE_BITS   (16*32)   //!< end of the guard zone at the beginning of a sector (wild guess!)
149
150/**
151 * @brief description of the sector layout
152 * <PRE>
153 *
154 *                                   xx.x msec sector mark pulses
155 * -+   +-------------------------------------------------------------------------------+   +--
156 *  |   |                                                                               |   |
157 *  +---+                                                                               +---+
158 *
159 *    |                                                                                   |
160 *
161 *    +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
162 *    | PRE- |SYNC|HEADER|CKSUM| PRE- |SYNC| LABEL |CKSUM| PRE- |SYNC| DATA  |CKSUM| POST |
163 *    |AMBLE1|  1 |      |  1  |AMBLE2|  2 |       |  2  |AMBLE3|  3 |       |  3  |AMBLE |
164 *    +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
165 *
166 *    |                                                                                   |
167 *
168 *    +-----------------------------------------------------------------------------------+
169 *    |                                                                                   |
170 * ---+                                                                                   +----
171 *      FORMAT WRITE GATE FOR INITIALIZING
172 *    |                                                                                   |
173 *
174 *    |                                                    +------------------------------+
175 *                                                         |                              |
176 * ---|----------------------------------------------------+                              +----
177 *      WRITE GATE FOR DATA XFER (*)
178 *    |                                                                                   |
179 *
180 *    |                          +-----------------------+-+------------------------------+
181 *                               |                       | | may be continuous (?)        |
182 * ------------------------------+                       +-+                              +----
183 * ???  WRITE GATE FOR LABEL AND DATA XFER (*)
184 *    |                                                                                   |
185 *
186 *    |   +--------------------+   +---------------------+   +----------------------------+
187 *        |                    |   |                     |   |                            |
188 * -------+                    +---+                     +---+                            +----
189 *      READ GATE FOR INITIALIZING OR DATA XFER (**)
190 *
191 *
192 *  (*) Enable should be delayed 1 byte/word time from last bit of checks sum.
193 *  (**) Read Gate should be enabled half way through the preamble area. This
194 *       ensures reading a zero field for data separator synchronization.
195 *
196 * </PRE>
197 */
198
199#define   DIABLO_PAGENO_WORDS   1      //!< number of words in a page number (this doesn't really belong here)
200#define   DIABLO_HEADER_WORDS   2      //!< number of words in a header (this doesn't really belong here)
201#define   DIABLO_LABEL_WORDS   8      //!< number of words in a label (this doesn't really belong here)
202#define   DIABLO_DATA_WORDS   256      //!< number of data words (this doesn't really belong here)
203#define   DIABLO_CKSUM_WORDS   1      //!< number of words for a checksum (this doesn't really belong here)
204
205/**
206 * @brief format of the cooked disk image sectors, i.e. pure data
207 *
208 * The available images are a multiple of 267 words (534 bytes) per sector,
209 * 1 word page number
210 * 2 words header
211 * 8 words label
212 * 256 words data
213 */
214typedef struct {
215   UINT8 pageno[2*DIABLO_PAGENO_WORDS];   //!< sector page number
216   UINT8 header[2*DIABLO_HEADER_WORDS];   //!< sector header words
217   UINT8 label[2*DIABLO_LABEL_WORDS];      //!< sector label words
218   UINT8 data[2*DIABLO_DATA_WORDS];      //!< sector data words
219}   diablo_sector_t;
220
221/** @brief write a bit into an array of UINT32 */
222static inline size_t WRBIT(UINT32* bits, size_t dst, int bit)
223{
224   if (bit) {
225      bits[(dst)/32] |= 1 << ((dst) % 32);
226   } else {
227      bits[(dst)/32] &= ~(1 << ((dst) % 32));
228   }
229   return ++dst;
230}
231
232/** @brief read a bit from an array of UINT32 */
233static inline size_t RDBIT(UINT32* bits, size_t src, int& bit)
234{
235   bit = (bits[src/32] >> (src % 32)) & 1;
236   return ++src;
237}
238
239/**
240 * @brief calculate the sector from the logical block address and read it
241 *
242 * Modifies drive's page by calculating the logical
243 * block address from cylinder, head, and sector.
244 */
245void diablo_hd_device::read_sector()
246{
247   /* If there's no drive, just reset the page number */
248   if (!m_drive) {
249      m_page = -1;
250      return;
251   }
252   if (m_cylinder < 0 || m_cylinder >= DIABLO_CYLINDERS) {
253      LOG_DRIVE((9,"   DRIVE C/H/S:%d/%d/%d => invalid cylinder\n", m_cylinder, m_head, m_sector));
254      m_page = -1;
255      return;
256   }
257   if (m_head < 0 || m_head >= DIABLO_HEADS) {
258      LOG_DRIVE((9,"   DRIVE C/H/S:%d/%d/%d => invalid head\n", m_cylinder, m_head, m_sector));
259      m_page = -1;
260      return;
261   }
262   if (m_sector < 0 || m_sector >= DIABLO_SPT) {
263      LOG_DRIVE((9,"   DRIVE C/H/S:%d/%d/%d => invalid sector\n", m_cylinder, m_head, m_sector));
264      m_page = -1;
265      return;
266   }
267   /* calculate the new disk relative sector offset */
268   m_page = DRIVE_PAGE(m_cylinder, m_head, m_sector);
269   LOG_DRIVE((9,"   DRIVE C/H/S:%d/%d/%d => page:%d\n", m_cylinder, m_head, m_sector, m_page));
270
271   // already have the sector image?
272   if (m_image[m_page])
273      return;
274
275   hard_disk_file *file = m_drive->get_hard_disk_file();
276   if (!file) {
277      LOG_DRIVE((0,"   no file for unit #%d\n", m_unit));
278      return;
279   }
280
281   /* allocate a sector buffer */
282   m_image[m_page] = global_alloc_array(UINT8, sizeof(diablo_sector_t));
283   if (!hard_disk_read(file, m_page, m_image[m_page])) {
284      LOG_DRIVE((0,"   read failed for #%d page #%d\n", m_unit, m_page));
285      global_free(m_image[m_page]);
286      m_image[m_page] = 0;
287      return;
288   }
289}
290
291/**
292 * @brief compute the checksum of a record
293 *
294 * @param src pointer to a record (header, label, data)
295 * @param size size of the record in bytes
296 * @param start start value for the checksum
297 * @return returns the checksum of the record
298 */
299int diablo_hd_device::cksum(UINT8 *src, size_t size, int start)
300{
301   int sum = start;
302   /* compute XOR of all words */
303   for (size_t offs = 0; offs < size; offs += 2) {
304      int word = src[size - 2 - offs] + 256 * src[size - 2 - offs + 1];
305      sum ^= word;
306   }
307   return sum;
308}
309
310/**
311 * @brief expand a series of clock bits and 0 data bits
312 *
313 * @param bits pointer to the sector bits
314 * @param dst destination offset into bits (bit number)
315 * @param size number of words to write
316 * @return offset to next destination bit
317 */
318size_t diablo_hd_device::expand_zeroes(UINT32 *bits, size_t dst, size_t size)
319{
320   for (size_t offs = 0; offs < 32 * size; offs += 2) {
321      dst = WRBIT(bits, dst, 1);      // write the clock bit
322      dst = WRBIT(bits, dst, 0);      // write the 0 data bit
323   }
324   return dst;
325}
326
327/**
328 * @brief expand a series of 0 words and write a final sync bit
329 *
330 * @param bits pointer to the sector bits
331 * @param dst destination offset into bits (bit number)
332 * @param size number of words to write
333 * @return offset to next destination bit
334 */
335size_t diablo_hd_device::expand_sync(UINT32 *bits, size_t dst, size_t size)
336{
337   for (size_t offs = 0; offs < 32 * size - 2; offs += 2) {
338      dst = WRBIT(bits, dst, 1);      // write the clock bit
339      dst = WRBIT(bits, dst, 0);      // write the 0 data bit
340   }
341   dst = WRBIT(bits, dst, 1);   // write the final clock bit
342   dst = WRBIT(bits, dst, 1);   // write the 1 data bit
343   return dst;
344}
345
346/**
347 * @brief expand a record of words into a array of bits at dst
348 *
349 * @param bits pointer to the sector bits
350 * @param dst destination offset into bits (bit number)
351 * @param field pointer to the record data (bytes)
352 * @param size size of the record in bytes
353 * @return offset to next destination bit
354 */
355size_t diablo_hd_device::expand_record(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
356{
357   for (size_t offs = 0; offs < size; offs += 2) {
358      int word = field[size - 2 - offs] + 256 * field[size - 2 - offs + 1];
359      for (size_t bit = 0; bit < 16; bit++) {
360         dst = WRBIT(bits, dst, 1);               // write the clock bit
361         dst = WRBIT(bits, dst, (word >> 15) & 1);   // write the data bit
362         word <<= 1;
363      }
364   }
365   return dst;
366}
367
368/**
369 * @brief Expand a record's checksum word to 32 bits
370 *
371 * @param bits pointer to the sector bits
372 * @param dst destination offset into bits (bit number)
373 * @param field pointer to the record data (bytes)
374 * @param size size of the record in bytes
375 * @return offset to next destination bit
376 */
377size_t diablo_hd_device::expand_cksum(UINT32 *bits, size_t dst, UINT8 *field, size_t size)
378{
379   int word = cksum(field, size, 0521);
380   for (size_t bit = 0; bit < 32; bit += 2) {
381      dst = WRBIT(bits, dst, 1);            // write the clock bit
382      dst = WRBIT(bits, dst, (word >> 15) & 1);   // write the data bit
383      word <<= 1;
384   }
385   return dst;
386}
387
388/**
389 * @brief Expand a sector into an array of clock and data bits
390 *
391 * @param page page number (0 to DRIVE_PAGES-1)
392 */
393UINT32* diablo_hd_device::expand_sector()
394{
395   size_t dst;
396
397   /* already expanded this sector? */
398   if (m_bits[m_page])
399      return m_bits[m_page];
400
401   /* allocate a sector buffer */
402   if (!m_image[m_page]) {
403      LOG_DRIVE((0,"   no image for #%d page #%d\n", m_unit, m_page));
404      return NULL;
405   }
406   diablo_sector_t *s = reinterpret_cast<diablo_sector_t *>(m_image[m_page]);
407
408   /* allocate a bits image */
409   UINT32 *bits = reinterpret_cast<UINT32 *>(global_alloc_array(UINT32, 400));
410
411   if (m_diablo31) {
412      /* write sync bit after 31 words - 1 bit */
413      dst = expand_sync(bits, 0, 31);
414      dst = expand_record(bits, dst, s->header, sizeof(s->header));
415      dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
416
417      /* write sync bit after 2 * 5 + 1 words - 1 bit */
418      dst = expand_sync(bits, dst, 2 * MWPAL);
419      dst = expand_record(bits, dst, s->label, sizeof(s->label));
420      dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
421
422      /* write sync bit after 2 * 5 + 1 words - 1 bit */
423      dst = expand_sync(bits, dst, 2 * MWPAL);
424      dst = expand_record(bits, dst, s->data, sizeof(s->data));
425      dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
426
427      /* fill MWPAL words of clock and 0 data bits */
428      dst = expand_zeroes(bits, dst, MWPAL);
429   } else {
430      /* write sync bit after 31 words - 1 bit */
431      dst = expand_sync(bits, 0, 31);
432      dst = expand_record(bits, dst, s->header, sizeof(s->header));
433      dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
434
435      /* write sync bit after 2 * 5 words - 1 bit */
436      dst = expand_sync(bits, dst, 2 * MWPAL);
437      dst = expand_record(bits, dst, s->label, sizeof(s->label));
438      dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
439
440      /* write sync bit after 2 * 5 words - 1 bit */
441      dst = expand_sync(bits, dst, 2 * MWPAL);
442      dst = expand_record(bits, dst, s->data, sizeof(s->data));
443      dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
444
445      /* fill MWPAL words of clock and 0 data bits */
446      dst = expand_zeroes(bits, dst, MWPAL);
447   }
448   m_bits[m_page] = bits;
449
450   LOG_DRIVE((0,"   BITS #%d: %03d/%d/%02d #%-5d bits (@%03d.%02d)\n",
451      m_unit, m_cylinder, m_head, m_sector,
452      dst, dst / 32, dst % 32));
453
454   return bits;
455}
456
457#if   DIABLO_DEBUG
458void diablo_hd_device::drive_dump_ascii(UINT8 *src, size_t size)
459{
460   size_t offs;
461   LOG_DRIVE((0," ["));
462   for (offs = 0; offs < size; offs++) {
463      char ch = (char)src[offs ^ 1];
464      LOG_DRIVE((0, "%c", ch < 32 || ch > 126 ? '.' : ch));
465   }
466   LOG_DRIVE((0,"]\n"));
467}
468
469
470/**
471 * @brief Dump a record's contents
472 *
473 * @param src pointer to a record (header, label, data)
474 * @param size size of the record in bytes
475 * @param name name to print before the dump
476 */
477size_t diablo_hd_device::dump_record(UINT8 *src, size_t addr, size_t size, const char *name, int cr)
478{
479   size_t offs;
480   LOG_DRIVE((0,"%s:", name));
481   for (offs = 0; offs < size; offs += 2) {
482      int word = src[offs] + 256 * src[offs + 1];
483      if (offs % 16) {
484         LOG_DRIVE((0," %06o", word));
485      } else {
486         if (offs > 0)
487            drive_dump_ascii(&src[offs-16], 16);
488         LOG_DRIVE((0,"\t%05o: %06o", (addr + offs) / 2, word));
489      }
490   }
491   if (offs % 16) {
492      drive_dump_ascii(&src[offs - (offs % 16)], offs % 16);
493   } else {
494      drive_dump_ascii(&src[offs-16], 16);
495   }
496   if (cr) {
497      LOG_DRIVE((0,"\n"));
498   }
499   return size;
500}
501#endif
502
503/**
504 * @brief find a sync bit in an array of clock and data bits
505 *
506 * @param bits pointer to the sector's bits
507 * @param src source offset into bits (bit number)
508 * @param size number of words to scan for a sync word
509 * @return next source offset for reading
510 */
511size_t diablo_hd_device::squeeze_sync(UINT32 *bits, size_t src, size_t size)
512{
513   UINT32 accu = 0;
514   /* hunt for the first 0x0001 word */
515   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
516      /*
517       * accumulate clock and data bits until we are
518       * on the clock bit boundary
519       */
520      int bit;
521      src = RDBIT(bits,src,bit);
522      accu = (accu << 1) | bit;
523      /*
524       * look for 15 alternating clocks and 0-bits
525       * and the 16th clock with a 1-bit
526       */
527      if (accu == 0xaaaaaaab)
528         return src;
529      if (++bitcount == 32) {
530         bitcount = 0;
531         offs++;
532      }
533   }
534   /* return if no sync found within size*32 clock and data bits */
535   LOG_DRIVE((0,"   no sync within %d words\n", size));
536   return src;
537}
538
539/**
540 * @brief find a 16 x 0 bits sequence in an array of clock and data bits
541 *
542 * @param bits pointer to the sector's bits
543 * @param src source offset into bits (bit number)
544 * @param size number of words to scan for a sync word
545 * @return next source offset for reading
546 */
547size_t diablo_hd_device::squeeze_unsync(UINT32 *bits, size_t src, size_t size)
548{
549   UINT32 accu = 0;
550   /* hunt for the first 0 word (16 x 0 bits) */
551   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
552      /*
553       * accumulate clock and data bits until we are
554       * on the clock bit boundary
555       */
556      int bit;
557      src = RDBIT(bits,src,bit);
558      accu = (accu << 1) | bit;
559      /*
560       * look for 16 alternating clocks and 0 data bits
561       */
562      if (accu == 0xaaaaaaaa)
563         return src;
564      if (++bitcount == 32) {
565         bitcount = 0;
566         offs++;
567      }
568   }
569   /* return if no sync found within size*32 clock and data bits */
570   LOG_DRIVE((0,"   no unsync within %d words\n", size));
571   return src;
572}
573
574/**
575 * @brief squeeze an array of clock and data bits into a sector's record
576 *
577 * @param bits pointer to the sector's bits
578 * @param src source offset into bits (bit number)
579 * @param field pointer to the record data (bytes)
580 * @param size size of the record in bytes
581 * @return next source offset for reading
582 */
583size_t diablo_hd_device::squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size)
584{
585   UINT32 accu = 0;
586   for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
587      int bit;
588      src = RDBIT(bits,src,bit);      // skip clock
589      assert(bit == 1);
590      src = RDBIT(bits,src,bit);      // get data bit
591      accu = (accu << 1) | bit;
592      bitcount += 2;
593      if (bitcount == 32) {
594         /* collected a word */
595         field[size - 2 - offs + 0] = accu % 256;
596         field[size - 2 - offs + 1] = accu / 256;
597         offs += 2;
598         bitcount = 0;
599      }
600   }
601   return src;
602}
603
604/**
605 * @brief squeeze an array of 32 clock and data bits into a checksum word
606 *
607 * @param bits pointer to the sector's bits
608 * @param src source offset into bits (bit number)
609 * @param cksum pointer to an int to receive the checksum word
610 * @return next source offset for reading
611 */
612size_t diablo_hd_device::squeeze_cksum(UINT32 *bits, size_t src, int *cksum)
613{
614   UINT32 accu = 0;
615
616   for (size_t bitcount = 0; bitcount < 32; bitcount += 2) {
617      int bit;
618      src = RDBIT(bits,src,bit);      // skip clock
619      assert(bit == 1);
620      src = RDBIT(bits,src,bit);      // get data bit
621      accu = (accu << 1) | bit;
622   }
623
624   /* set the cksum to the extracted word */
625   *cksum = accu;
626   return src;
627}
628
629/**
630 * @brief Squeeze a array of clock and data bits into a sector's data
631 */
632void diablo_hd_device::squeeze_sector()
633{
634   diablo_sector_t *s;
635   size_t src;
636   int cksum_header, cksum_label, cksum_data;
637
638   if (m_rdfirst >= 0) {
639      LOG_DRIVE((0,
640         "   RD #%d %03d/%d/%02d bit#%-5d (@%03d.%02d) ... bit#%-5d (@%03d.%02d)\n",
641         m_unit, m_cylinder, m_head, m_sector,
642         m_rdfirst, m_rdfirst / 32, m_rdfirst % 32,
643         m_rdlast, m_rdlast / 32, m_rdlast % 32));
644   }
645   m_rdfirst = -1;
646   m_rdlast = -1;
647
648   /* not written to, just drop it now */
649   if (m_wrfirst < 0) {
650      m_wrfirst = -1;
651      m_wrlast = -1;
652      return;
653   }
654
655   /* did write into the next sector (?) */
656   if (m_wrlast > m_wrfirst && m_wrlast < 256) {
657      m_wrfirst = -1;
658      m_wrlast = -1;
659      return;
660   }
661
662   if (m_wrfirst >= 0) {
663      LOG_DRIVE((0,
664         "   WR #%d %03d/%d/%02d bit#%-5d (@%03d.%02d) ... bit#%-5d (@%03d.%02d)\n",
665         m_unit, m_cylinder, m_head, m_sector,
666         m_wrfirst, m_wrfirst / 32, m_wrfirst % 32,
667         m_wrlast, m_wrlast / 32, m_wrlast % 32));
668   }
669   m_wrfirst = -1;
670   m_wrlast = -1;
671
672   if (m_page < 0 || m_page >= DIABLO_PAGES) {
673      LOG_DRIVE((0,"   no sector for #%d: %d/%d/%d\n", m_unit, m_cylinder, m_head, m_sector));
674      return;
675   }
676
677   if (!m_image[m_page]) {
678      LOG_DRIVE((0,"   no image for #%d: %d/%d/%d\n", m_unit, m_cylinder, m_head, m_sector));
679      return;
680   }
681
682   /* no bits to write? */
683   if (!m_bits[m_page]) {
684      LOG_DRIVE((0,"   no bits for #%d: %d/%d/%d\n", m_unit, m_cylinder, m_head, m_sector));
685      return;
686   }
687   UINT32 *bits = m_bits[m_page];
688
689   /* pointer to sector buffer */
690   s = reinterpret_cast<diablo_sector_t *>(m_image[m_page]);
691
692   /* zap the sector first */
693   memset(s, 0, sizeof(*s));
694
695   src = MFRRDL * 32;
696   /* skip first words and garbage until 0 bits are coming in */
697   src = squeeze_unsync(bits, src, 40);
698   /* sync on header preamble */
699   src = squeeze_sync(bits, src, 40);
700   LOG_DRIVE((0,"   header sync bit #%d (@%03d.%02d)\n", src, src / 32, src % 32));
701   src = squeeze_record(bits, src, s->header, sizeof(s->header));
702   src = squeeze_cksum(bits, src, &cksum_header);
703#if   DIABLO_DEBUG
704   dump_record(s->header, 0, sizeof(s->header), "header", 0);
705#endif
706
707   /* skip garbage until 0 bits are coming in */
708   src = squeeze_unsync(bits, src, 40);
709   /* sync on label preamble */
710   src = squeeze_sync(bits, src, 40);
711   LOG_DRIVE((0,"   label  sync bit #%d (@%03d.%02d)\n", src, src / 32, src % 32));
712   src = squeeze_record(bits, src, s->label, sizeof(s->label));
713   src = squeeze_cksum(bits, src, &cksum_label);
714#if   DIABLO_DEBUG
715   dump_record(s->label, 0, sizeof(s->label), "label", 0);
716#endif
717
718   /* skip garbage until 0 bits are coming in */
719   src = squeeze_unsync(bits, src, 40);
720   /* sync on data preamble */
721   src = squeeze_sync(bits, src, 40);
722   LOG_DRIVE((0,"   data   sync bit #%d (@%03d.%02d)\n", src, src / 32, src % 32));
723   src = squeeze_record(bits, src, s->data, sizeof(s->data));
724   src = squeeze_cksum(bits, src, &cksum_data);
725#if   DIABLO_DEBUG
726   dump_record(s->data, 0, sizeof(s->data), "data", 1);
727#endif
728
729   /* TODO: what is the cksum start value for the data record? */
730   cksum_header ^= cksum(s->header, sizeof(s->header), 0521);
731   cksum_label ^= cksum(s->label, sizeof(s->label), 0521);
732   cksum_data ^= cksum(s->data, sizeof(s->data), 0521);
733
734   if (cksum_header || cksum_label || cksum_data) {
735#if   DIABLO_DEBUG
736      LOG_DRIVE((0,"   cksum check - header:%06o label:%06o data:%06o\n", cksum_header, cksum_label, cksum_data));
737#else
738      printf("   cksum check - header:%06o label:%06o data:%06o\n", cksum_header, cksum_label, cksum_data);
739#endif
740   }
741   global_free(m_bits[m_page]);
742   m_bits[m_page] = 0;
743}
744
745/**
746 * @brief return number of bitclk edges for a sector
747 * @return number of bitclk edges for a sector
748 */
749int diablo_hd_device::bits_per_sector() const
750{
751   return m_diablo31 ? DIABLO31_SECTOR_WORDS * 32 : DIABLO44_SECTOR_WORDS * 32;
752}
753
754/**
755 * @brief return a pointer to a drive's description
756 * @return a pointer to the string description
757 */
758const char* diablo_hd_device::description() const
759{
760   return m_description;
761}
762
763/**
764 * @brief return the number of a drive unit
765 * @return the unit number
766 */
767int diablo_hd_device::unit() const
768{
769   return m_unit;
770}
771
772/**
773 * @brief return the time for a full rotation
774 * @return the time for a full track rotation in atto seconds
775 */
776attotime diablo_hd_device::rotation_time() const
777{
778   return m_rotation_time;
779}
780
781/**
782 * @brief return the time for a sector
783 * @return the time for a sector in atto seconds
784 */
785attotime diablo_hd_device::sector_time() const
786{
787   return m_sector_time;
788}
789
790/**
791 * @brief return the time for a data bit
792 * @return the time in atto seconds per bit clock
793 */
794attotime diablo_hd_device::bit_time() const
795{
796   return m_bit_time;
797}
798
799/**
800 * @brief return the seek/read/write status of a drive
801 * @return the seek/read/write status for the drive unit (0: active, 1: inactive)
802 */
803int diablo_hd_device::get_seek_read_write_0() const
804{
805   return m_s_r_w_0;
806}
807
808/**
809 * @brief return the ready status of a drive
810 * @return the ready status for the drive unit (0: ready, 1: not ready)
811 */
812int diablo_hd_device::get_ready_0() const
813{
814   return m_ready_0;
815}
816
817/**
818 * @brief return the current sector mark status of a drive
819 *
820 * The sector mark is derived from the offset into the current sector.
821 *
822 * @return the current sector mark for the drive (0:active 1:inactive)
823 */
824int diablo_hd_device::get_sector_mark_0() const
825{
826   /* no sector marks while seeking (?) */
827   if (m_s_r_w_0)
828      return 1;
829
830   /* return the sector mark */
831   return m_sector_mark_0;
832}
833
834/**
835 * @brief return the address acknowledge state
836 * @return the address acknowledge state
837 */
838int diablo_hd_device::get_addx_acknowledge_0() const
839{
840   return m_addx_acknowledge_0;
841}
842
843/**
844 * @brief return the log address interlock state
845 * @return the log address interlock state (0:active 1:inactive)
846 */
847int diablo_hd_device::get_log_addx_interlock_0() const
848{
849   return m_log_addx_interlock_0;
850}
851
852/**
853 * @brief return the seek incomplete state
854 * @return the address acknowledge state (0:active 1:inactive)
855 */
856int diablo_hd_device::get_seek_incomplete_0() const
857{
858   return m_seek_incomplete_0;
859}
860
861/**
862 * @brief return the current cylinder of a drive unit
863 *
864 * Note: the bus lines are active low, thus the XOR with DRIVE_CYLINDER_MASK.
865 *
866 * @return the current cylinder number for the drive
867 */
868int diablo_hd_device::get_cylinder() const
869{
870   return m_cylinder ^ DIABLO_CYLINDER_MASK;
871}
872
873/**
874 * @brief return the current head of a drive unit
875 *
876 * Note: the bus lines are active low, thus the XOR with DRIVE_HEAD_MASK.
877 *
878 * @return the currently selected head for the drive
879 */
880int diablo_hd_device::get_head() const
881{
882   return m_head ^ DIABLO_HEAD_MASK;
883}
884
885/**
886 * @brief return the current sector of a drive unit
887 *
888 * The current sector number is derived from the time since the
889 * most recent track rotation started.
890 *
891 * Note: the bus lines are active low, thus the XOR with DRIVE_SECTOR_MASK.
892 *
893 * @return the current sector for the drive
894 */
895int diablo_hd_device::get_sector() const
896{
897   return m_sector ^ DIABLO_SECTOR_MASK;
898}
899
900/**
901 * @brief return the current page of a drive unit
902 *
903 * The current page number is derived from the cylinder, head,
904 * and sector numbers.
905 *
906 * @return the current page for the drive
907 */
908int diablo_hd_device::get_page() const
909{
910   return m_page;
911}
912
913/**
914 * @brief select a drive unit and head
915 *
916 * @param unit unit number
917 * @param head head number
918 */
919void diablo_hd_device::select(int unit, int head)
920{
921   assert(unit == m_unit);
922   /* this drive is selected */
923   if ((head & DIABLO_HEAD_MASK) != m_head) {
924      m_head = head & DIABLO_HEAD_MASK;
925      printf("select unit:%d head:%d\n", unit, head);
926   }
927
928   if (m_drive) {
929      /* it is ready */
930      m_ready_0 = 0;
931      /* and can take seek/read/write commands */
932      m_s_r_w_0 = 0;
933      /* address acknowledge (?) */
934      m_addx_acknowledge_0 = 0;
935      /* clear log address interlock (?) */
936      m_log_addx_interlock_0 = 1;
937      LOG_DRIVE((1,"   UNIT select %d ready\n", unit));
938   } else {
939      /* it is not ready (?) */
940      m_ready_0 = 1;
941      /* can't take seek/read/write commands (?) */
942      m_s_r_w_0 = 1;
943      /* address acknowledge (?) */
944      m_addx_acknowledge_0 = 0;
945      LOG_DRIVE((1,"   UNIT select %d not ready (no image)\n", unit));
946   }
947   read_sector();
948}
949
950/**
951 * @brief strobe a seek operation
952 *
953 * Seek to the cylinder cylinder, or restore.
954 *
955 * @param unit unit number
956 * @param cylinder cylinder number to seek to
957 * @param restore true, if the drive should restore to cylinder 0
958 * @param strobe current level of the strobe signal (for edge detection)
959 */
960void diablo_hd_device::set_strobe(int cylinder, bool restore, int strobe)
961{
962   int seekto = restore ? 0 : cylinder;
963   if (strobe) {
964      LOG_DRIVE((1,"   STROBE end of interlock\n", seekto));
965      /* deassert the log address interlock */
966      m_log_addx_interlock_0 = 1;
967      return;
968   }
969
970   /* assert the log address interlock */
971   m_log_addx_interlock_0 = 0;
972
973   if (seekto == m_cylinder) {
974      LOG_DRIVE((1,"   STROBE to cylinder %d acknowledge\n", seekto));
975      m_addx_acknowledge_0 = 0;   /* address acknowledge, if cylinder is reached */
976      m_seek_incomplete_0 = 1;   /* reset seek incomplete */
977      return;
978   }
979
980   m_s_r_w_0 = 0;
981
982   if (seekto < m_cylinder) {
983      /* decrement cylinder */
984      m_cylinder--;
985      if (m_cylinder < 0) {
986         m_cylinder = 0;
987         m_log_addx_interlock_0 = 1;   /* deassert the log address interlock */
988         m_seek_incomplete_0 = 1;   /* deassert seek incomplete */
989         m_addx_acknowledge_0 = 0;   /* assert address acknowledge  */
990         LOG_DRIVE((1,"   STROBE to cylinder %d incomplete\n", seekto));
991         return;
992      }
993   } else {
994      /* increment cylinder */
995      m_cylinder++;
996      if (m_cylinder >= DIABLO_CYLINDERS) {
997         m_cylinder = DIABLO_CYLINDERS - 1;
998         m_log_addx_interlock_0 = 1;   /* deassert the log address interlock */
999         m_seek_incomplete_0 = 1;   /* deassert seek incomplete */
1000         m_addx_acknowledge_0 = 0;   /* assert address acknowledge  */
1001         LOG_DRIVE((1,"   STROBE to cylinder %d incomplete\n", seekto));
1002         return;
1003      }
1004   }
1005   LOG_DRIVE((1,"   STROBE to cylinder %d (now %d) - interlock\n", seekto, m_cylinder));
1006
1007   m_addx_acknowledge_0 = 1;   /* deassert address acknowledge  */
1008   m_seek_incomplete_0 = 1;   /* deassert seek incomplete */
1009   read_sector();
1010}
1011
1012/**
1013 * @brief set the drive erase gate
1014 * @param gate value of erase gate
1015 */
1016void diablo_hd_device::set_egate(int gate)
1017{
1018   m_egate_0 = gate & 1;
1019}
1020
1021/**
1022 * @brief set the drive write gate
1023 * @param gate value of write gate
1024 */
1025void diablo_hd_device::set_wrgate(int gate)
1026{
1027   m_wrgate_0 = gate & 1;
1028}
1029
1030/**
1031 * @brief set the drive read gate
1032 * @param gate value of read gate
1033 */
1034void diablo_hd_device::set_rdgate(int gate)
1035{
1036   m_rdgate_0 = gate & 1;
1037}
1038
1039/**
1040 * @brief write the sector relative bit at index
1041 *
1042 * The disk controller writes a combined clock and data pulse to one output
1043 * <PRE>
1044 * Encoding of binary 01011
1045 *
1046 *   clk   data  clk   data  clk   data  clk   data  clk   data
1047 *   0     1     2     3     4     5     6     7     8     9
1048 *   +--+        +--+  +--+  +--+        +--+  +--+  +--+  +--+  +--
1049 *   |  |        |  |  |  |  |  |        |  |  |  |  |  |  |  |  |
1050 * --+  +--------+  +--+  +--+  +--------+  +--+  +--+  +--+  +--+
1051 * </PRE>
1052 *
1053 * @param index relative index of bit/clock into sector
1054 * @param wrdata write data clock or bit
1055 */
1056void diablo_hd_device::wr_data(int index, int wrdata)
1057{
1058   if (m_wrgate_0) {
1059      /* write gate is not asserted (active 0) */
1060      return;
1061   }
1062
1063   /* don't write before or beyond the sector */
1064   if (index < 0 || index >= bits_per_sector())
1065      return;
1066
1067   if (-1 == m_page) {
1068      /* invalid page */
1069      return;
1070   }
1071
1072   UINT32 *bits = expand_sector();
1073   if (-1 == m_wrfirst)
1074      m_wrfirst = index;
1075
1076   LOG_DRIVE((7,"   write #%d %d/%d/%d bit #%d bit:%d\n", unit, m_cylinder, m_head, m_sector, index, wrdata));
1077
1078   if (index < GUARD_ZONE_BITS) {
1079      /* don't write in the guard zone (?) */
1080   } else {
1081      WRBIT(bits,index,wrdata);
1082   }
1083   m_wrlast = index;
1084}
1085
1086/**
1087 * @brief read the sector relative bit at index
1088 *
1089 * Note: this is a gross hack to allow the controller pulling bits
1090 * at its will, rather than clocking them with the drive's RDCLK-
1091 *
1092 * @param index is the sector relative bit index
1093 * @return returns the sector's bit by index
1094 */
1095int diablo_hd_device::rd_data(int index)
1096{
1097   int bit = 0;
1098
1099   if (m_rdgate_0) {
1100      /* read gate is not asserted (active 0) */
1101      return 0;
1102   }
1103
1104   /* don't read before or beyond the sector */
1105   if (index < 0 || index >= bits_per_sector())
1106      return 1;
1107
1108   /* no data while sector mark is low (?) */
1109   if (0 == m_sector_mark_0)
1110      return 1;
1111
1112   if (-1 == m_page) {
1113      /* invalid page */
1114      return 1;
1115   }
1116
1117   UINT32 *bits = expand_sector();
1118
1119   if (-1 == m_rdfirst)
1120      m_rdfirst = index;
1121
1122   RDBIT(bits,index,bit);
1123   LOG_DRIVE((7,"   read #%d %d/%d/%d bit #%d:%d\n", unit, m_cylinder, m_head, m_sector, index, bit));
1124   m_rdlast = index;
1125   return bit;
1126}
1127
1128/**
1129 * @brief get the sector relative clock at index
1130 *
1131 * Note: this is a gross hack to allow the controller pulling bits
1132 * at its will, rather than clocking them with the drive's RDCLK-
1133 *
1134 * @param index is the sector relative bit index
1135 * @return returns the sector's clock bit by index
1136 */
1137int diablo_hd_device::rd_clock(int index)
1138{
1139   int clk = 0;
1140
1141   /* don't read before or beyond the sector */
1142   if (index < 0 || index >= bits_per_sector())
1143      return 1;
1144
1145   /* no clock while sector mark is low (?) */
1146   if (0 == m_sector_mark_0)
1147      return 1;
1148
1149   if (-1 == m_page) {
1150      /* invalid page */
1151      return 1;
1152   }
1153
1154   UINT32 *bits = expand_sector();
1155
1156   if (-1 == m_rdfirst)
1157      m_rdfirst = index;
1158
1159   if (index & 1) {
1160      // clock bits are on even bit positions only
1161      clk = 0;
1162   } else {
1163      RDBIT(bits,index,clk);
1164   }
1165   LOG_DRIVE((7,   "   read #%d %d/%d/%d clk #%d:%d\n", unit, m_cylinder, m_head, m_sector, index, clk));
1166   m_rdlast = index;
1167   return clk ^ 1;
1168}
1169
1170/**
1171 * @brief timer callback that is called once per sector in the rotation
1172 *
1173 * @param id timer id
1174 * @param arg argument supplied to timer_insert (unused)
1175 */
1176void diablo_hd_device::next_sector(void* ptr, int arg)
1177{
1178   (void)ptr;
1179
1180   switch (arg) {
1181   case 0:
1182      m_sector_timer->adjust(m_sector_mark_0_time, 1);
1183      /* deassert sector mark */
1184      sector_mark_1();
1185      break;
1186   case 1:
1187      m_sector_timer->adjust(m_sector_mark_1_time, 2);
1188      /* assert sector mark */
1189      sector_mark_0();
1190      break;
1191   case 2:
1192      /* next sector starting soon now */
1193      m_sector_timer->adjust(m_sector_time - m_sector_mark_0_time, 0);
1194      /* call the sector_callback, if any */
1195      if (m_sector_callback)
1196         (*m_sector_callback)();
1197   }
1198}
1199
1200/**
1201 * @brief timer callback that deasserts the sector mark
1202 *
1203 * @param unit drive unit number
1204 */
1205void diablo_hd_device::sector_mark_1()
1206{
1207   LOG_DRIVE((5, "   %s (unit #%d C/H/S:%d/%d/%d)\n", __FUNCTION__, m_unit, m_cylinder, m_head, m_sector));
1208   /* set sector mark to 1 */
1209   m_sector_mark_0 = 1;
1210}
1211
1212/**
1213 * @brief timer callback that asserts the sector mark
1214 *
1215 * @param unit drive unit number
1216 */
1217void diablo_hd_device::sector_mark_0()
1218{
1219   LOG_DRIVE((5,"   %s (unit #%d C/H/S:%d/%d/%d)\n", __FUNCTION__, m_unit, m_cylinder, m_head, m_sector));
1220
1221   /* squeeze previous sector, if it was written to */
1222   squeeze_sector();
1223
1224   m_sector_mark_0 = 0;
1225
1226   /* reset read and write bit locations */
1227   m_rdfirst = -1;
1228   m_rdlast = -1;
1229   m_wrfirst = -1;
1230   m_wrlast = -1;
1231
1232   /* count sectors */
1233   m_sector = (m_sector + 1) % DIABLO_SPT;
1234   read_sector();
1235}
1236
1237void diablo_hd_device::device_start()
1238{
1239   if (m_diablo31) {
1240      snprintf(m_description, sizeof(m_description), "DIABLO31");
1241      m_rotation_time = DIABLO31_ROTATION_TIME;
1242      m_sector_time = DIABLO31_ROTATION_TIME / DIABLO_SPT;
1243      m_sector_mark_0_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
1244      m_sector_mark_1_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
1245      m_bit_time = DIABLO31_BIT_TIME(1);
1246   } else {
1247      snprintf(m_description, sizeof(m_description), "DIABLO44");
1248      m_rotation_time = DIABLO44_ROTATION_TIME;
1249      m_sector_time = DIABLO44_ROTATION_TIME / DIABLO_SPT;
1250      m_sector_mark_0_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
1251      m_sector_mark_1_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
1252      m_bit_time = DIABLO44_BIT_TIME(1);
1253   }
1254
1255   m_sector_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(diablo_hd_device::next_sector),this));
1256   m_sector_timer->adjust(m_sector_time - m_sector_mark_0_time, 0);
1257}
1258
1259void diablo_hd_device::device_reset()
1260{
1261   m_s_r_w_0 = 1;               /* seek/read/write not ready */
1262   m_ready_0 = 1;               /* drive is not ready */
1263   m_sector_mark_0 = 1;         /* sector mark clear */
1264   m_addx_acknowledge_0 = 1;      /* drive address acknowledge is not active */
1265   m_log_addx_interlock_0 = 1;   /* drive log address interlock is not active */
1266   m_seek_incomplete_0 = 1;      /* drive seek incomplete is not active */
1267
1268   /* reset the disk drive's address */
1269   m_cylinder = 0;
1270   m_head = 0;
1271   m_sector = 0;
1272   m_page = -1;
1273
1274   /* disable the gates */
1275   m_egate_0 = 1;
1276   m_wrgate_0 = 1;
1277   m_rdgate_0 = 1;
1278
1279   /* reset read/write first and last indices */
1280   m_wrfirst = -1;
1281   m_wrlast = -1;
1282   m_rdfirst = -1;
1283   m_rdlast = -1;
1284
1285   m_drive = static_cast<harddisk_image_device *>(subdevice("drive"));
1286}
1287
1288MACHINE_CONFIG_FRAGMENT( diablo_drive )
1289   MCFG_HARDDISK_ADD("drive")
1290MACHINE_CONFIG_END
1291
1292machine_config_constructor diablo_hd_device::device_mconfig_additions() const
1293{
1294   return MACHINE_CONFIG_NAME( diablo_drive );
1295}
1296
1297const device_type DIABLO_HD = &device_creator<diablo_hd_device>;
Property changes on: branches/alto2/src/mess/machine/diablo_hd.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
branches/alto2/src/mess/machine/diablo_hd.h
r0r26123
1/**********************************************************
2 *   DIABLO31 and DIABLO44 hard drive support
3 *
4 *   Copyright: Juergen Buchmueller <pullmoll@t-online.de>
5 *
6 *   Licenses: MAME, GPLv2
7 **********************************************************/
8
9#if   !defined(_DIABLO_HD_DEVICE_)
10#define _DIABLO_HD_DEVICE_
11
12#include "emu.h"
13#include "imagedev/harddriv.h"
14
15#define   DIABLO_DEBUG   0
16
17#define DIABLO_HD_0 "diablo:0"
18#define DIABLO_HD_1 "diablo:1"
19
20extern const device_type DIABLO_HD;
21
22class diablo_hd_device : public device_t
23{
24public:
25   diablo_hd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
26
27   int bits_per_sector() const;
28   const char* description() const;
29   int unit() const;
30   attotime rotation_time() const;
31   attotime sector_time() const;
32   attotime bit_time() const;
33
34   int get_seek_read_write_0() const;
35   int get_ready_0() const;
36   int get_sector_mark_0() const;
37   int get_addx_acknowledge_0() const;
38   int get_log_addx_interlock_0() const;
39   int get_seek_incomplete_0() const;
40   int get_cylinder() const;
41   int get_head() const;
42   int get_sector() const;
43   int get_page() const;
44   void select(int unit, int head);
45   void set_strobe(int cylinder, bool restore, int strobe);
46   void set_egate(int gate);
47   void set_wrgate(int gate);
48   void set_rdgate(int gate);
49   void wr_data(int index, int wrdata);
50   int rd_data(int index);
51   int rd_clock(int index);
52
53protected:
54   virtual void    device_start();
55   virtual void    device_reset();
56   virtual machine_config_constructor device_mconfig_additions() const;
57
58private:
59#if   DIABLO_DEBUG
60   int m_log_level;
61   void logprintf(int level, const char* format, ...);
62#   define   LOG_DRIVE(x) logprintf x
63#else
64#   define   LOG_DRIVE(x)
65#endif
66
67   static const int DIABLO_UNIT_MAX = 2;         //!< max number of drive units
68   static const int DIABLO_CYLINDERS = 203;      //!< number of cylinders per drive
69   static const int DIABLO_CYLINDER_MASK = 0777;   //!< bit maks for cylinder number (9 bits)
70   static const int DIABLO_SPT = 12;            //!< number of sectors per track
71   static const int DIABLO_SECTOR_MASK = 017;      //!< bit maks for cylinder number (4 bits)
72   static const int DIABLO_HEADS = 2;            //!< number of heads per drive
73   static const int DIABLO_HEAD_MASK = 1;         //!< bit maks for cylinder number (4 bits)
74   static const int DIABLO_PAGES = 203*2*12;      //!< number of pages per drive
75   //! convert a cylinder/head/sector to a logical block address (page)
76   static inline int DRIVE_PAGE(int c,int h,int s)   { return (c * DIABLO_HEADS + h) * DIABLO_SPT + s; }
77
78   bool m_diablo31;            //!< true, if this is a DIABLO31 drive
79   int m_unit;                  //!< drive unit number (0 or 1)
80   char m_description[32];         //!< description of the drive(s)
81   int m_packs;               //!< number of packs in drive (1 or 2)
82   attotime m_rotation_time;      //!< rotation time in atto seconds
83   attotime m_sector_time;         //!< sector time in atto seconds
84   attotime m_sector_mark_0_time;   //!< sector mark going 0 before sector pulse time
85   attotime m_sector_mark_1_time;   //!< sector mark going 1 after sector pulse time
86   attotime m_bit_time;         //!< bit time in atto seconds
87   int m_s_r_w_0;               //!< drive seek/read/write signal (active 0)
88   int m_ready_0;               //!< drive ready signal (active 0)
89   int m_sector_mark_0;         //!< sector mark (0 if new sector)
90   int m_addx_acknowledge_0;      //!< address acknowledge, i.e. seek successful (active 0)
91   int m_log_addx_interlock_0;      //!< log address interlock, i.e. seek in progress (active 0)
92   int m_seek_incomplete_0;      //!< seek incomplete, i.e. seek in progress (active 0)
93   int m_egate_0;               //!< erase gate
94   int m_wrgate_0;               //!< write gate
95   int m_rdgate_0;               //!< read gate
96   int m_cylinder;               //!< current cylinder number
97   int m_head;                  //!< current head (track) number on cylinder
98   int m_sector;               //!< current sector number in track
99   int m_page;                  //!< current page
100   UINT8* m_image[DIABLO_PAGES];   //!< page raw bytes
101   UINT32* m_bits[DIABLO_PAGES];   //!< page expanded to bits
102   int m_rdfirst;               //!< set to first bit of a sector that is read from
103   int m_rdlast;               //!< set to last bit of a sector that was read from
104   int m_wrfirst;               //!< set to non-zero if a sector is written to
105   int m_wrlast;               //!< set to last bit of a sector that was written to
106   void (*m_sector_callback)();   //!< callback to call at the start of each sector
107   emu_timer* m_sector_timer;      //!< sector timer
108   harddisk_image_device *m_drive;
109
110   void read_sector();            //!< translate C/H/S to a page and read the sector
111   int cksum(UINT8 *src, size_t size, int start);
112   size_t expand_zeroes(UINT32 *bits, size_t dst, size_t size);
113   size_t expand_sync(UINT32 *bits, size_t dst, size_t size);
114   size_t expand_record(UINT32 *bits, size_t dst, UINT8 *field, size_t size);
115   size_t expand_cksum(UINT32 *bits, size_t dst, UINT8 *field, size_t size);
116   UINT32* expand_sector();
117
118   size_t squeeze_sync(UINT32 *bits, size_t src, size_t size);
119   size_t squeeze_unsync(UINT32 *bits, size_t src, size_t size);
120   size_t squeeze_record(UINT32 *bits, size_t src, UINT8 *field, size_t size);
121   size_t squeeze_cksum(UINT32 *bits, size_t src, int *cksum);
122   void squeeze_sector();
123
124   void next_sector(void* ptr, int arg);
125   void sector_mark_1();
126   void sector_mark_0();
127};
128
129#define MCFG_DIABLO_DRIVES_ADD()   \
130   MCFG_DEVICE_ADD(DIABLO_HD_0, DIABLO_HD, 0)   \
131   MCFG_DEVICE_ADD(DIABLO_HD_1, DIABLO_HD, 0)   \
132
133#endif   // !defined(_DIABLO_HD_DEVICE_)
Property changes on: branches/alto2/src/mess/machine/diablo_hd.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain

Previous 199869 Revisions Next


© 1997-2024 The MAME Team