/* gcr.c - Group Code Recording helper functions (C) 2001-2005 Markus Brenner and Pete Rittwage based on code by Andreas Boose V 0.30 created file based on n2d V 0.31 improved error handling of convert_GCR_sector() V 0.32 removed some functions, added sector-2-GCR conversion V 0.33 improved sector extraction, added find_track_cycle() function V 0.34 added MAX_SYNC_OFFSET constant, for better error conversion V 0.35 improved find_track_cycle() function V 0.36 added bad GCR code detection V 0.36b improved find_sync(), added find_sector_gap(), find_sector0() V 0.36c convert_GCR_sector: search good header before using a damaged one improved find_track_cycle(), return max len if no cycle found V 0.36d most additions/fixes referenced in mnib.c (pjr) */ #include #include #include #include #include "gcr.h" #include "prot.h" #include "debug.h" char sector_map_1541[MAX_TRACKS_1541 + 1] = { 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, /* 1 - 10 */ 21, 21, 21, 21, 21, 21, 21, 19, 19, 19, /* 11 - 20 */ 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, /* 21 - 30 */ 17, 17, 17, 17, 17, /* 31 - 35 */ 17, 17, 17, 17, 17, 17, 17 /* 36 - 42 (non-standard) */ }; BYTE speed_map_1541[MAX_TRACKS_1541] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 1 - 10 */ 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, /* 11 - 20 */ 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, /* 21 - 30 */ 0, 0, 0, 0, 0, /* 31 - 35 */ 0, 0, 0, 0, 0, 0, 0 /* 36 - 42 (non-standard) */ }; /* Burst Nibbler defaults int capacity_min[] = { 6183, 6598, 7073, 7616 }; int capacity[] = { 6231, 6646, 7121, 7664 }; int capacity_max[] = { 6311, 6726, 7201, 7824 }; */ /* New calculated defaults: 296rpm, 300rpm, 304rpm */ int capacity_min[] = { (int) (DENSITY0 / 303), (int) (DENSITY1 / 303), (int) (DENSITY2 / 303), (int) (DENSITY3 / 303) }; int capacity[] = { (int) (DENSITY0 / 300), (int) (DENSITY1 / 300), (int) (DENSITY2 / 300), (int) (DENSITY3 / 300) }; int capacity_max[] = { (int) (DENSITY0 / 296), (int) (DENSITY1 / 296), (int) (DENSITY2 / 296), (int) (DENSITY3 / 296) }; /* Nibble-to-GCR conversion table */ static BYTE GCR_conv_data[16] = { 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 }; /* GCR-to-Nibble conversion tables */ static BYTE GCR_decode_high[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x10, 0xff, 0xc0, 0x40, 0x50, 0xff, 0xff, 0x20, 0x30, 0xff, 0xf0, 0x60, 0x70, 0xff, 0x90, 0xa0, 0xb0, 0xff, 0xd0, 0xe0, 0xff }; static BYTE GCR_decode_low[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x01, 0xff, 0x0c, 0x04, 0x05, 0xff, 0xff, 0x02, 0x03, 0xff, 0x0f, 0x06, 0x07, 0xff, 0x09, 0x0a, 0x0b, 0xff, 0x0d, 0x0e, 0xff }; int find_sync(BYTE ** gcr_pptr, BYTE * gcr_end) { while (1) { if ((*gcr_pptr) + 1 >= gcr_end) { *gcr_pptr = gcr_end; return 0; /* not found */ } // sync flag goes up after the 10th bit if ( ((*gcr_pptr)[0] & 0x03) == 0x03 && (*gcr_pptr)[1] == 0xff) break; (*gcr_pptr)++; } (*gcr_pptr)++; while (*gcr_pptr < gcr_end && **gcr_pptr == 0xff) (*gcr_pptr)++; return (*gcr_pptr < gcr_end); } void convert_4bytes_to_GCR(BYTE * buffer, BYTE * ptr) { *ptr = GCR_conv_data[(*buffer) >> 4] << 3; *ptr |= GCR_conv_data[(*buffer) & 0x0f] >> 2; ptr++; *ptr = GCR_conv_data[(*buffer) & 0x0f] << 6; buffer++; *ptr |= GCR_conv_data[(*buffer) >> 4] << 1; *ptr |= GCR_conv_data[(*buffer) & 0x0f] >> 4; ptr++; *ptr = GCR_conv_data[(*buffer) & 0x0f] << 4; buffer++; *ptr |= GCR_conv_data[(*buffer) >> 4] >> 1; ptr++; *ptr = GCR_conv_data[(*buffer) >> 4] << 7; *ptr |= GCR_conv_data[(*buffer) & 0x0f] << 2; buffer++; *ptr |= GCR_conv_data[(*buffer) >> 4] >> 3; ptr++; *ptr = GCR_conv_data[(*buffer) >> 4] << 5; *ptr |= GCR_conv_data[(*buffer) & 0x0f]; } int convert_4bytes_from_GCR(BYTE * gcr, BYTE * plain) { BYTE hnibble, lnibble; int badGCR, nConverted; badGCR = 0; hnibble = GCR_decode_high[gcr[0] >> 3]; lnibble = GCR_decode_low[((gcr[0] << 2) | (gcr[1] >> 6)) & 0x1f]; if ((hnibble == 0xff || lnibble == 0xff) && !badGCR) badGCR = 1; *plain++ = hnibble | lnibble; hnibble = GCR_decode_high[(gcr[1] >> 1) & 0x1f]; lnibble = GCR_decode_low[((gcr[1] << 4) | (gcr[2] >> 4)) & 0x1f]; if ((hnibble == 0xff || lnibble == 0xff) && !badGCR) badGCR = 2; *plain++ = hnibble | lnibble; hnibble = GCR_decode_high[((gcr[2] << 1) | (gcr[3] >> 7)) & 0x1f]; lnibble = GCR_decode_low[(gcr[3] >> 2) & 0x1f]; if ((hnibble == 0xff || lnibble == 0xff) && !badGCR) badGCR = 3; *plain++ = hnibble | lnibble; hnibble = GCR_decode_high[((gcr[3] << 3) | (gcr[4] >> 5)) & 0x1f]; lnibble = GCR_decode_low[gcr[4] & 0x1f]; if ((hnibble == 0xff || lnibble == 0xff) && !badGCR) badGCR = 4; *plain++ = hnibble | lnibble; nConverted = (badGCR == 0) ? 4 : (badGCR - 1); return (nConverted); } int extract_id(BYTE * gcr_track, BYTE * id) { BYTE header[10]; BYTE *gcr_ptr, *gcr_end; int track, sector; track = 18; sector = 0; gcr_ptr = gcr_track; gcr_end = gcr_track + NIB_TRACK_LENGTH; do { if (!find_sync(&gcr_ptr, gcr_end)) return (0); convert_4bytes_from_GCR(gcr_ptr, header); convert_4bytes_from_GCR(gcr_ptr + 5, header + 4); } while (header[0] != 0x08 || header[2] != sector || header[3] != track); id[0] = header[5]; id[1] = header[4]; return (1); } int extract_cosmetic_id(BYTE * gcr_track, BYTE * id) { BYTE secbuf[260]; int error; /* get sector into buffer- we don't care about id mismatch here */ error = convert_GCR_sector(gcr_track, gcr_track + NIB_TRACK_LENGTH, secbuf, 18, 0, id); /* no valid 18,0 sector */ if(error != SECTOR_OK && error != ID_MISMATCH) return (0); id[0] = secbuf[0xa3]; id[1] = secbuf[0xa4]; return (1); } BYTE convert_GCR_sector(BYTE * gcr_start, BYTE * gcr_cycle, BYTE * d64_sector, int track, int sector, BYTE * id) { BYTE header[10]; /* block header */ BYTE hdr_chksum; /* header checksum */ BYTE blk_chksum; /* block checksum */ BYTE gcr_buffer[2 * NIB_TRACK_LENGTH]; BYTE *gcr_ptr, *gcr_end, *gcr_last; BYTE *sectordata; BYTE error_code; int sync_found, i, j, nConverted; size_t track_len; error_code = SECTOR_OK; if (track > MAX_TRACK_D64) return (0); if (gcr_cycle == NULL || gcr_cycle <= gcr_start) return (0); /* initialize sector data with Original Format Pattern */ memset(d64_sector, 0x01, 260); d64_sector[0] = 0x07; /* Block header mark */ d64_sector[1] = 0x4b; /* Use Original Format Pattern */ for (blk_chksum = 0, i = 1; i < 257; i++) blk_chksum ^= d64_sector[i + 1]; d64_sector[257] = blk_chksum; /* copy to temp. buffer with twice the track data */ track_len = gcr_cycle - gcr_start; memcpy(gcr_buffer, gcr_start, track_len); memcpy(gcr_buffer + track_len, gcr_start, track_len); track_len *= 2; /* Check for at least one Sync */ gcr_end = gcr_buffer + track_len; sync_found = 0; for (gcr_ptr = gcr_buffer; gcr_ptr < gcr_end; gcr_ptr++) { if (*gcr_ptr == 0xff) { if (sync_found < 2) sync_found++; } else /* (*gcr_ptr != 0xff) */ { if (sync_found < 2) sync_found = 0; else sync_found = 3; } } if (sync_found != 3) return (SYNC_NOT_FOUND); /* Check for missing SYNCs */ gcr_last = gcr_ptr = gcr_buffer; while (gcr_ptr < gcr_end) { find_sync(&gcr_ptr, gcr_end); if ((gcr_ptr - gcr_last) > MAX_SYNC_OFFSET) { //printf("no sync for %d\n", gcr_ptr-gcr_last); return (SYNC_NOT_FOUND); } else gcr_last = gcr_ptr; } /* Try to find a good block header for Track/Sector */ gcr_ptr = gcr_buffer; gcr_end = gcr_buffer + track_len; do { if (!find_sync(&gcr_ptr, gcr_end) || gcr_ptr >= gcr_end - 10) { error_code = HEADER_NOT_FOUND; break; } convert_4bytes_from_GCR(gcr_ptr, header); convert_4bytes_from_GCR(gcr_ptr + 5, header + 4); gcr_ptr++; //printf("%0.2d: t%0.2d s%0.2d id1:%0.1x id2:%0.1x\n", //header[0],header[3],header[2],header[5],header[4]); } while (header[0] != 0x08 || header[2] != sector || header[3] != track || header[5] != id[0] || header[4] != id[1]); if (error_code == HEADER_NOT_FOUND) { error_code = SECTOR_OK; /* Try to find next best match for header */ gcr_ptr = gcr_buffer; gcr_end = gcr_buffer + track_len; do { if (!find_sync(&gcr_ptr, gcr_end)) return (HEADER_NOT_FOUND); if (gcr_ptr >= gcr_end - 10) return (HEADER_NOT_FOUND); convert_4bytes_from_GCR(gcr_ptr, header); convert_4bytes_from_GCR(gcr_ptr + 5, header + 4); gcr_ptr++; } while (header[0] == 0x07 || header[2] != sector || header[3] != track); } if (header[0] != 0x08) error_code = (error_code == SECTOR_OK) ? HEADER_NOT_FOUND : error_code; /* Header checksum */ hdr_chksum = 0; for (i = 1; i <= 4; i++) hdr_chksum = hdr_chksum ^ header[i]; if (hdr_chksum != header[5]) { //printf("(S%d HEAD_CHKSUM $%0.2x != $%0,2x) ", // sector, hdr_chksum, header[5]); error_code = (error_code == SECTOR_OK) ? BAD_HEADER_CHECKSUM : error_code; } // verify that our header contains no bad GCR, since it can be false positive checksum match for(j = 0; j < 10; j++) { if (is_bad_gcr(gcr_ptr - 1, 10, j)) error_code = (error_code == SECTOR_OK) ? BAD_GCR_CODE : error_code; } // next look for data portion if (!find_sync(&gcr_ptr, gcr_end)) return (DATA_NOT_FOUND); for (i = 0, sectordata = d64_sector; i < 65; i++) { if (gcr_ptr >= gcr_end - 5) return (DATA_NOT_FOUND); if (4 != (nConverted = convert_4bytes_from_GCR(gcr_ptr, sectordata))) { #if 0 // XXX Disabled for unknown reason. if ((i < 64) || (nConverted == 0)) error_code = BAD_GCR_CODE; #endif } gcr_ptr += 5; sectordata += 4; } /* check for correct disk ID */ if (header[5] != id[0] || header[4] != id[1]) error_code = (error_code == SECTOR_OK) ? ID_MISMATCH : error_code; /* check for Block header mark */ if (d64_sector[0] != 0x07) error_code = (error_code == SECTOR_OK) ? DATA_NOT_FOUND : error_code; /* Block checksum */ for (i = 1, blk_chksum = 0; i <= 256; i++) blk_chksum ^= d64_sector[i]; if (blk_chksum != d64_sector[257]) { //printf("(S%d DATA_CHKSUM $%0.2x != $%0.2x) ", // sector, blk_chksum, d64_sector[257]); error_code = (error_code == SECTOR_OK) ? BAD_DATA_CHECKSUM : error_code; } // verify that our data contains no bad GCR, since it can be false positive checksum match for(j = 0; j < 320; j++) if (is_bad_gcr(gcr_ptr - 325, 320, j)) error_code = (error_code == SECTOR_OK) ? BAD_GCR_CODE : error_code; return (error_code); } void convert_sector_to_GCR(BYTE * buffer, BYTE * ptr, int track, int sector, BYTE * diskID, int error) { int i; BYTE buf[4], databuf[0x104], chksum; BYTE tempID[3]; memcpy(tempID, diskID, 3); memset(ptr, 0x55, 361); /* 'unformat' GCR sector */ if (error == SYNC_NOT_FOUND) return; if (error == HEADER_NOT_FOUND) { ptr += 24; } else { memset(ptr, 0xff, 5); /* Sync */ ptr += 5; if (error == ID_MISMATCH) { tempID[0] ^= 0xff; tempID[1] ^= 0xff; } buf[0] = 0x08; /* Header identifier */ buf[1] = sector ^ track ^ tempID[1] ^ tempID[0]; buf[2] = (BYTE) sector; buf[3] = (BYTE) track; if (error == BAD_HEADER_CHECKSUM) buf[1] ^= 0xff; convert_4bytes_to_GCR(buf, ptr); ptr += 5; buf[0] = tempID[1]; buf[1] = tempID[0]; buf[2] = buf[3] = 0x0f; convert_4bytes_to_GCR(buf, ptr); ptr += 5; memset(ptr, 0x55, 9); /* Header Gap */ ptr += 9; } if (error == DATA_NOT_FOUND) return; memset(ptr, 0xff, 5); /* Sync */ ptr += 5; chksum = 0; databuf[0] = 0x07; for (i = 0; i < 0x100; i++) { databuf[i + 1] = buffer[i]; chksum ^= buffer[i]; } if (error == BAD_DATA_CHECKSUM) chksum ^= 0xff; databuf[0x101] = chksum; databuf[0x102] = 0; /* 2 bytes filler */ databuf[0x103] = 0; for (i = 0; i < 65; i++) { convert_4bytes_to_GCR(databuf + (4 * i), ptr); ptr += 5; } /* 7 0x55 gap bytes in my reference disk */ memset(ptr, 0x55, 7); /* Gap before next sector */ ptr += 7; } size_t find_track_cycle(BYTE ** cycle_start, BYTE ** cycle_stop, int cap_min, int cap_max) { BYTE *nib_track; /* start of nibbled track data */ BYTE *start_pos; /* start of periodic area */ BYTE *cycle_pos; /* start of cycle repetition */ BYTE *stop_pos; /* maximum position allowed for cycle */ BYTE *data_pos; /* cycle search variable */ BYTE *p1, *p2; /* local pointers for comparisons */ nib_track = *cycle_start; stop_pos = nib_track + NIB_TRACK_LENGTH - gap_match_length; cycle_pos = NULL; /* try to find a normal track cycle */ for (start_pos = nib_track;; find_sync(&start_pos, stop_pos)) { if ((data_pos = start_pos + cap_min) >= stop_pos) break; /* no cycle found */ while (find_sync(&data_pos, stop_pos)) { p1 = start_pos; cycle_pos = data_pos; for (p2 = cycle_pos; p2 < stop_pos;) { /* try to match all remaining syncs, too */ if (memcmp(p1, p2, gap_match_length) != 0) { cycle_pos = NULL; break; } if (!find_sync(&p1, stop_pos)) break; if (!find_sync(&p2, stop_pos)) break; } if (cycle_pos != NULL && check_valid_data(data_pos, gap_match_length)) { *cycle_start = start_pos; *cycle_stop = cycle_pos; return (cycle_pos - start_pos); } } } /* we got nothing useful, return it all */ *cycle_start = nib_track; *cycle_stop = nib_track + NIB_TRACK_LENGTH; return NIB_TRACK_LENGTH; } size_t find_nondos_track_cycle(BYTE ** cycle_start, BYTE ** cycle_stop, int cap_min, int cap_max) { BYTE *nib_track; /* start of nibbled track data */ BYTE *start_pos; /* start of periodic area */ BYTE *cycle_pos; /* start of cycle repetition */ BYTE *stop_pos; /* maximum position allowed for cycle */ BYTE *p1, *p2; /* local pointers for comparisons */ nib_track = *cycle_start; start_pos = nib_track; stop_pos = nib_track + NIB_TRACK_LENGTH - gap_match_length; cycle_pos = NULL; /* try to find a track cycle ignoring sync */ for (p1 = start_pos; p1 < stop_pos; p1++) { /* now try to match it */ for (p2 = p1 + cap_min; p2 < stop_pos; p2++) { /* try to match data */ if (memcmp(p1, p2, gap_match_length) != 0) cycle_pos = NULL; else cycle_pos = p2; /* we found one! */ if (cycle_pos != NULL && check_valid_data(cycle_pos, gap_match_length)) { *cycle_start = p1; *cycle_stop = cycle_pos; return (cycle_pos - p1); } } } /* we got nothing useful */ *cycle_start = nib_track; *cycle_stop = nib_track + NIB_TRACK_LENGTH; return NIB_TRACK_LENGTH; } int check_valid_data(BYTE * data, int matchlen) { //makes assumptions on whether this is good data to match cycles int i, redund = 0; for (i = 0; i < matchlen; i++) { if (data[i] == data[i + 1] || data[i] == data[i + 2] || data[i] == data[i + 3] || data[i] == data[i + 4]) redund++; } if (redund > 1) return 0; else return 1; } BYTE * find_sector0(BYTE * work_buffer, int tracklen, size_t * p_sectorlen) { BYTE *pos, *buffer_end, *sync_last; pos = work_buffer; buffer_end = work_buffer + 2 * tracklen - 10; *p_sectorlen = 0; if (!find_sync(&pos, buffer_end)) return NULL; sync_last = pos; /* try to find sector 0 */ while (pos < buffer_end) { if (!find_sync(&pos, buffer_end)) return NULL; if (pos[0] == 0x52 && (pos[1] & 0xc0) == 0x40 && (pos[2] & 0x0f) == 0x05 && (pos[3] & 0xfc) == 0x28) { *p_sectorlen = pos - sync_last; break; } sync_last = pos; } /* find last GCR byte before sync */ do { pos -= 1; if (pos == work_buffer) pos += tracklen; } while (*pos == 0xff); /* move to first sync byte */ pos += 1; while (pos >= work_buffer + tracklen) pos -= tracklen; /* make sure sync is long enough or else shift back */ //if(*(pos+1) != 0xff) pos -= 1; return pos; } BYTE * find_sector_gap(BYTE * work_buffer, int tracklen, size_t * p_sectorlen) { size_t gap, maxgap; BYTE *pos; BYTE *buffer_end; BYTE *sync_last; BYTE *sync_max; pos = work_buffer; buffer_end = work_buffer + 2 * tracklen - 10; *p_sectorlen = 0; if (!find_sync(&pos, buffer_end)) return NULL; sync_last = pos; maxgap = 0; /* try to find biggest (sector) gap */ while (pos < buffer_end) { if (!find_sync(&pos, buffer_end)) break; gap = pos - sync_last; if (gap > maxgap) { maxgap = gap; sync_max = pos; } sync_last = pos; } *p_sectorlen = maxgap; if (maxgap == 0) return NULL; /* no gap found */ /* find last GCR byte before sync */ pos = sync_max; do { pos -= 1; if (pos == work_buffer) pos += tracklen; } while (*pos == 0xff); /* move to first sync GCR byte */ pos += 1; while (pos >= work_buffer + tracklen) pos -= tracklen; /* make sure sync is long enough or else shift back */ //if(*(pos+1) != 0xff) pos -= 1; return pos; } // checks if there is any reasonable section of formatted (GCR) data int check_formatted(BYTE * gcrdata) { int i, run = 0; /* try to find longest good gcr run */ for (i = 0; i < NIB_TRACK_LENGTH; i++) { if (is_bad_gcr(gcrdata, NIB_TRACK_LENGTH, i)) run = 0; else run++; if (run >= GCR_MIN_FORMATTED) return 1; } return 0; } /* Try to extract one complete cycle of GCR data from an 8kB buffer. Align track to sector gap if possible, else align to sector 0, else copy cyclic loop from begin of source. If buffer has no good GCR, return tracklen = 0; [Input] destination buffer, source buffer [Return] length of copied track fragment */ int extract_GCR_track(BYTE * destination, BYTE * source, int * align, int force_align, size_t cap_min, size_t cap_max) { BYTE work_buffer[NIB_TRACK_LENGTH*2]; /* working buffer */ BYTE *cycle_start; /* start position of cycle */ BYTE *cycle_stop; /* stop position of cycle */ size_t track_len; BYTE *sector0_pos; /* position of sector 0 */ BYTE *sectorgap_pos;/* position of sector gap */ BYTE *longsync_pos; /* position of longest sync run */ BYTE *weakgap_pos; /* position of weak bit run */ BYTE *marker_pos; /* generic marker used by protection handlers */ size_t sector0_len; /* length of gap before sector 0 */ size_t sectorgap_len; /* length of longest gap */ sector0_pos = NULL; sectorgap_pos = NULL; longsync_pos = NULL; weakgap_pos = NULL; marker_pos = NULL; cap_min -= CAP_MIN_ALLOWANCE; /* if this track doesn't have enough formatted data, return blank */ if (!check_formatted(source)) return 0; cycle_start = source; memset(work_buffer, 0, sizeof(work_buffer)); memcpy(work_buffer, cycle_start, NIB_TRACK_LENGTH); /* find cycle */ find_track_cycle(&cycle_start, &cycle_stop, cap_min, cap_max); track_len = cycle_stop - cycle_start; /* second pass to find a cycle in track w/o syncs */ if (track_len > cap_max || track_len < cap_min) { //printf("(N)"); find_nondos_track_cycle(&cycle_start, &cycle_stop, cap_min, cap_max); track_len = cycle_stop - cycle_start; } /* copy twice the data to work buffer */ memcpy(work_buffer, cycle_start, track_len); memcpy(work_buffer + track_len, cycle_start, track_len); // forced track alignments if (force_align != ALIGN_NONE) { if (force_align == ALIGN_VMAX) { *align = ALIGN_VMAX; marker_pos = align_vmax(work_buffer, track_len); } if (force_align == ALIGN_AUTOGAP) { *align = ALIGN_AUTOGAP; marker_pos = auto_gap(work_buffer, track_len); } if (force_align == ALIGN_LONGSYNC) { *align = ALIGN_LONGSYNC; marker_pos = find_long_sync(work_buffer, track_len); } if (force_align == ALIGN_WEAK) { *align = ALIGN_WEAK; marker_pos = find_weak_gap(work_buffer, track_len); } if (force_align == ALIGN_GAP) { *align = ALIGN_GAP; marker_pos = find_sector_gap(work_buffer, track_len, §orgap_len); } if (force_align == ALIGN_SEC0) { *align = ALIGN_SEC0;; marker_pos = find_sector0(work_buffer, track_len, §or0_len); } // we found a protection track if (marker_pos) { memcpy(destination, marker_pos, track_len); return (track_len); } } /* autogap tracks with no detected cycle */ if (track_len == NIB_TRACK_LENGTH) { marker_pos = auto_gap(work_buffer, track_len); if (marker_pos) { memcpy(destination, marker_pos, track_len); *align = ALIGN_AUTOGAP; return (track_len); } } /* try to guess original alignment on "normal" sized tracks */ sector0_pos = find_sector0(work_buffer, track_len, §or0_len); sectorgap_pos = find_sector_gap(work_buffer, track_len, §orgap_len); //if (sectorgap_len >= sector0_len + 0x40) /* Burstnibbler's calc */ if (sectorgap_len > GCR_BLOCK_DATA_LEN + SIGNIFICANT_GAPLEN_DIFF) { *align = ALIGN_GAP; memcpy(destination, sectorgap_pos, track_len); return (track_len); } // no good gap found, try sector 0 if (sector0_len != 0) { *align = ALIGN_SEC0; memcpy(destination, sector0_pos, track_len); return (track_len); } // no sector 0 found, use gap anyway if (sectorgap_len) { memcpy(destination, sectorgap_pos, track_len); *align = ALIGN_GAP; return (track_len); } // we aren't dealing with a normal track here, so autogap it marker_pos = auto_gap(work_buffer, track_len); if (marker_pos) { memcpy(destination, marker_pos, track_len); *align = ALIGN_AUTOGAP; return (track_len); } // we give up, just return everything memcpy(destination, work_buffer, track_len); *align = ALIGN_NONE; return (track_len); } /* * This strips exactly one byte at minrun from each * eligible run when called. It can be called repeatedly * for a proportional reduction. */ int strip_runs(BYTE * buffer, int length, int minrun, BYTE target) { int run, skipped; BYTE *source, *end; run = 0; skipped = 0; end = buffer + length; for (source = buffer; source < end - 2; source++) { if (*source == target) { // fixed to only remove bytes before minimum amount of sync if ( run == minrun && target == 0xff ) skipped++; else if ( run == minrun && *(source+2) == 0xff ) skipped++; else *buffer++ = target; run++; } else { run = 0; *buffer++ = *source; } } return skipped; } /* try to shorten inert data until length <= length_max */ int reduce_runs(BYTE * buffer, int length, int length_max, int minrun, BYTE target) { int skipped; do { if (length <= length_max) return (length); skipped = strip_runs(buffer, length, minrun, target); length -= skipped; } while (skipped > 0 && length > length_max); return (length); } // this routine checks the track data and makes simple decisions // about the special cases of being all sync or having no sync int check_sync_flags(BYTE * gcrdata, int density, int length) { int i, syncs = 0; // check manually for SYNCKILL for (i = 0; i < length; i++) { if (gcrdata[i] == 0xff) syncs++; } //printf("syncs: %d\n",syncs); if(!syncs) return (density |= BM_NO_SYNC); else if (syncs == length) return (density |= BM_FF_TRACK); else return(density); } int compare_tracks(BYTE * track1, BYTE * track2, int length1, int length2, int same_disk, char *outputstring) { int match, j, k, sync_diff, presync_diff; int gap_diff, weak_diff, size_diff; char tmpstr[256]; match = 0; j = 0; k = 0; sync_diff = 0; presync_diff = 0; gap_diff = 0; weak_diff = 0; size_diff= 0; outputstring[0] = '\0'; if (length1 == length2 && 0 == memcmp(track1, track2, length1)) match = 1; else if (length1 > 0 && length2 > 0) { for (j = k = 0; j < length2 && k < length1; j++, k++) { if (track1[j] == track2[k]) continue; // we ignore sync length differences if (track1[j] == 0xff) { sync_diff++; k--; continue; } if (track2[k] == 0xff) { sync_diff++; j--; continue; } // we ignore start of sync differences if (j < length1 - 1 && k < length2 - 1) { if ((track1[j] & 0x01) == 0x01 && track1[j + 1] == 0xff) { presync_diff++; k--; continue; } if ((track2[k] & 0x01) == 0x01 && track2[k + 1] == 0xff) { presync_diff++; j--; continue; } } // we ignore bad gcr bytes if (is_bad_gcr(track1, length1, j) || is_bad_gcr(track2, length2, k)) { weak_diff++; j++; k++; continue; } // it just didn't work out. :) break; } if (j < length1 - 1 || k < length2 - 1) size_diff++; // we got to the end of one of them OK and not all sync/weak if ((j >= length1 - 1 || k >= length2 - 1) && (sync_diff < 0x100 && weak_diff < 0x100)) match = 1; } if (sync_diff) { sprintf(tmpstr, "(sync:%d)", sync_diff); strcat(outputstring, tmpstr); } if (presync_diff) { sprintf(tmpstr, "(presync:%d)", presync_diff); strcat(outputstring, tmpstr); } if (gap_diff) { sprintf(tmpstr, "(gap:%d)", gap_diff); strcat(outputstring, tmpstr); } if (weak_diff) { sprintf(tmpstr, "(weak:%d)", weak_diff); strcat(outputstring, tmpstr); } if (size_diff) { sprintf(tmpstr, "(size:%d)", size_diff); strcat(outputstring, tmpstr); } return match; } int compare_sectors(BYTE * track1, BYTE * track2, int length1, int length2, BYTE * id1, BYTE * id2, int track, char * outputstring) { int sec_match, numsecs; int sector, error1, error2, i, empty; BYTE checksum1, checksum2; BYTE secbuf1[260], secbuf2[260]; char tmpstr[256]; sec_match = 0; numsecs = 0; checksum1 = 0; checksum2 = 0; outputstring[0] = '\0'; // ignore dead tracks if (!length1 || !length2 || length1 + length2 == 0x4000) return 0; // check for sector matches for (sector = 0; sector < sector_map_1541[track / 2]; sector++) { numsecs++; memset(secbuf1, 0, sizeof(secbuf1)); memset(secbuf2, 0, sizeof(secbuf2)); tmpstr[0] = '\0'; error1 = convert_GCR_sector(track1, track1 + length1, secbuf1, track / 2, sector, id1); error2 = convert_GCR_sector(track2, track2 + length2, secbuf2, track / 2, sector, id2); // compare data returned checksum1 = checksum2 = empty = 0; for (i = 2; i <= 256; i++) { checksum1 ^= secbuf1[i]; checksum2 ^= secbuf2[i]; if (secbuf1[i] == 0x01) empty++; } // continue checking if (checksum1 == checksum2 && error1 == error2 && empty < 254) { if(error1 == SECTOR_OK) { //sprintf(tmpstr,"S%d: std sector data match\n",sector); } else { sprintf(tmpstr,"S%d: non-std sector data match (%.2x)\n",sector, checksum1); } sec_match++; } else if (checksum1 == checksum2 && error1 == error2) { if(error1 == SECTOR_OK) { //sprintf(tmpstr,"S%d: empty sector match\n",sector); sec_match++; } else { sprintf(tmpstr,"S%d: unrecognized sector (%.2x/%.2x)(%.2x/%.2x)\n",sector,checksum1,error1,checksum2,error2); } } else { if (checksum1 != checksum2) sprintf(tmpstr, "S%d: data/error mismatch (%.2x/E%d)(%.2x/E%d)\n", sector, checksum1, error1, checksum2, error2); else sprintf(tmpstr, "S%d: error mismatch (E%d/E%d)\n", sector, error1, error2); } strcat(outputstring, tmpstr); } if (sec_match == sector_map_1541[track / 2]) return sec_match; else return 0; } // check for CBM DOS errors int check_errors(BYTE * gcrdata, int length, int track, BYTE * id, char * errorstring) { int errors, sector, errorcode; char tmpstr[16]; BYTE secbuf[260]; errors = 0; errorstring[0] = '\0'; for (sector = 0; sector < sector_map_1541[track/2]; sector++) { errorcode = convert_GCR_sector(gcrdata, gcrdata + length, secbuf, (track/2), sector, id); if (errorcode != SECTOR_OK) { errors++; sprintf(tmpstr, "[E%dS%d]", errorcode, sector); strcat(errorstring, tmpstr); } } return errors; } // check for CBM DOS empty sectors int check_empty(BYTE * gcrdata, int length, int track, BYTE * id, char * errorstring) { int i, empty, sector, errorcode; char tmpstr[16], temp_errorstring[256]; BYTE secbuf[260]; empty = 0; errorstring[0] = '\0'; temp_errorstring[0] = '\0'; for (sector = 0; sector < sector_map_1541[track / 2]; sector++) { errorcode = convert_GCR_sector(gcrdata, gcrdata + length, secbuf, (track / 2), sector, id); if (errorcode == SECTOR_OK) { /* checks for empty (unused) sector */ for (i = 2; i <= 256; i++) { if (secbuf[i] != 0x01) { //printf("%d:%0.2x ",i,secbuf[i]); break; } } if (i == 257) { sprintf(tmpstr, "%d-", sector); strcat(temp_errorstring, tmpstr); empty++; } } } if (empty) sprintf(errorstring, "EMPTY:%d (%s)", empty, temp_errorstring); return (empty); } /* * Replace 'srcbyte' by 'dstbyte' * Returns total number of bytes replaced */ int replace_bytes(BYTE * buffer, int length, BYTE srcbyte, BYTE dstbyte) { int i, replaced; replaced = 0; for (i = 0; i < length; i++) { if (buffer[i] == srcbyte) { buffer[i] = dstbyte; replaced++; } } return replaced; } // Check if byte at pos contains a 000 bit combination int is_bad_gcr(BYTE * gcrdata, size_t length, size_t pos) { unsigned int lastbyte, mask, data; lastbyte = (pos == 0) ? gcrdata[length - 1] : gcrdata[pos - 1]; data = ((lastbyte & 0x03) << 8) | gcrdata[pos]; for (mask = (7 << 7); mask >= 7; mask >>= 1) { if ((data & mask) == 0) break; } return (mask >= 7); } /* * Check and "correct" bad GCR bits: * substitute bad GCR bytes by 0x00 until next good GCR byte * when two in a row or row+1 occur (bad bad -or- bad good bad) * * all known disks that use this for protection break out * of it with $55, $AA, or $FF byte. * * fix_first, fix_last not used because while "correct", the real hardware * is not this precise and it fails the protection checks sometimes. */ int check_bad_gcr(BYTE * gcrdata, int length, int fix) { /* state machine definitions */ enum ebadgcr { S_BADGCR_OK, S_BADGCR_ONCE_BAD, S_BADGCR_LOST }; int i, lastpos; enum ebadgcr sbadgcr; int total, b_badgcr, n_badgcr; i = 0; total = 0; lastpos = length - 1; if (is_bad_gcr(gcrdata, length, length - 1)) sbadgcr = S_BADGCR_ONCE_BAD; else sbadgcr = S_BADGCR_OK; for (i = 0; i < length - 1; i++) { b_badgcr = is_bad_gcr(gcrdata, length, i); n_badgcr = is_bad_gcr(gcrdata, length, i + 1); switch (sbadgcr) { case S_BADGCR_OK: if (b_badgcr) { total++; //sbadgcr = S_BADGCR_ONCE_BAD; sbadgcr = S_BADGCR_LOST; } break; case S_BADGCR_ONCE_BAD: if (b_badgcr || n_badgcr) { sbadgcr = S_BADGCR_LOST; //if(fix) fix_first_gcr(gcrdata, length, lastpos); if (fix) gcrdata[lastpos] = 0x00; total++; } else sbadgcr = S_BADGCR_OK; break; case S_BADGCR_LOST: if (b_badgcr || n_badgcr) { if (fix) gcrdata[lastpos] = 0x00; } else { sbadgcr = S_BADGCR_OK; //if(fix) fix_last_gcr(gcrdata, length, lastpos); if (fix) gcrdata[lastpos] = 0x00; } total++; break; } lastpos = i; } // clean up after last byte; lastpos = length - 1 b_badgcr = is_bad_gcr(gcrdata, length, 0); n_badgcr = is_bad_gcr(gcrdata, length, 1); switch (sbadgcr) { case S_BADGCR_OK: break; case S_BADGCR_ONCE_BAD: if (b_badgcr || n_badgcr) { //if(fix) fix_first_gcr(gcrdata, length, lastpos); if (fix) gcrdata[lastpos] = 0x00; total++; } break; case S_BADGCR_LOST: if (b_badgcr || n_badgcr) gcrdata[lastpos] = 0x00; else { //if(fix) fix_last_gcr(gcrdata, length, lastpos); if (fix) gcrdata[lastpos] = 0x00; } total++; break; } return total; }