1 /*************************************************************************
2 * *
3 * $Id: s100_disk2.c 1995 2008-07-15 03:59:13Z 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 /* Debug flags */
53 #define ERROR_MSG (1 << 0)
54 #define SEEK_MSG (1 << 1)
55 #define CMD_MSG (1 << 2)
56 #define RD_DATA_MSG (1 << 3)
57 #define WR_DATA_MSG (1 << 4)
58 #define STATUS_MSG (1 << 5)
59 #define IRQ_MSG (1 << 6)
60 #define VERBOSE_MSG (1 << 7)
61
62 #define DISK2_MAX_DRIVES 4
63
64 typedef union {
65 uint8 raw[2051];
66 struct {
67 uint8 header[3];
68 uint8 data[2048];
69 } u;
70 } SECTOR_FORMAT;
71
72 static SECTOR_FORMAT sdata;
73
74 typedef struct {
75 UNIT *uptr;
76 DISK_INFO *imd;
77 uint16 ntracks; /* number of tracks */
78 uint8 nheads; /* number of heads */
79 uint8 nsectors; /* number of sectors/track */
80 uint32 sectsize; /* sector size, not including pre/postamble */
81 uint16 track; /* Current Track */
82 uint8 ready; /* Is drive ready? */
83 } DISK2_DRIVE_INFO;
84
85 typedef struct {
86 PNP_INFO pnp; /* Plug and Play */
87 uint8 sel_drive; /* Currently selected drive */
88 uint8 head_sel; /* Head select (signals to drive itself) */
89 uint8 head; /* Head set by write to the HEAD register */
90 uint8 cyl; /* Cyl that the current operation is targetting */
91 uint8 sector; /* Sector the current READ/WRITE operation is targetting */
92 uint8 hdr_sector; /* Current sector for WRITE_HEADER */
93 uint8 ctl_attn;
94 uint8 ctl_run;
95 uint8 ctl_op;
96 uint8 ctl_fault_clr;
97 uint8 ctl_us;
98 uint8 timeout;
99 uint8 crc_error;
100 uint8 overrun;
101 uint8 seek_complete;
102 uint8 write_fault;
103 DISK2_DRIVE_INFO drive[DISK2_MAX_DRIVES];
104 } DISK2_INFO;
105
106 static DISK2_INFO disk2_info_data = { { 0x0, 0, 0xC8, 2 } };
107 static DISK2_INFO *disk2_info = &disk2_info_data;
108
109 /* Default geometry for a 20MB hard disk. */
110 #define C20MB_NTRACKS 243
111 #define C20MB_NHEADS 8
112 #define C20MB_NSECTORS 11
113 #define C20MB_SECTSIZE 1024
114
115 static int32 ntracks = C20MB_NTRACKS;
116 static int32 nheads = C20MB_NHEADS;
117 static int32 nsectors = C20MB_NSECTORS;
118 static int32 sectsize = C20MB_SECTSIZE;
119
120 extern uint32 PCX;
121 extern REG *sim_PC;
122 extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);
123 extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);
124 extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,
125 int32 (*routine)(const int32, const int32, const int32), uint8 unmap);
126 extern int32 selchan_dma(uint8 *buf, uint32 len);
127 extern int32 find_unit_index(UNIT *uptr);
128 extern void raise_ss1_interrupt(uint8 intnum);
129
130 #define UNIT_V_DISK2_WLK (UNIT_V_UF + 0) /* write locked */
131 #define UNIT_DISK2_WLK (1 << UNIT_V_DISK2_WLK)
132 #define UNIT_V_DISK2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
133 #define UNIT_DISK2_VERBOSE (1 << UNIT_V_DISK2_VERBOSE)
134 #define DISK2_CAPACITY (C20MB_NTRACKS*C20MB_NHEADS*C20MB_NSECTORS*C20MB_SECTSIZE) /* Default Disk Capacity */
135
136 static t_stat disk2_reset(DEVICE *disk2_dev);
137 static t_stat disk2_attach(UNIT *uptr, char *cptr);
138 static t_stat disk2_detach(UNIT *uptr);
139 static void raise_disk2_interrupt(void);
140
141 static int32 disk2dev(const int32 port, const int32 io, const int32 data);
142
143 static uint8 DISK2_Read(const uint32 Addr);
144 static uint8 DISK2_Write(const uint32 Addr, uint8 cData);
145
146 static UNIT disk2_unit[] = {
147 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
148 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
149 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) },
150 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }
151 };
152
153 static REG disk2_reg[] = {
154 { DRDATA (NTRACKS, ntracks, 10), },
155 { DRDATA (NHEADS, nheads, 8), },
156 { DRDATA (NSECTORS, nsectors, 8), },
157 { DRDATA (SECTSIZE, sectsize, 11), },
158 { HRDATA (SEL_DRIVE, disk2_info_data.sel_drive, 3), },
159 { HRDATA (CYL, disk2_info_data.cyl, 8), },
160 { HRDATA (HEAD, disk2_info_data.head, 8), },
161 { HRDATA (SECTOR, disk2_info_data.sector, 8), },
162
163 { NULL }
164 };
165
166 static MTAB disk2_mod[] = {
167 { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },
168 { UNIT_DISK2_WLK, 0, "WRTENB", "WRTENB", NULL },
169 { UNIT_DISK2_WLK, UNIT_DISK2_WLK, "WRTLCK", "WRTLCK", NULL },
170 /* quiet, no warning messages */
171 { UNIT_DISK2_VERBOSE, 0, "QUIET", "QUIET", NULL },
172 /* verbose, show warning messages */
173 { UNIT_DISK2_VERBOSE, UNIT_DISK2_VERBOSE, "VERBOSE", "VERBOSE", NULL },
174 { 0 }
175 };
176
177 /* Debug Flags */
178 static DEBTAB disk2_dt[] = {
179 { "ERROR", ERROR_MSG },
180 { "SEEK", SEEK_MSG },
181 { "CMD", CMD_MSG },
182 { "RDDATA", RD_DATA_MSG },
183 { "WRDATA", WR_DATA_MSG },
184 { "STATUS", STATUS_MSG },
185 { "IRQ", IRQ_MSG },
186 { "VERBOSE",VERBOSE_MSG },
187 { NULL, 0 }
188 };
189
190 DEVICE disk2_dev = {
191 "DISK2", disk2_unit, disk2_reg, disk2_mod,
192 DISK2_MAX_DRIVES, 10, 31, 1, DISK2_MAX_DRIVES, DISK2_MAX_DRIVES,
193 NULL, NULL, &disk2_reset,
194 NULL, &disk2_attach, &disk2_detach,
195 &disk2_info_data, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), ERROR_MSG,
196 disk2_dt, NULL, "Compupro Hard Disk Controller DISK2"
197 };
198
199 /* Reset routine */
disk2_reset(DEVICE * dptr)200 static t_stat disk2_reset(DEVICE *dptr)
201 {
202 PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;
203
204 if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */
205 sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, TRUE);
206 } else {
207 /* Connect DISK2 at base address */
208 if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, FALSE) != 0) {
209 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base);
210 return SCPE_ARG;
211 }
212 }
213 return SCPE_OK;
214 }
215
216
217 /* Attach routine */
disk2_attach(UNIT * uptr,char * cptr)218 static t_stat disk2_attach(UNIT *uptr, char *cptr)
219 {
220 t_stat r = SCPE_OK;
221 DISK2_DRIVE_INFO *pDrive;
222 int i = 0;
223
224 i = find_unit_index(uptr);
225 if (i == -1) {
226 return (SCPE_IERR);
227 }
228 pDrive = &disk2_info->drive[i];
229
230 pDrive->ready = 1;
231 disk2_info->write_fault = 1;
232 pDrive->track = 5;
233 pDrive->ntracks = ntracks;
234 pDrive->nheads = nheads;
235 pDrive->nsectors = nsectors;
236 pDrive->sectsize = sectsize;
237
238 r = attach_unit(uptr, cptr); /* attach unit */
239 if ( r != SCPE_OK) /* error? */
240 return r;
241
242 /* Determine length of this disk */
243 if(sim_fsize(uptr->fileref) != 0) {
244 uptr->capac = sim_fsize(uptr->fileref);
245 } else {
246 uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize);
247 }
248
249 pDrive->uptr = uptr;
250
251 /* Default for new file is DSK */
252 uptr->u3 = IMAGE_TYPE_DSK;
253
254 if(uptr->capac > 0) {
255 r = assignDiskType(uptr);
256 if (r != SCPE_OK) {
257 disk2_detach(uptr);
258 return r;
259 }
260 }
261
262 if (uptr->flags & UNIT_DISK2_VERBOSE)
263 printf("DISK2%d, attached to '%s', type=%s, len=%d\n", i, cptr,
264 uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK",
265 uptr->capac);
266
267 if(uptr->u3 == IMAGE_TYPE_IMD) {
268 if(uptr->capac < 318000) {
269 printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n");
270 disk2_detach(uptr);
271 return SCPE_OPENERR;
272 }
273
274 if (uptr->flags & UNIT_DISK2_VERBOSE)
275 printf("--------------------------------------------------------\n");
276 disk2_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_DISK2_VERBOSE));
277 if (uptr->flags & UNIT_DISK2_VERBOSE)
278 printf("\n");
279 } else {
280 disk2_info->drive[i].imd = NULL;
281 }
282
283 return SCPE_OK;
284 }
285
286
287 /* Detach routine */
disk2_detach(UNIT * uptr)288 t_stat disk2_detach(UNIT *uptr)
289 {
290 t_stat r;
291 int8 i;
292
293 i = find_unit_index(uptr);
294
295 if (i == -1) {
296 return (SCPE_IERR);
297 }
298
299 if (uptr->flags & UNIT_DISK2_VERBOSE)
300 printf("Detach DISK2%d\n", i);
301
302 r = detach_unit(uptr); /* detach unit */
303 if ( r != SCPE_OK)
304 return r;
305
306 return SCPE_OK;
307 }
308
309
disk2dev(const int32 port,const int32 io,const int32 data)310 static int32 disk2dev(const int32 port, const int32 io, const int32 data)
311 {
312 /* sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT " IO %s, Port %02x\n", PCX, io ? "WR" : "RD", port); */
313 if(io) {
314 DISK2_Write(port, data);
315 return 0;
316 } else {
317 return(DISK2_Read(port));
318 }
319 }
320
321 #define DISK2_CSR 0 /* R=DISK2 Status / W=DISK2 Control Register */
322 #define DISK2_DATA 1 /* R=Step Pulse / W=Write Data Register */
323
DISK2_Read(const uint32 Addr)324 static uint8 DISK2_Read(const uint32 Addr)
325 {
326 uint8 cData;
327 DISK2_DRIVE_INFO *pDrive;
328
329 pDrive = &disk2_info->drive[disk2_info->sel_drive];
330 cData = 0x00;
331
332 switch(Addr & 0x1) {
333 case DISK2_CSR:
334 cData = (disk2_info->ctl_attn) << 7;
335 cData |= (disk2_info->timeout) << 6;
336 cData |= (disk2_info->crc_error) << 5;
337 cData |= (disk2_info->overrun) << 4;
338 cData |= (pDrive->ready == 0) ? 0x08 : 0x00;
339 cData |= (disk2_info->seek_complete == 0) ? 0x04 : 0x00;
340 cData |= (disk2_info->write_fault) << 1;
341 cData |= ((pDrive->track != 0) || (disk2_info->seek_complete == 0)) ? 0x01 : 0x00;
342 sim_debug(STATUS_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
343 " RD STATUS = 0x%02x\n", PCX, cData);
344
345 disk2_info->seek_complete = 1;
346 break;
347 case DISK2_DATA:
348 if(disk2_info->ctl_op & 0x04) {
349 if(pDrive->track < pDrive->ntracks) {
350 pDrive->track ++;
351 }
352 } else {
353 if(pDrive->track > 0) {
354 pDrive->track --;
355 }
356 }
357 sim_debug(SEEK_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
358 " Step %s, Track=%d\n", PCX,
359 disk2_info->ctl_op & 0x04 ? "IN" : "OUT", pDrive->track);
360 disk2_info->seek_complete = 0;
361 cData = 0xFF; /* Return High-Z data */
362 break;
363 }
364
365 return (cData);
366 }
367
368 #define DISK2_OP_DRIVE 0x00
369 #define DISK2_OP_CYL 0x01
370 #define DISK2_OP_HEAD 0x02
371 #define DISK2_OP_SECTOR 0x03
372
373 #define DISK2_CMD_NULL 0x00
374 #define DISK2_CMD_READ_DATA 0x01
375 #define DISK2_CMD_WRITE_DATA 0x02
376 #define DISK2_CMD_WRITE_HEADER 0x03
377 #define DISK2_CMD_READ_HEADER 0x04
378
DISK2_Write(const uint32 Addr,uint8 cData)379 static uint8 DISK2_Write(const uint32 Addr, uint8 cData)
380 {
381 uint32 track_offset;
382 uint8 result = 0;
383 uint8 i;
384 long file_offset;
385 DISK2_DRIVE_INFO *pDrive;
386 size_t rtn;
387
388 pDrive = &disk2_info->drive[disk2_info->sel_drive];
389
390 switch(Addr & 0x1) {
391 case DISK2_CSR: /* Write CTL register */
392 disk2_info->ctl_attn = (cData & 0x80) >> 7;
393 disk2_info->ctl_run = (cData & 0x40) >> 6;
394 disk2_info->ctl_op = (cData & 0x38) >> 3;
395 disk2_info->ctl_fault_clr = (cData & 0x04) >> 2;
396 if(disk2_info->ctl_fault_clr == 1) {
397 disk2_info->timeout = 0;
398 }
399 disk2_info->ctl_us = (cData & 0x03);
400 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
401 " ATTN*=%d, RUN=%d, OP=%d, FAULT_CLR=%d, US=%d\n",
402 PCX, disk2_info->ctl_attn, disk2_info->ctl_run,
403 disk2_info->ctl_op, disk2_info->ctl_fault_clr,
404 disk2_info->ctl_us);
405
406 /* FIXME: seek_complete = 1 is needed by CP/M, but why? Also, maybe related,
407 * there appears to be a bug in the seeking logic. For some reason, the
408 * pDrive->track does not equal the disk2_info->cyl, when doing READ_DATA and
409 * WRITE_DATA commands. For this reason, disk2_info->cyl is used instead of
410 * pDrive->track for these commands. For READ_HEADER and WRITE_HEADER,
411 * pDrive->track is used, because the DISK2 format program (DISK2.COM) does not
412 * issue DISK2_OP_CYL. The root cause of this anomaly needs to be determined,
413 * because it is surely a bug in the logic somewhere.
414 */
415 /* pDrive->track may be different from disk2_info->cyl when a program such as DISK2.COM
416 moves the position of the track without informing the CP/M BIOS which stores the
417 current track for each drive. This appears to be an application program bug.
418 */
419 disk2_info->seek_complete = 1;
420
421 if(disk2_info->ctl_run == 1) {
422 disk2_info->timeout = 0;
423 track_offset = disk2_info->cyl * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
424
425 switch(disk2_info->ctl_op) {
426 case DISK2_CMD_NULL:
427 sim_debug(CMD_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
428 " NULL Command\n", PCX);
429 break;
430 case DISK2_CMD_READ_DATA:
431 sim_debug(RD_DATA_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
432 " READ_DATA: (C:%d/H:%d/S:%d)\n", PCX, disk2_info->cyl, disk2_info->head, disk2_info->sector);
433 if(disk2_info->head_sel != disk2_info->head) {
434 printf("DISK2: " ADDRESS_FORMAT
435 " READ_DATA: head_sel != head" NLP, PCX);
436 }
437 /* See FIXME above... that might be why this does not work properly... */
438 if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */
439 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
440 " READ_DATA: cyl=%d, track=%d\n", PCX, disk2_info->cyl, pDrive->track);
441 pDrive->track = disk2_info->cyl; /* update track */
442 }
443 sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
444 for(i=0;i<pDrive->nsectors;i++) {
445 /* Read sector */
446 rtn = sim_fread(sdata.raw, 1, (pDrive->sectsize + 3), (pDrive->uptr)->fileref);
447 if (rtn != (size_t)(pDrive->sectsize + 3)) {
448 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
449 " READ_DATA: sim_fread error.\n", PCX);
450 }
451 if(sdata.u.header[2] == disk2_info->sector) {
452 if(sdata.u.header[0] != disk2_info->cyl) { /*pDrive->track) { */
453 printf("DISK2: " ADDRESS_FORMAT
454 " READ_DATA Incorrect header: track" NLP, PCX);
455 disk2_info->timeout = 1;
456 }
457 if(sdata.u.header[1] != disk2_info->head) {
458 printf("DISK2: " ADDRESS_FORMAT
459 " READ_DATA Incorrect header: head" NLP, PCX);
460 disk2_info->timeout = 1;
461 }
462
463 selchan_dma(sdata.u.data, pDrive->sectsize);
464 break;
465 }
466 if(i == pDrive->nsectors) {
467 printf("DISK2: " ADDRESS_FORMAT
468 " Sector not found" NLP, PCX);
469 disk2_info->timeout = 1;
470 }
471 }
472
473 break;
474 case DISK2_CMD_WRITE_DATA:
475 sim_debug(WR_DATA_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
476 " WRITE_DATA: (C:%d/H:%d/S:%d)\n", PCX, disk2_info->cyl, disk2_info->head, disk2_info->sector);
477 if(disk2_info->head_sel != disk2_info->head) {
478 printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA: head_sel != head" NLP, PCX);
479 }
480 if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */
481 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
482 " WRITE_DATA = 0x%02x, cyl=%d, track=%d\n", PCX, cData, disk2_info->cyl, pDrive->track);
483 pDrive->track = disk2_info->cyl; /* update track */
484 }
485
486 sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
487 for(i=0;i<pDrive->nsectors;i++) {
488 /* Read sector */
489 file_offset = ftell((pDrive->uptr)->fileref);
490 rtn = sim_fread(sdata.raw, 1, 3, (pDrive->uptr)->fileref);
491 if (rtn != 3) {
492 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
493 " WRITE_DATA: sim_fread error.\n", PCX);
494 }
495 if(sdata.u.header[2] == disk2_info->sector) {
496 if(sdata.u.header[0] != disk2_info->cyl) {
497 printf("DISK2: " ADDRESS_FORMAT
498 " WRITE_DATA Incorrect header: track" NLP, PCX);
499 disk2_info->timeout = 1;
500 }
501 if(sdata.u.header[1] != disk2_info->head) {
502 printf("DISK2: " ADDRESS_FORMAT
503 " WRITE_DATA Incorrect header: head" NLP, PCX);
504 disk2_info->timeout = 1;
505 }
506
507 selchan_dma(sdata.u.data, pDrive->sectsize);
508 sim_fseek((pDrive->uptr)->fileref, file_offset+3, SEEK_SET);
509 sim_fwrite(sdata.u.data, 1, (pDrive->sectsize), (pDrive->uptr)->fileref);
510 break;
511 }
512 rtn = sim_fread(sdata.raw, 1, pDrive->sectsize, (pDrive->uptr)->fileref);
513 if (rtn != (size_t)(pDrive->sectsize)) {
514 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
515 " WRITE_DATA: sim_fread error.\n", PCX);
516 }
517 if(i == pDrive->nsectors) {
518 printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX);
519 disk2_info->timeout = 1;
520 }
521 }
522 break;
523 case DISK2_CMD_WRITE_HEADER:
524 track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
525 sim_debug(CMD_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
526 " WRITE_HEADER Command: track=%d (%d), Head=%d, Sector=%d\n",
527 PCX, pDrive->track, disk2_info->cyl,
528 disk2_info->head_sel, disk2_info->hdr_sector);
529
530 i = disk2_info->hdr_sector;
531 selchan_dma(sdata.raw, 3);
532 sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * (pDrive->sectsize + 3) * pDrive->nsectors) + (i * (pDrive->sectsize + 3)), SEEK_SET);
533 sim_fwrite(sdata.raw, 1, 3, (pDrive->uptr)->fileref);
534
535 disk2_info->hdr_sector++;
536 if(disk2_info->hdr_sector >= pDrive->nsectors) {
537 disk2_info->hdr_sector = 0;
538 disk2_info->timeout = 1;
539 }
540 break;
541 case DISK2_CMD_READ_HEADER:
542 track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3);
543 sim_debug(CMD_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
544 " READ_HEADER Command\n", PCX);
545 sim_fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET);
546 rtn = sim_fread(sdata.raw, 1, 3, (pDrive->uptr)->fileref);
547 if (rtn != 3) {
548 sim_debug(ERROR_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
549 " READ_HEADER: sim_fread error.\n", PCX);
550 }
551 selchan_dma(sdata.raw, 3);
552
553 break;
554 default:
555 printf("DISK2: " ADDRESS_FORMAT " Unknown CMD=%d" NLP, PCX, disk2_info->ctl_op);
556 break;
557 }
558
559 raise_disk2_interrupt();
560 disk2_info->ctl_attn = 0;
561 }
562
563 break;
564 case DISK2_DATA:
565 switch(disk2_info->ctl_op) {
566 case DISK2_OP_DRIVE:
567 switch(cData >> 4) {
568 case 0x01:
569 disk2_info->sel_drive = 0;
570 break;
571 case 0x02:
572 disk2_info->sel_drive = 1;
573 break;
574 case 0x04:
575 disk2_info->sel_drive = 2;
576 break;
577 case 0x08:
578 disk2_info->sel_drive = 3;
579 break;
580 default:
581 printf("DISK2: " ADDRESS_FORMAT
582 " Error, invalid drive select=0x%x" NLP, PCX, cData >> 4);
583 break;
584 }
585
586 disk2_info->head_sel = cData & 0x0F;
587
588 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
589 " Write DATA [DRIVE]=%d, Head=%d\n",
590 PCX, disk2_info->sel_drive, disk2_info->head);
591 break;
592 case DISK2_OP_CYL:
593 disk2_info->cyl = cData;
594 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
595 " Write DATA [CYL] = %02x\n", PCX, cData);
596 break;
597 case DISK2_OP_HEAD:
598 disk2_info->head = cData;
599 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
600 " Write DATA [HEAD] = %02x\n", PCX, cData);
601 break;
602 case DISK2_OP_SECTOR:
603 disk2_info->sector = cData;
604 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
605 " Write Register [SECTOR] = %02x\n", PCX, cData);
606 break;
607 default:
608 sim_debug(VERBOSE_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
609 " Write Register unknown op [%d] = %02x\n",
610 PCX, disk2_info->ctl_op, cData);
611 break;
612 }
613 }
614
615 return (result);
616 }
617
618 #define SS1_VI1_INT 1 /* DISK2/DISK3 interrupts tied to VI1 */
619
raise_disk2_interrupt(void)620 static void raise_disk2_interrupt(void)
621 {
622 sim_debug(IRQ_MSG, &disk2_dev, "DISK2: " ADDRESS_FORMAT
623 " Interrupt\n", PCX);
624
625 raise_ss1_interrupt(SS1_VI1_INT);
626
627 }
628