1 /* disk.c: Routines for handling disk images
2 Copyright (c) 2007-2017 Gergely Szasz
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 Author contact information:
20 Philip: philip-fuse@shadowmagic.org.uk
28 #include <sys/types.h>
32 #ifndef LIBFUSE_NO_LIBSPECTRUM
33 # include <libspectrum.h>
43 void (*libfdcMessageCB
) (int type
, const char *msg
) = NULL
;
46 #define MSGBUF_SIZE (1024)
47 static __thread
char lfdcmsgbuf
[MSGBUF_SIZE
];
50 //==========================================================================
54 //==========================================================================
55 __attribute__((format(printf
,2,3))) void libfdcMsg (int type
, const char *fmt
, ...) {
56 if (!libfdcMessageCB
) return;
59 vsnprintf(lfdcmsgbuf
, sizeof(lfdcmsgbuf
), fmt
, va
);
60 lfdcmsgbuf
[sizeof(lfdcmsgbuf
)-1] = 0;
62 libfdcMessageCB(type
, lfdcmsgbuf
);
66 /* The ordering of these strings must match the order of the
67 * disk_error_t enumeration in disk.h */
68 static const char * const disk_error
[] = {
70 "Feature not implemented", /* DISK_IMPL */
71 "Out of memory", /* DISK_MEM */
72 "Invalid disk geometry", /* DISK_GEOM */
73 "Cannot open disk image", /* DISK_OPEN */
74 "Unsupported file feature", /* DISK_UNSUP */
75 "Read only disk", /* DISK_RDONLY */
76 "Cannot close file", /* DISK_CLOSE */
77 "Cannot write disk image", /* DISK_WRFILE */
78 "Partially written file", /* DISK_WRPART */
80 "Invalid head", /* DISK_INVALID_HEAD */
81 "Cylinder not found", /* DISK_NO_CYLINDER */
82 "Sector not found", /* DISK_NO_SECTOR */
83 "Invalid sector data", /* DISK_BAD_SECTOR */
84 "Sector too big", /* DISK_SECTOR_TOO_BIG */
85 "Write error", /* DISK_WRITE_ERROR */
87 "Unknown error code" /* DISK_LAST_ERROR */
91 static const int disk_bpt
[] = {
92 6250, /* AUTO assumes DD */
97 6500, /* DD+ e.g. Coin Op Hits */
102 typedef struct disk_gap_t
{
103 int gap
; /* gap byte */
104 int sync
; /* sync byte */
106 int mark
; /* mark byte 0xa1 for MFM -1 for MF */
110 static const disk_gap_t gaps
[] = {
111 { 0x4e, 0x00, 12, 0xa1, { 0, 60, 22, 24 } }, /* MGT MFM */
112 { 0x4e, 0x00, 12, 0xa1, { 0, 10, 22, 60 } }, /* TRD MFM */
113 { 0xff, 0x00, 6, -1, { 40, 26, 11, 27 } }, /* IBM3740 FM */
114 { 0x4e, 0x00, 12, 0xa1, { 80, 50, 22, 54 } }, /* IBM34 MFM */
115 { 0xff, 0x00, 6, -1, { 0, 16, 11, 10 } }, /* MINIMAL FM */
116 { 0x4e, 0x00, 12, 0xa1, { 0, 32, 22, 24 } }, /* MINIMAL MFM */
120 static inline void bitmap_set (uint8_t *b
, const size_t n
) { b
[n
/8] |= (1<<(n
%8)); }
121 static inline void bitmap_reset (uint8_t *b
, const size_t n
) { b
[n
/8] &= ~(1<<(n
%8)); }
122 static inline int bitmap_test (const uint8_t *b
, const size_t n
) { return b
[n
/8]&(1<<(n
%8));}
125 /* insert simple TR-DOS autoloader? */
126 int opt_beta_autoload
= 1; /* default is 1 */
129 #define GAP_MGT_PLUSD (0)
130 #define GAP_TRDOS (1)
131 #define GAP_IBM3740 (2)
132 #define GAP_IBM34 (3)
133 #define GAP_MINIMAL_FM (4)
134 #define GAP_MINIMAL_MFM (5)
136 #define buffavail(buffer) (buffer->file.length-buffer->index)
138 #define buff (buffer->file.buffer+buffer->index)
140 #define ARRAY_SIZE(a) ((sizeof(a)/sizeof(*a)))
143 typedef struct utils_file
{
149 #define compat_fd FILE *
151 static const compat_fd COMPAT_FILE_OPEN_FAILED
= NULL
;
154 //==========================================================================
158 //==========================================================================
159 static compat_fd
compat_file_open (const char *path
, int write
) {
163 if (stat(path
, &statbuf
)) return NULL
;
164 /* Check file type */
165 if (!S_ISREG(statbuf
.st_mode
)) {
171 return fopen( path
, write
? "wb" : "rb" );
175 //==========================================================================
177 // compat_file_get_length
179 //==========================================================================
180 static off_t
compat_file_get_length (compat_fd fd
) {
181 struct stat file_info
;
183 if (fstat(fileno(fd
), &file_info
)) {
184 fprintf(stderr
, "ERROR: couldn't stat file: %s\n", strerror(errno
));
188 return file_info
.st_size
;
192 //==========================================================================
196 //==========================================================================
197 static int compat_file_read (compat_fd fd
, utils_file
*file
) {
198 size_t bytes
= fread(file
->buffer
, 1, file
->length
, fd
);
199 if (bytes
!= file
->length
) {
200 fprintf(stderr
, "error reading file: expected %lu bytes, but read only %lu\n",
201 (unsigned long)file
->length
, (unsigned long)bytes
);
208 //==========================================================================
212 //==========================================================================
214 static int compat_file_write (compat_fd fd, const unsigned char *buffer, size_t length) {
215 size_t bytes = fwrite( buffer, 1, length, fd );
216 if (bytes != length) {
217 fprintf(stderr, "error writing file: expected %lu bytes, but wrote only %lu\n",
218 (unsigned long)length, (unsigned long)bytes );
226 //==========================================================================
230 //==========================================================================
231 static int compat_file_close (compat_fd fd
) {
236 //==========================================================================
238 // compat_file_exists
240 //==========================================================================
242 static int compat_file_exists (const char *path) {
243 return (access( path, R_OK) != -1);
248 //==========================================================================
252 //==========================================================================
253 static int utils_read_fd (compat_fd fd
, const char *filename
, utils_file
*file
) {
254 file
->length
= compat_file_get_length(fd
);
255 if (file
->length
== -1) return 1;
257 file
->buffer
= malloc(file
->length
+1);
259 if (compat_file_read(fd
, file
)) {
261 compat_file_close(fd
);
265 if (compat_file_close(fd
)) {
266 fprintf(stderr
, "Couldn't close '%s': %s\n", filename
, strerror(errno
));
275 //==========================================================================
279 //==========================================================================
280 static int utils_read_file (const char *filename
, utils_file
*file
) {
281 compat_fd fd
= compat_file_open(filename
, 0);
283 if (fd
== COMPAT_FILE_OPEN_FAILED
) {
284 fprintf(stderr
, "couldn't open '%s': %s\n", filename
, strerror(errno
));
288 int error
= utils_read_fd( fd
, filename
, file
);
289 if (error
) return error
;
295 //==========================================================================
299 //==========================================================================
300 static void utils_close_file (utils_file
*file
) {
305 // ////////////////////////////////////////////////////////////////////////// //
306 typedef struct buffer_t
{ /* to store buffer data */
307 utils_file file
; /* buffer, length */
312 //==========================================================================
316 //==========================================================================
317 const char *disk_strerror (int error
) {
318 if (error
< 0 || error
> DISK_LAST_ERROR
) error
= DISK_LAST_ERROR
;
319 return disk_error
[error
];
323 //==========================================================================
327 //==========================================================================
328 static int buffread (void *data
, size_t len
, buffer_t
*buffer
) {
329 if (len
> buffer
->file
.length
- buffer
->index
) return 0;
330 memcpy(data
, buffer
->file
.buffer
+buffer
->index
, len
);
331 buffer
->index
+= len
;
336 //==========================================================================
340 //==========================================================================
341 static int buffseek (buffer_t
*buffer
, long offset
, int whence
) {
342 if (whence
== SEEK_CUR
) offset
+= buffer
->index
;
343 if (offset
>= buffer
->file
.length
) return -1;
344 buffer
->index
= offset
;
349 //==========================================================================
353 //==========================================================================
354 static int id_read (disk_t
*d
, int *head
, int *track
, int *sector
, int *length
) {
357 while (d
->i
< d
->bpt
) {
358 if (d
->track
[d
->i
] == 0xa1 && bitmap_test(d
->clocks
, d
->i
)) {
359 /* 0xa1 with clock */
361 } else if (d
->track
[d
->i
] == 0xfe &&
362 (bitmap_test(d
->clocks
, d
->i
) || /* 0xfe with clock */
363 a1mark
)) /* or 0xfe with 0xa1 */
366 *track
= d
->track
[d
->i
++];
367 *head
= d
->track
[d
->i
++];
368 *sector
= d
->track
[d
->i
++];
369 *length
= d
->track
[d
->i
++];
370 d
->i
+= 2; /* skip CRC */
381 //==========================================================================
385 //==========================================================================
386 static int datamark_read (disk_t
*d
, int *deleted
) {
388 while (d
->i
< d
->bpt
) {
389 if (d
->track
[d
->i
] == 0xa1 && bitmap_test(d
->clocks
, d
->i
)) {
390 /* 0xa1 with clock */
392 } else if (d
->track
[d
->i
] >= 0xf8 && d
->track
[d
->i
] <= 0xfe &&
393 (bitmap_test(d
->clocks
, d
->i
) || a1mark
))
395 /* 0xfe with clock or 0xfe after 0xa1 mark */
396 *deleted
= (d
->track
[d
->i
] == 0xf8 ? 1 : 0);
408 //==========================================================================
412 //==========================================================================
413 static int id_seek (disk_t
*d
, int sector
) {
415 d
->i
= 0; /* start of the track */
416 while (id_read(d
, &h
, &t
, &s
, &b
)) {
417 if (s
== sector
) return 1;
423 //==========================================================================
427 // returns sector size code, or -1
429 //==========================================================================
430 static int id_seek_ex (disk_t
*d
, int sector
) {
432 d
->i
= 0; /* start of the track */
433 while (id_read(d
, &h
, &t
, &s
, &b
)) {
434 if (s
== sector
) return b
;
440 //==========================================================================
444 // seclen 00 -> 128, 01 -> 256 ... byte
446 //==========================================================================
447 static int data_write_file ( disk_t
*d
, FILE *file
, int seclen
) {
448 int len
= 0x80<<seclen
;
449 if (fwrite(&d
->track
[d
->i
], len
, 1, file
) != 1) return 1;
454 //==========================================================================
458 //==========================================================================
459 static int savetrack (disk_t
*d
, FILE *file
, int head
, int track
,
460 int sector_base
, int sectors
, int seclen
)
465 DISK_SET_TRACK(d
, head
, track
);
467 for (s
= sector_base
; s
< sector_base
+sectors
; ++s
) {
469 if (datamark_read(d
, &del
)) {
470 /* write data if we have data */
471 if (data_write_file( d
, file
, seclen
)) return 1;
481 //==========================================================================
485 //==========================================================================
486 static int saverawtrack (disk_t
*d
, FILE *file
, int head
, int track
) {
490 DISK_SET_TRACK(d
, head
, track
);
492 while (id_read(d
, &h
, &t
, &s
, &seclen
)) {
493 if (datamark_read(d
, &del
)) {
494 /* write data if we have data */
495 if (data_write_file(d
, file
, seclen
)) return 1;
503 #define DISK_ID_NOTMATCH (1)
504 #define DISK_SECLEN_VARI (2)
505 #define DISK_SPT_VARI (4)
506 #define DISK_SBASE_VARI (8)
507 #define DISK_MFM_VARI (16)
508 #define DISK_DDAM (32)
509 #define DISK_CORRUPT_SECTOR (64)
510 #define DISK_UNFORMATTED_TRACK (128)
511 #define DISK_FM_DATA (256)
512 #define DISK_WEAK_DATA (512)
515 //==========================================================================
519 //==========================================================================
520 static int guess_track_geom (disk_t
*d
, int head
, int track
, int *sector_base
,
521 int *sectors
, int *seclen
, int *mfm
)
531 DISK_SET_TRACK(d
, head
, track
);
533 while (id_read(d
, &h
, &t
, &s
, &sl
)) {
534 if (*sector_base
== -1) *sector_base
= s
;
535 if (*seclen
== -1) *seclen
= sl
;
536 if (*mfm
== -1) *mfm
= (d
->track
[d
->i
] == 0x4e ? 1 : 0); /* not so robust */
537 if (!datamark_read(d
, &del
)) r
|= DISK_CORRUPT_SECTOR
;
538 if (t
!= track
) r
|= DISK_ID_NOTMATCH
;
539 if (s
< *sector_base
) *sector_base
= s
;
541 r
|= DISK_SECLEN_VARI
;
542 if (sl
> *seclen
) *seclen
= sl
;
544 if (del
) r
|= DISK_DDAM
;
552 //==========================================================================
554 // update_tracks_mode
556 //==========================================================================
557 static void update_tracks_mode (disk_t
*d
) {
560 for (i
= 0; i
< d
->cylinders
*d
->sides
; ++i
) {
561 DISK_SET_TRACK_IDX(d
, i
);
562 mfm
= 0, fm
= 0, weak
= 0;
563 bpt
= d
->track
[-3]+256*d
->track
[-2];
564 for (j
= DISK_CLEN(bpt
)-1; j
>= 0; --j
) {
569 if (mfm
&& !fm
) d
->track
[-1] = 0x00;
570 if (!mfm
&& fm
) d
->track
[-1] = 0x01;
571 if (mfm
&& fm
) d
->track
[-1] = 0x02;
573 d
->track
[-1] |= 0x80;
580 //==========================================================================
584 //==========================================================================
585 static int check_disk_geom (disk_t
*d
, int *sector_base
, int *sectors
,
586 int *seclen
, int *mfm
, int *unf
)
588 int h
, t
, s
, slen
, sbase
, m
;
591 DISK_SET_TRACK_IDX(d
, 0);
598 for (t
= 0; t
< d
->cylinders
; ++t
) {
599 for (h
= 0; h
< d
->sides
; ++h
) {
600 r
|= ((d
->track
[-1]&0x80) ? DISK_WEAK_DATA
: 0);
601 r
|= ((d
->track
[-1]&0x03) == 0x02 ? DISK_MFM_VARI
: 0);
602 r
|= ((d
->track
[-1]&0x03) == 0x01 ? DISK_FM_DATA
: 0);
603 r
|= guess_track_geom(d
, h
, t
, &sbase
, &s
, &slen
, &m
);
604 if (*sector_base
== -1) *sector_base
= sbase
;
605 if (*sectors
== -1) *sectors
= s
;
606 if (*seclen
== -1) *seclen
= slen
;
607 if (*mfm
== -1) *mfm
= m
;
610 if (*unf
== -1 && h
> 0) *unf
= -2;
611 if (*unf
== -1) *unf
= t
;
614 if (*unf
> -1) *unf
= -2;
615 if (sbase
!= *sector_base
) {
616 r
|= DISK_SBASE_VARI
;
617 if (sbase
< *sector_base
) *sector_base
= sbase
;
621 if (s
> *sectors
) *sectors
= s
;
623 if (slen
!= *seclen
) {
624 r
|= DISK_SECLEN_VARI
;
625 if (slen
> *seclen
) *seclen
= slen
;
635 r
|= DISK_UNFORMATTED_TRACK
;
643 //==========================================================================
647 //==========================================================================
648 static int gap_add (disk_t
*d
, int gap
, int gaptype
) {
649 const disk_gap_t
*g
= &gaps
[gaptype
];
650 if (d
->i
+g
->len
[gap
] >= d
->bpt
) return 1; /* too many data bytes */
651 /*-------------------------------- given gap --------------------------------*/
652 memset(d
->track
+d
->i
, g
->gap
, g
->len
[gap
]);
658 //==========================================================================
662 // preindex gap and index mark
664 //==========================================================================
665 static int preindex_len (disk_t
*d
, int gaptype
) {
666 const disk_gap_t
*g
= &gaps
[gaptype
];
667 return g
->len
[0]+g
->sync_len
+(g
->mark
>= 0 ? 3 : 0 )+1;
672 [ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ]
673 |------------------------------------------->
676 //==========================================================================
680 // preindex gap and index mark
682 //==========================================================================
683 static int preindex_add (disk_t
*d
, int gaptype
) {
684 const disk_gap_t
*g
= &gaps
[gaptype
];
685 if (d
->i
+preindex_len(d
, gaptype
) >= d
->bpt
) return 1;
686 /*------------------------------ pre-index gap -------------------------------*/
687 if (gap_add(d
, 0, gaptype
)) return 1;
688 /*------------------------------ sync ---------------------------*/
689 memset(d
->track
+d
->i
, g
->sync
, g
->sync_len
);
692 memset(d
->track
+d
->i
, g
->mark
, 3);
693 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
694 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
695 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
697 /*------------------------------ mark ------------------------------*/
700 bitmap_set(d
->clocks
, d
->i
); /* set clock mark */
702 d
->track
[d
->i
++] = 0xfc; /* index mark */
707 //==========================================================================
711 // preindex gap and index mark
713 //==========================================================================
714 static inline int postindex_len (disk_t
*d
, int gaptype
) {
715 const disk_gap_t
*g
= &gaps
[gaptype
];
720 //==========================================================================
726 //==========================================================================
727 static inline int postindex_add (disk_t
*d
, int gaptype
) {
728 return gap_add(d
, 1, gaptype
);
732 //==========================================================================
736 //==========================================================================
737 static int gap4_add (disk_t
*d
, int gaptype
) {
738 int len
= d
->bpt
-d
->i
;
739 const disk_gap_t
*g
= &gaps
[gaptype
];
740 if (len
< 0) return 1;
741 /*------------------------------ GAP IV ------------------------------*/
742 memset(d
->track
+d
->i
, g
->gap
, len
); /* GAP IV fill until end of track */
748 #define SECLEN_128 (0x00)
749 #define SECLEN_256 (0x01)
750 #define SECLEN_512 (0x02)
751 #define SECLEN_1024 (0x03)
754 #define CRC_ERROR (1)
757 [ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ] [ .. DATA .. ] [ . CRC . ]
758 |------------------------------------------------------->
761 //==========================================================================
765 //==========================================================================
766 static int id_add (disk_t
*d
, int h
, int t
, int s
, int l
, int gaptype
, int crc_error
) {
767 uint16_t crc
= 0xffff;
768 const disk_gap_t
*g
= &gaps
[gaptype
];
769 if (d
->i
+g
->sync_len
+(g
->mark
>= 0 ? 3 : 0)+7 >= d
->bpt
) return 1;
770 /*------------------------------ sync ---------------------------*/
771 memset(d
->track
+d
->i
, g
->sync
, g
->sync_len
);
774 memset(d
->track
+d
->i
, g
->mark
, 3);
775 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
776 crc
= crc_fdc(crc
, g
->mark
);
777 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
778 crc
= crc_fdc(crc
, g
->mark
);
779 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
780 crc
= crc_fdc(crc
, g
->mark
);
782 /*------------------------------ mark ------------------------------*/
785 bitmap_set(d
->clocks
, d
->i
); /* set clock mark */
787 d
->track
[d
->i
++] = 0xfe; /* ID mark */
788 crc
= crc_fdc(crc
, 0xfe);
789 /*------------------------------ header ------------------------------*/
790 d
->track
[d
->i
++] = t
; crc
= crc_fdc(crc
, t
);
791 d
->track
[d
->i
++] = h
; crc
= crc_fdc(crc
, h
);
792 d
->track
[d
->i
++] = s
; crc
= crc_fdc(crc
, s
);
793 d
->track
[d
->i
++] = l
; crc
= crc_fdc(crc
, l
);
794 d
->track
[d
->i
++] = crc
>>8;
796 d
->track
[d
->i
++] = (~crc
)&0xff; /* record a CRC error */
798 d
->track
[d
->i
++] = crc
&0xff; /* CRC */
800 /*------------------------------ GAP II ------------------------------*/
801 return gap_add(d
, 2, gaptype
);
806 [ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ] [ .. DATA .. ] [ . CRC . ]
807 |-------------------------------------------->
810 //==========================================================================
814 //==========================================================================
815 static int datamark_add (disk_t
*d
, int ddam
, int gaptype
) {
816 const disk_gap_t
*g
= &gaps
[gaptype
];
817 if (d
->i
+g
->len
[2]+g
->sync_len
+(g
->mark
>= 0 ? 3 : 0)+1 >= d
->bpt
) return 1;
818 /*------------------------------ sync ---------------------------*/
819 memset(d
->track
+d
->i
, g
->sync
, g
->sync_len
);
822 memset(d
->track
+d
->i
, g
->mark
, 3);
823 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
824 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
825 bitmap_set(d
->clocks
, d
->i
); ++d
->i
;
827 /*------------------------------ mark ------------------------------*/
830 bitmap_set(d
->clocks
, d
->i
); /* set clock mark */
832 d
->track
[d
->i
++] = (ddam
? 0xf8 : 0xfb); /* DATA mark 0xf8 -> deleted data */
839 #define NO_AUTOFILL (-1)
841 //==========================================================================
845 // copy data from *buffer and update *buffer->index
846 // if 'buffer' == NULL, then copy data bytes from 'data'
848 //==========================================================================
849 static int data_add (disk_t
*d
, buffer_t
*buffer
, const unsigned char *data
, int len
,
850 int ddam
, int gaptype
, int crc_error
, int autofill
, int *start_data
)
853 uint16_t crc
= 0xffff;
854 const disk_gap_t
*g
= &gaps
[gaptype
];
856 if (datamark_add(d
, ddam
, gaptype
)) return 1;
859 crc
= crc_fdc(crc
, g
->mark
);
860 crc
= crc_fdc(crc
, g
->mark
);
861 crc
= crc_fdc(crc
, g
->mark
);
863 crc
= crc_fdc(crc
, (ddam
? 0xf8 : 0xfb)); /* deleted or normal */
864 if (len
< 0) goto header_crc_error
; /* CRC error */
865 if (d
->i
+len
+2 >= d
->bpt
) return 1; /* too many data bytes */
866 /*------------------------------ data ------------------------------*/
867 if (start_data
!= NULL
) *start_data
= d
->i
; /* record data start position */
868 if (buffer
== NULL
) {
869 memcpy(d
->track
+d
->i
, data
, len
);
872 length
= buffavail(buffer
);
873 if (length
> len
) length
= len
;
874 buffread(d
->track
+d
->i
, length
, buffer
);
877 /* autofill with 'autofill' */
878 if (autofill
< 0) return 1;
879 while (length
< len
) {
880 d
->track
[d
->i
+length
] = autofill
;
885 while (length
< len
) {
887 crc
= crc_fdc(crc
, d
->track
[d
->i
]);
891 if (crc_error
) crc
^= 1; /* mess up CRC */
892 d
->track
[d
->i
++] = crc
>>8;
893 d
->track
[d
->i
++] = crc
&0xff; /* CRC */
894 /*------------------------------ GAP III ------------------------------*/
896 return gap_add(d
, 3, gaptype
);
900 //==========================================================================
904 //==========================================================================
905 static int calc_sectorlen (int mfm
, int sector_length
, int gaptype
) {
907 const disk_gap_t
*g
= &gaps
[gaptype
];
908 /*------------------------------ ID ------------------------------*/
909 len
+= g
->sync_len
+(g
->mark
>= 0 ? 3 : 0)+7;
910 /*------------------------------ GAP II ------------------------------*/
912 /*--------------------------------- data ---------------------------------*/
913 len
+= g
->sync_len
+(g
->mark
>= 0 ? 3 : 0)+1; /* DAM */
914 len
+= sector_length
;
916 /*------------------------------ GAP III ------------------------------*/
922 //==========================================================================
926 //==========================================================================
927 static int calc_lenid (int sector_length
) {
929 while (sector_length
> 0x80) {
937 //==========================================================================
941 // update tracks TLEN
943 //==========================================================================
944 static void disk_update_tlens (disk_t
*d
) {
946 for (int i
= 0; i
< d
->sides
*d
->cylinders
; ++i
) {
947 DISK_SET_TRACK_IDX(d
, i
);
948 if (d
->track
[-3]+256*d
->track
[-2] == 0) {
949 d
->track
[-3] = d
->bpt
&0xff;
950 d
->track
[-2] = (d
->bpt
>>8)&0xff;
956 #define NO_INTERLEAVE (1)
957 #define INTERLEAVE_2 (2)
958 #define INTERLEAVE_OPUS (13)
960 #define NO_PREINDEX (0)
963 //==========================================================================
967 //==========================================================================
968 static int trackgen (disk_t
*d
, buffer_t
*buffer
, int head
, int track
,
969 int sector_base
, int sectors
, int sector_length
, int preindex
,
970 int gap
, int interleave
, int autofill
)
973 int slen
= calc_sectorlen((d
->density
!= DISK_SD
&& d
->density
!= DISK_8_SD
),
978 DISK_SET_TRACK(d
, head
, track
);
980 if (preindex
&& preindex_add(d
, gap
)) return 1;
981 if (postindex_add(d
, gap
)) return 1;
985 for (s
= sector_base
; s
< sector_base
+sectors
; ++s
) {
987 if (id_add(d
, head
, track
, s
, calc_lenid(sector_length
), gap
, CRC_OK
)) return 1;
988 if (data_add(d
, buffer
, NULL
, sector_length
, NO_DDAM
, gap
, CRC_OK
, autofill
, NULL
)) return 1;
990 if (pos
>= sectors
) {
994 /* we fill this pos already */
995 ++pos
; /* skip one more pos */
1000 d
->i
= idx
+sectors
*slen
;
1001 return gap4_add(d
, gap
);
1005 //==========================================================================
1009 // close and destroy a disk structure and data
1011 //==========================================================================
1012 void disk_close (disk_t
*d
) {
1013 if (d
->data
!= NULL
) {
1017 if (d
->filename
!= NULL
) {
1021 d
->type
= DISK_TYPE_NONE
;
1026 * if d->density == DISK_DENS_AUTO =>
1027 * use d->tlen if d->bpt == 0
1028 * or use d->bpt to determine d->density
1031 //==========================================================================
1035 //==========================================================================
1036 static int disk_alloc (disk_t
*d
) {
1039 if (d
->density
!= DISK_DENS_AUTO
) d
->bpt
= disk_bpt
[d
->density
];
1040 else if (d
->bpt
> 12500) return d
->status
= DISK_UNSUP
;
1041 else if (d
->bpt
> 10416) { d
->density
= DISK_HD
; d
->bpt
= disk_bpt
[DISK_HD
]; }
1042 else if (d
->bpt
> 6500) { d
->density
= DISK_8_DD
; d
->bpt
= disk_bpt
[DISK_8_DD
]; }
1043 else if (d
->bpt
> 6250) { d
->density
= DISK_DD_PLUS
; d
->bpt
= disk_bpt
[DISK_DD_PLUS
]; }
1044 else if (d
->bpt
> 5208) { d
->density
= DISK_DD
; d
->bpt
= disk_bpt
[DISK_DD
]; }
1045 else if (d
->bpt
> 3125) { d
->density
= DISK_8_SD
; d
->bpt
= disk_bpt
[DISK_8_SD
]; }
1046 else if (d
->bpt
> 0) { d
->density
= DISK_SD
; d
->bpt
= disk_bpt
[DISK_SD
]; }
1048 if (d
->bpt
> 0) d
->tlen
= 4+d
->bpt
+3*DISK_CLEN(d
->bpt
);
1050 dlen
= d
->sides
*d
->cylinders
*d
->tlen
; /* track len with clock and other marks */
1051 if (dlen
== 0) return d
->status
= DISK_GEOM
;
1053 d
->data
= calloc(1, dlen
);
1054 if (!d
->data
) return (d
->status
= DISK_MEM
);
1056 return (d
->status
= DISK_OK
);
1060 //==========================================================================
1064 // create a new unformatted disk
1066 //==========================================================================
1067 int disk_new (disk_t
*d
, int sides
, int cylinders
, disk_dens_t density
, disk_type_t type
) {
1070 if (density
< DISK_DENS_AUTO
|| density
> DISK_HD
|| /* unknown density */
1071 type
<= DISK_TYPE_NONE
|| type
>= DISK_TYPE_LAST
|| /* unknown type */
1072 sides
< 1 || sides
> 2 || /* 1 or 2 side */
1073 cylinders
< 35 || cylinders
> 83) /* 35 .. 83 cylinder */
1075 return (d
->status
= DISK_GEOM
);
1079 d
->density
= (density
== DISK_DENS_AUTO
? DISK_DD
: density
);
1081 d
->cylinders
= cylinders
;
1083 if (disk_alloc(d
) != DISK_OK
) return d
->status
;
1087 disk_update_tlens(d
);
1089 return (d
->status
= DISK_OK
);
1093 //==========================================================================
1095 // alloc_uncompress_buffer
1097 //==========================================================================
1098 static int alloc_uncompress_buffer (unsigned char **buffer
, int length
) {
1099 if (*buffer
!= NULL
) return 0; /* return if allocated */
1100 unsigned char *b
= calloc(1, length
);
1101 if (b
== NULL
) return 1;
1107 //==========================================================================
1111 //==========================================================================
1112 int disk_preformat (disk_t
*d
) {
1115 buffer
.file
.length
= 0;
1118 if (d
->sides
== 2) {
1119 if (trackgen(d
, &buffer
, 1, 0, 0xff, 1, 128,
1120 NO_PREINDEX
, GAP_MINIMAL_MFM
, NO_INTERLEAVE
, 0xff))
1125 if (trackgen(d
, &buffer
, 1, 2, 0xfe, 1, 128,
1126 NO_PREINDEX
, GAP_MINIMAL_MFM
, NO_INTERLEAVE
, 0xff))
1132 if (trackgen(d
, &buffer
, 0, 0, 0xff, 1, 128,
1133 NO_PREINDEX
, GAP_MINIMAL_MFM
, NO_INTERLEAVE
, 0xff))
1138 if (trackgen(d
, &buffer
, 0, 2, 0xfe, 1, 128,
1139 NO_PREINDEX
, GAP_MINIMAL_MFM
, NO_INTERLEAVE
, 0xff))
1148 /* open a disk image */
1149 #define GEOM_CHECK \
1150 if (d->sides < 1 || d->sides > 2 || \
1151 d->cylinders < 1 || d->cylinders > 85) return d->status = DISK_GEOM
1154 #include "disk_udi.c"
1155 #include "disk_mgt.c"
1156 #include "disk_d40d80.c"
1157 #include "disk_sad.c"
1158 #include "disk_trd.c"
1159 #include "disk_scl.c"
1160 #include "disk_fdi.c"
1161 #include "disk_td0.c"
1162 #include "disk_cpc.c"
1165 /* open a disk image file, read and convert to our format
1166 * if preindex != 0 we generate preindex gap if needed
1168 static int disk_open2 (disk_t
*d
, const char *filename
, int preindex
) {
1171 d
->wrprot
= (access(filename
, W_OK
) != 0); /* file read only */
1173 if (utils_read_file(filename
, &buffer
.file
)) {
1174 return (d
->status
= DISK_OPEN
);
1179 d
->type
= disk_detect_format(buffer
.file
.buffer
, buffer
.file
.length
);
1180 if (d
->type
== DISK_TYPE_NONE
&& buffer
.file
.length
> 16) {
1181 const size_t nlen
= strlen(filename
);
1182 const char *ext
= (nlen
>= 4 ? filename
+nlen
-4 : "");
1183 if (strcasecmp(ext
, ".trd") == 0) d
->type
= DISK_TRD
;
1184 else if (strcasecmp(ext
, ".mgt") == 0) d
->type
= DISK_MGT
;
1185 else if (strcasecmp(ext
, ".opd") == 0) d
->type
= DISK_OPD
;
1186 else if (strcasecmp(ext
, ".opu") == 0) d
->type
= DISK_OPD
;
1187 else if (strcasecmp(ext
, ".d40") == 0) d
->type
= DISK_D40
;
1188 else if (strcasecmp(ext
, ".d80") == 0) d
->type
= DISK_D80
;
1191 case DISK_UDI
: open_udi(&buffer
, d
, preindex
); break;
1192 case DISK_FDI
: open_fdi(&buffer
, d
, preindex
); break;
1193 case DISK_TD0
: open_td0(&buffer
, d
, preindex
); break;
1194 case DISK_MGT
: open_img_mgt_opd(&buffer
, d
); break;
1195 case DISK_IMG
: open_img_mgt_opd(&buffer
, d
); break;
1196 case DISK_SAD
: open_sad(&buffer
, d
, preindex
); break;
1197 case DISK_CPC
: open_cpc(&buffer
, d
, preindex
); break;
1198 case DISK_ECPC
: open_cpc(&buffer
, d
, preindex
); break;
1199 case DISK_TRD
: open_trd(&buffer
, d
, preindex
); break;
1200 case DISK_SCL
: open_scl(&buffer
, d
, preindex
); break;
1201 case DISK_OPD
: open_img_mgt_opd(&buffer
, d
); break;
1202 case DISK_D40
: open_d40_d80(&buffer
, d
); break;
1203 case DISK_D80
: open_d40_d80(&buffer
, d
); break;
1205 utils_close_file(&buffer
.file
);
1206 return (d
->status
= DISK_OPEN
);
1209 if (d
->status
!= DISK_OK
) {
1210 if (d
->data
!= NULL
) free(d
->data
);
1211 utils_close_file(&buffer
.file
);
1215 utils_close_file(&buffer
.file
);
1217 disk_update_tlens(d
);
1218 update_tracks_mode(d
);
1219 d
->filename
= /*utils_safe_*/strdup(filename
);
1221 return (d
->status
= DISK_OK
);
1225 //==========================================================================
1229 //==========================================================================
1230 int disk_open (disk_t
*d
, const char *filename
, int preindex
) {
1232 if (filename
== NULL
|| *filename
== 0) return (d
->status
= DISK_OPEN
);
1233 return disk_open2(d
, filename
, preindex
);
1237 //==========================================================================
1241 // create a two sided disk (d) from two one sided (d1 and d2)
1243 //==========================================================================
1244 int disk_merge_sides (disk_t
*d
, disk_t
*d1
, disk_t
*d2
, int autofill
) {
1248 if (d1
->sides
!= 1 || d2
->sides
!= 1 ||
1249 d1
->bpt
!= d2
->bpt
||
1250 (autofill
< 0 && d1
->cylinders
!= d2
->cylinders
))
1259 d
->cylinders
= (d2
->cylinders
> d1
->cylinders
? d2
->cylinders
: d1
->cylinders
);
1261 d
->density
= DISK_DENS_AUTO
;
1263 if (disk_alloc(d
) != DISK_OK
) return d
->status
;
1265 clen
= DISK_CLEN(d
->bpt
);
1267 d1
->track
= d1
->data
;
1268 d2
->track
= d2
->data
;
1270 for (i
= 0; i
< d
->cylinders
; ++i
) {
1271 if (i
< d1
->cylinders
) {
1272 memcpy( d
->track
, d1
->track
, d
->tlen
);
1274 d
->track
[0] = d
->bpt
&0xff;
1275 d
->track
[1] = (d
->bpt
>>8)&0xff;
1277 memset(d
->track
+3, (autofill
&0xff), d
->bpt
); /* fill data */
1278 memset(d
->track
+3+d
->bpt
, 0x00, 3*clen
); /* no clock and other marks */
1280 d
->track
+= d
->tlen
;
1281 d1
->track
+= d1
->tlen
;
1282 if (i
< d2
->cylinders
) {
1283 memcpy( d
->track
, d2
->track
, d
->tlen
);
1285 d
->track
[0] = d
->bpt
&0xff;
1286 d
->track
[1] = (d
->bpt
>>8)&0xff;
1288 memset(d
->track
+1, autofill
& 0xff, d
->bpt
); /* fill data */
1289 memset(d
->track
+1+d
->bpt
, 0x00, 3*clen
); /* no clock and other marks */
1291 d
->track
+= d
->tlen
;
1292 d2
->track
+= d2
->tlen
;
1296 return (d
->status
= DISK_OK
);
1300 //==========================================================================
1304 //==========================================================================
1305 static int write_log (FILE *file
, disk_t
*d
) {
1306 int i
, j
, k
, del
, rev
;
1311 fprintf(file
, "DISK tracks log!\n");
1312 fprintf(file
, "Sides: %d, cylinders: %d\n", d
->sides
, d
->cylinders
);
1313 for (j
= 0; j
< d
->cylinders
; ++j
) {
1315 for (i
= 0; i
< d
->sides
; ++i
) {
1316 DISK_SET_TRACK(d
, i
, j
);
1318 fprintf(file
, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
1319 i
, j
, d
->track
[-1], d
->track
[-3]+256*d
->track
[-2]);
1320 while (id_read(d
, &h
, &t
, &s
, &b
)) {
1321 fprintf(file
, " h:%d t:%d s:%d l:%d(%d)", h
, t
, s
, b
, (0x80<<b
));
1322 if (datamark_read(d
, &del
)) {
1323 fprintf(file
, " %s\n", (del
? "deleted" : "normal"));
1325 fprintf(file
, " noDAM\n" );
1331 fprintf(file
, "\n***************************\nSector Data Dump:\n");
1332 for (j
= 0; j
< d
->cylinders
; ++j
) {
1334 for (i
= 0; i
< d
->sides
; ++i
) {
1335 DISK_SET_TRACK(d
, i
, j
);
1337 fprintf(file
, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
1338 i
, j
, d
->track
[-1], d
->track
[-3]+256*d
->track
[-2]);
1340 while (id_read(d
, &h
, &t
, &s
, &b
)) {
1342 if (datamark_read(d
, &del
)) {
1343 fprintf(file
, " h:%d t:%d s:%d l:%d (%s)\n", h
, t
, s
, b
, (del
? "deleted" : "normal"));
1345 fprintf(file
, " h:%d t:%d s:%d l:%d (missing data)\n", h
, t
, s
, b
);
1349 if (k
%16 == 0) fprintf(file
, "0x%08x:", k
);
1350 fprintf(file
, " 0x%02x", d
->track
[d
->i
]);
1351 str
[k
&0x0f] = (d
->track
[d
->i
] >= 32 && d
->track
[d
->i
] < 127 ? d
->track
[d
->i
] : '.');
1353 if (k
%16 == 0) fprintf(file
, " | %s\n", str
);
1355 if (d
->i
>= d
->bpt
) {
1358 if (rev
== 6) break;
1365 fprintf(file
, "\n***************************\n**Full Dump:\n");
1366 for (j
= 0; j
< d
->cylinders
; ++j
) {
1368 for (i
= 0; i
< d
->sides
; ++i
) {
1369 DISK_SET_TRACK(d
, i
, j
);
1371 fprintf(file
, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
1372 i
, j
, d
->track
[-1], d
->track
[-3]+256*d
->track
[-2]);
1374 while (d
->i
< d
->bpt
) {
1375 if (k
%8 == 0) fprintf( file
, "0x%08x:", d
->i
);
1376 fprintf(file
, " 0x%04x", d
->track
[d
->i
]|
1377 (bitmap_test(d
->clocks
, d
->i
) ? 0x0c00 : 0x0000)|
1378 (bitmap_test(d
->fm
, d
->i
) ? 0x1000 : 0x0000)|
1379 (bitmap_test(d
->weak
, d
->i
) ? 0x8000 : 0x0000));
1381 if (k
%8 == 0) fprintf( file
, "\n" );
1387 return (d
->status
= DISK_OK
);
1391 //==========================================================================
1395 //==========================================================================
1396 int disk_write (disk_t
*d
, const char *filename
) {
1397 if (!d
) return DISK_OPEN
;
1399 if (!filename
|| !filename
[0]) return (d
->status
= DISK_WRFILE
);
1401 const size_t namelen
= strlen(filename
);
1402 const char *ext
= (namelen
< 4 ? "" : filename
+namelen
-4);
1404 if (!strcasecmp(ext
, ".udi")) d
->type
= DISK_UDI
;
1405 else if (!strcasecmp(ext
, ".dsk")) d
->type
= DISK_CPC
;
1406 else if (!strcasecmp(ext
, ".mgt")) d
->type
= DISK_MGT
;
1407 else if (!strcasecmp(ext
, ".opd")) d
->type
= DISK_OPD
;
1408 else if (!strcasecmp(ext
, ".opu")) d
->type
= DISK_OPD
;
1409 else if (!strcasecmp(ext
, ".img")) d
->type
= DISK_IMG
;
1410 else if (!strcasecmp(ext
, ".trd")) d
->type
= DISK_TRD
;
1411 else if (!strcasecmp(ext
, ".sad")) d
->type
= DISK_SAD
;
1412 else if (!strcasecmp(ext
, ".fdi")) d
->type
= DISK_FDI
;
1413 else if (!strcasecmp(ext
, ".d40")) d
->type
= DISK_D40
;
1414 else if (!strcasecmp(ext
, ".d80")) d
->type
= DISK_D80
;
1415 else if (!strcasecmp(ext
, ".scl")) d
->type
= DISK_SCL
;
1416 else if (!strcasecmp(ext
, ".td0")) d
->type
= DISK_TD0
;
1417 else if (!strcasecmp(ext
, ".log")) d
->type
= DISK_LOG
;
1418 else if (d
->type
== DISK_TYPE_NONE
) d
->type
= DISK_UDI
;
1420 if (d
->type
== DISK_TD0
) return (d
->status
= DISK_IMPL
);
1422 FILE *file
= fopen(filename
, "wb");
1423 if (file
== NULL
) return (d
->status
= DISK_WRFILE
);
1425 /* save position of current data */
1426 disk_position_t diskctx
;
1427 disk_position_save(d
, &diskctx
);
1429 update_tracks_mode(d
);
1437 write_img_mgt_opd(file
, d
);
1441 write_d40_d80(file
, d
);
1462 d
->status
= DISK_WRFILE
;
1466 disk_position_restore(d
, &diskctx
);
1468 if (d
->status
!= DISK_OK
) {
1473 if (fclose(file
) == -1) return (d
->status
= DISK_WRFILE
);
1475 return (d
->status
= DISK_OK
);
1479 //==========================================================================
1481 // disk_detect_format
1483 //==========================================================================
1484 disk_type_t
disk_detect_format (const void *data
, int size
) {
1485 if (!data
|| size
< 18) return DISK_TYPE_NONE
;
1486 if (memcmp(data
, "UDI!", 4) == 0) return DISK_UDI
;
1487 if (memcmp(data
, "FDI", 3) == 0) return DISK_FDI
;
1488 if (memcmp(data
, "EXTENDED", 8) == 0) return DISK_ECPC
;
1489 if (memcmp(data
, "MV - CPC", 8) == 0) return DISK_CPC
;
1490 if (memcmp(data
, "Aley's disk backup", 18) == 0) return DISK_SAD
;
1491 if (memcmp(data
, "TD", 2) == 0) return DISK_TD0
;
1492 if (memcmp(data
, "td", 2) == 0) return DISK_TD0
;
1493 if (memcmp(data
, "SINCLAIR", 8) == 0) return DISK_SCL
;
1494 return DISK_TYPE_NONE
;
1498 //==========================================================================
1502 //==========================================================================
1503 static int create_buffer (buffer_t
*buf
, const void *data
, int size
) {
1504 if (size
<= 16 || !data
) return -1;
1506 buf
->file
.buffer
= calloc(1, (uint32_t)size
);
1507 if (!buf
->file
.buffer
) return -1;
1508 buf
->file
.length
= (uint32_t)size
;
1509 memcpy(buf
->file
.buffer
, data
, (uint32_t)size
);
1515 #define XXOPEN(dskfmt,dtype) \
1516 int disk_open_buf_##dskfmt (disk_t *d, const void *data, int size) { \
1517 if (!d) return DISK_OPEN; \
1519 if (create_buffer(&buf, data, size) != 0) return DISK_OPEN; \
1520 const int res = open_##dskfmt(&buf, d, 0); \
1521 if (res != DISK_OK) { \
1527 disk_update_tlens(d); \
1528 update_tracks_mode(d); \
1529 d->filename = NULL; \
1531 utils_close_file(&buf.file); \
1535 XXOPEN(udi
, DISK_UDI
)
1536 XXOPEN(fdi
, DISK_FDI
)
1537 XXOPEN(trd
, DISK_TRD
)
1538 XXOPEN(scl
, DISK_SCL
)
1539 XXOPEN(td0
, DISK_TD0
)
1542 //==========================================================================
1544 // disk_open_buf_dsk
1546 //==========================================================================
1547 int disk_open_buf_dsk (disk_t
*d
, const void *data
, int size
) {
1548 if (!d
|| size
< 16) return DISK_OPEN
;
1550 const disk_type_t type
= disk_detect_format(data
, size
);
1551 if (type
!= DISK_CPC
&& type
!= DISK_ECPC
) return DISK_OPEN
;
1555 if (create_buffer(&buf
, data
, size
) != 0) return -1;
1556 const int res
= open_cpc(&buf
, d
, 0);
1557 if (res
!= DISK_OK
) {
1562 disk_update_tlens(d
);
1563 update_tracks_mode(d
);
1566 utils_close_file(&buf
.file
);
1571 //==========================================================================
1573 // disk_is_sector_exist
1575 //==========================================================================
1576 int disk_is_sector_exist (disk_t
*d
, int head
, int cylinder
, int sector
) {
1579 if (head
< 0 || head
>= d
->sides
) return 0;
1580 if (cylinder
< 0 || cylinder
>= d
->cylinders
) return 0;
1582 disk_position_t diskctx
;
1583 disk_position_save(d
, &diskctx
);
1588 DISK_SET_TRACK(d
, head
, cylinder
);
1590 /* TR-DOS descriptor */
1591 int szcode
= id_seek_ex(d
, sector
);
1592 if (szcode
< 0 || szcode
> 3) goto quit
;
1593 if (!datamark_read(d
, &del
)) goto quit
;
1599 disk_position_restore(d
, &diskctx
);
1604 //==========================================================================
1609 // returns error code (DISK_xxx)
1610 // automatically saves and restores disk position
1612 //==========================================================================
1613 int disk_read_sector (disk_t
*d
, int head
, int cylinder
, int sector
,
1614 void *buf
, size_t bufsize
, int *sectorsize
)
1616 if (!d
) return DISK_OPEN
;
1618 if (head
< 0 || head
>= d
->sides
) return DISK_INVALID_HEAD
;
1619 if (cylinder
< 0 || cylinder
>= d
->cylinders
) return DISK_NO_CYLINDER
;
1621 disk_position_t diskctx
;
1622 disk_position_save(d
, &diskctx
);
1627 DISK_SET_TRACK(d
, head
, cylinder
);
1629 /* TR-DOS descriptor */
1630 int szcode
= id_seek_ex(d
, sector
);
1631 if (szcode
< 0 || szcode
> 3) { res
= DISK_NO_SECTOR
; goto quit
; }
1632 if (!datamark_read(d
, &del
)) { res
= DISK_BAD_SECTOR
; goto quit
; }
1633 if (del
) { res
= DISK_BAD_SECTOR
; goto quit
; }
1635 uint32_t secsz
= 0x80<<szcode
;
1636 if (secsz
== 0) { res
= DISK_BAD_SECTOR
; goto quit
; }
1637 if (secsz
> bufsize
) { res
= DISK_SECTOR_TOO_BIG
; goto quit
; }
1639 if (!buf
) { res
= DISK_OPEN
; goto quit
; }
1640 memcpy(buf
, d
->track
+d
->i
, secsz
);
1642 if (sectorsize
) *sectorsize
= (int)secsz
;
1645 disk_position_restore(d
, &diskctx
);
1650 //==========================================================================
1652 // disk_get_sector_data_ptr
1654 // return pointer to sector data or NULL
1656 //==========================================================================
1657 const void *disk_get_sector_data_ptr (disk_t
*d
, int head
, int cylinder
, int sector
,
1660 if (!d
) return NULL
;
1662 if (head
< 0 || head
>= d
->sides
) return NULL
;
1663 if (cylinder
< 0 || cylinder
>= d
->cylinders
) return NULL
;
1665 disk_position_t diskctx
;
1666 disk_position_save(d
, &diskctx
);
1671 DISK_SET_TRACK(d
, head
, cylinder
);
1673 /* TR-DOS descriptor */
1674 int szcode
= id_seek_ex(d
, sector
);
1675 if (szcode
< 0 || szcode
> 3) goto quit
;
1676 if (!datamark_read(d
, &del
)) goto quit
;
1679 uint32_t secsz
= 0x80<<szcode
;
1680 if (secsz
== 0 || szcode
> 3) goto quit
;
1682 res
= d
->track
+d
->i
;
1684 if (sectorsize
) *sectorsize
= (int)secsz
;
1687 disk_position_restore(d
, &diskctx
);
1692 //==========================================================================
1694 // disk_write_sector
1696 //==========================================================================
1697 int disk_write_sector (disk_t
*d
, int head
, int cylinder
, int sector
,
1698 const void *buf
, size_t bufsize
, int type
)
1700 if (!d
) return DISK_OPEN
;
1702 if (type
!= DISK_WR_BETA
) return DISK_UNSUP
;
1704 if (head
< 0 || head
>= d
->sides
) return DISK_INVALID_HEAD
;
1705 if (cylinder
< 0 || cylinder
>= d
->cylinders
) return DISK_NO_CYLINDER
;
1707 disk_position_t diskctx
;
1708 disk_position_save(d
, &diskctx
);
1712 DISK_SET_TRACK(d
, head
, cylinder
);
1714 int szcode
= id_seek_ex(d
, sector
);
1715 if (szcode
< 0 || szcode
> 3) { res
= DISK_NO_SECTOR
; goto quit
; }
1717 uint32_t secsz
= 0x80<<szcode
;
1718 if (secsz
== 0) { res
= DISK_BAD_SECTOR
; goto quit
; }
1720 uint8_t *xbuf
= (uint8_t *)buf
;
1721 if (secsz
> bufsize
) {
1722 xbuf
= calloc(1, secsz
);
1723 if (!xbuf
) { res
= DISK_MEM
; goto quit
; }
1724 if (bufsize
) memcpy(xbuf
, buf
, bufsize
);
1727 if (data_add(d
, NULL
, xbuf
, secsz
, NO_DDAM
, GAP_TRDOS
, CRC_OK
, NO_AUTOFILL
, NULL
) != 0) {
1728 res
= DISK_WRITE_ERROR
;
1730 if ((void *)xbuf
!= buf
) free(xbuf
);
1733 disk_position_restore(d
, &diskctx
);
1738 //==========================================================================
1742 //==========================================================================
1743 int disk_format_beta (disk_t
*d
, int cylinders
, int interleave
) {
1744 if (!d
) return DISK_OPEN
;
1748 if (cylinders
< 1 || cylinders
> 83) return (d
->status
= DISK_GEOM
);
1749 if (cylinders
< 80) cylinders
= 80;
1753 d
->cylinders
= cylinders
;
1754 d
->density
= DISK_DD
;
1755 if (disk_alloc(d
) != DISK_OK
) return d
->status
;
1760 // build sector number table
1763 if (interleave
< 1) interleave
= 2; else if (interleave
> 15) interleave
= 15;
1764 memset(sidxtbl
, 0, sizeof(sidxtbl
));
1765 for (int sidx
= 1; sidx
<= 16; ++sidx
) {
1766 while (sidxtbl
[spos
] != 0) spos
= (spos
+1)%16;
1767 sidxtbl
[spos
] = sidx
;
1768 spos
= (spos
+interleave
)%16;
1773 memset(sdata
, 0, sizeof(sdata
));
1774 for (int head
= 0; head
< 2; ++head
) {
1775 for (int cyl
= 0; cyl
< cylinders
; ++cyl
) {
1776 DISK_SET_TRACK(d
, head
, cyl
);
1778 postindex_add(d
, GAP_TRDOS
);
1779 const int seclen
= calc_sectorlen(1, 256, GAP_TRDOS
); // one sector raw length
1781 for (int snum
= 0; snum
< 16; ++snum
) {
1782 id_add(d
, head
, cyl
, sidxtbl
[snum
], SECLEN_256
, GAP_TRDOS
, CRC_OK
);
1783 data_add(d
, NULL
, sdata
, 256, NO_DDAM
, GAP_TRDOS
, CRC_OK
, NO_AUTOFILL
, NULL
);
1784 // move to the next sector
1787 gap4_add(d
, GAP_TRDOS
);
1792 disk_update_tlens(d
);
1793 return (d
->status
= DISK_OK
);