1 /*************************************************************************
3 * $Id: vfdhd.c 1773 2008-01-11 05:46:19Z hharte $ *
5 * Copyright (c) 2007-2008 Howard M. Harte. *
6 * http://www.hartetec.com *
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: *
16 * The above copyright notice and this permission notice shall be *
17 * included in all copies or substantial portions of the Software. *
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. *
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 *
32 * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. *
34 * Module Description: *
35 * Micropolis FDC module for SIMH *
40 *************************************************************************/
43 #define USE_VGI /* Use 275-byte VGI-format sectors (includes all metadata) */
45 #include "altairz80_defs.h"
54 #define DBG_PRINT(args) printf args
56 #define DBG_PRINT(args)
60 #define ORDERS_MSG 0x02
62 #define RD_DATA_MSG 0x08
63 #define WR_DATA_MSG 0x10
64 #define STATUS_MSG 0x20
66 static void VFDHD_Command(void);
68 #define VFDHD_MAX_DRIVES 4
70 #define VFDHD_SECTOR_LEN 275
74 uint8 preamble
[40]; /* Hard disk uses 30 bytes of preamble, floppy uses 40. */
81 uint8 ecc_valid
; /* 0xAA indicates ECC is being used. */
84 uint8 raw
[VFDHD_SECTOR_LEN
];
91 uint16 ntracks
; /* number of tracks */
92 uint8 nheads
; /* number of heads */
93 uint8 nspt
; /* number of sectors per track */
94 uint8 npre_len
; /* preamble length */
95 uint32 sectsize
; /* sector size, not including pre/postamble */
97 uint8 wp
; /* Disk write protected */
98 uint8 ready
; /* Drive is ready */
102 uint32 sector_wait_count
;
106 PNP_INFO pnp
; /* Plug and Play */
107 uint8 xfr_flag
; /* Indicates controller is ready to send/receive data */
108 uint8 sel_drive
; /* Currently selected drive */
109 uint8 selected
; /* 1 if drive is selected */
110 uint8 track0
; /* Set it selected drive is on track 0 */
111 uint8 head
; /* Currently selected head */
112 uint8 wr_latch
; /* Write enable latch */
113 uint8 int_enable
; /* Interrupt Enable */
114 uint32 datacount
; /* Number of data bytes transferred from controller for current sector */
124 uint8 controller_busy
;
127 VFDHD_DRIVE_INFO drive
[VFDHD_MAX_DRIVES
];
130 static VFDHD_INFO vfdhd_info_data
= { { 0x0, 0, 0xC0, 4 } };
131 static VFDHD_INFO
*vfdhd_info
= &vfdhd_info_data
;
133 static SECTOR_FORMAT sdata
;
135 extern t_stat
set_iobase(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
136 extern t_stat
show_iobase(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
137 extern uint32
sim_map_resource(uint32 baseaddr
, uint32 size
, uint32 resource_type
,
138 int32 (*routine
)(const int32
, const int32
, const int32
), uint8 unmap
);
140 #define UNIT_V_VFDHD_WLK (UNIT_V_UF + 0) /* write locked */
141 #define UNIT_VFDHD_WLK (1 << UNIT_V_VFDHD_WLK)
142 #define UNIT_V_VFDHD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
143 #define UNIT_VFDHD_VERBOSE (1 << UNIT_V_VFDHD_VERBOSE)
144 #define VFDHD_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */
145 #define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */
146 #define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */
147 #define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */
149 static t_stat
vfdhd_reset(DEVICE
*vfdhd_dev
);
150 static t_stat
vfdhd_attach(UNIT
*uptr
, char *cptr
);
151 static t_stat
vfdhd_detach(UNIT
*uptr
);
153 static int32
vfdhddev(const int32 port
, const int32 io
, const int32 data
);
155 static uint8
VFDHD_Read(const uint32 Addr
);
156 static uint8
VFDHD_Write(const uint32 Addr
, uint8 cData
);
158 static int32 trace_level
= FALSE
;
159 static int32 hdSize
= 5;
161 static UNIT vfdhd_unit
[] = {
162 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, VFDHD_CAPACITY
) },
163 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, VFDHD_CAPACITY
) },
164 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, VFDHD_CAPACITY
) },
165 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, VFDHD_CAPACITY
) }
168 static REG vfdhd_reg
[] = {
169 { HRDATA (TRACELEVEL
, trace_level
, 16), },
170 { DRDATA (HDSIZE
, hdSize
, 10), },
174 static MTAB vfdhd_mod
[] = {
175 { MTAB_XTD
|MTAB_VDV
, 0, "IOBASE", "IOBASE", &set_iobase
, &show_iobase
, NULL
},
176 { UNIT_VFDHD_WLK
, 0, "WRTENB", "WRTENB", NULL
},
177 { UNIT_VFDHD_WLK
, UNIT_VFDHD_WLK
, "WRTLCK", "WRTLCK", NULL
},
178 /* quiet, no warning messages */
179 { UNIT_VFDHD_VERBOSE
, 0, "QUIET", "QUIET", NULL
},
180 /* verbose, show warning messages */
181 { UNIT_VFDHD_VERBOSE
, UNIT_VFDHD_VERBOSE
, "VERBOSE", "VERBOSE", NULL
},
186 "VFDHD", vfdhd_unit
, vfdhd_reg
, vfdhd_mod
,
187 VFDHD_MAX_DRIVES
, 10, 31, 1, VFDHD_MAX_DRIVES
, VFDHD_MAX_DRIVES
,
188 NULL
, NULL
, &vfdhd_reset
,
189 NULL
, &vfdhd_attach
, &vfdhd_detach
,
190 &vfdhd_info_data
, (DEV_DISABLE
| DEV_DIS
), 0,
195 static t_stat
vfdhd_reset(DEVICE
*dptr
)
197 PNP_INFO
*pnp
= (PNP_INFO
*)dptr
->ctxt
;
199 if(dptr
->flags
& DEV_DIS
) {
200 sim_map_resource(pnp
->io_base
, pnp
->io_size
, RESOURCE_TYPE_IO
, &vfdhddev
, TRUE
);
202 /* Connect MFDC at base address */
203 if(sim_map_resource(pnp
->io_base
, pnp
->io_size
, RESOURCE_TYPE_IO
, &vfdhddev
, FALSE
) != 0) {
204 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__
, pnp
->io_base
);
213 static t_stat
vfdhd_attach(UNIT
*uptr
, char *cptr
)
219 r
= attach_unit(uptr
, cptr
); /* attach unit */
220 if ( r
!= SCPE_OK
) /* error? */
223 /* Determine length of this disk */
224 uptr
->capac
= sim_fsize(uptr
->fileref
);
226 for(i
= 0; i
< VFDHD_MAX_DRIVES
; i
++) {
227 vfdhd_info
->drive
[i
].uptr
= &vfdhd_dev
.units
[i
];
230 for(i
= 0; i
< VFDHD_MAX_DRIVES
; i
++) {
231 if(vfdhd_dev
.units
[i
].fileref
== uptr
->fileref
) {
236 if(uptr
->capac
> 0) {
237 fgets(header
, 4, uptr
->fileref
);
238 if(!strcmp(header
, "IMD")) {
239 uptr
->u3
= IMAGE_TYPE_IMD
;
240 } else if(!strcmp(header
, "CPT")) {
241 printf("CPT images not yet supported\n");
242 uptr
->u3
= IMAGE_TYPE_CPT
;
246 uptr
->u3
= IMAGE_TYPE_DSK
;
249 /* creating file, must be DSK format. */
250 uptr
->u3
= IMAGE_TYPE_DSK
;
253 if (uptr
->flags
& UNIT_VFDHD_VERBOSE
)
254 printf("VFDHD%d: attached to '%s', type=%s, len=%d\n", i
, cptr
,
255 uptr
->u3
== IMAGE_TYPE_IMD
? "IMD" : uptr
->u3
== IMAGE_TYPE_CPT
? "CPT" : "DSK",
258 if(uptr
->u3
== IMAGE_TYPE_IMD
) {
259 if(uptr
->capac
< 318000) {
260 printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n");
265 if (uptr
->flags
& UNIT_VFDHD_VERBOSE
)
266 printf("--------------------------------------------------------\n");
267 vfdhd_info
->drive
[i
].imd
= diskOpen((uptr
->fileref
), (uptr
->flags
& UNIT_VFDHD_VERBOSE
));
270 vfdhd_info
->drive
[i
].imd
= NULL
;
273 if(i
>0) { /* Floppy Disk, Unit 1-3 */
274 vfdhd_info
->drive
[i
].ntracks
= 77; /* number of tracks */
275 vfdhd_info
->drive
[i
].nheads
= 2; /* number of heads */
276 vfdhd_info
->drive
[i
].nspt
= 16; /* number of sectors per track */
277 vfdhd_info
->drive
[i
].npre_len
= 40; /* preamble length */
278 vfdhd_info
->drive
[i
].sectsize
= VFDHD_SECTOR_LEN
; /* sector size, not including pre/postamble */
279 } else { /* Hard Disk, Unit 0 */
281 vfdhd_info
->drive
[i
].ntracks
= 153; /* number of tracks */
282 vfdhd_info
->drive
[i
].nheads
= 6; /* number of heads */
283 vfdhd_info
->hdsk_type
= 1;
285 } else if (hdSize
== 5) {
286 vfdhd_info
->drive
[i
].ntracks
= 153; /* number of tracks */
287 vfdhd_info
->drive
[i
].nheads
= 4; /* number of heads */
288 vfdhd_info
->hdsk_type
= 0;
291 vfdhd_info
->drive
[i
].ntracks
= 512; /* number of tracks */
292 vfdhd_info
->drive
[i
].nheads
= 8; /* number of heads */
293 vfdhd_info
->hdsk_type
= 1;
297 vfdhd_info
->drive
[i
].nheads
= 4; /* number of heads */
298 vfdhd_info
->drive
[i
].nspt
= 32; /* number of sectors per track */
299 vfdhd_info
->drive
[i
].npre_len
= 30; /* preamble length */
300 vfdhd_info
->drive
[i
].sectsize
= VFDHD_SECTOR_LEN
; /* sector size, not including pre/postamble */
301 vfdhd_info
->drive
[i
].ready
= 1;
302 vfdhd_info
->drive
[i
].seek_complete
= 1;
303 vfdhd_info
->drive
[i
].sync_lost
= 1; /* Active LOW */
306 vfdhd_info
->motor_on
= 1;
312 static t_stat
vfdhd_detach(UNIT
*uptr
)
317 for(i
= 0; i
< VFDHD_MAX_DRIVES
; i
++) {
318 if(vfdhd_dev
.units
[i
].fileref
== uptr
->fileref
) {
323 DBG_PRINT(("Detach VFDHD%d\n", i
));
324 diskClose(vfdhd_info
->drive
[i
].imd
);
326 r
= detach_unit(uptr
); /* detach unit */
335 static uint8
adc(uint8 sum
, uint8 a1
)
339 total
= sum
+ a1
+ cy
;
347 return(total
& 0xFF);
350 static int32
vfdhddev(const int32 port
, const int32 io
, const int32 data
)
352 DBG_PRINT(("VFDHD: " ADDRESS_FORMAT
" IO %s, Port %02x" NLP
, PCX
, io
? "WR" : "RD", port
));
354 VFDHD_Write(port
, data
);
357 return(VFDHD_Read(port
));
361 #define FDHD_CTRL_STATUS0 0 /* R=Status Port 0, W=Control Port 0 */
362 #define FDHD_CTRL_STATUS1 1 /* R=Status Port 1, W=Control Port 0 */
363 #define FDHD_DATA 2 /* R/W=Data Port */
364 #define FDHD_RESET_START 3 /* R=RESET, W=START */
366 static uint8
VFDHD_Read(const uint32 Addr
)
369 VFDHD_DRIVE_INFO
*pDrive
;
371 pDrive
= &vfdhd_info
->drive
[vfdhd_info
->sel_drive
];
376 case FDHD_CTRL_STATUS0
:
377 cData
= (pDrive
->wp
& 1); /* [0] Write Protect (FD) */
378 cData
|= (pDrive
->ready
& 1) << 1; /* [1] Drive ready (HD) */
379 cData
|= (pDrive
->track
== 0) ? 0x04 : 0; /* [2] TK0 (FD/HD) */
380 cData
|= (pDrive
->write_fault
& 1) << 3; /* [3] Write Fault (HD) */
381 cData
|= (pDrive
->seek_complete
& 1) << 4; /* [4] Seek Complete (HD) */
382 cData
|= (pDrive
->sync_lost
& 1) << 5; /* [5] Loss of Sync (HD) */
383 cData
|= 0xC0; /* [7:6] Reserved (pulled up) */
384 TRACE_PRINT(STATUS_MSG
, ("VFDHD: " ADDRESS_FORMAT
" RD S0 = 0x%02x" NLP
, PCX
, cData
));
386 case FDHD_CTRL_STATUS1
:
387 vfdhd_info
->floppy_sel
= (vfdhd_info
->sel_drive
== 0) ? 0 : 1;
388 cData
= (vfdhd_info
->floppy_sel
& 0x1); /* [0] Floppy Selected */
389 cData
|= (vfdhd_info
->controller_busy
& 0x1) << 1; /* [1] Controller busy */
390 cData
|= (vfdhd_info
->motor_on
& 0x1) << 2; /* [2] Motor On (FD) */
391 cData
|= (vfdhd_info
->hdsk_type
& 0x1) << 3; /* [3] Hard Disk Type (0=5MB, 1=10MB) */
392 cData
|= 0xF0; /* [7:4] Reserved (pulled up) */
393 if(vfdhd_info
->sel_drive
== 0) {
397 vfdhd_info
->controller_busy
= 0;
399 TRACE_PRINT(STATUS_MSG
, ("VFDHD: " ADDRESS_FORMAT
" RD S1 = 0x%02x" NLP
, PCX
, cData
));
402 /* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data" NLP, PCX)); */
403 cData
= sdata
.raw
[vfdhd_info
->datacount
+40];
405 vfdhd_info
->datacount
++;
407 /* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data Sector %d[%03d]: 0x%02x" NLP, PCX, pDrive->sector, vfdhd_info->datacount, cData)); */
409 case FDHD_RESET_START
: /* Reset */
410 TRACE_PRINT(CMD_MSG
, ("VFDHD: " ADDRESS_FORMAT
" Reset" NLP
, PCX
));
411 vfdhd_info
->datacount
= 0;
412 cData
= 0xFF; /* Return High-Z data */
419 static uint8
VFDHD_Write(const uint32 Addr
, uint8 cData
)
421 VFDHD_DRIVE_INFO
*pDrive
;
423 pDrive
= &vfdhd_info
->drive
[vfdhd_info
->sel_drive
];
426 case FDHD_CTRL_STATUS0
:
427 vfdhd_info
->sel_drive
= cData
& 3;
428 vfdhd_info
->head
= (cData
>> 2) & 0x7;
429 vfdhd_info
->step
= (cData
>> 5) & 1;
430 vfdhd_info
->direction
= (cData
>> 6) & 1;
431 vfdhd_info
->rwc
= (cData
>> 7) & 1;
433 TRACE_PRINT(WR_DATA_MSG
, ("VFDHD: " ADDRESS_FORMAT
" WR C0=%02x: sel_drive=%d, head=%d, step=%d, dir=%d, rwc=%d" NLP
,
436 vfdhd_info
->sel_drive
,
439 vfdhd_info
->direction
,
442 if(vfdhd_info
->step
== 1) {
443 if(vfdhd_info
->direction
== 1) { /* Step IN */
445 } else { /* Step OUT */
446 if(pDrive
->track
!= 0) {
450 TRACE_PRINT(SEEK_MSG
, ("VFDHD: " ADDRESS_FORMAT
" Drive %d on track %d" NLP
,
451 PCX
, vfdhd_info
->sel_drive
, pDrive
->track
));
455 case FDHD_CTRL_STATUS1
:
456 vfdhd_info
->sector
= (cData
& 0x1f);
457 vfdhd_info
->read
= (cData
>> 5) & 1;
458 vfdhd_info
->ecc_enable
= (cData
>> 6) & 1;
459 vfdhd_info
->precomp
= (cData
>> 7) & 1;
461 TRACE_PRINT(SEEK_MSG
, ("VFDHD: " ADDRESS_FORMAT
" Home Disk %d" NLP
,
462 PCX
, vfdhd_info
->sel_drive
));
465 DBG_PRINT(("VFDHD: " ADDRESS_FORMAT
" WR C1=%02x: sector=%d, read=%d, ecc_en=%d, precomp=%d" NLP
,
470 vfdhd_info
->ecc_enable
,
471 vfdhd_info
->precomp
));
473 case FDHD_DATA
: /* Data Port */
474 DBG_PRINT(("VFDHD: " ADDRESS_FORMAT
" WR Data" NLP
, PCX
));
476 if(vfdhd_info
->sel_drive
> 0) { /* Floppy */
477 sdata
.raw
[vfdhd_info
->datacount
] = cData
;
479 sdata
.raw
[vfdhd_info
->datacount
+10] = cData
;
482 sdata
.u
.data
[vfdhd_info
->datacount
-13] = cData
;
485 vfdhd_info
->datacount
++;
488 case FDHD_RESET_START
:
489 TRACE_PRINT(CMD_MSG
, ("VFDHD: " ADDRESS_FORMAT
" Start Command" NLP
, PCX
));
499 static void VFDHD_Command(void)
501 VFDHD_DRIVE_INFO
*pDrive
;
503 uint32 bytesPerTrack
;
509 pDrive
= &(vfdhd_info
->drive
[vfdhd_info
->sel_drive
]);
511 bytesPerTrack
= pDrive
->sectsize
* pDrive
->nspt
;
512 bytesPerHead
= bytesPerTrack
* pDrive
->ntracks
;
514 sec_offset
= (pDrive
->track
* bytesPerTrack
) + \
515 (vfdhd_info
->head
* bytesPerHead
) + \
516 (vfdhd_info
->sector
* pDrive
->sectsize
);
518 vfdhd_info
->controller_busy
= 1;
520 if(vfdhd_info
->read
== 1) { /* Perform a Read operation */
521 unsigned int i
, checksum
;
522 unsigned int readlen
;
524 TRACE_PRINT(RD_DATA_MSG
, ("VFDHD: " ADDRESS_FORMAT
" RD: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP
,
526 vfdhd_info
->sel_drive
,
529 vfdhd_info
->sector
));
531 /* Clear out unused portion of sector. */
532 memset(&sdata
.u
.unused
[0], 0x00, 10);
535 sdata
.u
.header
[0] = pDrive
->track
& 0xFF;
536 sdata
.u
.header
[1] = vfdhd_info
->sector
;
538 switch((pDrive
->uptr
)->u3
)
541 if(pDrive
->imd
== NULL
) {
542 printf(".imd is NULL!" NLP
);
544 printf("%s: Read: imd=%p" NLP
, __FUNCTION__
, pDrive
->imd
);
545 sectRead(pDrive
->imd
,
554 adc(0,0); /* clear Carry bit */
557 /* Checksum everything except the sync byte */
559 checksum
= adc(checksum
, sdata
.raw
[i
]);
562 sdata
.u
.checksum
= checksum
& 0xFF;
563 sdata
.u
.ecc_valid
= 0xAA;
566 if(pDrive
->uptr
->fileref
== NULL
) {
567 printf(".fileref is NULL!" NLP
);
569 fseek((pDrive
->uptr
)->fileref
, sec_offset
, SEEK_SET
);
570 fread(&sdata
.u
.sync
, 274, /*VFDHD_SECTOR_LEN,*/ 1, (pDrive
->uptr
)->fileref
);
572 memset(&sdata
.u
.preamble
, 0, 40);
573 memset(&sdata
.u
.ecc
, 0, 5); /* Clear out the ECC and ECC Valid bytes */
574 sdata
.u
.ecc_valid
= 0xAA;
575 for(vfdhd_info
->datacount
= 0; sdata
.raw
[vfdhd_info
->datacount
] == 0x00; vfdhd_info
->datacount
++) {
578 DBG_PRINT(("VFDHD: " ADDRESS_FORMAT
" READ: Sync found at offset %d" NLP
, PCX
, vfdhd_info
->datacount
));
583 printf("%s: CPT Format not supported" NLP
, __FUNCTION__
);
586 printf("%s: Unknown image Format" NLP
, __FUNCTION__
);
590 } else { /* Perform a Write operation */
591 unsigned int writelen
;
593 TRACE_PRINT(WR_DATA_MSG
, ("VFDHD: " ADDRESS_FORMAT
" WR: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP
,
595 vfdhd_info
->sel_drive
,
598 vfdhd_info
->sector
));
602 int data_index
= vfdhd_info
->datacount
- 13;
604 sec_offset
= (pDrive
->track
* 4096) + \
605 (vfdhd_info
->head
* 315392) + \
606 (vfdhd_info
->sector
* 256);
609 switch((pDrive
->uptr
)->u3
)
612 if(pDrive
->imd
== NULL
) {
613 printf(".imd is NULL!" NLP
);
615 sectWrite(pDrive
->imd
,
625 if(pDrive
->uptr
->fileref
== NULL
) {
626 printf(".fileref is NULL!" NLP
);
628 DBG_PRINT(("VFDHD: " ADDRESS_FORMAT
" WR drive=%d, track=%d, head=%d, sector=%d" NLP
,
630 vfdhd_info
->sel_drive
,
633 vfdhd_info
->sector
));
634 fseek((pDrive
->uptr
)->fileref
, sec_offset
, SEEK_SET
);
636 fwrite(&sdata
.u
.sync
, VFDHD_SECTOR_LEN
, 1, (pDrive
->uptr
)->fileref
);
638 fwrite(sdata
.u
.data
, 256, 1, (pDrive
->uptr
)->fileref
);
643 printf("%s: CPT Format not supported" NLP
, __FUNCTION__
);
646 printf("%s: Unknown image Format" NLP
, __FUNCTION__
);