First Commit of my working state
[simh.git] / AltairZ80 / s100_disk2.c
1 /*************************************************************************
2 * *
3 * $Id: s100_disk2.c 1771 2008-01-09 07:10:46Z hharte $ *
4 * *
5 * Copyright (c) 2007-2008 Howard M. Harte. *
6 * http://www.hartetec.com *
7 * *
8 * Permission is hereby granted, free of charge, to any person obtaining *
9 * a copy of this software and associated documentation files (the *
10 * "Software"), to deal in the Software without restriction, including *
11 * without limitation the rights to use, copy, modify, merge, publish, *
12 * distribute, sublicense, and/or sell copies of the Software, and to *
13 * permit persons to whom the Software is furnished to do so, subject to *
14 * the following conditions: *
15 * *
16 * The above copyright notice and this permission notice shall be *
17 * included in all copies or substantial portions of the Software. *
18 * *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *
22 * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY *
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
26 * *
27 * Except as contained in this notice, the name of Howard M. Harte shall *
28 * not be used in advertising or otherwise to promote the sale, use or *
29 * other dealings in this Software without prior written authorization *
30 * Howard M. Harte. *
31 * *
32 * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. *
33 * *
34 * Module Description: *
35 * CompuPro DISK2 Hard Disk Controller module for SIMH. *
36 * This module must be used in conjunction with the CompuPro Selector *
37 * Channel Module for proper operation. *
38 * *
39 * Environment: *
40 * User mode only *
41 * *
42 *************************************************************************/
43
44 #include "altairz80_defs.h"
45
46 #if defined (_WIN32)
47 #include <windows.h>
48 #endif
49
50 #include "sim_imd.h"
51
52 #define SEEK_MSG 0x01
53 #define BUG_MSG 0x02
54 #define CMD_MSG 0x04
55 #define RD_DATA_MSG 0x08
56 #define WR_DATA_MSG 0x10
57 #define STATUS_MSG 0x20
58 #define VERBOSE_MSG 0x80
59
60 #define DISK2_MAX_DRIVES 4
61
62 typedef union {
63 uint8 raw[2051];
64 struct {
65 uint8 header[3];
66 uint8 data[2048];
67 } u;
68 } SECTOR_FORMAT;
69
70 static SECTOR_FORMAT sdata;
71
72 typedef struct {
73 UNIT *uptr;
74 DISK_INFO *imd;
75 uint16 ntracks; /* number of tracks */
76 uint8 nheads; /* number of heads */
77 uint8 nsectors; /* number of sectors/track */
78 uint32 sectsize; /* sector size, not including pre/postamble */
79 uint16 track; /* Current Track */
80 uint8 ready; /* Is drive ready? */
81 } DISK2_DRIVE_INFO;
82
83 typedef struct {
84 PNP_INFO pnp; /* Plug and Play */
85 uint8 sel_drive; /* Currently selected drive */
86 uint8 head_sel; /* Head select (signals to drive itself) */
87 uint8 head; /* Head set by write to the HEAD register */
88 uint8 cyl; /* Cyl that the current operation is targetting */
89 uint8 sector; /* Sector the current READ/WRITE operation is targetting */
90 uint8 hdr_sector; /* Current sector for WRITE_HEADER */
91 uint8 ctl_attn;
92 uint8 ctl_run;
93 uint8 ctl_op;
94 uint8 ctl_fault_clr;
95 uint8 ctl_us;
96 uint8 timeout;
97 uint8 crc_error;
98 uint8 overrun;
99 uint8 seek_complete;
100 uint8 write_fault;
101 DISK2_DRIVE_INFO drive[DISK2_MAX_DRIVES];
102 } DISK2_INFO;
103
104 static DISK2_INFO disk2_info_data = { { 0x0, 0, 0xC8, 2 } };
105 static DISK2_INFO *disk2_info = &disk2_info_data;
106
107 extern uint32 PCX;
108 extern REG *sim_PC;
109 extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
110 extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
111 extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
112 int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
113 extern int32 selchan_dma(uint8 *buf, uint32 len);
114 extern int32 find_unit_index(UNIT *uptr);
115
116 /* These are needed for DMA. PIO Mode has not been implemented yet. */
117 extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);
118 extern uint8 GetBYTEWrapper(const uint32 Addr);
119
120 #define UNIT_V_DISK2_WLK (UNIT_V_UF + 0) /* write locked */
121 #define UNIT_DISK2_WLK (1 << UNIT_V_DISK2_WLK)
122 #define UNIT_V_DISK2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
123 #define UNIT_DISK2_VERBOSE (1 << UNIT_V_DISK2_VERBOSE)
124 #define DISK2_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */
125 #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */
126 #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */
127 #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */
128
129 static t_stat disk2_reset(DEVICE *disk2_dev);
130 static t_stat disk2_attach(UNIT *uptr, char *cptr);
131 static t_stat disk2_detach(UNIT *uptr);
132
133 static int32 disk2dev(const int32 port, const int32 io, const int32 data);
134
135 static uint8 DISK2_Read(const uint32 Addr);
136 static uint8 DISK2_Write(const uint32 Addr, uint8 cData);
137
138 static int32 trace_level = 0; /* Disable all tracing by default */
139
140 static UNIT disk2_unit[] = {
141 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
142 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
143 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
144 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }
145 };
146
147 static REG disk2_reg[] = {
148 { HRDATA (TRACELEVEL, trace_level, 16), },
149 { NULL }
150 };
151
152 static MTAB disk2_mod[] = {
153 { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },
154 { UNIT_DISK2_WLK, 0, "WRTENB", "WRTENB", NULL },
155 { UNIT_DISK2_WLK, UNIT_DISK2_WLK, "WRTLCK", "WRTLCK", NULL },
156 /* quiet, no warning messages */
157 { UNIT_DISK2_VERBOSE, 0, "QUIET", "QUIET", NULL },
158 /* verbose, show warning messages */
159 { UNIT_DISK2_VERBOSE, UNIT_DISK2_VERBOSE, "VERBOSE", "VERBOSE", NULL },
160 { 0 }
161 };
162
163 DEVICE disk2_dev = {
164 "DISK2", disk2_unit, disk2_reg, disk2_mod,
165 DISK2_MAX_DRIVES, 10, 31, 1, DISK2_MAX_DRIVES, DISK2_MAX_DRIVES,
166 NULL, NULL, &disk2_reset,
167 NULL, &disk2_attach, &disk2_detach,
168 &disk2_info_data, (DEV_DISABLE | DEV_DIS), 0,
169 NULL, NULL, NULL
170 };
171
172 /* Reset routine */
173 static t_stat disk2_reset(DEVICE *dptr)
174 {
175 PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
176
177 if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */
178 sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, TRUE);
179 } else {
180 /* Connect DISK2 at base address */
181 if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, FALSE) != 0) {
182 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
183 return SCPE_ARG;
184 }
185 }
186 return SCPE_OK;
187 }
188
189
190 /* Attach routine */
191 static t_stat disk2_attach(UNIT *uptr, char *cptr)
192 {
193 t_stat r = SCPE_OK;
194 DISK2_DRIVE_INFO *pDrive;
195 char header[4];
196 unsigned int i = 0;
197
198 i = find_unit_index(uptr);
199 if (i == -1) {
200 return (SCPE_IERR);
201 }
202 pDrive = &disk2_info->drive[i];
203
204 pDrive->ready = 1;
205 disk2_info->write_fault = 1;
206 pDrive->track = 5;
207 pDrive->ntracks = 243;
208 pDrive->nheads = 8;
209 pDrive->nsectors = 11;
210 pDrive->sectsize = 1024;
211
212 r = attach_unit(uptr, cptr); /* attach unit */
213 if ( r != SCPE_OK) /* error? */
214 return r;
215
216 /* Determine length of this disk */
217 if(sim_fsize(uptr->fileref) != 0) {
218 uptr->capac = sim_fsize(uptr->fileref);
219 } else {
220 uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize);
221 }
222
223 pDrive->uptr = uptr;
224
225 /* Default for new file is DSK */
226 uptr->u3 = IMAGE_TYPE_DSK;
227
228 if(uptr->capac > 0) {
229 fgets(header, 4, uptr->fileref);
230 if(!strcmp(header, "IMD")) {
231 uptr->u3 = IMAGE_TYPE_IMD;
232 } else if(!strcmp(header, "CPT")) {
233 printf("CPT images not yet supported\n");
234 uptr->u3 = IMAGE_TYPE_CPT;
235 disk2_detach(uptr);
236 return SCPE_OPENERR;
237 } else {
238 uptr->u3 = IMAGE_TYPE_DSK;
239 }
240 }
241
242 if (uptr->flags & UNIT_DISK2_VERBOSE)
243 printf("DISK2%d, attached to '%s', type=%s, len=%d\n", i, cptr,
244 uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
245 uptr->capac);
246
247 if(uptr->u3 == IMAGE_TYPE_IMD) {
248 if(uptr->capac < 318000) {
249 printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n");
250 disk2_detach(uptr);
251 return SCPE_OPENERR;
252 }
253
254 if (uptr->flags & UNIT_DISK2_VERBOSE)
255 printf("--------------------------------------------------------\n");
256 disk2_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_DISK2_VERBOSE));
257 if (uptr->flags & UNIT_DISK2_VERBOSE) printf("\n");
258 } else {
259 disk2_info->drive[i].imd = NULL;
260 }
261
262 return SCPE_OK;
263 }
264
265
266 /* Detach routine */
267 t_stat disk2_detach(UNIT *uptr)
268 {
269 t_stat r;
270 int8 i;
271
272 i = find_unit_index(uptr);
273
274 if (i == -1) {
275 return (SCPE_IERR);
276 }
277
278 if (uptr->flags & UNIT_DISK2_VERBOSE)
279 printf("Detach DISK2%d\n", i);
280
281 r = detach_unit(uptr); /* detach unit */
282 if ( r != SCPE_OK)
283 return r;
284
285 return SCPE_OK;
286 }
287
288
289 static int32 disk2dev(const int32 port, const int32 io, const int32 data)
290 {
291 /* TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); */
292 if(io) {
293 DISK2_Write(port, data);
294 return 0;
295 } else {
296 return(DISK2_Read(port));
297 }
298 }
299
300 #define DISK2_CSR 0 /* R=DISK2 Status / W=DISK2 Control Register */
301 #define DISK2_DATA 1 /* R=Step Pulse / W=Write Data Register */
302
303 static uint8 DISK2_Read(const uint32 Addr)
304 {
305 uint8 cData;
306 DISK2_DRIVE_INFO *pDrive;
307
308 pDrive = &disk2_info->drive[disk2_info->sel_drive];
309 cData = 0x00;
310
311 switch(Addr & 0x1) {
312 case DISK2_CSR:
313 cData = (disk2_info->ctl_attn) << 7;
314 cData |= (disk2_info->timeout) << 6;
315 cData |= (disk2_info->crc_error) << 5;
316 cData |= (disk2_info->overrun) << 4;
317 cData |= (pDrive->ready == 0) ? 0x08 : 0x00;
318 cData |= (disk2_info->seek_complete == 0) ? 0x04 : 0x00;
319 cData |= (disk2_info->write_fault) << 1;
320 cData |= ((pDrive->track != 0) || (disk2_info->seek_complete == 0)) ? 0x01 : 0x00;
321 TRACE_PRINT(STATUS_MSG, ("DISK2: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData));
322
323 disk2_info->seek_complete = 1;
324 break;
325 case DISK2_DATA:
326 if(disk2_info->ctl_op & 0x04) {
327 if(pDrive->track < pDrive->ntracks) {
328 pDrive->track ++;
329 }
330 } else {
331 if(pDrive->track > 0) {
332 pDrive->track --;
333 }
334 }
335 TRACE_PRINT(SEEK_MSG, ("DISK2: " ADDRESS_FORMAT " Step %s, Track=%d" NLP,
336 PCX, disk2_info->ctl_op & 0x04 ? "IN" : "OUT", pDrive->track));
337 disk2_info->seek_complete = 0;
338 cData = 0xFF; /* Return High-Z data */
339 break;
340 }
341
342 return (cData);
343 }
344
345 #define DISK2_OP_DRIVE 0x00
346 #define DISK2_OP_CYL 0x01
347 #define DISK2_OP_HEAD 0x02
348 #define DISK2_OP_SECTOR 0x03
349
350 #define DISK2_CMD_NULL 0x00
351 #define DISK2_CMD_READ_DATA 0x01
352 #define DISK2_CMD_WRITE_DATA 0x02
353 #define DISK2_CMD_WRITE_HEADER 0x03
354 #define DISK2_CMD_READ_HEADER 0x04
355
356 static uint8 DISK2_Write(const uint32 Addr, uint8 cData)
357 {
358 uint32 track_offset;
359 uint8 result = 0;
360 uint8 i;
361 long file_offset;
362 DISK2_DRIVE_INFO *pDrive;
363
364 pDrive = &disk2_info->drive[disk2_info->sel_drive];
365
366 switch(Addr & 0x1) {
367 case DISK2_CSR: /* Write CTL register */
368 disk2_info->ctl_attn = (cData & 0x80) >> 7;
369 disk2_info->ctl_run = (cData & 0x40) >> 6;
370 disk2_info->ctl_op = (cData & 0x38) >> 3;
371 disk2_info->ctl_fault_clr = (cData & 0x04) >> 2;
372 if(disk2_info->ctl_fault_clr == 1) {
373 disk2_info->timeout = 0;
374 }
375 disk2_info->ctl_us = (cData & 0x03);
376 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " ATTN*=%d, RUN=%d, OP=%d, FAULT_CLR=%d, US=%d" NLP,
377 PCX,
378 disk2_info->ctl_attn,
379 disk2_info->ctl_run,
380 disk2_info->ctl_op,
381 disk2_info->ctl_fault_clr,
382 disk2_info->ctl_us));
383
384 /* FIXME: seek_complete = 1 is needed by CP/M, but why? Also, maybe related,
385 * there appears to be a bug in the seeking logic. For some reason, the
386 * pDrive->track does not equal the disk2_info->cyl, when doing READ_DATA and
387 * WRITE_DATA commands. For this reason, disk2_info->cyl is used instead of
388 * pDrive->track for these commands. For READ_HEADER and WRITE_HEADER,
389 * pDrive->track is used, because the DISK2 format program (DISK2.COM) does not
390 * issue DISK2_OP_CYL. The root cause of this anomaly needs to be determined,
391 * because it is surely a bug in the logic somewhere.
392 */
393 /* pDrive->track may be different from disk2_info->cyl when a program such as DISK2.COM
394 moves the position of the track without informing the CP/M BIOS which stores the
395 current track for each drive. This appears to be an application program bug.
396 */
397 disk2_info->seek_complete = 1;
398
399 if(disk2_info->ctl_run == 1) {
400 disk2_info->timeout = 0;
401 track_offset = disk2_info->cyl * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
402
403 switch(disk2_info->ctl_op) {
404 case DISK2_CMD_NULL:
405 TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " NULL Command" NLP, PCX));
406 break;
407 case DISK2_CMD_READ_DATA:
408 TRACE_PRINT(RD_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: (C:%d/H:%d/S:%d)" NLP,
409 PCX,
410 disk2_info->cyl,
411 disk2_info->head,
412 disk2_info->sector));
413 if(disk2_info->head_sel != disk2_info->head) {
414 printf("DISK2: " ADDRESS_FORMAT " READ_DATA: head_sel != head" NLP, PCX);
415 }
416 /* See FIXME above... that might be why this does not work properly... */
417 if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */
418 TRACE_PRINT(BUG_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: cyl=%d, track=%d" NLP,
419 PCX, disk2_info->cyl, pDrive->track));
420 pDrive->track = disk2_info->cyl; /* update track */
421 }
422 fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
423 for(i=0;i<pDrive->nsectors;i++) {
424 /* Read sector */
425 fread(sdata.raw, (pDrive->sectsize + 3), 1, (pDrive->uptr)->fileref);
426 if(sdata.u.header[2] == disk2_info->sector) {
427 if(sdata.u.header[0] != disk2_info->cyl) { /*pDrive->track) { */
428 printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: track" NLP, PCX);
429 disk2_info->timeout = 1;
430 }
431 if(sdata.u.header[1] != disk2_info->head) {
432 printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: head" NLP, PCX);
433 disk2_info->timeout = 1;
434 }
435
436 selchan_dma(sdata.u.data, pDrive->sectsize);
437 break;
438 }
439 if(i == pDrive->nsectors) {
440 printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX);
441 disk2_info->timeout = 1;
442 }
443 }
444
445 break;
446 case DISK2_CMD_WRITE_DATA:
447 TRACE_PRINT(WR_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA: (C:%d/H:%d/S:%d)" NLP,
448 PCX,
449 disk2_info->cyl,
450 disk2_info->head,
451 disk2_info->sector));
452 if(disk2_info->head_sel != disk2_info->head) {
453 printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA: head_sel != head" NLP, PCX);
454 }
455 if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */
456 TRACE_PRINT(BUG_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA = 0x%02x, cyl=%d, track=%d" NLP,
457 PCX, cData, disk2_info->cyl, pDrive->track));
458 pDrive->track = disk2_info->cyl; /* update track */
459 }
460
461 fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
462 for(i=0;i<pDrive->nsectors;i++) {
463 /* Read sector */
464 file_offset = ftell((pDrive->uptr)->fileref);
465 fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref);
466 if(sdata.u.header[2] == disk2_info->sector) {
467 if(sdata.u.header[0] != disk2_info->cyl) {
468 printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: track" NLP, PCX);
469 disk2_info->timeout = 1;
470 }
471 if(sdata.u.header[1] != disk2_info->head) {
472 printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: head" NLP, PCX);
473 disk2_info->timeout = 1;
474 }
475
476 selchan_dma(sdata.u.data, pDrive->sectsize);
477 fseek((pDrive->uptr)->fileref, file_offset+3, SEEK_SET);
478 fwrite(sdata.u.data, (pDrive->sectsize), 1, (pDrive->uptr)->fileref);
479 break;
480 }
481 fread(sdata.raw, pDrive->sectsize, 1, (pDrive->uptr)->fileref);
482 if(i == pDrive->nsectors) {
483 printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX);
484 disk2_info->timeout = 1;
485 }
486 }
487 break;
488 case DISK2_CMD_WRITE_HEADER:
489 track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
490 TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_HEADER Command: track=%d (%d), Head=%d, Sector=%d" NLP,
491 PCX,
492 pDrive->track,
493 disk2_info->cyl,
494 disk2_info->head_sel,
495 disk2_info->hdr_sector));
496
497 i = disk2_info->hdr_sector;
498 selchan_dma(sdata.raw, 3);
499 fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * (pDrive->sectsize + 3) * pDrive->nsectors) + (i * (pDrive->sectsize + 3)), SEEK_SET);
500 fwrite(sdata.raw, 3, 1, (pDrive->uptr)->fileref);
501
502 disk2_info->hdr_sector++;
503 if(disk2_info->hdr_sector >= pDrive->nsectors) {
504 disk2_info->hdr_sector = 0;
505 disk2_info->timeout = 1;
506 }
507 break;
508 case DISK2_CMD_READ_HEADER:
509 track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
510 TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " READ_HEADER Command" NLP, PCX));
511 fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
512 fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref);
513 selchan_dma(sdata.raw, 3);
514 break;
515 default:
516 printf("DISK2: " ADDRESS_FORMAT " Unknown CMD=%d" NLP, PCX, disk2_info->ctl_op);
517 break;
518 }
519
520 disk2_info->ctl_attn = 0;
521 }
522
523 break;
524 case DISK2_DATA:
525 switch(disk2_info->ctl_op) {
526 case DISK2_OP_DRIVE:
527 switch(cData >> 4) {
528 case 0x01:
529 disk2_info->sel_drive = 0;
530 break;
531 case 0x02:
532 disk2_info->sel_drive = 1;
533 break;
534 case 0x04:
535 disk2_info->sel_drive = 2;
536 break;
537 case 0x08:
538 disk2_info->sel_drive = 3;
539 break;
540 default:
541 printf("DISK2: " ADDRESS_FORMAT " Error, invalid drive select=0x%x" NLP, PCX, cData >> 4);
542 break;
543 }
544
545 disk2_info->head_sel = cData & 0x0F;
546
547 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [DRIVE]=%d, Head=%d" NLP,
548 PCX, disk2_info->sel_drive, disk2_info->head));
549 break;
550 case DISK2_OP_CYL:
551 disk2_info->cyl = cData;
552 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [CYL] = %02x" NLP,
553 PCX, cData));
554 break;
555 case DISK2_OP_HEAD:
556 disk2_info->head = cData;
557 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [HEAD] = %02x" NLP,
558 PCX, cData));
559 break;
560 case DISK2_OP_SECTOR:
561 disk2_info->sector = cData;
562 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register [SECTOR] = %02x" NLP,
563 PCX, cData));
564 break;
565 default:
566 TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register unknown op [%d] = %02x" NLP,
567 PCX, disk2_info->ctl_op, cData));
568 break;
569 }
570 }
571
572 return (result);
573 }
574