src/mess/formats/d64_dsk.c
| r7598 | r7599 | |
| 2 | 2 | |
| 3 | 3 | formats/d64_dsk.c |
| 4 | 4 | |
| 5 | | Floppy format code for Commodore 2040/8050 type disk images |
| 6 | | and for Commodore 1541 D64 disk images |
| 5 | Floppy format code for Commodore 1541/2040/8050 disk images |
| 7 | 6 | |
| 8 | 7 | *********************************************************************/ |
| 9 | 8 | |
| r7598 | r7599 | |
| 11 | 10 | |
| 12 | 11 | TODO: |
| 13 | 12 | |
| 14 | | - disk errors |
| 13 | - write to disk |
| 14 | - disk errors 24, 25, 26, 28, 74 |
| 15 | 15 | - variable gaps |
| 16 | 16 | |
| 17 | | 2010-02 FP: I added partial support for disk images with errors |
| 18 | | but due to lack of docs I'm not sure if we can do better |
| 19 | 17 | */ |
| 20 | 18 | |
| 21 | 19 | #include "emu.h" |
| r7598 | r7599 | |
| 66 | 64 | ERROR_21, /* no sync character */ |
| 67 | 65 | ERROR_22, /* data block not present */ |
| 68 | 66 | ERROR_23, /* checksum error in data block */ |
| 69 | | ERROR_24, /* write verify (on format) */ |
| 70 | | ERROR_25, /* write verify error */ |
| 71 | | ERROR_26, /* write protect on */ |
| 67 | ERROR_24, /* write verify (on format) UNIMPLEMENTED */ |
| 68 | ERROR_25, /* write verify error UNIMPLEMENTED */ |
| 69 | ERROR_26, /* write protect on UNIMPLEMENTED */ |
| 72 | 70 | ERROR_27, /* checksum error in header block */ |
| 73 | | ERROR_28, /* write error */ |
| 71 | ERROR_28, /* write error UNIMPLEMENTED */ |
| 74 | 72 | ERROR_29, /* disk ID mismatch */ |
| 75 | | ERROR_74, /* disk not ready (no device 1) */ |
| 73 | ERROR_74, /* disk not ready (no device 1) UNIMPLEMENTED */ |
| 76 | 74 | }; |
| 77 | 75 | |
| 76 | static const char *const ERROR_CODE[] = { "00", "00", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "74" }; |
| 77 | |
| 78 | 78 | static const UINT8 bin_2_gcr[] = |
| 79 | 79 | { |
| 80 | 80 | 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, |
| r7598 | r7599 | |
| 158 | 158 | int dos_tracks; /* number of logical tracks */ |
| 159 | 159 | int track_offset[MAX_HEADS][MAX_TRACKS]; /* offset within image for each physical track */ |
| 160 | 160 | UINT32 speed_zone[MAX_TRACKS]; /* speed zone for each physical track */ |
| 161 | | bool has_errors; /* flag to check for available error codes */ |
| 162 | | int error[MAX_ERROR_SECTORS]; /* error code for each logical sector */ |
| 161 | bool has_errors; /* flag to check for available error codes */ |
| 162 | UINT8 error[MAX_ERROR_SECTORS]; /* error code for each logical sector */ |
| 163 | int error_offset[MAX_HEADS][MAX_TRACKS]; /* offset within error array for sector 0 of each logical track */ |
| 163 | 164 | |
| 164 | 165 | UINT8 id1, id2; /* DOS disk format ID */ |
| 165 | 166 | }; |
| r7598 | r7599 | |
| 243 | 244 | } |
| 244 | 245 | |
| 245 | 246 | /*------------------------------------------------- |
| 246 | | d64_get_sectors_per_track - returns the offset |
| 247 | | of the error correspondent to the 1st sector |
| 248 | | of a given track |
| 247 | get_sector_error_code - returns the error |
| 248 | code for the given sector |
| 249 | 249 | -------------------------------------------------*/ |
| 250 | 250 | |
| 251 | | static int d64_get_error_offset(floppy_image *floppy, int head, int track) |
| 251 | static int get_sector_error_code(floppy_image *floppy, int head, int dos_track, int sector) |
| 252 | 252 | { |
| 253 | 253 | struct d64dsk_tag *tag = get_tag(floppy); |
| 254 | | int i, j; |
| 255 | | int error_offset = 0; |
| 254 | int sector_error = tag->error[tag->error_offset[head][dos_track] + sector]; |
| 256 | 255 | |
| 257 | | for (i = 0; i <= head; i++) |
| 256 | if (sector_error) |
| 258 | 257 | { |
| 259 | | for (j = 0; j < track; j++) |
| 260 | | { |
| 261 | | if (tag->dos == DOS25) |
| 262 | | { |
| 263 | | error_offset += DOS25_SECTORS_PER_TRACK[j]; |
| 264 | | } |
| 265 | | else if (tag->dos == DOS2) |
| 266 | | { |
| 267 | | if(!(j % 2)) |
| 268 | | error_offset += DOS2_SECTORS_PER_TRACK[j / 2]; |
| 269 | | } |
| 270 | | else |
| 271 | | { |
| 272 | | if(!(j % 2)) |
| 273 | | error_offset += DOS1_SECTORS_PER_TRACK[j / 2]; |
| 274 | | } |
| 275 | | } |
| 258 | logerror("D64 error %s head %u track %u sector %u\n", ERROR_CODE[sector_error], head, dos_track, sector); |
| 276 | 259 | } |
| 277 | 260 | |
| 278 | | return error_offset; |
| 261 | return sector_error; |
| 279 | 262 | } |
| 280 | 263 | |
| 281 | 264 | /*------------------------------------------------- |
| r7598 | r7599 | |
| 389 | 372 | UINT16 gcr_track_size; |
| 390 | 373 | UINT8 *gcr_track_data; |
| 391 | 374 | UINT64 gcr_pos = G64_DATA_START; |
| 392 | | int error_offset = 0, tmp1, tmp2; |
| 375 | int error_offset = 0; |
| 393 | 376 | |
| 394 | 377 | /* determine logical track number */ |
| 395 | 378 | int dos_track = get_dos_track(track); |
| r7598 | r7599 | |
| 405 | 388 | /* determine number of sectors per track */ |
| 406 | 389 | sectors_per_track = d64_get_sectors_per_track(floppy, head, track); |
| 407 | 390 | |
| 408 | | /* determine the position of the error byte for the first sector of this track */ |
| 409 | | if (tag->has_errors) |
| 410 | | error_offset = d64_get_error_offset(floppy, head, track); |
| 411 | | |
| 412 | 391 | /* allocate D64 track data buffer */ |
| 413 | 392 | d64_track_size = sectors_per_track * SECTOR_SIZE; |
| 414 | 393 | d64_track_data = (UINT8 *)alloca(d64_track_size); |
| r7598 | r7599 | |
| 431 | 410 | { |
| 432 | 411 | // here we convert the sector data to gcr directly! |
| 433 | 412 | // IMPORTANT: errors in reading sectors can modify e.g. header info $01 & $05 |
| 413 | int sector_error = get_sector_error_code(floppy, head, track, sector); |
| 434 | 414 | |
| 435 | 415 | /* first we set the position at which sector data starts in the image */ |
| 436 | 416 | UINT64 d64_pos = sector * SECTOR_SIZE; |
| r7598 | r7599 | |
| 445 | 425 | 6. Inter-sector gap 55 55 55 55...55 55 (4 to 19 bytes, never read) |
| 446 | 426 | */ |
| 447 | 427 | |
| 448 | | if (tag->has_errors) |
| 449 | | { |
| 450 | | error_offset += sector; |
| 428 | if (sector_error == ERROR_29) |
| 429 | id1 ^= 0xff; |
| 451 | 430 | |
| 452 | | if (tag->error[error_offset] == ERROR_29) |
| 453 | | id1 ^= 0xff; |
| 431 | /* Header sync */ |
| 432 | if (sector_error != ERROR_21) |
| 433 | { |
| 434 | for (i = 0; i < 5; i++) |
| 435 | gcr_track_data[gcr_pos + i] = 0xff; |
| 436 | gcr_pos += 5; |
| 454 | 437 | } |
| 455 | 438 | |
| 456 | | /* Header sync */ |
| 457 | | for (i = 0; i < 5; i++) |
| 458 | | gcr_track_data[gcr_pos + i] = 0xff; |
| 459 | | gcr_pos += 5; |
| 460 | | |
| 461 | | |
| 462 | 439 | /* Header info */ |
| 463 | 440 | /* These are 8 bytes unencoded, which become 10 bytes encoded */ |
| 464 | 441 | // $00 - header block ID ($08) // this byte can be modified by error code 20 -> 0xff |
| 465 | 442 | // $01 - header block checksum (EOR of $02-$05) // this byte can be modified by error code 27 -> ^ 0xff |
| 466 | 443 | // $02 - Sector# of data block |
| 467 | 444 | // $03 - Track# of data block |
| 468 | | tmp1 = (tag->has_errors && tag->error[error_offset] == ERROR_20) ? 0xff : 0x08; |
| 469 | | tmp2 = sector ^ dos_track ^ id2 ^ id1; |
| 445 | UINT8 header_block_id = (sector_error == ERROR_20) ? 0xff : 0x08; |
| 446 | UINT8 header_block_checksum = sector ^ dos_track ^ id2 ^ id1; |
| 470 | 447 | |
| 471 | | if (tag->has_errors && tag->error[error_offset] == ERROR_27) |
| 472 | | tmp2 ^= 0xff; |
| 448 | if (sector_error == ERROR_27) |
| 449 | header_block_checksum ^= 0xff; |
| 473 | 450 | |
| 474 | | gcr_double_2_gcr(tmp1, tmp2, sector, dos_track, gcr_track_data + gcr_pos); |
| 451 | gcr_double_2_gcr(header_block_id, header_block_checksum, sector, dos_track, gcr_track_data + gcr_pos); |
| 475 | 452 | gcr_pos += 5; |
| 476 | 453 | |
| 477 | 454 | // $04 - Format ID byte #2 |
| r7598 | r7599 | |
| 492 | 469 | gcr_pos += 5; |
| 493 | 470 | |
| 494 | 471 | /* Data block */ |
| 472 | UINT8 data_block_id = (sector_error == ERROR_22) ? 0xff : 0x07; |
| 473 | |
| 495 | 474 | // we first need to calculate the checksum of the 256 bytes of the sector |
| 496 | 475 | UINT8 sector_checksum = d64_track_data[d64_pos]; |
| 497 | 476 | for (i = 1; i < 256; i++) |
| 498 | 477 | sector_checksum ^= d64_track_data[d64_pos + i]; |
| 499 | | |
| 478 | |
| 479 | if (sector_error == ERROR_23) |
| 480 | sector_checksum ^= 0xff; |
| 481 | |
| 500 | 482 | /* |
| 501 | 483 | $00 - data block ID ($07) |
| 502 | 484 | $01-100 - 256 bytes sector data |
| 503 | 485 | $101 - data block checksum (EOR of $01-100) |
| 504 | 486 | $102-103 - $00 ("off" bytes, to make the sector size a multiple of 5) |
| 505 | 487 | */ |
| 506 | | gcr_double_2_gcr(0x07, d64_track_data[d64_pos], d64_track_data[d64_pos + 1], d64_track_data[d64_pos + 2], gcr_track_data + gcr_pos); |
| 488 | gcr_double_2_gcr(data_block_id, d64_track_data[d64_pos], d64_track_data[d64_pos + 1], d64_track_data[d64_pos + 2], gcr_track_data + gcr_pos); |
| 507 | 489 | gcr_pos += 5; |
| 508 | 490 | |
| 509 | 491 | for (i = 1; i < 64; i++) |
| r7598 | r7599 | |
| 522 | 504 | for (i = 0; i < 14; i++) |
| 523 | 505 | gcr_track_data[gcr_pos + i] = 0x55; |
| 524 | 506 | gcr_pos += 14; |
| 507 | |
| 508 | error_offset++; |
| 525 | 509 | } |
| 526 | 510 | |
| 527 | 511 | /* copy GCR track data to buffer */ |
| r7598 | r7599 | |
| 736 | 720 | else |
| 737 | 721 | { |
| 738 | 722 | tag->track_offset[head][track] = track_offset; |
| 739 | | track_offset += DOS25_SECTORS_PER_TRACK[track] * SECTOR_SIZE; |
| 723 | tag->error_offset[head][track] = errors_size; |
| 740 | 724 | |
| 725 | track_offset += DOS25_SECTORS_PER_TRACK[track] * SECTOR_SIZE; |
| 741 | 726 | /* also store an error entry for each sector */ |
| 742 | 727 | errors_size += DOS25_SECTORS_PER_TRACK[track]; |
| 743 | 728 | |
| 744 | | if (LOG) logerror("D64 head %u track %u data offset: %04x\n", head, track + 1, tag->track_offset[head][track]); |
| 729 | if (LOG) |
| 730 | { |
| 731 | logerror("D64 head %u track %u data offset: %04x\n", head, track + 1, tag->track_offset[head][track]); |
| 732 | logerror("D64 head %u track %u error offset: %04x\n", head, track + 1, tag->error_offset[head][track]); |
| 733 | } |
| 745 | 734 | } |
| 746 | 735 | } |
| 747 | 736 | else |
| r7598 | r7599 | |
| 755 | 744 | { |
| 756 | 745 | /* full track */ |
| 757 | 746 | tag->track_offset[head][track] = track_offset; |
| 747 | tag->error_offset[head][track] = errors_size; |
| 758 | 748 | |
| 759 | 749 | if (dos == DOS1) |
| 760 | 750 | { |
| r7598 | r7599 | |
| 769 | 759 | errors_size += DOS2_SECTORS_PER_TRACK[track / 2]; |
| 770 | 760 | } |
| 771 | 761 | |
| 772 | | if (LOG) logerror("D64 head %u track %.1f data offset: %04x\n", head, get_dos_track(track), tag->track_offset[head][track]); |
| 762 | if (LOG) |
| 763 | { |
| 764 | logerror("D64 head %u track %.1f data offset: %04x\n", head, get_dos_track(track), tag->track_offset[head][track]); |
| 765 | logerror("D64 head %u track %.1f error offset: %04x\n", head, get_dos_track(track), tag->error_offset[head][track]); |
| 766 | } |
| 773 | 767 | } |
| 774 | 768 | } |
| 775 | 769 | } |
| r7598 | r7599 | |
| 811 | 805 | |
| 812 | 806 | /* read errors */ |
| 813 | 807 | if (tag->has_errors) |
| 808 | { |
| 809 | if (LOG) logerror("D64 error blocks: %u %u\n", errors_size, track_offset); |
| 814 | 810 | floppy_image_read(floppy, tag->error, track_offset, errors_size); |
| 811 | } |
| 815 | 812 | else |
| 816 | | memset(tag->error, 0, MAX_ERROR_SECTORS); |
| 813 | { |
| 814 | memset(tag->error, ERROR_00, MAX_ERROR_SECTORS); |
| 815 | } |
| 817 | 816 | |
| 818 | 817 | /* set callbacks */ |
| 819 | 818 | callbacks = floppy_callbacks(floppy); |