/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Implementation of the PCjs JSON floppy image format. * * * * Authors: Fred N. van Kempen, * * Copyright 2017-2019 Fred N. van Kempen. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * 1. Redistributions of source code must retain the entire * above notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names * of its contributors may be used to endorse or promote * products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/timer.h> #include <86box/plat.h> #include <86box/fdd.h> #include <86box/fdd_86f.h> #include <86box/fdc.h> #include <86box/fdd_common.h> #include <86box/fdd_json.h> #define NTRACKS 256 #define NSIDES 2 #define NSECTORS 256 typedef struct { uint8_t track, /* ID: track number */ side, /* side number */ sector; /* sector number 1.. */ uint16_t size; /* encoded size of sector */ uint8_t *data; /* allocated data for it */ } sector_t; typedef struct { FILE *f; /* Geometry. */ uint8_t tracks, /* number of tracks */ sides, /* number of sides */ sectors, /* number of sectors per track */ spt[NTRACKS][NSIDES]; /* number of sectors per track */ uint8_t track, /* current track */ side, /* current side */ sector[NSIDES]; /* current sector */ uint8_t dmf; /* disk is DMF format */ uint8_t interleave; #if 0 uint8_t skew; #endif uint8_t gap2_len; uint8_t gap3_len; int track_width; uint16_t disk_flags, /* flags for the entire disk */ track_flags; /* flags for the current track */ uint8_t interleave_ordered[NTRACKS][NSIDES]; sector_t sects[NTRACKS][NSIDES][NSECTORS]; } json_t; static json_t *images[FDD_NUM]; #define ENABLE_JSON_LOG 1 #ifdef ENABLE_JSON_LOG int json_do_log = ENABLE_JSON_LOG; static void json_log(const char *fmt, ...) { va_list ap; if (json_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define json_log(fmt, ...) #endif static void handle(json_t *dev, char *name, char *str) { sector_t *sec = NULL; uint32_t l, pat; uint8_t *p; char *sp; int i, s; /* Point to the currently selected sector. */ sec = &dev->sects[dev->track][dev->side][dev->dmf - 1]; /* If no name given, assume sector is done. */ if (name == NULL) { /* If no buffer, assume one with 00's. */ if (sec->data == NULL) { sec->data = (uint8_t *) malloc(sec->size); memset(sec->data, 0x00, sec->size); } /* Encode the sector size. */ sec->size = fdd_sector_size_code(sec->size); /* Set up the rest of the Sector ID. */ sec->track = dev->track; sec->side = dev->side; return; } if (!strcmp(name, "sector")) { sec->sector = atoi(str); sec->size = 512; } else if (!strcmp(name, "length")) { sec->size = atoi(str); } else if (!strcmp(name, "pattern")) { pat = atol(str); if (sec->data == NULL) sec->data = (uint8_t *) malloc(sec->size); p = sec->data; s = (sec->size / sizeof(uint32_t)); for (i = 0; i < s; i++) { l = pat; *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); } } else if (!strcmp(name, "data")) { if (sec->data == NULL) sec->data = (uint8_t *) malloc(sec->size); p = sec->data; while (str && *str) { sp = strchr(str, ','); if (sp != NULL) *sp++ = '\0'; l = atol(str); *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); l >>= 8; *p++ = (l & 0x000000ff); str = sp; } } } static int unexpect(int c, int state, int level) { json_log("JSON: Unexpected '%c' in state %d/%d.\n", c, state, level); return (-1); } static int load_image(json_t *dev) { char buff[4096], name[32]; int c, i, j, state, level; char *ptr; if (dev->f == NULL) { json_log("JSON: no file loaded!\n"); return (0); } /* Initialize. */ for (i = 0; i < NTRACKS; i++) { for (j = 0; j < NSIDES; j++) memset(dev->sects[i][j], 0x00, sizeof(sector_t)); } dev->track = dev->side = dev->dmf = 0; /* "dmf" is "sector#" */ /* Now run the state machine. */ ptr = NULL; level = state = 0; while (state >= 0) { /* Get a character from the input. */ c = fgetc(dev->f); if ((c == EOF) || ferror(dev->f)) { state = -1; break; } /* Process it. */ switch (state) { case 0: /* read level header */ dev->dmf = 1; if ((c != '[') && (c != '{') && (c != '\r') && (c != '\n')) { state = unexpect(c, state, level); } else if (c == '[') { if (++level == 3) state++; } break; case 1: /* read sector header */ if (c != '{') state = unexpect(c, state, level); else state++; break; case 2: /* begin sector data name */ if (c != '\"') { state = unexpect(c, state, level); } else { ptr = name; state++; } break; case 3: /* read sector data name */ if (c == '\"') { *ptr = '\0'; state++; } else { *ptr++ = c; } break; case 4: /* end of sector data name */ if (c != ':') { state = unexpect(c, state, level); } else { ptr = buff; state++; } break; case 5: /* read sector value data */ switch (c) { case ',': case '}': *ptr = '\0'; handle(dev, name, buff); if (c == '}') state = 7; /* done */ else state = 2; /* word */ break; case '[': state++; break; default: *ptr++ = c; } break; case 6: /* read sector data complex */ if (c != ']') *ptr++ = c; else state = 5; break; case 7: /* sector done */ handle(dev, NULL, NULL); switch (c) { case ',': /* next sector */ dev->dmf++; state = 1; break; case ']': /* all sectors done */ if (--level == 0) state = -1; else state++; break; default: state = unexpect(c, state, level); } break; case 8: /* side done */ switch (c) { case ',': /* next side */ state = 0; break; case ']': /* all sides done */ if (--level == 0) state = -1; else state++; break; default: state = unexpect(c, state, level); } dev->spt[dev->track][dev->side] = dev->dmf; dev->side++; break; case 9: /* track done */ switch (c) { case ',': /* next track */ dev->side = 0; state = 0; break; case ']': /* all tracks done */ if (--level == 0) state = -1; else state++; break; default: state = unexpect(c, state, level); } dev->track++; break; } } /* Save derived values. */ dev->tracks = dev->track; dev->sides = dev->side; return (1); } /* Seek the heads to a track, and prepare to read data from that track. */ static void json_seek(int drive, int track) { uint8_t id[4] = { 0, 0, 0, 0 }; json_t *dev = images[drive]; int side, sector; int rate, gap2, gap3, pos; int ssize, rsec, asec; if (dev->f == NULL) { json_log("JSON: seek: no file loaded!\n"); return; } /* Allow for doublestepping tracks. */ if (!dev->track_width && fdd_doublestep_40(drive)) track /= 2; /* Set the new track. */ dev->track = track; d86f_set_cur_track(drive, track); /* Reset the 86F state machine. */ d86f_reset_index_hole_pos(drive, 0); d86f_destroy_linked_lists(drive, 0); d86f_reset_index_hole_pos(drive, 1); d86f_destroy_linked_lists(drive, 1); if (track > dev->tracks) { d86f_zero_track(drive); return; } for (side = 0; side < dev->sides; side++) { /* Get transfer rate for this side. */ rate = dev->track_flags & 0x07; if (!rate && (dev->track_flags & 0x20)) rate = 4; /* Get correct GAP3 value for this side. */ gap3 = fdd_get_gap3_size(rate, dev->sects[track][side][0].size, dev->spt[track][side]); /* Get correct GAP2 value for this side. */ gap2 = ((dev->track_flags & 0x07) >= 3) ? 41 : 22; pos = d86f_prepare_pretrack(drive, side, 0); for (sector = 0; sector < dev->spt[track][side]; sector++) { rsec = dev->sects[track][side][sector].sector; asec = sector; id[0] = track; id[1] = side; id[2] = rsec; if (dev->sects[track][side][asec].size > 255) perror("fdd_json.c: json_seek: sector size too big."); id[3] = dev->sects[track][side][asec].size & 0xff; ssize = fdd_sector_code_size(dev->sects[track][side][asec].size & 0xff); pos = d86f_prepare_sector( drive, side, pos, id, dev->sects[track][side][asec].data, ssize, gap2, gap3, 0 /*flags*/ ); if (sector == 0) d86f_initialize_last_sector_id(drive, id[0], id[1], id[2], id[3]); } } } static uint16_t disk_flags(int drive) { json_t *dev = images[drive]; return (dev->disk_flags); } static uint16_t track_flags(int drive) { json_t *dev = images[drive]; return (dev->track_flags); } static void set_sector(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n) { json_t *dev = images[drive]; int i; dev->sector[side] = 0; /* Make sure we are on the desired track. */ if (c != dev->track) return; /* Set the desired side. */ dev->side = side; /* Now loop over all sector ID's on this side to find our sector. */ for (i = 0; i < dev->spt[c][side]; i++) { if ((dev->sects[dev->track][side][i].track == c) && (dev->sects[dev->track][side][i].side == h) && (dev->sects[dev->track][side][i].sector == r) && (dev->sects[dev->track][side][i].size == n)) { dev->sector[side] = i; } } } static uint8_t poll_read_data(int drive, int side, uint16_t pos) { json_t *dev = images[drive]; uint8_t sec = dev->sector[side]; return (dev->sects[dev->track][side][sec].data[pos]); } void json_init(void) { memset(images, 0x00, sizeof(images)); } void json_load(int drive, char *fn) { double bit_rate; int temp_rate; sector_t *sec; json_t *dev; int i; /* Just in case- remove ourselves from 86F. */ d86f_unregister(drive); /* Allocate a drive block. */ dev = (json_t *) malloc(sizeof(json_t)); memset(dev, 0x00, sizeof(json_t)); /* Open the image file. */ dev->f = plat_fopen(fn, "rb"); if (dev->f == NULL) { free(dev); memset(fn, 0x00, sizeof(char)); return; } /* Our images are always RO. */ writeprot[drive] = 1; /* Set up the drive unit. */ images[drive] = dev; /* Load all sectors from the image file. */ if (!load_image(dev)) { json_log("JSON: failed to initialize\n"); (void) fclose(dev->f); free(dev); images[drive] = NULL; memset(fn, 0x00, sizeof(char)); return; } json_log("JSON(%d): %s (%i tracks, %i sides, %i sectors)\n", drive, fn, dev->tracks, dev->sides, dev->spt[0][0]); /* * If the image has more than 43 tracks, then * the tracks are thin (96 tpi). */ dev->track_width = (dev->tracks > 43) ? 1 : 0; /* If the image has 2 sides, mark it as such. */ dev->disk_flags = 0x00; if (dev->sides == 2) dev->disk_flags |= 0x08; /* JSON files are always assumed to be MFM-encoded. */ dev->track_flags = 0x08; dev->interleave = 0; #if 0 dev->skew = 0; #endif temp_rate = 0xff; sec = &dev->sects[0][0][0]; for (i = 0; i < 6; i++) { if (dev->spt[0][0] > fdd_max_sectors[sec->size][i]) continue; bit_rate = fdd_bit_rates_300[i]; temp_rate = fdd_rates[i]; dev->disk_flags |= (fdd_holes[i] << 1); if ((bit_rate == 500.0) && (dev->spt[0][0] == 21) && (sec->size == 2) && (dev->tracks >= 80) && (dev->tracks <= 82) && (dev->sides == 2)) { /* * This is a DMF floppy, set the flag so * we know to interleave the sectors. */ dev->dmf = 1; } else { if ((bit_rate == 500.0) && (dev->spt[0][0] == 22) && (sec->size == 2) && (dev->tracks >= 80) && (dev->tracks <= 82) && (dev->sides == 2)) { /* * This is marked specially because of the * track flag (a RPM slow down is needed). */ dev->interleave = 2; } dev->dmf = 0; } break; } if (temp_rate == 0xff) { json_log("JSON: invalid image (temp_rate=0xff)\n"); (void) fclose(dev->f); dev->f = NULL; free(dev); images[drive] = NULL; memset(fn, 0x00, sizeof(char)); return; } if (dev->interleave == 2) { dev->interleave = 1; dev->disk_flags |= 0x60; } dev->gap2_len = (temp_rate == 3) ? 41 : 22; if (dev->dmf) dev->gap3_len = 8; else dev->gap3_len = fdd_get_gap3_size(temp_rate, sec->size, dev->spt[0][0]); if (!dev->gap3_len) { json_log("JSON: image of unknown format was inserted into drive %c:!\n", 'C' + drive); (void) fclose(dev->f); dev->f = NULL; free(dev); images[drive] = NULL; memset(fn, 0x00, sizeof(char)); return; } dev->track_flags |= (temp_rate & 0x03); /* data rate */ if (temp_rate & 0x04) dev->track_flags |= 0x20; /* RPM */ json_log(" disk_flags: 0x%02x, track_flags: 0x%02x, GAP3 length: %i\n", dev->disk_flags, dev->track_flags, dev->gap3_len); json_log(" bit rate 300: %.2f, temporary rate: %i, hole: %i, DMF: %i\n", bit_rate, temp_rate, (dev->disk_flags >> 1), dev->dmf); /* Set up handlers for 86F layer. */ d86f_handler[drive].disk_flags = disk_flags; d86f_handler[drive].side_flags = track_flags; d86f_handler[drive].writeback = null_writeback; d86f_handler[drive].set_sector = set_sector; d86f_handler[drive].read_data = poll_read_data; d86f_handler[drive].write_data = null_write_data; d86f_handler[drive].format_conditions = null_format_conditions; d86f_handler[drive].extra_bit_cells = null_extra_bit_cells; d86f_handler[drive].encoded_data = common_encoded_data; d86f_handler[drive].read_revolution = common_read_revolution; d86f_handler[drive].index_hole_pos = null_index_hole_pos; d86f_handler[drive].get_raw_size = common_get_raw_size; d86f_handler[drive].check_crc = 1; d86f_set_version(drive, 0x0063); d86f_common_handlers(drive); drives[drive].seek = json_seek; } /* Close the image. */ void json_close(int drive) { json_t *dev = images[drive]; int t, h, s; if (dev == NULL) return; /* Unlink image from the system. */ d86f_unregister(drive); /* Release all the sector buffers. */ for (t = 0; t < 256; t++) { for (h = 0; h < 2; h++) { memset(dev->sects[t][h], 0x00, sizeof(sector_t)); for (s = 0; s < 256; s++) { if (dev->sects[t][h][s].data != NULL) free(dev->sects[t][h][s].data); dev->sects[t][h][s].data = NULL; } } } if (dev->f != NULL) (void) fclose(dev->f); /* Release the memory. */ free(dev); images[drive] = NULL; }