1 // license:BSD-3-Clause
2 // copyright-holders:Juergen Buchmueller
3 /**********************************************************
4 * DIABLO31 and DIABLO44 hard drive support
5 **********************************************************/
6 #include "emu.h"
7 #include "diablo_hd.h"
8
9 /**
10 *
11 * Just for completeness' sake:
12 * The mapping of disk controller connector P2 pins to the
13 * Winchester disk drive signals (see drive.h)
14 * <PRE>
15 * Alto Controller Winchester
16 * P2 signal disk bus
17 * -----------------------------------------------
18 * 1 GND D_GROUND
19 * 2 RDCLK' A_READ_CLOCK
20 * 3 WRDATA' B_WRITE_DATA_AND_CLOCK
21 * 4 SRWRDY' F_S_R_W
22 * 5 DISK L_SELECT_LINE_UNIT_1
23 * 6 CYL(7)' N_CYL_7
24 * 7 DISK' R_SELECT_LINE_UNIT_2
25 * 8 CYL(2)' T_CYL_2
26 * 9 ??? V_SELECT_LINE_UNIT_3
27 * 10 CYL(4)' X_CYL_4
28 * 11 CYL(0)' Z_CYL_0
29 * 12 CYL(1)' BB_CYL_1
30 * 13 CYL(3)' FF_CYL_3
31 * 14 ??? KK_BIT_2
32 * 15 CYL(8)' LL_CYL_8
33 * 16 ADRACK' NN_ADDX_ACKNOWLEDGE
34 * 17 SKINC' TT_SEEK_INCOMPLETE
35 * 18 LAI' XX_LOG_ADDX_INTERLOCK
36 * 19 CYL(6)' RR_CYL_6
37 * 20 RESTOR' VV_RESTORE
38 * 21 ??? UU_BIT_16
39 * 22 STROBE' SS_STROBE
40 * 23 ??? MM_BIT_8
41 * 24 ??? KK_BIT_4
42 * 25 ??? HH_WRITE_CHK
43 * 26 WRTGATE' EE_WRITE_GATE
44 * 27 ??? CC_BIT_SECTOR_ADDX
45 * 28 HEAD' AA_HEAD_SELECT
46 * 29 ??? Y_INDEX_MARK
47 * 30 SECT(4)' W_SECTOR_MARK
48 * 31 READY' U_FILE_READY
49 * 32 ??? S_PSEUDO_SECTOR_MARK
50 * 33 ??? P_WRITE_PROTECT_IND
51 * 34 ??? H_WRITE_PROTECT_INPUT_ATTENTION
52 * 35 ERGATE' K_ERASE_GATE
53 * 36 ??? M_HIGH_DENSITY
54 * 37 CYL(5)' J_CYL_5
55 * 38 RDDATA' C_READ_DATA
56 * 39 RDGATE' E_READ_GATE
57 * 40 GND ??
58 * </PRE>
59 */
60
61 #ifndef DIABLO_DEBUG
62 #define DIABLO_DEBUG 1 //!< set to 1 to enable debug log output
63 #endif
64
65 #define LOG_DRIVE(...) do { if (DIABLO_DEBUG) logprintf(__VA_ARGS__); } while (0)
66
67
diablo_hd_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)68 diablo_hd_device::diablo_hd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
69 device_t(mconfig, DIABLO_HD, tag, owner, clock),
70 m_log_level(8),
71 m_diablo31(true),
72 m_unit(0),
73 m_packs(1),
74 m_rotation_time(),
75 m_sector_time(),
76 m_sector_mark_0_time(),
77 m_sector_mark_1_time(),
78 m_bit_time(),
79 m_s_r_w_0(1),
80 m_ready_0(1),
81 m_sector_mark_0(1),
82 m_addx_acknowledge_0(1),
83 m_log_addx_interlock_0(1),
84 m_seek_incomplete_0(1),
85 m_egate_0(1),
86 m_wrgate_0(1),
87 m_rdgate_0(1),
88 m_cylinders(DIABLO_CYLINDERS),
89 m_pages(DIABLO_PAGES),
90 m_seekto(0),
91 m_restore(0),
92 m_cylinder(-1),
93 m_head(-1),
94 m_sector(-1),
95 m_page(-1),
96 m_bits(),
97 m_rdfirst(-1),
98 m_rdlast(-1),
99 m_wrfirst(-1),
100 m_wrlast(-1),
101 m_sector_callback_cookie(nullptr),
102 m_sector_callback(nullptr),
103 m_timer(nullptr),
104 m_image(nullptr),
105 m_handle(nullptr),
106 m_disk(nullptr)
107 {
108 memset(m_description, 0x00, sizeof(m_description));
109 }
110
111 /**
112 * @brief diablo_hd_device destructor
113 * Free all m_cache and m_bits pages and the arrays
114 */
~diablo_hd_device()115 diablo_hd_device::~diablo_hd_device()
116 {
117 }
118
119 template <typename Format, typename... Params>
logprintf(int level,Format && fmt,Params &&...args)120 void diablo_hd_device::logprintf(int level, Format &&fmt, Params &&... args)
121 {
122 if (level <= m_log_level)
123 logerror(std::forward<Format>(fmt), std::forward<Params>(args)...);
124 }
125
set_sector_callback(void * cookie,void (* callback)(void *,int))126 void diablo_hd_device::set_sector_callback(void *cookie, void (*callback)(void *, int))
127 {
128 if (m_sector_callback_cookie == cookie && m_sector_callback == callback)
129 return;
130 LOG_DRIVE(0,"[DHD%u] cookie=%p callback=%p\n", m_unit, cookie, (void *)callback);
131 m_sector_callback_cookie = cookie;
132 m_sector_callback = callback;
133 }
134
135 #define DIABLO31_ROTATION_TIME attotime::from_usec(39900) //!< DIABLO 31 rotation time is approx. 40ms
136 #define DIABLO31_SECTOR_TIME attotime::from_usec(39900/12) //!< DIABLO 31 sector time
137 /**
138 * @brief DIABLO 31 bit clock is 3330kHz ~= 300ns per bit
139 * ~= 133333 bits/track (?)
140 * ~= 11111 bits/sector
141 * ~= 347 words/sector
142 */
143 #define DIABLO31_BIT_TIME(bits) attotime::from_nsec(300*(bits))
144 #define DIABLO31_SECTOR_BITS 10432
145 #define DIABLO31_SECTOR_WORDS 347 //!< DIABLO 31 possible sector words
146 #define DIABLO31_SECTOR_MARK_PULSE_PRE DIABLO31_BIT_TIME(16) //!< pulse width of sector mark before the next sector begins
147 #define DIABLO31_SECTOR_MARK_PULSE_POST DIABLO31_BIT_TIME(16) //!< pulse width of sector mark after the next sector began
148
149 #define DIABLO44_ROTATION_TIME attotime::from_usec(25000) //!< DIABLO 44 rotation time is approx. 25ms
150 #define DIABLO44_SECTOR_TIME attotime::from_usec(25000/12) //!< DIABLO 44 sector time
151 /**
152 * @brief DIABLO 44 bit clock is 5000kHz ~= 200ns per bit
153 * ~= 125184 bits/track (?)
154 * ~= 10432 bits/sector
155 * ~= 325 words/sector
156 */
157 #define DIABLO44_BIT_TIME(bits) attotime::from_nsec(200*(bits))
158 #define DIABLO44_SECTOR_BITS 10432
159 #define DIABLO44_SECTOR_WORDS 325 //!< DIABLO 44 possible sector words
160 #define DIABLO44_SECTOR_MARK_PULSE_PRE DIABLO44_BIT_TIME(16) //!< pulse width of sector mark before the next sector begins
161 #define DIABLO44_SECTOR_MARK_PULSE_POST DIABLO44_BIT_TIME(16) //!< pulse width of sector mark after the next sector began
162
163 #define MFROBL 34 //!< from the microcode: disk header preamble is 34 words
164 #define MFRRDL 21 //!< from the microcode: disk header read delay is 21 words
165 #define MIRRDL 4 //!< from the microcode: interrecord read delay is 4 words
166 #define MIROBL 3 //!< from the microcode: disk interrecord preamble is 3 words
167 #define MRPAL 3 //!< from the microcode: disk read postamble length is 3 words
168 #define MWPAL 5 //!< from the microcode: disk write postamble length is 5 words
169
170 #define GUARD_ZONE_BITS (16*32) //!< end of the guard zone at the beginning of a sector (wild guess!)
171
172 /**
173 * @brief description of the sector layout (reverse engineered)
174 * <PRE>
175 *
176 * xx.x msec sector mark pulses
177 * -+ +-------------------------------------------------------------------------------+ +--
178 * | | | |
179 * +---+ +---+
180 *
181 * | |
182 *
183 * +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
184 * | PRE- |SYNC|HEADER|CKSUM| PRE- |SYNC| LABEL |CKSUM| PRE- |SYNC| DATA |CKSUM| POST |
185 * |AMBLE1| 1 | | 1 |AMBLE2| 2 | | 2 |AMBLE3| 3 | | 3 |AMBLE |
186 * +------+----+------+-----+------+----+-------+-----+------+----+-------+-----+------+
187 *
188 * | |
189 *
190 * +-----------------------------------------------------------------------------------+
191 * | |
192 * ---+ +----
193 * FORMAT WRITE GATE FOR INITIALIZING
194 * | |
195 *
196 * | +------------------------------+
197 * | |
198 * ---|----------------------------------------------------+ +----
199 * WRITE GATE FOR DATA XFER (*)
200 * | |
201 *
202 * | +-----------------------+-+------------------------------+
203 * | | | may be continuous (?) |
204 * ------------------------------+ +-+ +----
205 * ??? WRITE GATE FOR LABEL AND DATA XFER (*)
206 * | |
207 *
208 * | +--------------------+ +---------------------+ +----------------------------+
209 * | | | | | |
210 * -------+ +---+ +---+ +----
211 * READ GATE FOR INITIALIZING OR DATA XFER (**)
212 *
213 *
214 * (*) Enable should be delayed 1 byte/word time from last bit of checks sum.
215 * (**) Read Gate should be enabled half way through the preamble area. This
216 * ensures reading a zero field for data separator synchronization.
217 *
218 * </PRE>
219 */
220
221 #define DIABLO_PAGENO_WORDS 1 //!< number of words in a page number (this doesn't really belong here)
222 #define DIABLO_HEADER_WORDS 2 //!< number of words in a header (this doesn't really belong here)
223 #define DIABLO_LABEL_WORDS 8 //!< number of words in a label (this doesn't really belong here)
224 #define DIABLO_DATA_WORDS 256 //!< number of data words (this doesn't really belong here)
225 #define DIABLO_CKSUM_WORDS 1 //!< number of words for a checksum (this doesn't really belong here)
226
227 /**
228 * @brief format of the cooked disk image sectors, i.e. pure data
229 *
230 * The available images are a multiple of 267 words (534 bytes) per sector,
231 * 1 word page number
232 * 2 words header
233 * 8 words label
234 * 256 words data
235 */
236 typedef struct {
237 uint8_t pageno[2*DIABLO_PAGENO_WORDS]; //!< sector page number
238 uint8_t header[2*DIABLO_HEADER_WORDS]; //!< sector header words
239 uint8_t label[2*DIABLO_LABEL_WORDS]; //!< sector label words
240 uint8_t data[2*DIABLO_DATA_WORDS]; //!< sector data words
241 } diablo_sector_t;
242
243 /**
244 * @brief write a bit into an array of uint32_t
245 * @param bits pointer to array of bits
246 * @param dst destination index
247 * @param bit bit value
248 * @return next destination index
249 */
WRBIT(uint32_t * bits,size_t dst,int bit)250 static inline size_t WRBIT(uint32_t* bits, size_t dst, int bit)
251 {
252 if (bit) {
253 bits[(dst)/32] |= 1 << ((dst) % 32);
254 } else {
255 bits[(dst)/32] &= ~(1 << ((dst) % 32));
256 }
257 return ++dst;
258 }
259
260 /**
261 * @brief read a bit from an array of uint32_t
262 * @param bits pointer to array of bits
263 * @param src source index
264 * @param bit reference to the bit to set
265 * @return next source index
266 */
RDBIT(uint32_t * bits,size_t src,int & bit)267 static inline size_t RDBIT(uint32_t* bits, size_t src, int& bit)
268 {
269 bit = (bits[src/32] >> (src % 32)) & 1;
270 return ++src;
271 }
272
273 /**
274 * @brief calculate the sector from the logical block address and read it
275 *
276 * Modifies drive's page by calculating the logical
277 * block address from cylinder, head, and sector.
278 */
read_sector()279 void diablo_hd_device::read_sector()
280 {
281 /* If there's no drive, just reset the page number */
282 if (!m_image) {
283 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d => no image\n", m_unit, m_cylinder, m_head, m_sector);
284 m_page = -1;
285 return;
286 }
287 if (m_cylinder < 0 || m_cylinder >= m_cylinders) {
288 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d => invalid cylinder\n", m_unit, m_cylinder, m_head, m_sector);
289 m_page = -1;
290 return;
291 }
292 if (m_head < 0 || m_head >= DIABLO_HEADS) {
293 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d => invalid head\n", m_unit, m_cylinder, m_head, m_sector);
294 m_page = -1;
295 return;
296 }
297 if (m_sector < 0 || m_sector >= DIABLO_SPT) {
298 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d => invalid sector\n", m_unit, m_cylinder, m_head, m_sector);
299 m_page = -1;
300 return;
301 }
302 /* calculate the new disk relative sector offset */
303 m_page = DIABLO_PAGE(m_cylinder, m_head, m_sector);
304
305 // already have the sector image?
306 if (m_cache[m_page]) {
307 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d => page:%d is cached\n", m_unit, m_cylinder, m_head, m_sector, m_page);
308 return;
309 }
310
311 if (m_disk) {
312 // allocate a buffer for this page
313 m_cache[m_page] = std::make_unique<uint8_t[]>(sizeof(diablo_sector_t));
314 // and read the page from the hard_disk image
315 if (hard_disk_read(m_disk, m_page, m_cache[m_page].get())) {
316 LOG_DRIVE(2,"[DHD%u] CHS:%03d/%d/%02d => page:%d loaded\n", m_unit, m_cylinder, m_head, m_sector, m_page);
317 } else {
318 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d => page:%d read failed\n", m_unit, m_cylinder, m_head, m_sector, m_page);
319 m_cache[m_page] = nullptr;
320 }
321 } else {
322 LOG_DRIVE(2,"[DHD%u] no disk\n", m_unit);
323 }
324 }
325
326 /**
327 * @brief compute the checksum of a record
328 *
329 * @param src pointer to a record (header, label, data)
330 * @param size size of the record in bytes
331 * @param start start value for the checksum
332 * @return returns the checksum of the record
333 */
cksum(uint8_t * src,size_t size,int start)334 int diablo_hd_device::cksum(uint8_t *src, size_t size, int start)
335 {
336 int sum = start;
337 /* compute XOR of all words */
338 for (size_t offs = 0; offs < size; offs += 2) {
339 int word = src[size - 2 - offs] + 256 * src[size - 2 - offs + 1];
340 sum ^= word;
341 }
342 return sum;
343 }
344
345 /**
346 * @brief expand a series of clock bits and 0 data bits
347 *
348 * @param bits pointer to the sector bits
349 * @param dst destination offset into bits (bit number)
350 * @param size number of words to write
351 * @return offset to next destination bit
352 */
expand_zeroes(uint32_t * bits,size_t dst,size_t size)353 size_t diablo_hd_device::expand_zeroes(uint32_t *bits, size_t dst, size_t size)
354 {
355 for (size_t offs = 0; offs < 32 * size; offs += 2) {
356 dst = WRBIT(bits, dst, 1); // write the clock bit
357 dst = WRBIT(bits, dst, 0); // write the 0 data bit
358 }
359 return dst;
360 }
361
362 /**
363 * @brief expand a series of 0 words and write a final sync bit
364 *
365 * @param bits pointer to the sector bits
366 * @param dst destination offset into bits (bit number)
367 * @param size number of words to write
368 * @return offset to next destination bit
369 */
expand_sync(uint32_t * bits,size_t dst,size_t size)370 size_t diablo_hd_device::expand_sync(uint32_t *bits, size_t dst, size_t size)
371 {
372 for (size_t offs = 0; offs < 32 * size - 2; offs += 2) {
373 dst = WRBIT(bits, dst, 1); // write the clock bit
374 dst = WRBIT(bits, dst, 0); // write the 0 data bit
375 }
376 dst = WRBIT(bits, dst, 1); // write the final clock bit
377 dst = WRBIT(bits, dst, 1); // write the 1 data bit
378 return dst;
379 }
380
381 /**
382 * @brief expand a record of words into a array of bits at dst
383 *
384 * @param bits pointer to the sector bits
385 * @param dst destination offset into bits (bit number)
386 * @param field pointer to the record data (bytes)
387 * @param size size of the record in bytes
388 * @return offset to next destination bit
389 */
expand_record(uint32_t * bits,size_t dst,uint8_t * field,size_t size)390 size_t diablo_hd_device::expand_record(uint32_t *bits, size_t dst, uint8_t *field, size_t size)
391 {
392 for (size_t offs = 0; offs < size; offs += 2) {
393 int word = field[size - 2 - offs] + 256 * field[size - 2 - offs + 1];
394 for (size_t bit = 0; bit < 16; bit++) {
395 dst = WRBIT(bits, dst, 1); // write the clock bit
396 dst = WRBIT(bits, dst, (word >> 15) & 1); // write the data bit
397 word <<= 1;
398 }
399 }
400 return dst;
401 }
402
403 /**
404 * @brief expand a record's checksum word to 32 bits
405 *
406 * @param bits pointer to the sector bits
407 * @param dst destination offset into bits (bit number)
408 * @param field pointer to the record data (bytes)
409 * @param size size of the record in bytes
410 * @return offset to next destination bit
411 */
expand_cksum(uint32_t * bits,size_t dst,uint8_t * field,size_t size)412 size_t diablo_hd_device::expand_cksum(uint32_t *bits, size_t dst, uint8_t *field, size_t size)
413 {
414 int word = cksum(field, size, 0521);
415 for (size_t bit = 0; bit < 32; bit += 2) {
416 dst = WRBIT(bits, dst, 1); // write the clock bit
417 dst = WRBIT(bits, dst, (word >> 15) & 1); // write the data bit
418 word <<= 1;
419 }
420 return dst;
421 }
422
423 /**
424 * @brief expand a sector into an array of clock and data bits
425 *
426 * @param page page number (0 to DRIVE_PAGES-1)
427 * @return pointer to the newly allocated array of bits
428 */
expand_sector()429 uint32_t* diablo_hd_device::expand_sector()
430 {
431 size_t dst;
432
433 if (!m_bits)
434 return nullptr;
435 /* already expanded this sector? */
436 if (m_bits[m_page])
437 return m_bits[m_page].get();
438
439 /* allocate a sector buffer */
440 if (!m_cache[m_page]) {
441 LOG_DRIVE(0,"[DHD%u] no image for page #%d\n", m_unit, m_page);
442 return nullptr;
443 }
444 diablo_sector_t *s = reinterpret_cast<diablo_sector_t *>(m_cache[m_page].get());
445
446 /* allocate a bits image */
447 m_bits[m_page] = make_unique_clear<uint32_t[]>(400);
448 uint32_t* bits = m_bits[m_page].get();
449
450 if (m_diablo31) {
451 /* write sync bit after (MFROBL-MRPAL) words - 1 bit */
452 dst = expand_sync(bits, 0, (MFROBL - MRPAL));
453 dst = expand_record(bits, dst, s->header, sizeof(s->header));
454 dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
455
456 /* write sync bit after 2 * MWPAL + 1 words - 1 bit */
457 dst = expand_sync(bits, dst, 2 * MWPAL);
458 dst = expand_record(bits, dst, s->label, sizeof(s->label));
459 dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
460
461 /* write sync bit after 2 * MWPAL + 1 words - 1 bit */
462 dst = expand_sync(bits, dst, 2 * MWPAL);
463 dst = expand_record(bits, dst, s->data, sizeof(s->data));
464 dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
465
466 /* fill MWPAL words of clock and 0 data bits */
467 dst = expand_zeroes(bits, dst, MWPAL);
468 } else {
469 /* write sync bit after (MFROBL - MRPAL) words - 1 bit */
470 dst = expand_sync(bits, 0, (MFROBL - MRPAL));
471 dst = expand_record(bits, dst, s->header, sizeof(s->header));
472 dst = expand_cksum(bits, dst, s->header, sizeof(s->header));
473
474 /* write sync bit after 2 * MWPAL words - 1 bit */
475 dst = expand_sync(bits, dst, 2 * MWPAL);
476 dst = expand_record(bits, dst, s->label, sizeof(s->label));
477 dst = expand_cksum(bits, dst, s->label, sizeof(s->label));
478
479 /* write sync bit after 2 * MWPAL words - 1 bit */
480 dst = expand_sync(bits, dst, 2 * MWPAL);
481 dst = expand_record(bits, dst, s->data, sizeof(s->data));
482 dst = expand_cksum(bits, dst, s->data, sizeof(s->data));
483
484 /* fill MWPAL words of clock and 0 data bits */
485 dst = expand_zeroes(bits, dst, MWPAL);
486 }
487
488 LOG_DRIVE(0,"[DHD%u] CHS:%03d/%d/%02d #%5d bits\n", m_unit, m_cylinder, m_head, m_sector, dst);
489 if (DIABLO_DEBUG)
490 {
491 dump_record(s->pageno, 0, sizeof(s->pageno), "pageno", 0);
492 dump_record(s->header, 0, sizeof(s->header), "header", 0);
493 dump_record(s->label, 0, sizeof(s->label), "label", 0);
494 dump_record(s->data, 0, sizeof(s->data), "data", 1);
495 }
496 return bits;
497 }
498
dump_ascii(uint8_t * src,size_t size)499 void diablo_hd_device::dump_ascii(uint8_t *src, size_t size)
500 {
501 size_t offs;
502 LOG_DRIVE(0," [");
503 for (offs = 0; offs < size; offs++) {
504 char ch = char(src[offs ^ 1]);
505 LOG_DRIVE(0, "%c", ch < 32 || ch > 126 ? '.' : ch);
506 }
507 LOG_DRIVE(0,"]\n");
508 }
509
510
511 /**
512 * @brief dump a record's contents
513 *
514 * @param src pointer to a record (header, label, data)
515 * @param size size of the record in bytes
516 * @param name name to print before the dump
517 */
dump_record(uint8_t * src,size_t addr,size_t size,const char * name,int cr)518 size_t diablo_hd_device::dump_record(uint8_t *src, size_t addr, size_t size, const char *name, int cr)
519 {
520 size_t offs;
521 LOG_DRIVE(0,"%s:", name);
522 for (offs = 0; offs < size; offs += 2) {
523 int word = src[offs] + 256 * src[offs + 1];
524 if (offs % 16) {
525 LOG_DRIVE(0," %06o", word);
526 } else {
527 if (offs > 0)
528 dump_ascii(&src[offs-16], 16);
529 LOG_DRIVE(0,"\t%05o: %06o", (addr + offs) / 2, word);
530 }
531 }
532 if (offs % 16) {
533 dump_ascii(&src[offs - (offs % 16)], offs % 16);
534 } else {
535 dump_ascii(&src[offs-16], 16);
536 }
537 if (cr) {
538 LOG_DRIVE(0,"\n");
539 }
540 return size;
541 }
542
543 /**
544 * @brief find a sync bit in an array of clock and data bits
545 *
546 * @param bits pointer to the sector's bits
547 * @param src source index into bits (bit number)
548 * @param size number of words to scan for a sync word
549 * @return next source index for reading
550 */
squeeze_sync(uint32_t * bits,size_t src,size_t size)551 size_t diablo_hd_device::squeeze_sync(uint32_t *bits, size_t src, size_t size)
552 {
553 uint32_t accu = 0;
554 /* hunt for the first 0x0001 word */
555 for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
556 /*
557 * accumulate clock and data bits until we are
558 * on the clock bit boundary
559 */
560 int bit;
561 src = RDBIT(bits,src,bit);
562 accu = (accu << 1) | bit;
563 /*
564 * look for 15 alternating clocks and 0-bits
565 * and the 16th clock with a 1-bit
566 */
567 if (accu == 0xaaaaaaab)
568 return src;
569 if (++bitcount == 32) {
570 bitcount = 0;
571 offs++;
572 }
573 }
574 /* return if no sync found within size*32 clock and data bits */
575 LOG_DRIVE(0,"[DHD%u] no sync within %d words\n", m_unit, size);
576 return src;
577 }
578
579 /**
580 * @brief find a 16 x 0 bits sequence in an array of clock and data bits
581 *
582 * @param bits pointer to the sector's bits
583 * @param src source index into bits (bit number)
584 * @param size number of words to scan for a sync word
585 * @return next source index for reading
586 */
squeeze_unsync(uint32_t * bits,size_t src,size_t size)587 size_t diablo_hd_device::squeeze_unsync(uint32_t *bits, size_t src, size_t size)
588 {
589 uint32_t accu = 0;
590 /* hunt for the first 0 word (16 x 0 bits) */
591 for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
592 /*
593 * accumulate clock and data bits until we are
594 * on the clock bit boundary
595 */
596 int bit;
597 src = RDBIT(bits,src,bit);
598 accu = (accu << 1) | bit;
599 /*
600 * look for 16 alternating clocks and 0 data bits
601 */
602 if (accu == 0xaaaaaaaa)
603 return src;
604 if (++bitcount == 32) {
605 bitcount = 0;
606 offs++;
607 }
608 }
609 /* return if no sync found within size*32 clock and data bits */
610 LOG_DRIVE(0,"[DHD%u] no unsync within %d words\n", m_unit, size);
611 return src;
612 }
613
614 /**
615 * @brief squeeze an array of clock and data bits into a sector's record
616 *
617 * @param bits pointer to the sector's bits
618 * @param src source index into bits (bit number)
619 * @param field pointer to the record data (bytes)
620 * @param size size of the record in bytes
621 * @return next source index for reading
622 */
squeeze_record(uint32_t * bits,size_t src,uint8_t * field,size_t size)623 size_t diablo_hd_device::squeeze_record(uint32_t *bits, size_t src, uint8_t *field, size_t size)
624 {
625 uint32_t accu = 0;
626 for (size_t bitcount = 0, offs = 0; offs < size; /* */) {
627 int bit;
628 src = RDBIT(bits,src,bit); // skip clock
629 assert(bit == 1);
630 src = RDBIT(bits,src,bit); // get data bit
631 accu = (accu << 1) | bit;
632 bitcount += 2;
633 if (bitcount == 32) {
634 /* collected a word */
635 field[size - 2 - offs + 0] = accu % 256;
636 field[size - 2 - offs + 1] = accu / 256;
637 offs += 2;
638 bitcount = 0;
639 }
640 }
641 return src;
642 }
643
644 /**
645 * @brief squeeze an array of 32 clock and data bits into a checksum word
646 *
647 * @param bits pointer to the sector's bits
648 * @param src source index into bits (bit number)
649 * @param cksum pointer to an int to receive the checksum word
650 * @return next source index for reading
651 */
squeeze_cksum(uint32_t * bits,size_t src,int * cksum)652 size_t diablo_hd_device::squeeze_cksum(uint32_t *bits, size_t src, int *cksum)
653 {
654 uint32_t accu = 0;
655
656 for (size_t bitcount = 0; bitcount < 32; bitcount += 2) {
657 int bit;
658 src = RDBIT(bits,src,bit); // skip clock
659 assert(bit == 1);
660 src = RDBIT(bits,src,bit); // get data bit
661 accu = (accu << 1) | bit;
662 }
663
664 /* set the cksum to the extracted word */
665 *cksum = accu;
666 return src;
667 }
668
669 /**
670 * @brief squeeze a array of clock and data bits into a sector's data
671 *
672 * Find and squeeze header, label and data fields and verify for
673 * zero checksums, starting with a value of 0521.
674 * Write the page back to the media and free the bitmap
675 */
squeeze_sector()676 void diablo_hd_device::squeeze_sector()
677 {
678 diablo_sector_t *s;
679 size_t src;
680 int cksum_header, cksum_label, cksum_data;
681
682 if (m_rdfirst >= 0) {
683 LOG_DRIVE(0, "[DHD%u] READ CHS:%03d/%d/%02d bit#%d ... bit#%d\n",
684 m_unit, m_cylinder, m_head, m_sector, m_rdfirst, m_rdlast);
685 }
686 m_rdfirst = -1;
687 m_rdlast = -1;
688
689 /* not written to, just drop it now */
690 if (m_wrfirst < 0) {
691 m_wrfirst = -1;
692 m_wrlast = -1;
693 return;
694 }
695
696 /* did write into the next sector (?) */
697 if (m_wrlast > m_wrfirst && m_wrlast < 256) {
698 m_wrfirst = -1;
699 m_wrlast = -1;
700 return;
701 }
702
703 if (m_wrfirst >= 0) {
704 LOG_DRIVE(0, "[DHD%u] WRITE CHS:%03d/%d/%02d bit#%d ... bit#%d\n",
705 m_unit, m_cylinder, m_head, m_sector, m_wrfirst, m_wrlast);
706 }
707 m_wrfirst = -1;
708 m_wrlast = -1;
709
710 if (m_page < 0 || m_page >= m_pages) {
711 LOG_DRIVE(0,"[DHD%u] page not set\n", m_unit);
712 return;
713 }
714
715 if (!m_cache[m_page]) {
716 LOG_DRIVE(0,"[DHD%u] no image\n", m_unit);
717 return;
718 }
719
720 /* no bits to write? */
721 if (!m_bits[m_page]) {
722 LOG_DRIVE(0,"[DHD%u] no bits\n", m_unit);
723 return;
724 }
725 uint32_t *bits = m_bits[m_page].get();
726
727 // pointer to sector buffer
728 s = reinterpret_cast<diablo_sector_t *>(m_cache[m_page].get());
729
730 // zap the sector first
731 memset(s, 0, sizeof(*s));
732
733 src = MFRRDL * 32;
734 src = squeeze_unsync(bits, src, 40); // skip first words and garbage until 0 bits are coming in
735 src = squeeze_sync(bits, src, 40); // sync on header preamble
736 LOG_DRIVE(0,"[DHD%u] header sync bit #%5d\n", m_unit, src);
737 src = squeeze_record(bits, src, s->header, sizeof(s->header));
738 LOG_DRIVE(0,"[DHD%u] header CRC bit #%5d\n", m_unit, src);
739 src = squeeze_cksum(bits, src, &cksum_header);
740 if (DIABLO_DEBUG)
741 dump_record(s->header, 0, sizeof(s->header), "header", 0);
742
743 src = squeeze_unsync(bits, src, 40); // skip garbage until 0 bits are coming in
744 src = squeeze_sync(bits, src, 40); // sync on label preamble
745 LOG_DRIVE(0,"[DHD%u] label sync bit #%5d\n", m_unit, src);
746 src = squeeze_record(bits, src, s->label, sizeof(s->label));
747 LOG_DRIVE(0,"[DHD%u] label CRC bit #%5d\n", m_unit, src);
748 src = squeeze_cksum(bits, src, &cksum_label);
749 if (DIABLO_DEBUG)
750 dump_record(s->label, 0, sizeof(s->label), "label", 0);
751
752 src = squeeze_unsync(bits, src, 40); // skip garbage until 0 bits are coming in
753 src = squeeze_sync(bits, src, 40); // sync on data preamble
754 LOG_DRIVE(0,"[DHD%u] data sync bit #%5d\n", m_unit, src);
755 src = squeeze_record(bits, src, s->data, sizeof(s->data));
756 LOG_DRIVE(0,"[DHD%u] data CRC bit #%5d\n", m_unit, src);
757 src = squeeze_cksum(bits, src, &cksum_data);
758 if (DIABLO_DEBUG)
759 dump_record(s->data, 0, sizeof(s->data), "data", 1);
760 LOG_DRIVE(0,"[DHD%u] postamble bit #%5d\n", m_unit, src);
761
762 /* The checksum start value always seems to be 0521 */
763 cksum_header ^= cksum(s->header, sizeof(s->header), 0521);
764 cksum_label ^= cksum(s->label, sizeof(s->label), 0521);
765 cksum_data ^= cksum(s->data, sizeof(s->data), 0521);
766
767 if (cksum_header || cksum_label || cksum_data) {
768 LOG_DRIVE(0,"[DHD%u] cksum check - header:%06o label:%06o data:%06o\n", m_unit, cksum_header, cksum_label, cksum_data);
769 }
770 m_bits[m_page].reset();
771
772 if (m_disk) {
773 if (!hard_disk_write(m_disk, m_page, m_cache[m_page].get())) {
774 LOG_DRIVE(0,"[DHD%u] write failed for page #%d\n", m_unit, m_page);
775 }
776 } else {
777 LOG_DRIVE(2,"[DHD%u] no disk\n", m_unit);
778 }
779 }
780
781 /**
782 * @brief return number of bit clocks for a sector (clock and data)
783 * @return number of bitclks for a sector
784 */
bits_per_sector() const785 int diablo_hd_device::bits_per_sector() const
786 {
787 return m_diablo31 ? DIABLO31_SECTOR_BITS : DIABLO44_SECTOR_BITS;
788 }
789
790 /**
791 * @brief return a pointer to a drive's description
792 * @return a pointer to the string description
793 */
description() const794 const char* diablo_hd_device::description() const
795 {
796 return m_description;
797 }
798
799 /**
800 * @brief return the number of a drive unit
801 * @return the unit number of this instance
802 */
unit() const803 int diablo_hd_device::unit() const
804 {
805 return m_unit;
806 }
807
808 /**
809 * @brief return the time for a full rotation
810 * @return the time for a full track rotation in atto seconds
811 */
rotation_time() const812 attotime diablo_hd_device::rotation_time() const
813 {
814 return m_rotation_time;
815 }
816
817 /**
818 * @brief return the time for a sector
819 * @return the time for a sector in atto seconds
820 */
sector_time() const821 attotime diablo_hd_device::sector_time() const
822 {
823 return m_sector_time;
824 }
825
826 /**
827 * @brief return the time for a data bit
828 * @return the time in atto seconds per bit clock
829 */
bit_time() const830 attotime diablo_hd_device::bit_time() const
831 {
832 return m_bit_time;
833 }
834
835 /**
836 * @brief return the seek/read/write status of a drive
837 * @return the seek/read/write status for the drive unit (0:active 1:inactive)
838 */
get_seek_read_write_0() const839 int diablo_hd_device::get_seek_read_write_0() const
840 {
841 return m_s_r_w_0;
842 }
843
844 /**
845 * @brief return the ready status of a drive
846 * @return the ready status for the drive unit (0:ready 1:not ready)
847 */
get_ready_0() const848 int diablo_hd_device::get_ready_0() const
849 {
850 return m_ready_0;
851 }
852
853 /**
854 * @brief return the current sector mark status of a drive
855 *
856 * The sector mark is derived from the offset into the current sector.
857 * It is deasserted except for a short time (a few micro seconds)
858 * around each new sector.
859 *
860 * @return the current sector mark for the drive (0:active 1:inactive)
861 */
get_sector_mark_0() const862 int diablo_hd_device::get_sector_mark_0() const
863 {
864 /* no sector marks while seeking (?) */
865 if (m_s_r_w_0)
866 return 1;
867
868 /* return the sector mark */
869 return m_sector_mark_0;
870 }
871
872 /**
873 * @brief return the address acknowledge state
874 * @return address acknowledge state (0:active 1:inactive)
875 */
get_addx_acknowledge_0() const876 int diablo_hd_device::get_addx_acknowledge_0() const
877 {
878 return m_addx_acknowledge_0;
879 }
880
881 /**
882 * @brief return the log address interlock state
883 * @return log address interlock state (0:active 1:inactive)
884 */
get_log_addx_interlock_0() const885 int diablo_hd_device::get_log_addx_interlock_0() const
886 {
887 return m_log_addx_interlock_0;
888 }
889
890 /**
891 * @brief return the seek incomplete state
892 * @return address acknowledge state (0:active 1:inactive)
893 */
get_seek_incomplete_0() const894 int diablo_hd_device::get_seek_incomplete_0() const
895 {
896 return m_seek_incomplete_0;
897 }
898
899 /**
900 * @brief return the current cylinder of a drive unit
901 *
902 * This is a convenience function.
903 * There is no such signal on the BUS.
904 *
905 * Note: The bus lines are active low
906 * The value on the BUS needs an XOR with DIABLO_CYLINDER_MASK
907 * to resemble the physical line levels.
908 *
909 * @return current cylinder number for the drive
910 */
get_cylinder() const911 int diablo_hd_device::get_cylinder() const
912 {
913 return m_cylinder;
914 }
915
916 /**
917 * @brief return the current head of a drive unit
918 *
919 * This is a convenience function.
920 * There is no such signal on the BUS.
921 *
922 * Note: The bus lines are active low
923 * The value on the BUS needs an XOR with DIABLO_HEAD_MASK
924 * to resemble the physical line levels.
925 *
926 * @return currently selected head for the drive
927 */
get_head() const928 int diablo_hd_device::get_head() const
929 {
930 return m_head;
931 }
932
933 /**
934 * @brief return the current sector of a drive unit
935 *
936 * The current sector number is derived from the time since the
937 * most recent track rotation started.
938 * It counts modulo DIABLO_SPT (12).
939 *
940 * Note: The bus lines are active low
941 * The value on the BUS needs an XOR with DIABLO_SECTOR_MASK
942 * to resemble the physical line levels.
943 *
944 * @return current sector for the drive
945 */
get_sector() const946 int diablo_hd_device::get_sector() const
947 {
948 return m_sector;
949 }
950
951 /**
952 * @brief return the current page of a drive unit
953 *
954 * This is a convenience function.
955 * There is no such signal on the BUS.
956 *
957 * The current page number is derived from the cylinder,
958 * head, and sector numbers.
959 *
960 * @return the current page for the drive
961 */
get_page() const962 int diablo_hd_device::get_page() const
963 {
964 return m_page;
965 }
966
967 /**
968 * @brief select a drive unit
969 *
970 * Selecting a drive unit updates the ready status
971 *
972 * @param unit unit number
973 */
select(int unit)974 void diablo_hd_device::select(int unit)
975 {
976 assert(unit == m_unit); // this drive is selected
977
978 if (m_disk) {
979 m_ready_0 = 0; // it is ready
980 m_s_r_w_0 = 0; // and can take seek/read/write commands
981 m_addx_acknowledge_0 = 0; // assert address acknowledge (?)
982 m_log_addx_interlock_0 = 1; // deassert log address interlock (?)
983 LOG_DRIVE(1,"[DHD%u] select unit:%d ready\n", m_unit, unit);
984 read_sector();
985 } else {
986 m_ready_0 = 1; // it is not ready (?)
987 m_s_r_w_0 = 1; // can't take seek/read/write commands (?)
988 m_addx_acknowledge_0 = 0; // assert address acknowledge (?)
989 m_log_addx_interlock_0 = 1; // deassert log address interlock (?)
990 LOG_DRIVE(1,"[DHD%u] select unit:%d not ready (no image)\n", m_unit, unit);
991 }
992 }
993
994 /**
995 * @brief set the selected head
996 * @param head head number
997 */
set_head(int head)998 void diablo_hd_device::set_head(int head)
999 {
1000 if ((head & DIABLO_HEAD_MASK) != m_head) {
1001 m_head = head & DIABLO_HEAD_MASK;
1002 LOG_DRIVE(0,"[DHD%u] select head:%d\n", m_unit, m_head);
1003 }
1004 }
1005
1006 /**
1007 * @brief set the cylinder number to seek to
1008 *
1009 * This defines the cylinder to seek when the
1010 * STROBE line is pulsed.
1011 *
1012 * @param cylinder cylinder number (bus lines CYL[0-9])
1013 */
set_cylinder(int cylinder)1014 void diablo_hd_device::set_cylinder(int cylinder)
1015 {
1016 if ((cylinder & DIABLO_CYLINDER_MASK) != m_seekto) {
1017 m_seekto = cylinder & DIABLO_CYLINDER_MASK;
1018 LOG_DRIVE(0,"[DHD%u] seek to cylinder:%d\n", m_unit, m_seekto);
1019 }
1020 }
1021
1022 /**
1023 * @brief set the restore line
1024 *
1025 * If the restore line is asserted when the
1026 * STROBE line is pulsed, the drive seeks
1027 * towards cylinder 0.
1028 *
1029 * @param restore state of the restore line
1030 */
set_restore(int restore)1031 void diablo_hd_device::set_restore(int restore)
1032 {
1033 if ((restore & 1) != m_restore) {
1034 m_restore = restore & 1;
1035 LOG_DRIVE(0,"[DHD%u] restore:%d\n", m_unit, m_restore);
1036 }
1037 }
1038
1039 /**
1040 * @brief strobe a seek operation
1041 *
1042 * Seek to the specified cylinder m_seekto,
1043 * or restore to cylinder 0, if m_restore is set.
1044 *
1045 * @param strobe current level of the strobe signal (for edge detection)
1046 */
set_strobe(int strobe)1047 void diablo_hd_device::set_strobe(int strobe)
1048 {
1049 int seekto = m_restore ? 0 : m_seekto;
1050 if (strobe) {
1051 LOG_DRIVE(1,"[DHD%u] STROBE end of interlock\n", m_unit);
1052 // deassert the log address interlock
1053 m_log_addx_interlock_0 = 1;
1054 return;
1055 }
1056
1057 // assert the log address interlock
1058 m_log_addx_interlock_0 = 0;
1059
1060 if (seekto == m_cylinder) {
1061 LOG_DRIVE(1,"[DHD%u] STROBE to cylinder %d acknowledge\n", m_unit, seekto);
1062 m_addx_acknowledge_0 = 0; // address acknowledge, if cylinder is reached
1063 m_seek_incomplete_0 = 1; // reset seek incomplete
1064 return;
1065 }
1066 // assert the seek-read-write signal
1067 m_s_r_w_0 = 0;
1068
1069 bool complete = true;
1070 if (seekto < m_cylinder) {
1071 m_cylinder--; // previous cylinder
1072 if (m_cylinder < 0) {
1073 m_cylinder = 0;
1074 complete = false;
1075 }
1076 }
1077 if (seekto > m_cylinder) {
1078 /* increment cylinder */
1079 m_cylinder++;
1080 if (m_cylinder >= m_cylinders) {
1081 m_cylinder = m_cylinders - 1;
1082 complete = false;
1083 }
1084 }
1085 if (complete) {
1086 LOG_DRIVE(1,"[DHD%u] STROBE to cylinder %d (now %d) - interlock\n", m_unit, seekto, m_cylinder);
1087 m_addx_acknowledge_0 = 1; // deassert address acknowledge signal
1088 m_seek_incomplete_0 = 1; // deassert seek incomplete signal
1089 read_sector();
1090 } else {
1091 m_log_addx_interlock_0 = 0; // deassert the log address interlock signal
1092 m_seek_incomplete_0 = 1; // deassert seek incomplete signal
1093 m_addx_acknowledge_0 = 0; // assert address acknowledge signal
1094 LOG_DRIVE(1,"[DHD%u] STROBE to cylinder %d incomplete\n", m_unit, seekto);
1095 }
1096 }
1097
1098 /**
1099 * @brief set the drive erase gate
1100 * @param gate value of erase gate
1101 */
set_egate(int gate)1102 void diablo_hd_device::set_egate(int gate)
1103 {
1104 m_egate_0 = gate & 1;
1105 }
1106
1107 /**
1108 * @brief set the drive write gate
1109 * @param gate value of write gate
1110 */
set_wrgate(int gate)1111 void diablo_hd_device::set_wrgate(int gate)
1112 {
1113 m_wrgate_0 = gate & 1;
1114 }
1115
1116 /**
1117 * @brief set the drive read gate
1118 * @param gate value of read gate
1119 */
set_rdgate(int gate)1120 void diablo_hd_device::set_rdgate(int gate)
1121 {
1122 m_rdgate_0 = gate & 1;
1123 }
1124
1125 /**
1126 * @brief write the sector relative bit at index
1127 *
1128 * The disk controller writes a combined clock and data pulse to one output
1129 * <PRE>
1130 * Encoding of binary 01011
1131 *
1132 * clk data clk data clk data clk data clk data
1133 * 0 1 2 3 4 5 6 7 8 9
1134 * +--+ +--+ +--+ +--+ +--+ +--+ +--+ +--+ +--
1135 * | | | | | | | | | | | | | | | | |
1136 * --+ +--------+ +--+ +--+ +--------+ +--+ +--+ +--+ +--+
1137 * </PRE>
1138 *
1139 * @param index relative index of bit/clock into sector
1140 * @param wrdata write data clock or bit
1141 */
wr_data(int index,int wrdata)1142 void diablo_hd_device::wr_data(int index, int wrdata)
1143 {
1144 if (m_wrgate_0) {
1145 LOG_DRIVE(0,"[DHD%u] index=%d wrgate not asserted\n", m_unit, index);
1146 return; // write gate is not asserted (active 0)
1147 }
1148
1149 if (index < 0 || index >= bits_per_sector()) {
1150 LOG_DRIVE(0,"[DHD%u] index=%d out of range\n", m_unit, index);
1151 return; // don't write before or beyond the sector
1152 }
1153
1154 if (-1 == m_page) {
1155 LOG_DRIVE(0,"[DHD%u] invalid page\n", m_unit);
1156 return; // invalid page
1157 }
1158
1159 uint32_t *bits = expand_sector();
1160 if (!bits) {
1161 LOG_DRIVE(0,"[DHD%u] no bits\n", m_unit);
1162 return; // invalid unit
1163 }
1164
1165 if (-1 == m_wrfirst)
1166 m_wrfirst = index;
1167
1168 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d index #%d bit:%d\n", m_unit, m_cylinder, m_head, m_sector, index, wrdata);
1169
1170 if (index < GUARD_ZONE_BITS) {
1171 /* don't write in the guard zone (?) */
1172 } else {
1173 WRBIT(bits,index,wrdata);
1174 }
1175 m_wrlast = index;
1176 }
1177
1178 /**
1179 * @brief read the sector relative bit at index
1180 *
1181 * Note: this is a gross hack to allow the controller pulling bits
1182 * at its will, rather than clocking them with the drive's RDCLK-
1183 *
1184 * @param index is the sector relative bit index
1185 * @return returns the sector's bit by index
1186 */
rd_data(int index)1187 int diablo_hd_device::rd_data(int index)
1188 {
1189 int bit = 0;
1190
1191 if (m_rdgate_0) {
1192 LOG_DRIVE(1,"[DHD%u] index=%d rdgate not asserted\n", m_unit, index);
1193 return 0; // read gate is not asserted (active 0)
1194 }
1195
1196 if (index < 0 || index >= bits_per_sector()) {
1197 LOG_DRIVE(0,"[DHD%u] index=%d out of range\n", m_unit, index);
1198 return 1; // don't read before or beyond the sector
1199 }
1200
1201 if (0 == m_sector_mark_0) {
1202 LOG_DRIVE(0,"[DHD%u] read while sector mark is asserted\n", m_unit);
1203 return 1; // no data while sector mark is asserted
1204 }
1205
1206 if (-1 == m_page) {
1207 LOG_DRIVE(0,"[DHD%u] invalid page\n", m_unit);
1208 return 1; // invalid unit
1209 }
1210
1211 uint32_t *bits = expand_sector();
1212 if (!bits) {
1213 LOG_DRIVE(0,"[DHD%u] no bits\n", m_unit);
1214 return 1; // invalid page
1215 }
1216
1217 if (-1 == m_rdfirst)
1218 m_rdfirst = index;
1219
1220 RDBIT(bits,index,bit);
1221 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d index #%d bit:%d\n", m_unit, m_cylinder, m_head, m_sector, index, bit);
1222 m_rdlast = index;
1223 return bit;
1224 }
1225
1226 /**
1227 * @brief get the sector relative clock at index
1228 *
1229 * Note: this is a gross hack to allow the controller pulling bits
1230 * at its will, rather than clocking them with the drive's RDCLK-
1231 *
1232 * @param index is the sector relative bit index
1233 * @return returns the sector's clock bit by index
1234 */
rd_clock(int index)1235 int diablo_hd_device::rd_clock(int index)
1236 {
1237 int clk = 0;
1238
1239 if (index < 0 || index >= bits_per_sector()) {
1240 LOG_DRIVE(0,"[DHD%u] index out of range (%d)\n", m_unit, index);
1241 return 1; // don't read before or beyond the sector
1242 }
1243
1244 if (0 == m_sector_mark_0) {
1245 LOG_DRIVE(0,"[DHD%u] read while sector mark is asserted\n", m_unit);
1246 return 1; // no clock while sector mark is low (?)
1247 }
1248
1249 if (-1 == m_page) {
1250 LOG_DRIVE(0,"[DHD%u] invalid page\n", m_unit);
1251 return 1; // invalid page
1252 }
1253
1254 uint32_t *bits = expand_sector();
1255 if (!bits) {
1256 LOG_DRIVE(0,"[DHD%u] no bits\n", m_unit);
1257 return 1; // invalid unit
1258 }
1259
1260 if (-1 == m_rdfirst)
1261 m_rdfirst = index;
1262
1263 if (index & 1) {
1264 // clock bits are on even bit positions only
1265 clk = 0;
1266 } else if (bits) {
1267 RDBIT(bits,index,clk);
1268 } else {
1269 clk = 0;
1270 }
1271 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d index #%d clk:%d\n", m_unit, m_cylinder, m_head, m_sector, index, clk);
1272 m_rdlast = index;
1273 return clk ^ 1;
1274 }
1275
1276 /**
1277 * @brief deassert the sector mark
1278 *
1279 */
sector_mark_1()1280 void diablo_hd_device::sector_mark_1()
1281 {
1282 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d sector_mark_0=1\n", m_unit, m_cylinder, m_head, m_sector);
1283 m_sector_mark_0 = 1; // deassert sector mark (set to 1)
1284 }
1285
1286 /**
1287 * @brief assert the sector mark and read the next sector
1288 *
1289 * Assert the sector mark and reset the read and write
1290 * first and last bit indices.
1291 * Increment the sector number, wrap and read the
1292 * next sector from the media.
1293 */
sector_mark_0()1294 void diablo_hd_device::sector_mark_0()
1295 {
1296 LOG_DRIVE(9,"[DHD%u] CHS:%03d/%d/%02d sector_mark_0=0\n", m_unit, m_cylinder, m_head, m_sector);
1297
1298 // HACK: deassert wrgate
1299 // m_wrgate_0 = 1;
1300
1301 squeeze_sector(); // squeeze previous sector bits, if it was written to
1302 m_sector_mark_0 = 0; // assert sector mark (set to 0)
1303 // reset read and write bit locations
1304 m_rdfirst = -1;
1305 m_rdlast = -1;
1306 m_wrfirst = -1;
1307 m_wrlast = -1;
1308
1309 // count up the sector number
1310 m_sector = (m_sector + 1) % DIABLO_SPT;
1311 read_sector();
1312 }
1313
device_start()1314 void diablo_hd_device::device_start()
1315 {
1316 m_image = static_cast<diablo_image_device *>(subdevice("drive"));
1317
1318 m_packs = 1; // FIXME: get from configuration?
1319 m_unit = strstr(m_image->tag(), "diablo0") ? 0 : 1;
1320 m_timer = timer_alloc(1, nullptr);
1321 }
1322
device_reset()1323 void diablo_hd_device::device_reset()
1324 {
1325 // free previous page cache
1326 for (int page = 0; page < m_pages; page++)
1327 if (m_cache[page])
1328 m_cache[page] = nullptr;
1329 // free previous bits cache
1330 m_bits.reset();
1331 m_handle = m_image->get_chd_file();
1332 m_diablo31 = true; // FIXME: get from m_handle meta data?
1333 m_disk = m_image->get_hard_disk_file();
1334 if (m_diablo31) {
1335 snprintf(m_description, sizeof(m_description), "DIABLO31");
1336 m_rotation_time = DIABLO31_ROTATION_TIME;
1337 m_sector_time = DIABLO31_ROTATION_TIME / DIABLO_SPT;
1338 m_sector_mark_0_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
1339 m_sector_mark_1_time = DIABLO31_SECTOR_MARK_PULSE_PRE;
1340 m_bit_time = DIABLO31_BIT_TIME(1);
1341 m_cylinders = DIABLO_CYLINDERS;
1342 m_pages = DIABLO_PAGES;
1343 } else {
1344 snprintf(m_description, sizeof(m_description), "DIABLO44");
1345 m_rotation_time = DIABLO44_ROTATION_TIME;
1346 m_sector_time = DIABLO44_ROTATION_TIME / DIABLO_SPT;
1347 m_sector_mark_0_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
1348 m_sector_mark_1_time = DIABLO44_SECTOR_MARK_PULSE_PRE;
1349 m_bit_time = DIABLO44_BIT_TIME(1);
1350 m_cylinders = 2 * DIABLO_CYLINDERS;
1351 m_pages = 2 * DIABLO_PAGES;
1352 }
1353 LOG_DRIVE(0,"[DHD%u] m_handle : %p\n", m_unit, reinterpret_cast<void const *>(m_handle));
1354 LOG_DRIVE(0,"[DHD%u] m_disk : %p\n", m_unit, reinterpret_cast<void const *>(m_disk));
1355 LOG_DRIVE(0,"[DHD%u] rotation time : %.0fns\n", m_unit, m_rotation_time.as_double() * ATTOSECONDS_PER_NANOSECOND);
1356 LOG_DRIVE(0,"[DHD%u] sector time : %.0fns\n", m_unit, m_sector_time.as_double() * ATTOSECONDS_PER_NANOSECOND);
1357 LOG_DRIVE(0,"[DHD%u] sector mark 0 time : %.0fns\n", m_unit, m_sector_mark_0_time.as_double() * ATTOSECONDS_PER_NANOSECOND);
1358 LOG_DRIVE(0,"[DHD%u] sector mark 1 time : %.0fns\n", m_unit, m_sector_mark_1_time.as_double() * ATTOSECONDS_PER_NANOSECOND);
1359 LOG_DRIVE(0,"[DHD%u] bit time : %.0fns\n", m_unit, m_bit_time.as_double() * ATTOSECONDS_PER_NANOSECOND);
1360
1361 m_s_r_w_0 = 1; // deassert seek/read/write ready
1362 m_ready_0 = 1; // deassert drive ready
1363 m_sector_mark_0 = 1; // deassert sector mark
1364 m_addx_acknowledge_0 = 1; // deassert drive address acknowledge
1365 m_log_addx_interlock_0 = 1; // deassert drive log address interlock
1366 m_seek_incomplete_0 = 1; // deassert drive seek incomplete
1367
1368 // reset the disk drive's strobe info
1369 m_seekto = 0;
1370 m_restore = 0;
1371 // reset the disk drive's address
1372 m_cylinder = 0;
1373 m_head = 0;
1374 m_sector = 0;
1375 m_page = 0;
1376
1377 // disable the erase, write and read gates
1378 m_egate_0 = 1;
1379 m_wrgate_0 = 1;
1380 m_rdgate_0 = 1;
1381
1382 // reset read and write first and last indices
1383 m_wrfirst = -1;
1384 m_wrlast = -1;
1385 m_rdfirst = -1;
1386 m_rdlast = -1;
1387
1388 if (!m_handle)
1389 return;
1390 // for units with a CHD assigned to them start the timer
1391 m_bits = std::make_unique<std::unique_ptr<uint32_t[]>[]>(m_pages);
1392 timer_set(m_sector_time - m_sector_mark_0_time, 1, 0);
1393 read_sector();
1394 }
1395
1396 /**
1397 * @brief timer callback that is called thrice per sector in the rotation
1398 *
1399 * The timer is called three times at the events:
1400 * 0: sector mark goes active
1401 * 1: sector mark goes inactive
1402 * 2: in the middle of the active phase
1403 *
1404 * @param id timer id
1405 * @param arg argument supplied to timer_insert (unused)
1406 */
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)1407 void diablo_hd_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
1408 {
1409 LOG_DRIVE(9,"[DHD%u] TIMER id=%d param=%d ptr=%p @%.0fns\n", m_unit, id, param, ptr, timer.elapsed().as_double() * ATTOSECONDS_PER_NANOSECOND);
1410 if (!m_disk)
1411 return;
1412
1413 switch (param) {
1414 case 0:
1415 // assert sector mark
1416 sector_mark_0();
1417 // next sector timer event is in the middle between sector_mark going 0 and back to 1
1418 timer.adjust(m_sector_mark_0_time, 1);
1419 break;
1420 case 1:
1421 /* call the sector_callback, if any */
1422 if (m_sector_callback)
1423 (void)(*m_sector_callback)(m_sector_callback_cookie, m_unit);
1424 // next sector timer event is deassert of sector_mark_0 (set to 1)
1425 timer.adjust(m_sector_mark_1_time, 2);
1426 break;
1427 case 2:
1428 // deassert sector mark
1429 sector_mark_1();
1430 // next sector timer event is sector_mark_0 for next sector
1431 timer.adjust(m_sector_time - m_sector_mark_0_time, 0);
1432 break;
1433 }
1434 }
1435
device_add_mconfig(machine_config & config)1436 void diablo_hd_device::device_add_mconfig(machine_config &config)
1437 {
1438 DIABLO(config, "drive", 0);
1439 }
1440
1441
1442 DEFINE_DEVICE_TYPE(DIABLO_HD, diablo_hd_device, "diablo_hd", "Diablo Disk")
1443