1 /*************************************************************************
2 * *
3 * $Id: sim_imd.c 1999 2008-07-22 04:25:28Z 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 * ImageDisk (IMD) Disk Image File access module for SIMH. *
36 * see: http://www.classiccmp.org/dunfield/img/index.htm *
37 * for details on the ImageDisk format and other utilities. *
38 * *
39 * Environment: *
40 * User mode only *
41 * *
42 *************************************************************************/
43
44 /* Change log:
45 - 06-Aug-2008, Tony Nicholson, Add support for logical Head and
46 Cylinder maps in the .IMD image file (AGN)
47 */
48
49 #include "sim_defs.h"
50 #include "sim_imd.h"
51 #include <fcntl.h>
52 #ifdef _WIN32
53 #include <io.h> /* for _chsize() */
54 #else
55 #include <unistd.h>
56 #endif
57 /* #define DBG_MSG */
58
59 #ifdef DBG_MSG
60 #define DBG_PRINT(args) printf args
61 #else
62 #define DBG_PRINT(args)
63 #endif
64
65 /* use NLP for new line printing while the simulation is running */
66 #if defined (__linux) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__APPLE__)
67 #define UNIX_PLATFORM 1
68 #else
69 #define UNIX_PLATFORM 0
70 #endif
71
72 #if UNIX_PLATFORM
73 #define NLP "\r\n"
74 #else
75 #define NLP "\n"
76 #endif
77
78 #if (defined (__MWERKS__) && defined (macintosh)) || defined(__DECC)
79 #define __FUNCTION__ __FILE__
80 #endif
81
82 static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen);
83 static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose);
84 static t_stat diskFormat(DISK_INFO *myDisk);
85
86 /* Open an existing IMD disk image. It will be opened and parsed, and after this
87 * call, will be ready for sector read/write. The result is the corresponding
88 * DISK_INFO or NULL if an error occurred.
89 */
diskOpen(FILE * fileref,uint32 isVerbose)90 DISK_INFO *diskOpen(FILE *fileref, uint32 isVerbose)
91 {
92 DISK_INFO *myDisk = NULL;
93
94 myDisk = (DISK_INFO *)malloc(sizeof(DISK_INFO));
95 myDisk->file = fileref;
96
97 if (diskParse(myDisk, isVerbose) != SCPE_OK) {
98 free(myDisk);
99 myDisk = NULL;
100 }
101
102 return myDisk;
103 }
104
105 /* Scans the IMD file for the comment string, and returns it in comment buffer.
106 * After this function returns, the file pointer is placed after the comment and
107 * the 0x1A "EOF" marker.
108 *
109 * The comment parameter is optional, and if NULL, then the ocmment will not
110 * be extracted from the IMD file, but the file position will still be advanced
111 * to the end of the comment.
112 */
commentParse(DISK_INFO * myDisk,uint8 comment[],uint32 buffLen)113 static t_stat commentParse(DISK_INFO *myDisk, uint8 comment[], uint32 buffLen)
114 {
115 uint8 cData;
116 uint32 commentLen = 0;
117
118 /* rewind to the beginning of the file. */
119 rewind(myDisk->file);
120 cData = fgetc(myDisk->file);
121 while ((!feof(myDisk->file)) && (cData != 0x1a)) {
122 if ((comment != NULL) && (commentLen < buffLen)) {
123 comment[commentLen++] = cData;
124 }
125 cData = fgetc(myDisk->file);
126 }
127 if (comment != NULL) {
128 if (commentLen == buffLen)
129 commentLen--;
130 comment[commentLen] = 0;
131 }
132 return SCPE_OK;
133 }
134
headerOk(IMD_HEADER imd)135 static uint32 headerOk(IMD_HEADER imd) {
136 return (imd.cyl < MAX_CYL) && (imd.head < MAX_HEAD);
137 }
138
139 /* Parse an IMD image. This sets up sim_imd to be able to do sector read/write and
140 * track write.
141 */
diskParse(DISK_INFO * myDisk,uint32 isVerbose)142 static t_stat diskParse(DISK_INFO *myDisk, uint32 isVerbose)
143 {
144 uint8 comment[256];
145 uint8 sectorMap[256];
146 uint8 sectorHeadMap[256];
147 uint8 sectorCylMap[256];
148 uint32 sectorSize, sectorHeadwithFlags, sectRecordType;
149 uint32 i;
150 uint8 start_sect;
151
152 uint32 TotalSectorCount = 0;
153 IMD_HEADER imd;
154
155 if(myDisk == NULL) {
156 return (SCPE_OPENERR);
157 }
158
159 memset(myDisk->track, 0, (sizeof(TRACK_INFO)*MAX_CYL*MAX_HEAD));
160
161 if (commentParse(myDisk, comment, sizeof(comment)) != SCPE_OK) {
162 return (SCPE_OPENERR);
163 }
164
165 if(isVerbose)
166 printf("%s" NLP, comment);
167
168 myDisk->nsides = 1;
169 myDisk->ntracks = 0;
170 myDisk->flags = 0; /* Make sure all flags are clear. */
171
172 if(feof(myDisk->file)) {
173 printf("SIM_IMD: Disk image is blank, it must be formatted." NLP);
174 return (SCPE_OPENERR);
175 }
176
177 do {
178 DBG_PRINT(("start of track %d at file offset %ld" NLP, myDisk->ntracks, ftell(myDisk->file)));
179
180 sim_fread(&imd, 1, 5, myDisk->file);
181 if (feof(myDisk->file))
182 break;
183 sectorSize = 128 << imd.sectsize;
184 sectorHeadwithFlags = imd.head; /*AGN save the head and flags */
185 imd.head &= 1 ; /*AGN mask out flag bits to head 0 or 1 */
186
187 DBG_PRINT(("Track %d:" NLP, myDisk->ntracks));
188 DBG_PRINT(("\tMode=%d, Cyl=%d, Head=%d(%d), #sectors=%d, sectsize=%d (%d bytes)" NLP, imd.mode, imd.cyl, sectorHeadwithFlags, imd.head, imd.nsects, imd.sectsize, sectorSize));
189
190 if (!headerOk(imd)) {
191 printf("SIM_IMD: Corrupt header." NLP);
192 return (SCPE_OPENERR);
193 }
194
195 if((imd.head + 1) > myDisk->nsides) {
196 myDisk->nsides = imd.head + 1;
197 }
198
199 myDisk->track[imd.cyl][imd.head].mode = imd.mode;
200 myDisk->track[imd.cyl][imd.head].nsects = imd.nsects;
201 myDisk->track[imd.cyl][imd.head].sectsize = sectorSize;
202
203 if (sim_fread(sectorMap, 1, imd.nsects, myDisk->file) != imd.nsects) {
204 printf("SIM_IMD: Corrupt file [Sector Map]." NLP);
205 return (SCPE_OPENERR);
206 }
207 myDisk->track[imd.cyl][imd.head].start_sector = imd.nsects;
208 DBG_PRINT(("\tSector Map: "));
209 for(i=0;i<imd.nsects;i++) {
210 DBG_PRINT(("%d ", sectorMap[i]));
211 if(sectorMap[i] < myDisk->track[imd.cyl][imd.head].start_sector) {
212 myDisk->track[imd.cyl][imd.head].start_sector = sectorMap[i];
213 }
214 }
215 DBG_PRINT((", Start Sector=%d", myDisk->track[imd.cyl][imd.head].start_sector));
216
217 if(sectorHeadwithFlags & IMD_FLAG_SECT_HEAD_MAP) {
218 if (sim_fread(sectorHeadMap, 1, imd.nsects, myDisk->file) != imd.nsects) {
219 printf("SIM_IMD: Corrupt file [Sector Head Map]." NLP);
220 return (SCPE_OPENERR);
221 }
222 DBG_PRINT(("\tSector Head Map: "));
223 for(i=0;i<imd.nsects;i++) {
224 DBG_PRINT(("%d ", sectorHeadMap[i]));
225 }
226 DBG_PRINT(("" NLP));
227 } else {
228 /* Default Head is physical head for each sector */
229 for(i=0;i<imd.nsects;i++) {
230 sectorHeadMap[i] = imd.head;
231 };
232 }
233
234 if(sectorHeadwithFlags & IMD_FLAG_SECT_CYL_MAP) {
235 if (sim_fread(sectorCylMap, 1, imd.nsects, myDisk->file) != imd.nsects) {
236 printf("SIM_IMD: Corrupt file [Sector Cyl Map]." NLP);
237 return (SCPE_OPENERR);
238 }
239 DBG_PRINT(("\tSector Cyl Map: "));
240 for(i=0;i<imd.nsects;i++) {
241 DBG_PRINT(("%d ", sectorCylMap[i]));
242 }
243 DBG_PRINT((NLP));
244 } else {
245 /* Default Cyl Map is physical cylinder for each sector */
246 for(i=0;i<imd.nsects;i++) {
247 sectorCylMap[i] = imd.cyl;
248 }
249 }
250
251 DBG_PRINT((NLP "Sector data at offset 0x%08lx" NLP, ftell(myDisk->file)));
252
253 /* Build the table with location 0 being the start sector. */
254 start_sect = myDisk->track[imd.cyl][imd.head].start_sector;
255
256 /* Now read each sector */
257 for(i=0;i<imd.nsects;i++) {
258 TotalSectorCount++;
259 DBG_PRINT(("Sector Phys: %d/Logical: %d: %d bytes: ", i, sectorMap[i], sectorSize));
260 sectRecordType = fgetc(myDisk->file);
261 /* AGN Logical head mapping */
262 myDisk->track[imd.cyl][imd.head].logicalHead[i] = sectorHeadMap[i];
263 /* AGN Logical cylinder mapping */
264 myDisk->track[imd.cyl][imd.head].logicalCyl[i] = sectorCylMap[i];
265 switch(sectRecordType) {
266 case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */
267 if (sectorMap[i]-start_sect < MAX_SPT)
268 myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = 0xBADBAD;
269 else {
270 printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect);
271 return (SCPE_OPENERR);
272 }
273 break;
274 case SECT_RECORD_NORM: /* Normal Data */
275 case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */
276 case SECT_RECORD_NORM_ERR: /* Normal Data with read error */
277 case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */
278 /* DBG_PRINT(("Uncompressed Data" NLP)); */
279 if (sectorMap[i]-start_sect < MAX_SPT) {
280 myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file);
281 sim_fseek(myDisk->file, sectorSize, SEEK_CUR);
282 }
283 else {
284 printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect);
285 return (SCPE_OPENERR);
286 }
287 break;
288 case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */
289 case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */
290 case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */
291 case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */
292 if (sectorMap[i]-start_sect < MAX_SPT) {
293 myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file);
294 myDisk->flags |= FD_FLAG_WRITELOCK; /* Write-protect the disk if any sectors are compressed. */
295 #ifdef VERBOSE_DEBUG
296 DBG_PRINT(("Compressed Data = 0x%02x" NLP, fgetc(myDisk->file)));
297 #else
298 fgetc(myDisk->file);
299 #endif
300 }
301 else {
302 printf("SIM_IMD: ERROR: Illegal sector offset %d" NLP, sectorMap[i]-start_sect);
303 return (SCPE_OPENERR);
304 }
305 break;
306 default:
307 printf("SIM_IMD: ERROR: unrecognized sector record type %d" NLP, sectRecordType);
308 return (SCPE_OPENERR);
309 break;
310 }
311 DBG_PRINT((NLP));
312 }
313
314 myDisk->ntracks++;
315 } while (!feof(myDisk->file));
316
317 DBG_PRINT(("Processed %d sectors" NLP, TotalSectorCount));
318
319 #ifdef VERBOSE_DEBUG
320 for(i=0;i<myDisk->ntracks;i++) {
321 DBG_PRINT(("Track %02d: ", i));
322 for(j=0;j<imd.nsects;j++) {
323 DBG_PRINT(("0x%06x ", myDisk->track[i][0].sectorOffsetMap[j]));
324 }
325 DBG_PRINT((NLP));
326 }
327 #endif
328 if(myDisk->flags & FD_FLAG_WRITELOCK) {
329 printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress." NLP);
330 }
331
332 return SCPE_OK;
333 }
334
335 /*
336 * This function closes the IMD image. After closing, the sector read/write operations are not
337 * possible.
338 *
339 * The IMD file is not actually closed, we leave that to SIMH.
340 */
diskClose(DISK_INFO ** myDisk)341 t_stat diskClose(DISK_INFO **myDisk)
342 {
343 if(*myDisk == NULL)
344 return SCPE_OPENERR;
345 free(*myDisk);
346 *myDisk = NULL;
347 return SCPE_OK;
348 }
349
350 #define MAX_COMMENT_LEN 256
351
352 /*
353 * Create an ImageDisk (IMD) file. This function just creates the comment header, and allows
354 * the user to enter a comment. After the IMD is created, it must be formatted with a format
355 * program on the simulated operating system, ie CP/M, CDOS, 86-DOS.
356 *
357 * If the IMD file already exists, the user will be given the option of overwriting it.
358 */
diskCreate(FILE * fileref,char * ctlr_comment)359 t_stat diskCreate(FILE *fileref, char *ctlr_comment)
360 {
361 DISK_INFO *myDisk = NULL;
362 char *comment;
363 char *curptr;
364 char *result;
365 uint8 answer;
366 int32 len, remaining;
367
368 if(fileref == NULL) {
369 return (SCPE_OPENERR);
370 }
371
372 if(sim_fsize(fileref) != 0) {
373 printf("SIM_IMD: Disk image already has data, do you want to overwrite it? ");
374 answer = getchar();
375
376 if((answer != 'y') && (answer != 'Y')) {
377 return (SCPE_OPENERR);
378 }
379 }
380
381 if((curptr = comment = calloc(1, MAX_COMMENT_LEN)) == 0) {
382 printf("Memory allocation failure.\n");
383 return (SCPE_MEM);
384 }
385
386 printf("SIM_IMD: Enter a comment for this disk.\n"
387 "SIM_IMD: Terminate with a '.' on an otherwise blank line.\n");
388 remaining = MAX_COMMENT_LEN;
389 do {
390 printf("IMD> ");
391 result = fgets(curptr, remaining - 3, stdin);
392 if ((result == NULL) || (strcmp(curptr, ".\n") == 0)) {
393 remaining = 0;
394 } else {
395 len = strlen(curptr) - 1;
396 if (curptr[len] != '\n')
397 len++;
398 remaining -= len;
399 curptr += len;
400 *curptr++ = 0x0d;
401 *curptr++ = 0x0a;
402 }
403 } while (remaining > 4);
404 *curptr = 0x00;
405
406 /* rewind to the beginning of the file. */
407 rewind(fileref);
408
409 /* Erase the contents of the IMD file in case we are overwriting an existing image. */
410 #ifdef _WIN32 /* This might work under UNIX and/or VMS since this POSIX, but I haven't tried it. */
411 _chsize(_fileno(fileref), ftell (fileref));
412 #else
413 if (ftruncate(fileno(fileref), ftell (fileref)) == -1) {
414 printf("SIM_IMD: Error overwriting disk image.\n");
415 return(SCPE_OPENERR);
416 }
417 #endif
418
419 fprintf(fileref, "IMD SIMH %s %s\n", __DATE__, __TIME__);
420 fputs(comment, fileref);
421 free(comment);
422 fprintf(fileref, "\n\n$Id: sim_imd.c 1999 2008-07-22 04:25:28Z hharte $\n");
423 fprintf(fileref, "%s\n", ctlr_comment);
424 fputc(0x1A, fileref); /* EOF marker for IMD comment. */
425 fflush(fileref);
426
427 if((myDisk = diskOpen(fileref, 0)) == NULL) {
428 printf("SIM_IMD: Error opening disk for format.\n");
429 return(SCPE_OPENERR);
430 }
431
432 if(diskFormat(myDisk) != SCPE_OK) {
433 printf("SIM_IMD: error formatting disk.\n");
434 }
435
436 return diskClose(&myDisk);
437 }
438
439
diskFormat(DISK_INFO * myDisk)440 t_stat diskFormat(DISK_INFO *myDisk)
441 {
442 uint8 i;
443 uint8 sector_map[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
444 uint32 flags;
445
446 printf("SIM_IMD: Formatting disk in IBM 3740 SS/SD Format.\n");
447
448 for(i=0;i<77;i++) {
449 if((trackWrite(myDisk, i, 0, 26, 128, sector_map, IMD_MODE_500K_FM, 0xE5, &flags)) != 0) {
450 printf("SIM_IMD: Error formatting track %d\n", i);
451 return SCPE_IOERR;
452 } else {
453 putchar('.');
454 }
455 }
456
457 printf("\nSIM_IMD: Format Complete.\n");
458
459 return SCPE_OK;
460 }
461
imdGetSides(DISK_INFO * myDisk)462 uint32 imdGetSides(DISK_INFO *myDisk)
463 {
464 if(myDisk != NULL) {
465 return(myDisk->nsides);
466 }
467
468 return (0);
469 }
470
imdIsWriteLocked(DISK_INFO * myDisk)471 uint32 imdIsWriteLocked(DISK_INFO *myDisk)
472 {
473 if(myDisk != NULL) {
474 return((myDisk->flags & FD_FLAG_WRITELOCK) ? 1 : 0);
475 }
476
477 return (0);
478 }
479
480 /* Check that the given track/sector exists on the disk */
sectSeek(DISK_INFO * myDisk,uint32 Cyl,uint32 Head)481 t_stat sectSeek(DISK_INFO *myDisk,
482 uint32 Cyl,
483 uint32 Head)
484 {
485 if(Cyl >= myDisk->ntracks) {
486 return(SCPE_IOERR);
487 }
488
489 if(Head >= myDisk->nsides) {
490 return(SCPE_IOERR);
491 }
492
493 if(myDisk->track[Cyl][Head].nsects == 0) {
494 DBG_PRINT(("%s: invalid track/head" NLP, __FUNCTION__));
495 return(SCPE_IOERR);
496 }
497
498 return(SCPE_OK);
499 }
500
501 /* Read a sector from an IMD image. */
sectRead(DISK_INFO * myDisk,uint32 Cyl,uint32 Head,uint32 Sector,uint8 * buf,uint32 buflen,uint32 * flags,uint32 * readlen)502 t_stat sectRead(DISK_INFO *myDisk,
503 uint32 Cyl,
504 uint32 Head,
505 uint32 Sector,
506 uint8 *buf,
507 uint32 buflen,
508 uint32 *flags,
509 uint32 *readlen)
510 {
511 uint32 sectorFileOffset;
512 uint8 sectRecordType;
513 uint8 start_sect;
514 *readlen = 0;
515 *flags = 0;
516
517 /* Check parameters */
518 if(myDisk == NULL) {
519 *flags |= IMD_DISK_IO_ERROR_GENERAL;
520 return(SCPE_IOERR);
521 }
522
523 if(sectSeek(myDisk, Cyl, Head) != SCPE_OK) {
524 *flags |= IMD_DISK_IO_ERROR_GENERAL;
525 return(SCPE_IOERR);
526 }
527
528 if(Sector > myDisk->track[Cyl][Head].nsects) {
529 DBG_PRINT(("%s: invalid sector" NLP, __FUNCTION__));
530 *flags |= IMD_DISK_IO_ERROR_GENERAL;
531 return(SCPE_IOERR);
532 }
533
534 if(buflen < myDisk->track[Cyl][Head].sectsize) {
535 printf("%s: Reading C:%d/H:%d/S:%d, len=%d: user buffer too short, need %d" NLP, __FUNCTION__, Cyl, Head, Sector, buflen, myDisk->track[Cyl][Head].sectsize);
536 *flags |= IMD_DISK_IO_ERROR_GENERAL;
537 return(SCPE_IOERR);
538 }
539
540 start_sect = myDisk->track[Cyl][Head].start_sector;
541
542 sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect];
543
544 DBG_PRINT(("Reading C:%d/H:%d/S:%d, len=%d, offset=0x%08x" NLP, Cyl, Head, Sector, buflen, sectorFileOffset));
545
546 sim_fseek(myDisk->file, sectorFileOffset-1, 0);
547
548 sectRecordType = fgetc(myDisk->file);
549 switch(sectRecordType) {
550 case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */
551 *flags |= IMD_DISK_IO_ERROR_GENERAL;
552 break;
553 case SECT_RECORD_NORM_ERR: /* Normal Data with read error */
554 case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */
555 *flags |= IMD_DISK_IO_ERROR_CRC;
556 case SECT_RECORD_NORM: /* Normal Data */
557 case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */
558
559 /* DBG_PRINT(("Uncompressed Data" NLP)); */
560 if (sim_fread(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file) != myDisk->track[Cyl][Head].sectsize) {
561 printf("SIM_IMD[%s]: sim_fread error for SECT_RECORD_NORM_DAM." NLP, __FUNCTION__);
562 }
563 *readlen = myDisk->track[Cyl][Head].sectsize;
564 break;
565 case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */
566 case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */
567 *flags |= IMD_DISK_IO_ERROR_CRC;
568 case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */
569 case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */
570 /* DBG_PRINT(("Compressed Data" NLP)); */
571 memset(buf, fgetc(myDisk->file), myDisk->track[Cyl][Head].sectsize);
572 *readlen = myDisk->track[Cyl][Head].sectsize;
573 *flags |= IMD_DISK_IO_COMPRESSED;
574 break;
575 default:
576 printf("ERROR: unrecognized sector record type %d" NLP, sectRecordType);
577 break;
578 }
579
580 /* Set flags for deleted address mark. */
581 switch(sectRecordType) {
582 case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */
583 case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */
584 case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */
585 case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */
586 *flags |= IMD_DISK_IO_DELETED_ADDR_MARK;
587 default:
588 break;
589 }
590
591 return(SCPE_OK);
592 }
593
594 /* Write a sector to an IMD image. */
sectWrite(DISK_INFO * myDisk,uint32 Cyl,uint32 Head,uint32 Sector,uint8 * buf,uint32 buflen,uint32 * flags,uint32 * writelen)595 t_stat sectWrite(DISK_INFO *myDisk,
596 uint32 Cyl,
597 uint32 Head,
598 uint32 Sector,
599 uint8 *buf,
600 uint32 buflen,
601 uint32 *flags,
602 uint32 *writelen)
603 {
604 uint32 sectorFileOffset;
605 uint8 sectRecordType;
606 uint8 start_sect;
607 *writelen = 0;
608
609 DBG_PRINT(("Writing C:%d/H:%d/S:%d, len=%d" NLP, Cyl, Head, Sector, buflen));
610
611 /* Check parameters */
612 if(myDisk == NULL) {
613 *flags = IMD_DISK_IO_ERROR_GENERAL;
614 return(SCPE_IOERR);
615 }
616
617 if(sectSeek(myDisk, Cyl, Head) != 0) {
618 *flags = IMD_DISK_IO_ERROR_GENERAL;
619 return(SCPE_IOERR);
620 }
621
622 if(Sector > myDisk->track[Cyl][Head].nsects) {
623 DBG_PRINT(("%s: invalid sector" NLP, __FUNCTION__));
624 *flags = IMD_DISK_IO_ERROR_GENERAL;
625 return(SCPE_IOERR);
626 }
627
628 if(myDisk->flags & FD_FLAG_WRITELOCK) {
629 printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress." NLP);
630 *flags = IMD_DISK_IO_ERROR_WPROT;
631 return(SCPE_IOERR);
632 }
633
634 if(buflen < myDisk->track[Cyl][Head].sectsize) {
635 printf("%s: user buffer too short [buflen %i < sectsize %i]" NLP,
636 __FUNCTION__, buflen, myDisk->track[Cyl][Head].sectsize);
637 *flags = IMD_DISK_IO_ERROR_GENERAL;
638 return(SCPE_IOERR);
639 }
640
641 start_sect = myDisk->track[Cyl][Head].start_sector;
642
643 sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect];
644
645 sim_fseek(myDisk->file, sectorFileOffset-1, 0);
646
647 if (*flags & IMD_DISK_IO_ERROR_GENERAL) {
648 sectRecordType = SECT_RECORD_UNAVAILABLE;
649 } else if (*flags & IMD_DISK_IO_ERROR_CRC) {
650 if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK)
651 sectRecordType = SECT_RECORD_NORM_DAM_ERR;
652 else
653 sectRecordType = SECT_RECORD_NORM_ERR;
654 } else {
655 if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK)
656 sectRecordType = SECT_RECORD_NORM_DAM;
657 else
658 sectRecordType = SECT_RECORD_NORM;
659 }
660
661 fputc(sectRecordType, myDisk->file);
662 sim_fwrite(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file);
663 *writelen = myDisk->track[Cyl][Head].sectsize;
664
665 return(SCPE_OK);
666 }
667
668 /* Format an entire track. The new track to be formatted must be after any existing tracks on
669 * the disk.
670 *
671 * This routine should be enhanced to re-format an existing track to the same format (this
672 * does not involve changing the disk image size.)
673 *
674 * Any existing data on the disk image will be destroyed when Track 0, Head 0 is formatted.
675 * At that time, the IMD file is truncated. So for the trackWrite to be used to sucessfully
676 * format a disk image, then format program must format tracks starting with Cyl 0, Head 0,
677 * and proceed sequentially through all tracks/heads on the disk.
678 *
679 * Format programs that are known to work include:
680 * Cromemco CDOS "INIT.COM"
681 * ADC Super-Six (CP/M-80) "FMT8.COM"
682 * 86-DOS "INIT.COM"
683 *
684 */
trackWrite(DISK_INFO * myDisk,uint32 Cyl,uint32 Head,uint32 numSectors,uint32 sectorLen,uint8 * sectorMap,uint8 mode,uint8 fillbyte,uint32 * flags)685 t_stat trackWrite(DISK_INFO *myDisk,
686 uint32 Cyl,
687 uint32 Head,
688 uint32 numSectors,
689 uint32 sectorLen,
690 uint8 *sectorMap,
691 uint8 mode,
692 uint8 fillbyte,
693 uint32 *flags)
694 {
695 FILE *fileref;
696 IMD_HEADER track_header;
697 uint8 *sectorData;
698 unsigned long i;
699 unsigned long dataLen;
700
701 *flags = 0;
702
703 /* Check parameters */
704 if(myDisk == NULL) {
705 *flags |= IMD_DISK_IO_ERROR_GENERAL;
706 return(SCPE_IOERR);
707 }
708
709 if(myDisk->flags & FD_FLAG_WRITELOCK) {
710 printf("Disk write-protected, cannot format tracks." NLP);
711 *flags |= IMD_DISK_IO_ERROR_WPROT;
712 return(SCPE_IOERR);
713 }
714
715 fileref = myDisk->file;
716
717 DBG_PRINT(("Formatting C:%d/H:%d/N:%d, len=%d, Fill=0x%02x" NLP, Cyl, Head, numSectors, sectorLen, fillbyte));
718
719 /* Truncate the IMD file when formatting Cyl 0, Head 0 */
720 if((Cyl == 0) && (Head == 0))
721 {
722 /* Skip over IMD comment field. */
723 commentParse(myDisk, NULL, 0);
724
725 /* Truncate the IMD file after the comment field. */
726 #ifdef _WIN32 /* This might work under UNIX and/or VMS since this POSIX, but I haven't tried it. */
727 _chsize(_fileno(fileref), ftell (fileref));
728 #else
729 if (ftruncate(fileno(fileref), ftell (fileref)) == -1) {
730 printf("Disk truncation failed." NLP);
731 *flags |= IMD_DISK_IO_ERROR_GENERAL;
732 return(SCPE_IOERR);
733 }
734 #endif
735 /* Flush and re-parse the IMD file. */
736 fflush(fileref);
737 diskParse(myDisk, 0);
738 }
739
740 /* Check to make sure the Cyl / Head is not already formatted. */
741 if(sectSeek(myDisk, Cyl, Head) == 0) {
742 printf("SIM_IMD: ERROR: Not Formatting C:%d/H:%d, track already exists." NLP, Cyl, Head);
743 *flags |= IMD_DISK_IO_ERROR_GENERAL;
744 return(SCPE_IOERR);
745 }
746
747 track_header.mode = mode;
748 track_header.cyl = Cyl;
749 track_header.head = Head;
750 track_header.nsects = numSectors;
751 track_header.sectsize = sectorLen;
752
753 /* Forward to end of the file, write track header and sector map. */
754 sim_fseek(myDisk->file, 0, SEEK_END);
755 sim_fwrite(&track_header, 1, sizeof(IMD_HEADER), fileref);
756 sim_fwrite(sectorMap, 1, numSectors, fileref);
757
758 /* Compute data length, and fill a sector buffer with the
759 * sector record type as the first byte, and fill the sector
760 * data with the fillbyte.
761 */
762 dataLen = (128 << sectorLen)+1;
763 sectorData = malloc(dataLen);
764 memset(sectorData, fillbyte, dataLen);
765 sectorData[0] = SECT_RECORD_NORM;
766
767 /* For each sector on the track, write the record type and sector data. */
768 for(i=0;i<numSectors;i++) {
769 sim_fwrite(sectorData, 1, dataLen, fileref);
770 }
771
772 /* Flush the file, and free the sector buffer. */
773 fflush(fileref);
774 free(sectorData);
775
776 /* Now that the disk track/sector layout has been modified, re-parse the disk image. */
777 diskParse(myDisk, 0);
778
779 return(SCPE_OK);
780 }
781
782 /* Utility function to set the image type for a unit to the correct value.
783 * Prints an error message in case a CPT image is presented and returns
784 * SCPE_OPENERR in this case. Otherwise the return value is SCPE_OK
785 */
assignDiskType(UNIT * uptr)786 t_stat assignDiskType(UNIT *uptr) {
787 t_stat result = SCPE_OK;
788 char header[4];
789 if (fgets(header, 4, uptr->fileref) == NULL)
790 uptr->u3 = IMAGE_TYPE_DSK;
791 else if (strncmp(header, "IMD", 3) == 0)
792 uptr->u3 = IMAGE_TYPE_IMD;
793 else if(strncmp(header, "CPT", 3) == 0) {
794 printf("CPT images not yet supported.\n");
795 uptr->u3 = IMAGE_TYPE_CPT;
796 result = SCPE_OPENERR;
797 }
798 else
799 uptr->u3 = IMAGE_TYPE_DSK;
800 return result;
801 }
802
803