libfusefdc: partially ported +3DOS FS module
[zymosis.git] / src / libfusefdc / disk.c
blob68a652a4e6eeb72869c221cbf5eda28af7bf74ab
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
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #ifndef LIBFUSE_NO_LIBSPECTRUM
33 # include <libspectrum.h>
34 #endif
36 #include "crc.h"
37 #include "disk.h"
40 #include <stdarg.h>
43 void (*libfdcMessageCB) (int type, const char *msg) = NULL;
46 #define MSGBUF_SIZE (1024)
47 static __thread char lfdcmsgbuf[MSGBUF_SIZE];
50 //==========================================================================
52 // libfdcMsg
54 //==========================================================================
55 __attribute__((format(printf,2,3))) void libfdcMsg (int type, const char *fmt, ...) {
56 if (!libfdcMessageCB) return;
57 va_list va;
58 va_start(va, fmt);
59 vsnprintf(lfdcmsgbuf, sizeof(lfdcmsgbuf), fmt, va);
60 lfdcmsgbuf[sizeof(lfdcmsgbuf)-1] = 0;
61 va_end(va);
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[] = {
69 "OK", /* DISK_OK */
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 */
93 5208, /* 8" SD */
94 10416, /* 8" DD */
95 3125, /* SD */
96 6250, /* DD */
97 6500, /* DD+ e.g. Coin Op Hits */
98 12500, /* HD */
102 typedef struct disk_gap_t {
103 int gap; /* gap byte */
104 int sync; /* sync byte */
105 int sync_len;
106 int mark; /* mark byte 0xa1 for MFM -1 for MF */
107 int len[4];
108 } disk_gap_t;
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)
137 /* data buffer */
138 #define buff (buffer->file.buffer+buffer->index)
140 #define ARRAY_SIZE(a) ((sizeof(a)/sizeof(*a)))
143 typedef struct utils_file {
144 uint8_t *buffer;
145 size_t length;
146 } utils_file;
149 #define compat_fd FILE *
151 static const compat_fd COMPAT_FILE_OPEN_FAILED = NULL;
154 //==========================================================================
156 // compat_file_open
158 //==========================================================================
159 static compat_fd compat_file_open (const char *path, int write) {
160 struct stat statbuf;
162 if (!write) {
163 if (stat(path, &statbuf)) return NULL;
164 /* Check file type */
165 if (!S_ISREG(statbuf.st_mode)) {
166 errno = EINVAL;
167 return NULL;
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));
185 return -1;
188 return file_info.st_size;
192 //==========================================================================
194 // compat_file_read
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 );
202 return 1;
204 return 0;
208 //==========================================================================
210 // compat_file_write
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 );
219 return 1;
221 return 0;
226 //==========================================================================
228 // compat_file_close
230 //==========================================================================
231 static int compat_file_close (compat_fd fd) {
232 return fclose( fd );
236 //==========================================================================
238 // compat_file_exists
240 //==========================================================================
242 static int compat_file_exists (const char *path) {
243 return (access( path, R_OK) != -1);
248 //==========================================================================
250 // utils_read_fd
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)) {
260 free(file->buffer);
261 compat_file_close(fd);
262 return 1;
265 if (compat_file_close(fd)) {
266 fprintf(stderr, "Couldn't close '%s': %s\n", filename, strerror(errno));
267 free(file->buffer);
268 return 1;
271 return 0;
275 //==========================================================================
277 // utils_read_file
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));
285 return 1;
288 int error = utils_read_fd( fd, filename, file );
289 if (error) return error;
291 return 0;
295 //==========================================================================
297 // utils_close_file
299 //==========================================================================
300 static void utils_close_file (utils_file *file) {
301 free(file->buffer);
305 // ////////////////////////////////////////////////////////////////////////// //
306 typedef struct buffer_t { /* to store buffer data */
307 utils_file file; /* buffer, length */
308 size_t index;
309 } buffer_t;
312 //==========================================================================
314 // disk_strerror
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 //==========================================================================
325 // buffread
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;
332 return 1;
336 //==========================================================================
338 // buffseek
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;
345 return 0;
349 //==========================================================================
351 // id_read
353 //==========================================================================
354 static int id_read (disk_t *d, int *head, int *track, int *sector, int *length) {
355 int a1mark = 0;
357 while (d->i < d->bpt) {
358 if (d->track[d->i] == 0xa1 && bitmap_test(d->clocks, d->i)) {
359 /* 0xa1 with clock */
360 a1mark = 1;
361 } else if (d->track[d->i] == 0xfe &&
362 (bitmap_test(d->clocks, d->i) || /* 0xfe with clock */
363 a1mark)) /* or 0xfe with 0xa1 */
365 ++d->i;
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 */
371 return 1;
372 } else {
373 a1mark = 0;
375 ++d->i;
377 return 0;
381 //==========================================================================
383 // datamark_read
385 //==========================================================================
386 static int datamark_read (disk_t *d, int *deleted) {
387 int a1mark = 0;
388 while (d->i < d->bpt) {
389 if (d->track[d->i] == 0xa1 && bitmap_test(d->clocks, d->i)) {
390 /* 0xa1 with clock */
391 a1mark = 1;
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);
397 ++d->i;
398 return 1;
399 } else {
400 a1mark = 0;
402 ++d->i;
404 return 0;
408 //==========================================================================
410 // id_seek
412 //==========================================================================
413 static int id_seek (disk_t *d, int sector) {
414 int h, t, s, b;
415 d->i = 0; /* start of the track */
416 while (id_read(d, &h, &t, &s, &b)) {
417 if (s == sector) return 1;
419 return 0;
423 //==========================================================================
425 // id_seek_ex
427 // returns sector size code, or -1
429 //==========================================================================
430 static int id_seek_ex (disk_t *d, int sector) {
431 int h, t, s, b;
432 d->i = 0; /* start of the track */
433 while (id_read(d, &h, &t, &s, &b)) {
434 if (s == sector) return b;
436 return -1;
440 //==========================================================================
442 // data_write_file
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;
450 return 0;
454 //==========================================================================
456 // savetrack
458 //==========================================================================
459 static int savetrack (disk_t *d, FILE *file, int head, int track,
460 int sector_base, int sectors, int seclen)
462 int s;
463 int del;
465 DISK_SET_TRACK(d, head, track);
466 d->i = 0;
467 for (s = sector_base; s < sector_base+sectors; ++s) {
468 if (id_seek(d, s)) {
469 if (datamark_read(d, &del)) {
470 /* write data if we have data */
471 if (data_write_file( d, file, seclen)) return 1;
473 } else {
474 return 1;
477 return 0;
481 //==========================================================================
483 // saverawtrack
485 //==========================================================================
486 static int saverawtrack (disk_t *d, FILE *file, int head, int track) {
487 int h, t, s, seclen;
488 int del;
490 DISK_SET_TRACK(d, head, track);
491 d->i = 0;
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;
499 return 0;
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 //==========================================================================
517 // guess_track_geom
519 //==========================================================================
520 static int guess_track_geom (disk_t *d, int head, int track, int *sector_base,
521 int *sectors, int *seclen, int *mfm)
523 int r = 0;
524 int h, t, s, sl;
525 int del = 0;
526 *sector_base = -1;
527 *sectors = 0;
528 *seclen = -1;
529 *mfm = -1;
531 DISK_SET_TRACK(d, head, track);
532 d->i = 0;
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;
540 if (sl != *seclen) {
541 r |= DISK_SECLEN_VARI;
542 if (sl > *seclen) *seclen = sl;
544 if (del) r |= DISK_DDAM;
545 *sectors += 1;
548 return r;
552 //==========================================================================
554 // update_tracks_mode
556 //==========================================================================
557 static void update_tracks_mode (disk_t *d) {
558 int i, j, bpt;
559 int mfm, fm, weak;
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) {
565 mfm |= ~d->fm[j];
566 fm |= d->fm[j];
567 weak |= d->weak[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;
572 if (weak) {
573 d->track[-1] |= 0x80;
574 d->have_weak = 1;
580 //==========================================================================
582 // check_disk_geom
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;
589 int r = 0;
591 DISK_SET_TRACK_IDX(d, 0);
592 d->i = 0;
593 *sector_base = -1;
594 *sectors = -1;
595 *seclen = -1;
596 *mfm = -1;
597 *unf = -1;
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;
608 if (sbase == -1) {
609 /* unformatted */
610 if (*unf == -1 && h > 0) *unf = -2;
611 if (*unf == -1) *unf = t;
612 continue;
614 if (*unf > -1) *unf = -2;
615 if (sbase != *sector_base) {
616 r |= DISK_SBASE_VARI;
617 if (sbase < *sector_base) *sector_base = sbase;
619 if (s != *sectors) {
620 r |= DISK_SPT_VARI;
621 if (s > *sectors) *sectors = s;
623 if (slen != *seclen) {
624 r |= DISK_SECLEN_VARI;
625 if (slen > *seclen) *seclen = slen;
627 if (m != *mfm) {
628 r |= DISK_MFM_VARI;
629 *mfm = 1;
634 if (*unf == -2) {
635 r |= DISK_UNFORMATTED_TRACK;
636 *unf = -1;
639 return r;
643 //==========================================================================
645 // gap_add
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]);
653 d->i += g->len[gap];
654 return 0;
658 //==========================================================================
660 // preindex_len
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 |------------------------------------------->
674 Preindex
676 //==========================================================================
678 // preindex_add
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);
690 d->i += g->sync_len;
691 if (g->mark >= 0) {
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 ------------------------------*/
698 if (g->mark < 0) {
699 /* FM */
700 bitmap_set(d->clocks, d->i); /* set clock mark */
702 d->track[d->i++] = 0xfc; /* index mark */
703 return 0;
707 //==========================================================================
709 // postindex_len
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];
716 return g->len[1];
720 //==========================================================================
722 // postindex_add
724 // postindex gap
726 //==========================================================================
727 static inline int postindex_add (disk_t *d, int gaptype) {
728 return gap_add(d, 1, gaptype);
732 //==========================================================================
734 // gap4_add
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 */
743 d->i = d->bpt;
744 return 0;
748 #define SECLEN_128 (0x00)
749 #define SECLEN_256 (0x01)
750 #define SECLEN_512 (0x02)
751 #define SECLEN_1024 (0x03)
753 #define CRC_OK (0)
754 #define CRC_ERROR (1)
757 [ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ] [ .. DATA .. ] [ . CRC . ]
758 |------------------------------------------------------->
761 //==========================================================================
763 // id_add
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);
772 d->i += g->sync_len;
773 if (g->mark >= 0) {
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 ------------------------------*/
783 if (g->mark < 0) {
784 /* FM */
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;
795 if (crc_error) {
796 d->track[d->i++] = (~crc)&0xff; /* record a CRC error */
797 } else {
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 |-------------------------------------------->
808 datamark
810 //==========================================================================
812 // datamark_add
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);
820 d->i += g->sync_len;
821 if (g->mark >= 0) {
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 ------------------------------*/
828 if (g->mark < 0) {
829 /* FM */
830 bitmap_set(d->clocks, d->i); /* set clock mark */
832 d->track[d->i++] = (ddam ? 0xf8 : 0xfb); /* DATA mark 0xf8 -> deleted data */
833 return 0;
837 #define NO_DDAM (0)
838 #define DDAM (1)
839 #define NO_AUTOFILL (-1)
841 //==========================================================================
843 // data_add
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)
852 int length;
853 uint16_t crc = 0xffff;
854 const disk_gap_t *g = &gaps[gaptype];
856 if (datamark_add(d, ddam, gaptype)) return 1;
858 if (g->mark >= 0) {
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);
870 length = len;
871 } else {
872 length = buffavail(buffer);
873 if (length > len) length = len;
874 buffread(d->track+d->i, length, buffer);
876 if (length < len) {
877 /* autofill with 'autofill' */
878 if (autofill < 0) return 1;
879 while (length < len) {
880 d->track[d->i+length] = autofill;
881 ++length;
884 length = 0;
885 while (length < len) {
886 /* calculate CRC */
887 crc = crc_fdc(crc, d->track[d->i]);
888 ++d->i;
889 ++length;
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 ------------------------------*/
895 header_crc_error:
896 return gap_add(d, 3, gaptype);
900 //==========================================================================
902 // calc_sectorlen
904 //==========================================================================
905 static int calc_sectorlen (int mfm, int sector_length, int gaptype) {
906 int len = 0;
907 const disk_gap_t *g = &gaps[gaptype];
908 /*------------------------------ ID ------------------------------*/
909 len += g->sync_len+(g->mark >= 0 ? 3 : 0)+7;
910 /*------------------------------ GAP II ------------------------------*/
911 len += g->len[2];
912 /*--------------------------------- data ---------------------------------*/
913 len += g->sync_len+(g->mark >= 0 ? 3 : 0)+1; /* DAM */
914 len += sector_length;
915 len += 2; /* CRC */
916 /*------------------------------ GAP III ------------------------------*/
917 len += g->len[3];
918 return len;
922 //==========================================================================
924 // calc_lenid
926 //==========================================================================
927 static int calc_lenid (int sector_length) {
928 int id = 0;
929 while (sector_length > 0x80) {
930 ++id;
931 sector_length >>= 1;
933 return id;
937 //==========================================================================
939 // disk_update_tlens
941 // update tracks TLEN
943 //==========================================================================
944 static void disk_update_tlens (disk_t *d) {
945 /* check tracks */
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)
961 #define PREINDEX (1)
963 //==========================================================================
965 // trackgen
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)
972 int i, s, pos;
973 int slen = calc_sectorlen((d->density != DISK_SD && d->density != DISK_8_SD),
974 sector_length, gap);
975 int idx;
977 d->i = 0;
978 DISK_SET_TRACK(d, head, track);
980 if (preindex && preindex_add(d, gap)) return 1;
981 if (postindex_add(d, gap)) return 1;
983 idx = d->i;
984 pos = i = 0;
985 for (s = sector_base; s < sector_base+sectors; ++s) {
986 d->i = idx+pos*slen;
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;
989 pos += interleave;
990 if (pos >= sectors) {
991 /* wrap around */
992 pos -= sectors;
993 if (pos <= i) {
994 /* we fill this pos already */
995 ++pos; /* skip one more pos */
996 ++i;
1000 d->i = idx+sectors*slen;
1001 return gap4_add(d, gap);
1005 //==========================================================================
1007 // disk_close
1009 // close and destroy a disk structure and data
1011 //==========================================================================
1012 void disk_close (disk_t *d) {
1013 if (d->data != NULL) {
1014 free(d->data);
1015 d->data = NULL;
1017 if (d->filename != NULL) {
1018 free(d->filename);
1019 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
1029 * or use d->density
1031 //==========================================================================
1033 // disk_alloc
1035 //==========================================================================
1036 static int disk_alloc (disk_t *d) {
1037 size_t dlen;
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 //==========================================================================
1062 // disk_new
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) {
1068 d->filename = NULL;
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);
1078 d->type = type;
1079 d->density = (density == DISK_DENS_AUTO ? DISK_DD : density);
1080 d->sides = sides;
1081 d->cylinders = cylinders;
1083 if (disk_alloc(d) != DISK_OK) return d->status;
1085 d->wrprot = 0;
1086 d->dirty = 1;
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;
1102 *buffer = b;
1103 return 0;
1107 //==========================================================================
1109 // disk_preformat
1111 //==========================================================================
1112 int disk_preformat (disk_t *d) {
1113 buffer_t buffer;
1115 buffer.file.length = 0;
1116 buffer.index = 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))
1122 return DISK_GEOM;
1125 if (trackgen(d, &buffer, 1, 2, 0xfe, 1, 128,
1126 NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff))
1128 return DISK_GEOM;
1132 if (trackgen(d, &buffer, 0, 0, 0xff, 1, 128,
1133 NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff))
1135 return DISK_GEOM;
1138 if (trackgen(d, &buffer, 0, 2, 0xfe, 1, 128,
1139 NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff))
1141 return DISK_GEOM;
1144 return DISK_OK;
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) {
1169 buffer_t buffer;
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);
1177 buffer.index = 0;
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;
1190 switch (d->type) {
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;
1204 default:
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);
1212 return d->status;
1215 utils_close_file(&buffer.file);
1216 d->dirty = 0;
1217 disk_update_tlens(d);
1218 update_tracks_mode(d);
1219 d->filename = /*utils_safe_*/strdup(filename);
1221 return (d->status = DISK_OK);
1225 //==========================================================================
1227 // disk_open
1229 //==========================================================================
1230 int disk_open (disk_t *d, const char *filename, int preindex) {
1231 d->filename = NULL;
1232 if (filename == NULL || *filename == 0) return (d->status = DISK_OPEN);
1233 return disk_open2(d, filename, preindex);
1237 //==========================================================================
1239 // disk_merge_sides
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) {
1245 int i;
1246 int clen;
1248 if (d1->sides != 1 || d2->sides != 1 ||
1249 d1->bpt != d2->bpt ||
1250 (autofill < 0 && d1->cylinders != d2->cylinders))
1252 return DISK_GEOM;
1255 d->wrprot = 0;
1256 d->dirty = 0;
1257 d->sides = 2;
1258 d->type = d1->type;
1259 d->cylinders = (d2->cylinders > d1->cylinders ? d2->cylinders : d1->cylinders);
1260 d->bpt = d1->bpt;
1261 d->density = DISK_DENS_AUTO;
1263 if (disk_alloc(d) != DISK_OK) return d->status;
1265 clen = DISK_CLEN(d->bpt);
1266 d->track = d->data;
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 );
1273 } else {
1274 d->track[0] = d->bpt&0xff;
1275 d->track[1] = (d->bpt>>8)&0xff;
1276 d->track[2] = 0x00;
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 );
1284 } else {
1285 d->track[0] = d->bpt&0xff;
1286 d->track[1] = (d->bpt>>8)&0xff;
1287 d->track[2] = 0x00;
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;
1294 disk_close(d1);
1295 disk_close(d2);
1296 return (d->status = DISK_OK);
1300 //==========================================================================
1302 // write_log
1304 //==========================================================================
1305 static int write_log (FILE *file, disk_t *d) {
1306 int i, j, k, del, rev;
1307 int h, t, s, b;
1308 char str[17];
1310 str[16] = '\0';
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) {
1314 /* ALT :) */
1315 for (i = 0; i < d->sides; ++i) {
1316 DISK_SET_TRACK(d, i, j);
1317 d->i = 0;
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"));
1324 } else {
1325 fprintf(file, " noDAM\n" );
1331 fprintf(file, "\n***************************\nSector Data Dump:\n");
1332 for (j = 0; j < d->cylinders; ++j) {
1333 /* ALT :) */
1334 for (i = 0; i < d->sides; ++i) {
1335 DISK_SET_TRACK(d, i, j);
1336 d->i = 0;
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]);
1339 rev = k = 0;
1340 while (id_read(d, &h, &t, &s, &b)) {
1341 b = 0x80 << 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"));
1344 } else {
1345 fprintf(file, " h:%d t:%d s:%d l:%d (missing data)\n", h, t, s, b);
1347 k = 0;
1348 while (k < 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] : '.');
1352 ++k;
1353 if (k%16 == 0) fprintf(file, " | %s\n", str);
1354 ++d->i;
1355 if (d->i >= d->bpt) {
1356 d->i = 0;
1357 ++rev;
1358 if (rev == 6) break;
1365 fprintf(file, "\n***************************\n**Full Dump:\n");
1366 for (j = 0; j < d->cylinders; ++j) {
1367 /* ALT :) */
1368 for (i = 0; i < d->sides; ++i) {
1369 DISK_SET_TRACK(d, i, j);
1370 d->i = 0;
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]);
1373 k = 0;
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));
1380 ++k;
1381 if (k%8 == 0) fprintf( file, "\n" );
1382 ++d->i;
1387 return (d->status = DISK_OK);
1391 //==========================================================================
1393 // disk_write
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);
1430 switch (d->type) {
1431 case DISK_UDI:
1432 write_udi(file, d);
1433 break;
1434 case DISK_IMG:
1435 case DISK_MGT:
1436 case DISK_OPD:
1437 write_img_mgt_opd(file, d);
1438 break;
1439 case DISK_D40:
1440 case DISK_D80:
1441 write_d40_d80(file, d);
1442 break;
1443 case DISK_TRD:
1444 write_trd(file, d);
1445 break;
1446 case DISK_SAD:
1447 write_sad(file, d);
1448 break;
1449 case DISK_FDI:
1450 write_fdi(file, d);
1451 break;
1452 case DISK_SCL:
1453 write_scl(file, d);
1454 break;
1455 case DISK_CPC:
1456 write_cpc(file, d);
1457 break;
1458 case DISK_LOG:
1459 write_log(file, d);
1460 break;
1461 default:
1462 d->status = DISK_WRFILE;
1463 break;
1466 disk_position_restore(d, &diskctx);
1468 if (d->status != DISK_OK) {
1469 fclose(file);
1470 return d->status;
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 //==========================================================================
1500 // create_buffer
1502 //==========================================================================
1503 static int create_buffer (buffer_t *buf, const void *data, int size) {
1504 if (size <= 16 || !data) return -1;
1505 buf->index = 0;
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);
1510 return 0;
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; \
1518 buffer_t buf; \
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) { \
1522 disk_close(d); \
1523 } else { \
1524 d->type = dtype; \
1525 d->dirty = 0; \
1526 d->wrprot = 0; \
1527 disk_update_tlens(d); \
1528 update_tracks_mode(d); \
1529 d->filename = NULL; \
1531 utils_close_file(&buf.file); \
1532 return res; \
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;
1552 d->type = type;
1554 buffer_t buf;
1555 if (create_buffer(&buf, data, size) != 0) return -1;
1556 const int res = open_cpc(&buf, d, 0);
1557 if (res != DISK_OK) {
1558 disk_close(d);
1559 } else {
1560 d->dirty = 0;
1561 d->wrprot = 0;
1562 disk_update_tlens(d);
1563 update_tracks_mode(d);
1564 d->filename = NULL;
1566 utils_close_file(&buf.file);
1567 return res;
1571 //==========================================================================
1573 // disk_is_sector_exist
1575 //==========================================================================
1576 int disk_is_sector_exist (disk_t *d, int head, int cylinder, int sector) {
1577 if (!d) return 0;
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);
1585 int res = 0;
1586 int del = 0;
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;
1594 if (del) goto quit;
1596 res = 1;
1598 quit:
1599 disk_position_restore(d, &diskctx);
1600 return res;
1604 //==========================================================================
1606 // disk_read_sector
1608 // read disk sector
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);
1624 int res = DISK_OK;
1625 int del = 0;
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;
1644 quit:
1645 disk_position_restore(d, &diskctx);
1646 return res;
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,
1658 int *sectorsize)
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);
1668 void *res = NULL;
1669 int del = 0;
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;
1677 if (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;
1686 quit:
1687 disk_position_restore(d, &diskctx);
1688 return res;
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);
1710 int res = DISK_OK;
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);
1732 quit:
1733 disk_position_restore(d, &diskctx);
1734 return res;
1738 //==========================================================================
1740 // disk_format_beta
1742 //==========================================================================
1743 int disk_format_beta (disk_t *d, int cylinders, int interleave) {
1744 if (!d) return DISK_OPEN;
1746 disk_close(d);
1748 if (cylinders < 1 || cylinders > 83) return (d->status = DISK_GEOM);
1749 if (cylinders < 80) cylinders = 80;
1751 d->type = DISK_TRD;
1752 d->sides = 2;
1753 d->cylinders = cylinders;
1754 d->density = DISK_DD;
1755 if (disk_alloc(d) != DISK_OK) return d->status;
1756 d->wrprot = 0;
1757 d->dirty = 1;
1758 d->have_weak = 0;
1760 // build sector number table
1761 int sidxtbl[16];
1762 int spos = 0;
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;
1771 // format tracks
1772 uint8_t sdata[256];
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);
1777 d->i = 0;
1778 postindex_add(d, GAP_TRDOS);
1779 const int seclen = calc_sectorlen(1, 256, GAP_TRDOS); // one sector raw length
1780 // format sectors
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
1785 d->i += seclen;
1787 gap4_add(d, GAP_TRDOS);
1791 // just in case
1792 disk_update_tlens(d);
1793 return (d->status = DISK_OK);