├── 8FLOPPY.C ├── 8FLOPPY.H ├── 8FORMAT.C ├── 8FORMAT.H ├── 8TSR.C ├── 8TSR_A.ASM ├── FAT.C ├── FAT.H ├── ISADMA.C ├── ISADMA.H ├── MAIN.C ├── README.md ├── TESTREAD.C └── _BUILD.BAT /8FLOPPY.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "8format.h" 7 | #include "isadma.h" 8 | 9 | /* IRQ fired to acknowledge? */ 10 | volatile unsigned char nIRQTriggered = 0; 11 | 12 | unsigned char nISRInstalled = 0; 13 | unsigned char nDriveReady = 0; 14 | 15 | /* "Current" CHS used for seeking */ 16 | unsigned char nCurrentTrack = 0; 17 | unsigned char nCurrentHead = 0; 18 | 19 | /* 8" floppy parameters have not yet been specified to the FDC */ 20 | unsigned char nNeedsReset = 0; 21 | 22 | /* Sector size from bytes to 0-3 */ 23 | unsigned char ConvertSectorSize(unsigned int nSize) 24 | { 25 | switch(nSize) 26 | { 27 | case 128: 28 | default: 29 | return 0; 30 | case 256: 31 | return 1; 32 | case 512: 33 | return 2; 34 | case 1024: 35 | return 3; 36 | } 37 | } 38 | 39 | /* Provide GAP and Gap3 lengths */ 40 | unsigned char GetGapLength(unsigned char nFormatting) 41 | { 42 | /* User-provided gap length for read/write */ 43 | if ((nFormatting == 0) && (nCustomGapLength != 0)) 44 | { 45 | return nCustomGapLength; 46 | } 47 | 48 | /* User-provided GAP3 length for track format */ 49 | if ((nFormatting == 1) && (nCustomGap3Length != 0)) 50 | { 51 | return nCustomGap3Length; 52 | } 53 | 54 | /* Try to autodetect. (values per NEC uPD765 datasheet, Table 3) */ 55 | switch(nPhysicalSectorSize) 56 | { 57 | case 128: 58 | default: 59 | return (nFormatting == 0) ? 7 : 0x1B; 60 | 61 | case 256: 62 | { 63 | if (nUseFM == 1) 64 | { 65 | return (nFormatting == 0) ? 0x0E : 0x2A; 66 | } 67 | else 68 | { 69 | return (nFormatting == 0) ? 0x0E : 0x36; 70 | } 71 | } 72 | 73 | case 512: 74 | { 75 | if (nUseFM == 1) 76 | { 77 | return (nFormatting == 0) ? 0x1B : 0x3A; 78 | } 79 | else 80 | { 81 | /* EXT3: return vanilla recommended value */ 82 | if (strcmp(sFormatType, "EXT3") == 0) 83 | { 84 | return (nFormatting == 0) ? 0x1B : 0x54; 85 | } 86 | else 87 | { 88 | /* These gaps a little lower, to cram a maximum of 16 512B sectors per track :) */ 89 | return (nFormatting == 0) ? 0x18 : 0x50; 90 | } 91 | } 92 | } 93 | 94 | case 1024: 95 | { 96 | if (nUseFM == 1) 97 | { 98 | return (nFormatting == 0) ? 0x47 : 0x8A; 99 | } 100 | else 101 | { 102 | return (nFormatting == 0) ? 0x35 : 0x74; 103 | } 104 | } 105 | } 106 | } 107 | 108 | /* The old IRQ pointer */ 109 | void interrupt (*oldIrqISR)(); 110 | 111 | void interrupt newIrqISR() 112 | { 113 | /* Set triggered flag + inform the PIC to re-enable IRQs again */ 114 | asm cli 115 | asm mov [nIRQTriggered],1 116 | asm mov al,20h 117 | asm out 20h, al 118 | asm sti 119 | } 120 | 121 | /* Install IRQ ISR */ 122 | void InstallISR() 123 | { 124 | const unsigned char nINTVector = (nUseIRQ > 7) ? nUseIRQ+0x68 : nUseIRQ+8; 125 | 126 | if (nISRInstalled != 1) 127 | { 128 | oldIrqISR = getvect(nINTVector); 129 | setvect(nINTVector, newIrqISR); 130 | 131 | nISRInstalled = 1; 132 | } 133 | } 134 | 135 | void RemoveISR() 136 | { 137 | const unsigned char nINTVector = (nUseIRQ > 7) ? nUseIRQ+0x68 : nUseIRQ+8; 138 | 139 | if (nISRInstalled == 1) 140 | { 141 | setvect(nINTVector, oldIrqISR); 142 | } 143 | } 144 | 145 | /* Wait for controller response, with a 6 sec timeout */ 146 | unsigned char WaitForIRQ() 147 | { 148 | unsigned int nTimeout = 1200; 149 | 150 | if (nISRInstalled != 1) 151 | { 152 | return 0; 153 | } 154 | 155 | while(nTimeout != 0) 156 | { 157 | if (nIRQTriggered == 1) 158 | { 159 | nIRQTriggered = 0; 160 | return 1; 161 | } 162 | 163 | nTimeout--; 164 | delay(5); 165 | } 166 | 167 | printf("\nFloppy drive controller failed to respond in time (IRQ %d timeout).\n" 168 | "Check proper connection of the drive, disk presence, and if the FDC works okay.\n", 169 | nUseIRQ); 170 | Quit(EXIT_FAILURE); 171 | return 0; 172 | } 173 | 174 | unsigned char FDDGetData() 175 | { 176 | unsigned int nTimeout = 1200; 177 | 178 | while(nTimeout != 0) 179 | { 180 | /* Receive byte from FDC only if RQM=1 and direction is FDC->CPU */ 181 | if((inportb(nFDCBase + 4) & 0xC0) == 0xC0) 182 | { 183 | return inportb(nFDCBase + 5); 184 | } 185 | 186 | nTimeout--; 187 | delay(5); 188 | } 189 | 190 | printf("\nFailed to get data from the floppy drive controller (RQM / DIO timeout).\n" 191 | "Check proper connection of the drive, disk presence, and if the FDC works okay.\n"); 192 | Quit(EXIT_FAILURE); 193 | 194 | return 0; 195 | } 196 | 197 | void FDDSendData(unsigned char nData) 198 | { 199 | unsigned int nTimeout = 1200; 200 | 201 | while(nTimeout != 0) 202 | { 203 | /* Transmit a byte to FDC only if RQM=1 and direction is CPU->FDC */ 204 | if((inportb(nFDCBase + 4) & 0xC0) == 0x80) 205 | { 206 | outportb(nFDCBase + 5, nData); 207 | return; 208 | } 209 | 210 | nTimeout--; 211 | delay(5); 212 | } 213 | 214 | printf("\nFailed to send data to the floppy drive controller (RQM / DIO timeout).\n" 215 | "Check proper connection of the drive, disk presence, and if the FDC works okay.\n"); 216 | Quit(EXIT_FAILURE); 217 | } 218 | 219 | void FDDSendCommand(unsigned char nCommand) 220 | { 221 | /* Use modifier to clear the MFM flag for specifying main commands */ 222 | (nUseFM == 1) ? FDDSendData(nCommand & 0xbf) : FDDSendData(nCommand); 223 | } 224 | 225 | void FDDHeadLoad() 226 | { 227 | /* "Motor on" (in 8" drives always on), select the drive and allow IRQ6 from controller */ 228 | if (nDriveReady != 1) 229 | { 230 | outportb(nFDCBase + 2, (nDriveNumber & 0x03) | (1 << (4 + nDriveNumber)) | 0x0C); 231 | nDriveReady = 1; 232 | 233 | /* Disable IRQ0 to prevent BIOS from turning the drive motor off automatically 234 | Also mask IRQ1 (keyboard interrupts) */ 235 | { 236 | unsigned char nIRQMask = inportb(0x21); 237 | outportb(0x21, nIRQMask | 3); 238 | } 239 | } 240 | } 241 | 242 | void FDDHeadRetract() 243 | { 244 | if (nDriveReady == 1) 245 | { 246 | /* "Motor off", drive unselected */ 247 | outportb(nFDCBase + 2, (nDriveNumber & 0x03) | 0x0C); 248 | nDriveReady = 0; 249 | 250 | /* Enable IRQ0 & IRQ1 */ 251 | { 252 | unsigned char nIRQMask = inportb(0x21); 253 | outportb(0x21, nIRQMask & 0xfc); 254 | } 255 | } 256 | } 257 | 258 | void FDDSeek(unsigned char nTrack, unsigned char nHead) 259 | { 260 | unsigned char nIdx; 261 | unsigned char nST0; 262 | unsigned char nResultTrack; 263 | 264 | /* Three retries */ 265 | for (nIdx = 0; nIdx < 3; nIdx++) 266 | { 267 | FDDSendCommand(0xf); /* 0xF Seek */ 268 | FDDSendData((nHead << 2) | nDriveNumber); /* Which drive and head */ 269 | FDDSendData(nTrack); 270 | 271 | WaitForIRQ(); 272 | 273 | /* 0x8 Sense interrupt command after completion */ 274 | FDDSendCommand(8); 275 | nST0 = FDDGetData(); /* ST0 status register byte */ 276 | nResultTrack = FDDGetData(); /* Current cylinder */ 277 | 278 | /* Seek ended successfully ? */ 279 | if(nST0 & 0x20) 280 | { 281 | /* UC error */ 282 | if(nST0 & 0x10) 283 | { 284 | continue; 285 | } 286 | 287 | else 288 | { 289 | /* Not on our track */ 290 | if (nResultTrack != nTrack) 291 | { 292 | continue; 293 | } 294 | 295 | else 296 | { 297 | /* Seek success */ 298 | nCurrentTrack = nTrack; 299 | nCurrentHead = nHead; 300 | 301 | /* A 50ms delay after each seek as a safety margin for all drives */ 302 | delay(50); 303 | return; 304 | } 305 | } 306 | } 307 | 308 | /* Seek did not succeed */ 309 | else 310 | { 311 | continue; 312 | } 313 | } 314 | 315 | printf("\nFailed seeking to track %u on head %u after 3 attempts.\n", 316 | nTrack, nHead); 317 | 318 | Quit(EXIT_FAILURE); 319 | } 320 | 321 | void FDDReset() 322 | { 323 | unsigned char nIdx; 324 | 325 | InstallISR(); 326 | 327 | /* Controller reset */ 328 | outportb(nFDCBase + 2, 0); 329 | delay(25); 330 | outportb(nFDCBase + 2, 0x0c); /* IRQ allowed, motors off, no drive selected */ 331 | delay(25); 332 | 333 | WaitForIRQ(); 334 | 335 | /* Check interrupt status 3x (a must, drive is in polling mode) */ 336 | for(nIdx = 0; nIdx < 4; nIdx++) 337 | { 338 | FDDSendCommand(8); 339 | FDDGetData(); /* ST0, trashed */ 340 | FDDGetData(); /* current track, trashed */ 341 | } 342 | 343 | /* Always set the 500kbps FDC communication data rate, with 500kHz clock timing. 344 | Practical data rate on MFM (1 bit per 1 databit) is 500 kbps, and 250kbps on FM (2 bits per 1 databit) 345 | This does absolutely nothing on XT without a HD-capable FDC. */ 346 | outportb(nFDCBase + 7, 0); 347 | 348 | delay(25); 349 | 350 | /* 0x3 Fix drive data command - load new mechanical values 351 | Table data @ https://www.isdaman.com/alsos/hardware/fdc/floppy.htm */ 352 | nNeedsReset = 1; 353 | FDDSendCommand(3); 354 | 355 | /* 8 inch drives are old as the republic, so using very conservative stepper values: 356 | 8ms track-to-track step rate, 252ms head load time, 224ms unload time */ 357 | 358 | /* First byte is SRT (upper nibble) | HUT (lower nibble) 359 | Step rate time (SRT): 8 ms (500kbps FDC comm rate: 8 << 4) 360 | Head unload time (HUT): 224 ms (500kbps FDC comm rate: 14) */ 361 | FDDSendData(0x8e); 362 | 363 | /* Second byte is HLT (upper 7 bits) | non-DMA mode flag (bit 0) 364 | Head load time (HLT): 252 ms (500kbps FDC comm rate: 126 << 1) 365 | Non-DMA mode: 0 (we use DMA) */ 366 | FDDSendData(0xfc); 367 | } 368 | 369 | void FDDCalibrate() 370 | { 371 | unsigned int nIdx; 372 | unsigned char nST0; 373 | unsigned char nTrack; 374 | 375 | unsigned char nCalibrationError = 0; 376 | 377 | FDDHeadLoad(); 378 | 379 | /* Three retries */ 380 | for (nIdx = 0; nIdx < 3; nIdx++) 381 | { 382 | if (nCalibrationError == 1) 383 | { 384 | FDDHeadRetract(); 385 | FDDReset(); 386 | FDDHeadLoad(); 387 | nCalibrationError = 0; 388 | } 389 | 390 | FDDSendCommand(7); /* 0x7 Recalibrate command */ 391 | FDDSendData(nDriveNumber); /* Which drive (0 to 3) */ 392 | 393 | WaitForIRQ(); 394 | 395 | /* 0x8 Sense interrupt command after completion */ 396 | FDDSendCommand(8); 397 | nST0 = FDDGetData(); /* ST0 status register byte */ 398 | nTrack = FDDGetData(); /* Current track */ 399 | 400 | /* Seek ended successfully ? */ 401 | if(nST0 & 0x20) 402 | { 403 | /* UC error */ 404 | if(nST0 & 0x10) 405 | { 406 | nCalibrationError = 1; 407 | continue; 408 | } 409 | 410 | else 411 | { 412 | /* Track not 0 after recal */ 413 | if (nTrack > 0) 414 | { 415 | nCalibrationError = 1; 416 | continue; 417 | } 418 | 419 | else 420 | { 421 | /* Success */ 422 | return; 423 | } 424 | } 425 | } 426 | 427 | /* Seek did not succeed for whatever reason */ 428 | else 429 | { 430 | nCalibrationError = 1; 431 | continue; 432 | } 433 | } 434 | 435 | printf("\nFailed drive recalibration after 3 attempts.\n"); 436 | Quit(EXIT_FAILURE); 437 | } 438 | 439 | /* Called upon application exit */ 440 | void FDDResetBIOS() 441 | { 442 | /* Use BIOS INT13h to recalibrate the FDC before passing control to the OS. 443 | This is important, as it loads BIOS defaults (from INT 1Eh) into the FDC. */ 444 | if (nNeedsReset != 1) 445 | { 446 | return; 447 | } 448 | 449 | /* If the previous operation failed, assume the FDC seized up dead! */ 450 | outportb(nFDCBase + 2, 0); /* Hard reset of the controller */ 451 | delay(25); 452 | outportb(nFDCBase + 2, 0x0c); 453 | 454 | /* Now use BIOS to recalibrate and load defaults to all drives (3 to 0) */ 455 | asm mov cx,4 456 | blip: 457 | asm xor ax,ax 458 | asm mov dx,cx 459 | asm dec dx 460 | asm int 13h 461 | asm loop blip 462 | } 463 | 464 | unsigned char DetectErrors(unsigned char nST0, unsigned char nST1, unsigned char nST2) 465 | { 466 | unsigned char nError = 0; 467 | 468 | /* Any errors in the ST0 status register? */ 469 | if ((nST0 & 0xC0) != 0) 470 | { 471 | nError = 1; 472 | printf("\n!ST0 0x%x", nST0); 473 | 474 | /* Drive not ready error */ 475 | if ( ((nST0 & 0xC0) == 0xC0) || ((nST0 & 8) == 8) ) 476 | { 477 | printf(" Not ready"); 478 | } 479 | 480 | /* UC/equipment error */ 481 | if(nST0 & 0x10) 482 | { 483 | printf(" Drive fault"); 484 | } 485 | } 486 | 487 | /* Any errors in the ST1 status register ? */ 488 | if (nST1 > 0) 489 | { 490 | nError = 1; 491 | printf("\n!ST1 0x%x", nST1); 492 | 493 | if (nST1 & 0x80) 494 | { 495 | printf(" Sector count per track exceeded"); 496 | } 497 | if (nST1 & 0x20) 498 | { 499 | printf(" Error in data"); 500 | } 501 | if (nST1 & 0x10) 502 | { 503 | printf(" Timeout or data overrun"); 504 | } 505 | if (nST1 & 4) 506 | { 507 | printf(" No data (sector not found)"); 508 | } 509 | if (nST1 & 2) 510 | { 511 | printf("\nWrite protect error\n"); 512 | Quit(EXIT_FAILURE); /* No point of retrying the command, quit now */ 513 | } 514 | if (nST1 & 1) 515 | { 516 | printf(" No address mark"); 517 | } 518 | } 519 | 520 | /* Errors in ST2 status register */ 521 | if (nST2 > 0) 522 | { 523 | nError = 1; 524 | printf("\n!ST2 0x%x", nST2); 525 | 526 | if (nST2 & 0x20) 527 | { 528 | printf(" CRC error in data field"); 529 | } 530 | if (nST2 & 0x10) 531 | { 532 | printf(" Wrong track in ID address mark"); 533 | } 534 | if (nST2 & 2) 535 | { 536 | printf(" Bad track or defective media"); 537 | } 538 | if (nST2 & 1) 539 | { 540 | printf(" No deleted address mark"); 541 | } 542 | } 543 | 544 | /* Reset and recalibrate on error */ 545 | if (nError) 546 | { 547 | FDDHeadRetract(); 548 | FDDReset(); 549 | FDDCalibrate(); 550 | FDDSeek(nCurrentTrack, nCurrentHead); 551 | } 552 | 553 | return nError; 554 | } 555 | 556 | /* Formats a whole track on the active (seeked) track and head number */ 557 | void FDDFormat() 558 | { 559 | unsigned char nIdx; 560 | 561 | /* Clear 8K DMA buffer */ 562 | memset(pDMABuffer, 0, 8*1024); 563 | 564 | /* Three retries */ 565 | for (nIdx = 0; nIdx < 3; nIdx++) 566 | { 567 | unsigned char nIdx2 = 0; 568 | unsigned char nIdx3 = 0; 569 | 570 | unsigned char nST0; 571 | unsigned char nST1; 572 | unsigned char nST2; 573 | 574 | /* Prepare 4-byte 'CHSN' format buffer for all sectors on track */ 575 | for(nIdx2 = 0; nIdx2 < nSectorsPerTrack; nIdx2++) 576 | { 577 | pDMABuffer[nIdx3++] = nCurrentTrack; 578 | pDMABuffer[nIdx3++] = nCurrentHead; 579 | pDMABuffer[nIdx3++] = nIdx2+1; /* Sector number (1-based) */ 580 | pDMABuffer[nIdx3++] = ConvertSectorSize(nPhysicalSectorSize); /* Sector size 0 to 3 */ 581 | } 582 | 583 | /* Setup DMA */ 584 | PrepareDMABufferForTransfer(0, 4*nSectorsPerTrack); 585 | 586 | /* Now send command */ 587 | FDDSendCommand(0x4D); /* 0x4d Format track */ 588 | FDDSendData((nCurrentHead << 2) | nDriveNumber); /* Which drive and head */ 589 | FDDSendData(ConvertSectorSize(nPhysicalSectorSize)); /* Sector size, 0 to 3 */ 590 | FDDSendData(nSectorsPerTrack); /* Last sector on track */ 591 | FDDSendData(GetGapLength(1)); /* Format GAP3 length */ 592 | FDDSendData(nFormatByte); /* Format fill byte */ 593 | 594 | WaitForIRQ(); 595 | 596 | /* Get status information */ 597 | nST0 = FDDGetData(); 598 | nST1 = FDDGetData(); 599 | nST2 = FDDGetData(); 600 | 601 | /* Track, head, sector number and size trashed */ 602 | FDDGetData(); 603 | FDDGetData(); 604 | FDDGetData(); 605 | FDDGetData(); 606 | 607 | if (DetectErrors(nST0, nST1, nST2) == 0) 608 | { 609 | /* No errors, success */ 610 | return; 611 | } 612 | 613 | /* Errors detected - next attempt */ 614 | printf("\n"); 615 | } 616 | 617 | printf("\nFormatting failed after 3 attempts.\n"); 618 | Quit(EXIT_FAILURE); 619 | } 620 | 621 | /* Writes either a single sector on the active (seeked) track and head number, or whole track 622 | Input: pre-set DMA buffer with proper size 623 | sector number (1-based !), or 255 (0xff) for whole track */ 624 | void FDDWrite(unsigned char nSectorNo) 625 | { 626 | unsigned char nIdx; 627 | 628 | /* Three write retries */ 629 | for (nIdx = 0; nIdx < 3; nIdx++) 630 | { 631 | unsigned char nST0; 632 | unsigned char nST1; 633 | unsigned char nST2; 634 | 635 | /* Setup DMA */ 636 | PrepareDMABufferForTransfer(0, (nSectorNo != 0xff) ? 637 | nPhysicalSectorSize : 638 | nPhysicalSectorSize*nSectorsPerTrack); 639 | 640 | /* Now send command */ 641 | FDDSendCommand(0x45); /* 0x45 Write sector */ 642 | FDDSendData((nCurrentHead << 2) | nDriveNumber); /* Which drive and head */ 643 | FDDSendData(nCurrentTrack); /* currently seeked track and head */ 644 | FDDSendData(nCurrentHead); /* lol redundant but needed */ 645 | FDDSendData((nSectorNo != 0xff) ? nSectorNo : 1); /* Which sector to write, or whole track from sector 1 */ 646 | FDDSendData(ConvertSectorSize(nPhysicalSectorSize)); /* Sector size, 0 to 3 */ 647 | FDDSendData((nSectorNo != 0xff) ? nSectorNo : nSectorsPerTrack); /* Write only one sector or whole track */ 648 | FDDSendData(GetGapLength(0)); /* Gap length */ 649 | FDDSendData((nPhysicalSectorSize == 128) ? (unsigned char)nPhysicalSectorSize : 0xff); /* Data transfer length */ 650 | 651 | WaitForIRQ(); 652 | 653 | /* Get status information */ 654 | nST0 = FDDGetData(); 655 | nST1 = FDDGetData(); 656 | nST2 = FDDGetData(); 657 | 658 | /* Track, head, sector number and size trashed */ 659 | FDDGetData(); 660 | FDDGetData(); 661 | FDDGetData(); 662 | FDDGetData(); 663 | 664 | if (DetectErrors(nST0, nST1, nST2) == 0) 665 | { 666 | /* No errors, success */ 667 | return; 668 | } 669 | 670 | /* Errors detected - next attempt */ 671 | printf("\n"); 672 | } 673 | 674 | /* Format error message for single sector/whole track operation */ 675 | if (nSectorNo != 0xff) 676 | { 677 | printf("\nWrite operation of track %u, head %u, sector %u failed after 3 attempts.\n", 678 | nCurrentTrack, nCurrentHead, nSectorNo); 679 | } 680 | 681 | else 682 | { 683 | printf("\nWrite operation of track %u on head %u failed after 3 attempts.\n", 684 | nCurrentTrack, nCurrentHead); 685 | } 686 | 687 | Quit(EXIT_FAILURE); 688 | } 689 | 690 | /* To update the INT 1Eh BIOS diskette parameter table, execute 8TSR. */ 691 | void FDDWriteINT1Eh(unsigned char* pPath) 692 | { 693 | static unsigned char* pArguments[] = { NULL, "255", "255", "255", "255", "255", "255", "255", "255", NULL}; 694 | 695 | /* Here, just prepare the arguments to execute 8TSR just before the end in Quit() */ 696 | if (pPath == NULL) 697 | { 698 | /* Command line arguments: 699 | drivenumber tracks heads sectorsize EOT RWgap DTL GAP3 700 | All values decadic and unsigned char (0-255) */ 701 | sprintf(pArguments[1], "%u", nDriveNumber); 702 | sprintf(pArguments[2], "%u", nTracks); 703 | sprintf(pArguments[3], "%u", nHeads); 704 | sprintf(pArguments[4], "%u", ConvertSectorSize(nPhysicalSectorSize)); 705 | sprintf(pArguments[5], "%u", nSectorsPerTrack); 706 | sprintf(pArguments[6], "%u", GetGapLength(0)); 707 | sprintf(pArguments[7], "%u", (nPhysicalSectorSize == 128) ? 0x80 : 0xff); 708 | sprintf(pArguments[8], "%u", GetGapLength(1)); 709 | 710 | nLaunch8TSR = 1; 711 | return; 712 | } 713 | 714 | /* Overlay 8FORMAT with 8TSR. Called in Quit() */ 715 | pArguments[0] = pPath; 716 | execv(pPath, pArguments); 717 | } 718 | 719 | /* Reads either a single sector from the active (seeked) track and head number, or whole track 720 | Input: sector number (1-based !), or 255 (0xff) for whole track */ 721 | void FDDRead(unsigned char nSectorNo) 722 | { 723 | unsigned char nIdx; 724 | 725 | /* Clear 8K DMA buffer */ 726 | memset(pDMABuffer, 0, 8*1024); 727 | 728 | /* Three read retries */ 729 | for (nIdx = 0; nIdx < 3; nIdx++) 730 | { 731 | unsigned char nST0; 732 | unsigned char nST1; 733 | unsigned char nST2; 734 | 735 | /* Setup DMA */ 736 | PrepareDMABufferForTransfer(1, (nSectorNo != 0xff) ? 737 | nPhysicalSectorSize : 738 | nPhysicalSectorSize*nSectorsPerTrack); 739 | 740 | /* Now send command */ 741 | FDDSendCommand(0x46); /* 0x46 Read sector */ 742 | FDDSendData((nCurrentHead << 2) | nDriveNumber); /* Which drive and head */ 743 | FDDSendData(nCurrentTrack); /* currently seeked track and head */ 744 | FDDSendData(nCurrentHead); /* lol redundant but needed */ 745 | FDDSendData((nSectorNo != 0xff) ? nSectorNo : 1); /* Which sector to read, or whole track from sector 1 */ 746 | FDDSendData(ConvertSectorSize(nPhysicalSectorSize)); /* Sector size, 0 to 3 */ 747 | FDDSendData((nSectorNo != 0xff) ? nSectorNo : nSectorsPerTrack); /* Read only one sector or whole track */ 748 | FDDSendData(GetGapLength(0)); /* Gap length */ 749 | FDDSendData((nPhysicalSectorSize == 128) ? (unsigned char)nPhysicalSectorSize : 0xff); /* Data transfer length */ 750 | 751 | WaitForIRQ(); 752 | 753 | /* Get status information */ 754 | nST0 = FDDGetData(); 755 | nST1 = FDDGetData(); 756 | nST2 = FDDGetData(); 757 | 758 | /* Track, head, sector number and size trashed */ 759 | FDDGetData(); 760 | FDDGetData(); 761 | FDDGetData(); 762 | FDDGetData(); 763 | 764 | if (DetectErrors(nST0, nST1, nST2) == 0) 765 | { 766 | /* No errors, success */ 767 | return; 768 | } 769 | 770 | /* Errors detected - next attempt */ 771 | printf("\n"); 772 | } 773 | 774 | /* Format error message for single sector/whole track operation */ 775 | if (nSectorNo != 0xff) 776 | { 777 | printf("\nRead of track %u, head %u, sector %u failed after 3 attempts.\n", 778 | nCurrentTrack, nCurrentHead, nSectorNo); 779 | } 780 | 781 | else 782 | { 783 | printf("\nRead of track %u on head %u failed after 3 attempts.\n", 784 | nCurrentTrack, nCurrentHead); 785 | } 786 | 787 | Quit(EXIT_FAILURE); 788 | } -------------------------------------------------------------------------------- /8FLOPPY.H: -------------------------------------------------------------------------------- 1 | #ifndef _8FLOPPY_H_ 2 | #define _8FLOPPY_H_ 3 | 4 | unsigned char ConvertSectorSize(unsigned int); 5 | unsigned char GetGapLength(unsigned char); 6 | void RemoveISR(); 7 | 8 | void FDDHeadRetract(); 9 | 10 | void FDDReset(); 11 | void FDDResetBIOS(); 12 | 13 | void FDDFormat(); 14 | void FDDWrite(unsigned char); 15 | void FDDRead(unsigned char); 16 | 17 | void FDDWriteINT1Eh(unsigned char*); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /8FORMAT.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "8format.h" 7 | #include "8floppy.h" 8 | #include "isadma.h" 9 | 10 | /* Obtained from the command line later */ 11 | unsigned int nFDCBase = 0x3f0; 12 | unsigned char nUseFM = 0; 13 | unsigned char nFormatWithVerify = 0; 14 | unsigned char nQuickFormat = 0; 15 | unsigned char nNoCreateFilesystem = 1; 16 | unsigned char nDriveNumber = 0; 17 | unsigned char nTracks = 77; 18 | unsigned char nHeads = 2; 19 | unsigned int nPhysicalSectorSize = 0; 20 | unsigned int nLogicalSectorSize = 0; 21 | unsigned char nSectorsPerTrack = 0; 22 | unsigned char nLogicalSectorsPerTrack = 0; 23 | unsigned char nCustomGapLength = 0; 24 | unsigned char nCustomGap3Length = 0; 25 | unsigned char nOnlyReprogramBIOS = 0; 26 | unsigned char nOnlyRecalibrateFDD = 0; 27 | unsigned char nUseIRQ = 6; 28 | unsigned char nUseDMA = 2; 29 | unsigned char nLaunch8TSR = 0; 30 | unsigned char sFormatType[5] = {0}; 31 | unsigned char nFormatByte = 0xF6; 32 | 33 | /* Terminate with exit code, do cleanup beforehand */ 34 | void Quit(int nStatus) 35 | { 36 | RemoveISR(); 37 | FDDHeadRetract(); 38 | FreeDMABuffer(); 39 | 40 | /* Important before passing control to DOS */ 41 | FDDResetBIOS(); 42 | 43 | /* EXEC 8TSR if need be */ 44 | if (nLaunch8TSR == 1) 45 | { 46 | /* 8TSR.EXE must exist */ 47 | char* pSearchPath = searchpath("8tsr.exe"); 48 | 49 | /* Not in current directory, not in PATH */ 50 | if (pSearchPath == NULL) 51 | { 52 | printf("\n8TSR.EXE not found, cannot set the BIOS diskette parameter table.\n"); 53 | } 54 | 55 | /* Nested too much in 255byte PATH? */ 56 | else if (strlen(pSearchPath) > 200) 57 | { 58 | printf("\n8TSR.EXE nested in too many subdirectories. Aborting.\n"); 59 | } 60 | 61 | /* Overlay process */ 62 | else 63 | { 64 | FDDWriteINT1Eh(pSearchPath); 65 | } 66 | 67 | printf("\nError launching 8TSR.EXE\n"); 68 | } 69 | 70 | /* Exit to OS */ 71 | exit(nStatus); 72 | } 73 | 74 | /* Determine if the machine is an IBM 5150 or XT. */ 75 | unsigned char IsPCXT() 76 | { 77 | unsigned char nPcByte = *((unsigned char far*)MK_FP(0xf000, 0xfffe)); 78 | return (nPcByte > 0xfc) || (nPcByte == 0xfb); 79 | } 80 | 81 | /* Discard crappy systems right off the bat */ 82 | void DetectMemory() 83 | { 84 | asm int 0x12 85 | 86 | if (_AX < 256) 87 | { 88 | printf("\nNot enough memory\n"); 89 | Quit(EXIT_FAILURE); 90 | } 91 | } 92 | 93 | /* Redraw line */ 94 | void DelLine() 95 | { 96 | printf("\r \r"); 97 | } 98 | 99 | /* Print splash */ 100 | void PrintSplash() 101 | { 102 | printf("8FORMAT - 8\" floppy format utility for DOS, (c) J. Bogin\n"); 103 | 104 | /* And quit rightaway :) */ 105 | DetectMemory(); 106 | } 107 | 108 | /* Printed on incorrect or no command line arguments */ 109 | void PrintUsage() 110 | { 111 | printf("\nUse either 8FORMAT X: TYPE [/1] [/V] [/MFM] [/FAT12] [/Q]\n" 112 | " [/FDC port] [/IRQ num] [/DMA num] [/G len] [/G3 len]\n" 113 | " or 8FORMAT /USE TYPE [/1] [/G len] [/G3 len], where:\n\n" 114 | " X: the drive letter where the 77-track 8\" disk drive is installed; A to D,\n" 115 | " TYPE sets geometry, density (data encoding) and physical sector size:\n" 116 | " DSSD: 500kB double-sided, single-density (FM), 26 128B sectors,\n" 117 | " DSDD: 1.2MB double-sided, double-density (MFM), 8 1024B sectors,\n" 118 | " EXT1: 1.2MB double-sided, double-density (MFM), 16 512B sectors,\n" 119 | " EXT2: 1.0MB double-sided, double-density (MFM), 26 256B sectors,\n" 120 | " EXT3: 693kB double-sided, double-density (MFM), 9 512B sectors.\n" 121 | " /USE for BIOS to use new Diskette Parameter Table for given TYPE geometry.\n" 122 | " Applied for ALL floppy drives until reboot. Disk stays untouched.\n" 123 | " /1 (optional): Single-sided format for the chosen TYPE. Capacity halved.\n" 124 | " /V (optional): Format and verify. MUCH slower.\n" 125 | " /MFM (optional): Use MFM data encoding with type DSSD.\n" 126 | " /FAT12 (optional): Format and try writing the DOS boot sector and filesystem.\n" 127 | " /Q (optional): Do not format, just write the boot sector and filesystem.\n" 128 | " /FDC (optional): Use a different FD controller base hex port; default 0x3f0.\n" 129 | " Use /IRQ (3..15) and /DMA (0..3) to override default IRQ 6 and DMA 2.\n" 130 | " /G,/G3 (optional): Custom GAP (write) or Gap3 (format) sizes in hex. Max 0xff.\n"); 131 | 132 | Quit(EXIT_SUCCESS); 133 | } 134 | 135 | /* Parse the command line */ 136 | void ParseCommandLine(int argc, char* argv[]) 137 | { 138 | int indexArgs = 0; 139 | unsigned char nForce512Physical = 0; 140 | 141 | /* Incorrect number of arguments */ 142 | if ((argc < 3) || (argc > 20)) 143 | { 144 | PrintUsage(); 145 | } 146 | 147 | for (indexArgs = 1; indexArgs < argc; indexArgs++) 148 | { 149 | /* Case insensitive comparison */ 150 | const char* pArgument = strupr(argv[indexArgs]); 151 | 152 | /* Determine drive letter */ 153 | if (indexArgs == 1) 154 | { 155 | /* Only update the global BIOS Diskette Parameter Table, /USE TYPE 156 | Optional: /1, /G, /G3, /512PH (UNDOCUMENTED) */ 157 | if (strcmp(pArgument, "/USE") == 0) 158 | { 159 | nOnlyReprogramBIOS = 1; 160 | continue; 161 | } 162 | 163 | /* Determine drive letter (physical drive number) */ 164 | if ((strlen(pArgument) > 2) || ((strlen(pArgument) == 2) && pArgument[1] != ':')) 165 | { 166 | PrintUsage(); 167 | } 168 | 169 | /* Must be A: to D: */ 170 | nDriveNumber = pArgument[0] - 65; 171 | if (nDriveNumber > 3) 172 | { 173 | PrintUsage(); 174 | } 175 | } 176 | 177 | /* Determine TYPE (density and geometry combo) */ 178 | else if (indexArgs == 2) 179 | { 180 | /* ! UNDOCUMENTED: Only try to fix an out-of-alignment drive caused by BIOS POST seek 181 | Requires drive letter as a first argument */ 182 | if ((strcmp(pArgument, "/UNFUCK") == 0) && (nOnlyReprogramBIOS == 0)) 183 | { 184 | nOnlyRecalibrateFDD = 1; 185 | break; 186 | } 187 | 188 | /* Determine TYPE */ 189 | if (strlen(pArgument) != 4) 190 | { 191 | PrintUsage(); 192 | } 193 | 194 | /* Copy TYPE string. */ 195 | strncpy(sFormatType, pArgument, 4); 196 | 197 | /* 500kB DSSD */ 198 | if (strcmp(pArgument, "DSSD") == 0) 199 | { 200 | nUseFM = 1; 201 | nSectorsPerTrack = 26; 202 | nLogicalSectorsPerTrack = 26; 203 | nPhysicalSectorSize = 128; 204 | nLogicalSectorSize = 128; 205 | } 206 | 207 | /* 1.2MB DSDD */ 208 | else if (strcmp(pArgument, "DSDD") == 0) 209 | { 210 | nSectorsPerTrack = 8; 211 | nLogicalSectorsPerTrack = 8; 212 | nPhysicalSectorSize = 1024; 213 | nLogicalSectorSize = 1024; 214 | } 215 | 216 | /* 1.2MB EXT1 */ 217 | else if (strcmp(pArgument, "EXT1") == 0) 218 | { 219 | nSectorsPerTrack = 16; 220 | nLogicalSectorsPerTrack = 16; 221 | nPhysicalSectorSize = 512; 222 | nLogicalSectorSize = 512; 223 | } 224 | 225 | /* 1.0MB EXT2 */ 226 | else if (strcmp(pArgument, "EXT2") == 0) 227 | { 228 | nSectorsPerTrack = 26; 229 | nLogicalSectorsPerTrack = 26; 230 | nPhysicalSectorSize = 256; 231 | nLogicalSectorSize = 256; 232 | } 233 | 234 | /* 693kB EXT3 */ 235 | else if (strcmp(pArgument, "EXT3") == 0) 236 | { 237 | nSectorsPerTrack = 9; 238 | nLogicalSectorsPerTrack = 9; 239 | nPhysicalSectorSize = 512; 240 | nLogicalSectorSize = 512; 241 | } 242 | 243 | /* 1.3MB CRAM UNDOCUMENTED */ 244 | else if (strcmp(pArgument, "CRAM") == 0) 245 | { 246 | nTracks = 78; 247 | nSectorsPerTrack = 17; 248 | nLogicalSectorsPerTrack = 17; 249 | nPhysicalSectorSize = 512; 250 | nLogicalSectorSize = 512; 251 | nCustomGapLength = 16; 252 | nCustomGap3Length = 32; 253 | } 254 | 255 | /* Unrecognized */ 256 | else 257 | { 258 | PrintUsage(); 259 | } 260 | } 261 | 262 | /* Force MFM encoding */ 263 | else if (strcmp(pArgument, "/MFM") == 0) 264 | { 265 | nUseFM = 0; 266 | } 267 | 268 | /* Force single-sided operation */ 269 | else if (strcmp(pArgument, "/1") == 0) 270 | { 271 | nHeads = 1; 272 | } 273 | 274 | /* UNDOCUMENTED: Recalculate geometry with 512-byte physical sectors, treat TYPE as logical */ 275 | else if (strcmp(pArgument, "/512PH") == 0) 276 | { 277 | nForce512Physical = 1; 278 | } 279 | 280 | /* Format with verify */ 281 | else if (strcmp(pArgument, "/V") == 0) 282 | { 283 | nFormatWithVerify = 1; 284 | } 285 | 286 | /* Format and create filesystem */ 287 | else if (strcmp(pArgument, "/FAT12") == 0) 288 | { 289 | nNoCreateFilesystem = 0; 290 | } 291 | 292 | /* Create FAT12 only */ 293 | else if (strcmp(pArgument, "/Q") == 0) 294 | { 295 | nQuickFormat = 1; 296 | nNoCreateFilesystem = 0; 297 | } 298 | 299 | /* Custom floppy drive controller port has been specified */ 300 | else if ((strcmp(pArgument, "/FDC") == 0) && (argc > indexArgs+1)) 301 | { 302 | char* pEndPointer; 303 | unsigned long nPort = strtoul(argv[++indexArgs], &pEndPointer, 16); 304 | 305 | if ((nPort != 0) && (nPort < 0xffff)) 306 | { 307 | nFDCBase = (unsigned int)nPort; 308 | } 309 | else 310 | { 311 | PrintUsage(); 312 | } 313 | } 314 | 315 | /* Custom write gap length has been specified */ 316 | else if ((strcmp(pArgument, "/G") == 0) && (argc > indexArgs+1)) 317 | { 318 | char* pEndPointer; 319 | unsigned long nGapLen = strtoul(argv[++indexArgs], &pEndPointer, 16); 320 | 321 | if ((nGapLen != 0) && (nGapLen <= 0xff)) 322 | { 323 | nCustomGapLength = (unsigned char)nGapLen; 324 | } 325 | else 326 | { 327 | PrintUsage(); 328 | } 329 | } 330 | 331 | /* Custom format gap3 length has been specified */ 332 | else if ((strcmp(pArgument, "/G3") == 0) && (argc > indexArgs+1)) 333 | { 334 | char* pEndPointer; 335 | unsigned long nGapLen = strtoul(argv[++indexArgs], &pEndPointer, 16); 336 | 337 | if ((nGapLen != 0) && (nGapLen <= 0xff)) 338 | { 339 | nCustomGap3Length = (unsigned char)nGapLen; 340 | } 341 | else 342 | { 343 | PrintUsage(); 344 | } 345 | } 346 | 347 | /* Custom IRQ number (dec.) */ 348 | else if ((strcmp(pArgument, "/IRQ") == 0) && (argc > indexArgs+1)) 349 | { 350 | int nIrq = atoi(argv[++indexArgs]); 351 | 352 | if ((nIrq > 2) && (nIrq < 16)) 353 | { 354 | nUseIRQ = (unsigned char)nIrq; 355 | } 356 | else 357 | { 358 | PrintUsage(); 359 | } 360 | } 361 | 362 | /* Custom 8-bit DMA channel */ 363 | else if ((strcmp(pArgument, "/DMA") == 0) && (argc > indexArgs+1)) 364 | { 365 | int nDma = atoi(argv[++indexArgs]); 366 | 367 | if ((nDma >= 0) && (nDma < 4)) 368 | { 369 | nUseDMA = (unsigned char)nDma; 370 | } 371 | else 372 | { 373 | PrintUsage(); 374 | } 375 | } 376 | 377 | /* Invalid or misspelled command */ 378 | else 379 | { 380 | PrintUsage(); 381 | } 382 | } 383 | 384 | /* Force 512B physical sectors and logical TYPE - change the physical geometry */ 385 | if (nForce512Physical == 1) 386 | { 387 | unsigned int nTotalBytesPerTrack = nSectorsPerTrack * nPhysicalSectorSize; 388 | 389 | nPhysicalSectorSize = 512; 390 | 391 | /* Recalculate new (physical) sectors per track count, keep the logical count as is. */ 392 | nSectorsPerTrack = (unsigned char)(nTotalBytesPerTrack / nPhysicalSectorSize); 393 | if ((nTotalBytesPerTrack % nPhysicalSectorSize) > 0) 394 | { 395 | nSectorsPerTrack++; 396 | } 397 | } 398 | 399 | /* Only update the DPT or recal the drive - do not show any warnings following */ 400 | if ((nOnlyReprogramBIOS == 1) || (nOnlyRecalibrateFDD == 1)) 401 | { 402 | return; 403 | } 404 | 405 | /* Info or warning messages follow. */ 406 | 407 | /* Running on IBM PC/XT */ 408 | if (IsPCXT() == 1) 409 | { 410 | printf("\nWARNING: IBM 5150 or XT detected. Unless there's a high-density capable FDC," 411 | "\n8\" floppy access won't work (500kHz clock rate%sis required).\n", 412 | (nUseFM == 1) ? " with FM support " : " "); 413 | } 414 | 415 | /* Custom FDC port, IRQ or DMA specified ? */ 416 | if ((nFDCBase != 0x3f0) || (nUseIRQ != 6) || (nUseDMA != 2)) 417 | { 418 | printf("\nUsing floppy controller base address at 0x%03X, IRQ %d and DMA channel %d.\n", nFDCBase, nUseIRQ, nUseDMA); 419 | } 420 | 421 | /* FAT on a non-standard physical sector size */ 422 | if ((nNoCreateFilesystem == 0) && (nPhysicalSectorSize != 512)) 423 | { 424 | printf("\nWARNING: non-standard physical sector size for a FAT12 floppy (%u bytes).\n" 425 | "The disk might not be DOS-accessible.\n", nPhysicalSectorSize); 426 | } 427 | 428 | /* FAT on a non-standard 512B geometry */ 429 | if ((nNoCreateFilesystem == 0) && (nForce512Physical == 1)) 430 | { 431 | printf("\nWARNING: non-standard 512B sector geometry for a FAT12 floppy.\n" 432 | "Using FAT media ID 0xf0, the disk might not be universally accessible however.\n"); 433 | } 434 | } 435 | 436 | unsigned char WaitEnterOrEscape() 437 | { 438 | unsigned char nScanCode = 0; 439 | 440 | /* While not ENTER (or ESC) */ 441 | while ((nScanCode != 0x1C) && (nScanCode != 1)) 442 | { 443 | asm xor ax,ax 444 | asm int 16h 445 | asm mov nScanCode,ah 446 | } 447 | 448 | return nScanCode; 449 | } 450 | 451 | void AskToContinue() 452 | { 453 | printf("Insert a disk into drive %c: and press ENTER to continue; ESC to quit...", 454 | nDriveNumber + 65); 455 | 456 | /* ESC */ 457 | if (WaitEnterOrEscape() == 1) 458 | { 459 | printf(" ESC\n"); 460 | Quit(EXIT_SUCCESS); 461 | } 462 | 463 | DelLine(); 464 | } 465 | 466 | void DoOperations() 467 | { 468 | /* Only reprogram the BIOS floppy geometry ? */ 469 | if (nOnlyReprogramBIOS == 1) 470 | { 471 | FDDWriteINT1Eh(NULL); 472 | Quit(EXIT_SUCCESS); 473 | } 474 | 475 | /* UNDOCUMENTED: Only try to fix a drive out of alignment ? */ 476 | if (nOnlyRecalibrateFDD == 1) 477 | { 478 | FDDReset(); 479 | FDDCalibrate(); 480 | 481 | /* Seek to the last 77th track, and back to track 0 again */ 482 | FDDSeek(76, 0); 483 | delay(500); 484 | FDDCalibrate(); 485 | 486 | Quit(EXIT_SUCCESS); 487 | } 488 | 489 | /* Inform about the drive geometry */ 490 | printf("\nUsing%s TYPE %s with the following physical geometry and parameters:\n" 491 | "%u tracks, %u %s, %u %uB sectors, R/W gap 0x%02X, Gap3 0x%02X, 500kHz%s %s%s.\n\n", 492 | ((nPhysicalSectorSize != nLogicalSectorSize) && (nNoCreateFilesystem == 0)) ? " logical" : "", 493 | sFormatType, 494 | nTracks, 495 | nHeads, 496 | (nHeads > 1) ? "heads" : "head", 497 | nSectorsPerTrack, 498 | nPhysicalSectorSize, 499 | GetGapLength(0), 500 | GetGapLength(1), 501 | (IsPCXT() == 1) ? "(?)" : "", 502 | (nUseFM == 1) ? "FM" : "MFM", 503 | ((IsPCXT() == 1) && (nUseFM == 1)) ? "(?)" : ""); 504 | 505 | /* Ask to put the disk into drive */ 506 | AskToContinue(); 507 | 508 | /* Initialize DMA */ 509 | InitializeDMABuffer(); 510 | 511 | /* Prepare FDC and drive */ 512 | printf("Initializing floppy drive controller..."); 513 | FDDReset(); 514 | FDDCalibrate(); 515 | 516 | /* Seek to the last 77th track, and back to track 0 again */ 517 | FDDSeek(76, 0); 518 | delay(500); 519 | FDDCalibrate(); 520 | DelLine(); 521 | 522 | /* Format 77 tracks */ 523 | if (nQuickFormat == 0) 524 | { 525 | unsigned char nTrackIndex = 0; 526 | unsigned char nHeadIndex = 0; 527 | unsigned int nBadsOccurence = 0; 528 | 529 | randomize(); 530 | printf("Formatting...\n"); 531 | 532 | for (; nTrackIndex < nTracks; nTrackIndex++) 533 | { 534 | while (nHeadIndex != nHeads) 535 | { 536 | printf("\rHead: %u Track: %u ", nHeadIndex, nTrackIndex); 537 | FDDSeek(nTrackIndex, nHeadIndex); 538 | 539 | /* Format without verify? */ 540 | if (nFormatWithVerify == 0) 541 | { 542 | printf("\r"); 543 | FDDFormat(); 544 | } 545 | 546 | else 547 | { 548 | /* Format with verify */ 549 | unsigned char nVerifyResult; 550 | unsigned char nRandomIndex; 551 | const unsigned char nTestWriteByte = (unsigned char)random(256); 552 | 553 | printf(" Format"); 554 | FDDFormat(); /* Whole track at once */ 555 | 556 | /* Write random byte on all sectors on track */ 557 | printf(" Write"); 558 | memset(pDMABuffer, nTestWriteByte, nPhysicalSectorSize*nSectorsPerTrack); 559 | FDDWrite(0xff); /* whole track */ 560 | 561 | /* Read the sectors. Compare the last byte of each (sector buffer is filled with nTestWriteByte) */ 562 | printf(" Verify"); 563 | memset(pDMABuffer, 0, nPhysicalSectorSize*nSectorsPerTrack); 564 | FDDRead(0xff); /* whole track */ 565 | 566 | nRandomIndex = random(nPhysicalSectorSize*nSectorsPerTrack); /* Random buffer position */ 567 | nVerifyResult = pDMABuffer[nRandomIndex] == nTestWriteByte; /* Verify buffer contents */ 568 | 569 | if (nVerifyResult != 1) 570 | { 571 | printf(" - did not format correctly\n"); 572 | nBadsOccurence++; 573 | } 574 | 575 | /* Verify success */ 576 | else 577 | { 578 | DelLine(); 579 | 580 | /* Format the track again */ 581 | FDDFormat(); 582 | } 583 | } 584 | 585 | nHeadIndex++; 586 | } 587 | 588 | nHeadIndex = 0; 589 | } 590 | 591 | /* Skip creation of filesystem if any bad occurences were found during verify */ 592 | if (nBadsOccurence > 0) 593 | { 594 | printf("\n\nA total of %d tracks failed to format properly.", nBadsOccurence); 595 | 596 | if (nNoCreateFilesystem == 0) 597 | { 598 | printf("\nSkipping filesystem creation. To force it, run 8FORMAT without /V."); 599 | nNoCreateFilesystem = 1; 600 | } 601 | 602 | printf("\n"); 603 | } 604 | } 605 | 606 | DelLine(); 607 | 608 | /* Write FAT12 */ 609 | if (nNoCreateFilesystem == 0) 610 | { 611 | printf("Creating file system...\n"); 612 | WriteFAT12(); 613 | } 614 | 615 | printf("Finished.\n\n"); 616 | FDDHeadRetract(); 617 | 618 | /* Ask to update the BIOS INT 1Eh diskette parameter table */ 619 | printf("Apply a new BIOS Diskette Parameter Table (DPT) to reflect the new 8\" geometry?\n" 620 | "The BIOS/DOS will then try to use the new sector size, sector and track counts.\n" 621 | "Be warned that other floppy drives will not work until you reboot.\n" 622 | "Do not proceed if you run 8FORMAT from an another floppy drive.\n" 623 | "ENTER: continue, ESC: skip.\n"); 624 | 625 | /* Update BIOS floppy parameter table */ 626 | if (WaitEnterOrEscape() == 0x1C) 627 | { 628 | FDDWriteINT1Eh(NULL); 629 | } 630 | 631 | Quit(EXIT_SUCCESS); 632 | } -------------------------------------------------------------------------------- /8FORMAT.H: -------------------------------------------------------------------------------- 1 | #ifndef _8FORMAT_H_ 2 | #define _8FORMAT_H_ 3 | 4 | extern unsigned int nFDCBase; 5 | extern unsigned char nUseFM; 6 | extern unsigned char nFormatWithVerify; 7 | extern unsigned char nQuickFormat; 8 | extern unsigned char nNoCreateFilesystem; 9 | extern unsigned char nDriveNumber; 10 | extern unsigned char nTracks; 11 | extern unsigned char nHeads; 12 | extern unsigned int nPhysicalSectorSize; 13 | extern unsigned int nLogicalSectorSize; 14 | extern unsigned char nSectorsPerTrack; 15 | extern unsigned char nLogicalSectorsPerTrack; 16 | extern unsigned char nCustomGapLength; 17 | extern unsigned char nCustomGap3Length; 18 | extern unsigned char nOnlyReprogramBIOS; 19 | extern unsigned char nOnlyRecalibrateFDD; 20 | extern unsigned char nUseIRQ; 21 | extern unsigned char nUseDMA; 22 | extern unsigned char nLaunch8TSR; 23 | extern unsigned char sFormatType[5]; 24 | extern unsigned char nFormatByte; 25 | 26 | void Quit(int); 27 | unsigned char IsPCXT(); 28 | void DelLine(); 29 | void PrintSplash(); 30 | void ParseCommandLine(int, char*[]); 31 | void DoOperations(); 32 | 33 | #endif -------------------------------------------------------------------------------- /8TSR.C: -------------------------------------------------------------------------------- 1 | /* 8TSR resident stub for 8" drive support 2 | Compile for DOS (unsigned char: byte, unsigned int: word) 3 | SMALL memory model */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | /* Occupies interrupts INT 0E8h (TSR geometry modifier routine), 10 | INT 0E9h (original INT 08 - timer IRQ0), 11 | INT 0EAh (original INT 13h - disk services). */ 12 | 13 | /* From 8TSR.ASM */ 14 | extern void far DiskInterrupt(); /* new INT 013h */ 15 | extern void far TSRInterrupt(); /* new INT 0E8h */ 16 | 17 | /* Original vectors */ 18 | void interrupt (*oldINT08)(); 19 | void interrupt (*oldINT13)(); 20 | 21 | /* TSR EXE image size in bytes and PSP segment */ 22 | unsigned int nTSRImageSize; 23 | unsigned int nTSRPSP; 24 | 25 | /* Store installation check, TSR PSP and data segment */ 26 | unsigned long far* pInstallationCheck = (unsigned long far*)MK_FP(0, 0x4AC); 27 | unsigned int far* pTSRDataSegment = (unsigned int far*)MK_FP(0, 0x4B0); 28 | 29 | /* INT 1Eh Diskette parameter table (DPT) pointer */ 30 | unsigned int far* pDPTOffset = (unsigned int far*)MK_FP(0, 0x1E*4); 31 | unsigned int far* pDPTSegment = (unsigned int far*)MK_FP(0, 0x1E*4+2); 32 | 33 | /* Data from the DPT to be read and modified */ 34 | unsigned char far* pSectorSize = NULL; 35 | unsigned char far* pEOT = NULL; 36 | unsigned char far* pGapLength = NULL; 37 | unsigned char far* pDTL = NULL; 38 | unsigned char far* pGap3Length = NULL; 39 | unsigned char far* pSpecifyHUT = NULL; 40 | unsigned char far* pSpecifyHLT = NULL; 41 | unsigned char far* pSpecifyIdleTime = NULL; 42 | 43 | /* Data obtained from the command line 44 | Unfortunately, these must be uppercase as they are linked together with extrn 8TSR.ASM */ 45 | unsigned char NDRIVENUMBER; 46 | unsigned char NTRACKS; 47 | unsigned char NHEADS; 48 | 49 | /* New data obtained from the command line, to be updated in the DPT, also used in 8TSR.ASM */ 50 | unsigned char NNEWSECTORSIZE; 51 | unsigned char NNEWEOT; /* extrn for 8TSR, so uppercase */ 52 | unsigned char NNEWGAPLENGTH; 53 | unsigned char NNEWDTL; 54 | unsigned char NNEWGAP3LENGTH; 55 | 56 | /* Avoiding math.h, the maximum value obtained from here is 1024 anyway */ 57 | unsigned int pow2(unsigned char to) 58 | { 59 | unsigned int nResult = 2; 60 | unsigned char nIdx = 1; 61 | 62 | /* Skipping 2^0... :) */ 63 | for (; nIdx < to; nIdx++) 64 | { 65 | nResult *= 2; 66 | } 67 | 68 | return nResult; 69 | } 70 | 71 | /* Retrieve exact EXE image size. */ 72 | void GetEXEImageSize(const char* pPath) 73 | { 74 | FILE* pFile; 75 | 76 | if ((pFile = fopen(pPath, "rb")) == NULL) 77 | { 78 | printf("\n8TSR: Failed retrieving EXE image size. Aborting.\n"); 79 | exit(-1); 80 | } 81 | 82 | fseek(pFile, 0, SEEK_END); 83 | nTSRImageSize = (unsigned int)ftell(pFile); 84 | fclose(pFile); 85 | } 86 | 87 | /* Installation check. */ 88 | unsigned char InstallationCheck() 89 | { 90 | return *pInstallationCheck == 0x38545352; /* '8TSR' */ 91 | } 92 | 93 | /* Determine if we run from an A: or B: already */ 94 | unsigned char RunsFromFloppy() 95 | { 96 | return getdisk() < 2; 97 | } 98 | 99 | /* Update data in the resident part of the existing TSR and kill off this instance. */ 100 | void UpdateTSR() 101 | { 102 | /* All of these need to be on stack !!! */ 103 | unsigned int nMyDS = _DS; 104 | unsigned int nMyPSP = _psp; 105 | unsigned int nOldPSP; 106 | unsigned int nOldDS = *pTSRDataSegment; 107 | 108 | /* Not installed? Nothing to update. */ 109 | if (InstallationCheck() == 0) 110 | { 111 | return; 112 | } 113 | 114 | /* Disable IRQs */ 115 | asm cli 116 | 117 | /* Set DS of the resident part, and get its PSP. */ 118 | _DS = nOldDS; /* DS=old TSR DS */ 119 | nOldPSP = nTSRPSP; /* DS:nTSRPSP (we're not resident, ours is zero - uninitialized) */ 120 | _DS = nMyDS; /* DS=our data segment */ 121 | 122 | /* Switch current PSP to the (old) resident PSP */ 123 | asm mov ah,50h 124 | asm mov bx,[nOldPSP] 125 | asm int 21h 126 | 127 | /* Prepare arguments for INT E8h to update the data */ 128 | _AX = NTRACKS | ((unsigned int)NHEADS << 8); /* _AL = NTRACKS, _AH = NHEADS */ 129 | asm push ax /* The following assignments to registers destroy AX */ 130 | 131 | /* Variables obtained from the commandline, to be updated in the resident variant */ 132 | _BL = NNEWSECTORSIZE; 133 | _BH = NNEWEOT; 134 | _CL = NNEWGAPLENGTH; 135 | _CH = NNEWDTL; 136 | _DL = NNEWGAP3LENGTH; 137 | 138 | /* Update resident data */ 139 | asm pop ax 140 | asm int 0E8h 141 | 142 | /* Switch current PSP to the (old) resident PSP */ 143 | asm mov ah,50h 144 | asm mov bx,[nMyPSP] 145 | asm int 21h 146 | asm sti 147 | 148 | /* Resident data updated. Deallocate memory of this (nonresident) instance of 8TSR and quit. */ 149 | exit(0); 150 | } 151 | 152 | /* Determine where the DPT is located and copy it to a writable place in memory if need be. */ 153 | unsigned char RelocateDPT() 154 | { 155 | unsigned int nDPTSeg = *pDPTSegment; 156 | unsigned int nDPTOfs = *pDPTOffset; 157 | 158 | unsigned long nDPTLocation = ((unsigned long)nDPTSeg << 4) + nDPTOfs; 159 | if (nDPTLocation < 640L*1024L) 160 | { 161 | _fail: 162 | return 0; /* OK, under 640K (or memory allocation fail) */ 163 | } 164 | 165 | /* Needs relocation 166 | Allocate a DOS segment for a copy of the 11byte DPT: one 16byte paragraph */ 167 | asm mov ah,48h 168 | asm mov bx,1 169 | asm int 21h 170 | asm jc _fail 171 | 172 | /* Copy old DPT to the new memory location in AX:0000, and update nDPTSeg:nDPTOfs */ 173 | asm push ds 174 | asm push es 175 | asm push ax 176 | asm mov si,[nDPTOfs] 177 | asm mov ax,[nDPTSeg] 178 | asm mov ds,ax 179 | asm pop ax 180 | asm mov es,ax 181 | asm mov [nDPTSeg],ax 182 | asm xor di,di 183 | asm mov [nDPTOfs],di 184 | asm mov cx,0Bh 185 | asm rep movsb 186 | asm pop es 187 | asm pop ds 188 | 189 | /* Point to new memory location */ 190 | *pDPTSegment = nDPTSeg; /* AX from INT 21h */ 191 | *pDPTOffset = nDPTOfs; /* 0 */ 192 | 193 | return 1; 194 | } 195 | 196 | /* New INT 8 - timer interrupt. Just attempt to sync the DPT with what we have set fix. 197 | Regardless of whoever, or whatever, might have changed it... and then call it a day */ 198 | void interrupt TimerInterrupt() 199 | { 200 | asm cli 201 | *pSectorSize = NNEWSECTORSIZE; 202 | *pEOT = NNEWEOT; 203 | *pGapLength = NNEWGAPLENGTH; 204 | *pDTL = NNEWDTL; 205 | *pGap3Length = NNEWGAP3LENGTH; 206 | *pSpecifyHUT = 0xc7; 207 | *pSpecifyHLT = 0x7e; 208 | *pSpecifyIdleTime = 0xc8; 209 | asm sti 210 | 211 | /* Continue with the ISR cascade */ 212 | oldINT08(); 213 | } 214 | 215 | int main(int argc, char* argv[]) 216 | { 217 | unsigned char nRelocationResult = 0; 218 | 219 | /* Executed manually without proper arguments */ 220 | if (argc != 9) 221 | { 222 | printf("\nNot to be executed manually\n"); 223 | exit(-1); 224 | } 225 | 226 | /* Get EXE image size */ 227 | GetEXEImageSize(argv[0]); 228 | 229 | /* Clear the screen */ 230 | asm mov ax,3 231 | asm int 10h 232 | 233 | /* Did 8FORMAT run from a floppy? */ 234 | if (RunsFromFloppy() == 1) 235 | { 236 | printf("WARNING !\n" 237 | "8FORMAT ran from a floppy itself, and the choice was to update the BIOS DPT.\n" 238 | "Any subsequent 5.25\" or 3.5\" floppy access may fail, or cause data corruption.\n\n"); 239 | } 240 | 241 | /* Is the DPT somewhere over 640K (e.g. in BIOS)? */ 242 | nRelocationResult = RelocateDPT(); 243 | 244 | /* Obtain values from the command line 245 | 8TSR drivenumber tracks heads sectorspertrack sectorsize EOT RWgap DTL GAP3 */ 246 | NDRIVENUMBER = (unsigned char)atoi(argv[1]); 247 | NTRACKS = (unsigned char)atoi(argv[2]); 248 | NHEADS = (unsigned char)atoi(argv[3]); 249 | /* New values to be updated in the DPT */ 250 | NNEWSECTORSIZE = (unsigned char)atoi(argv[4]); 251 | NNEWEOT = (unsigned char)atoi(argv[5]); 252 | NNEWGAPLENGTH = (unsigned char)atoi(argv[6]); 253 | NNEWDTL = (unsigned char)atoi(argv[7]); 254 | NNEWGAP3LENGTH = (unsigned char)atoi(argv[8]); 255 | 256 | /* Write new data into the DPT table */ 257 | printf("New BIOS INT 1Eh diskette parameters table (DPT) for all drives:\n\n"); 258 | 259 | pSpecifyHUT = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+0); 260 | printf("Step rate time: was %u ms, now 8 ms\n", 32-(((unsigned int)(*pSpecifyHUT) >> 4) * 2)); 261 | printf("Head unload time: was %u ms, now 224 ms\n", ((unsigned int)(*pSpecifyHUT) & 0xf) * 32); 262 | *pSpecifyHUT = 0xc7; 263 | 264 | pSpecifyHLT = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+1); 265 | printf("Head load time: was %u ms, now 252 ms\n", ((unsigned int)(*pSpecifyHLT) >> 1) * 4); 266 | *pSpecifyHLT = 0x7e; 267 | 268 | pSpecifyIdleTime = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+2); 269 | printf("Maximum idle time: was %u ms, now 11000 ms\n", (unsigned int)(*pSpecifyIdleTime) * 55); 270 | *pSpecifyIdleTime = 0xc8; 271 | 272 | pSectorSize = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+3); 273 | printf("Sector size byte: was %u (%u bytes/sector)", *pSectorSize, pow2(7+(*pSectorSize))); 274 | *pSectorSize = NNEWSECTORSIZE; 275 | printf(", now %u (%u bytes/sector)\n", *pSectorSize, pow2(7+(*pSectorSize))); 276 | 277 | pEOT = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+4); 278 | printf("Sectors per track: was %u", *pEOT); 279 | *pEOT = NNEWEOT; 280 | printf(", now %u\n", *pEOT); 281 | 282 | pGapLength = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+5); 283 | printf("R/W gap length: was 0x%02X", *pGapLength); 284 | *pGapLength = NNEWGAPLENGTH; 285 | printf(", now 0x%02X\n", *pGapLength); 286 | 287 | pGap3Length = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+7); 288 | printf("Format gap length: was 0x%02X", *pGap3Length); 289 | *pGap3Length = NNEWGAP3LENGTH; 290 | printf(", now 0x%02X\n", *pGap3Length); 291 | 292 | pDTL = (unsigned char far*)MK_FP(*pDPTSegment, (*pDPTOffset)+6); 293 | printf("DTL (transfer len): was 0x%02X", *pDTL); 294 | *pDTL = NNEWDTL; 295 | printf(", now 0x%02X\n", *pDTL); 296 | 297 | /* Reset floppies */ 298 | asm mov cx,4 299 | blip: 300 | asm xor ax,ax 301 | asm mov dx,cx 302 | asm dec dx 303 | asm int 13h 304 | asm loop blip 305 | 306 | printf("\n8-inch drive mechanical parameters successfully applied.\n"); 307 | if (nRelocationResult == 1) 308 | { 309 | printf("NOTE: the original DPT was readonly - relocated to segment 0x%04X.\n", *pDPTSegment); 310 | } 311 | 312 | /* TSR already installed? Update the resident data then */ 313 | UpdateTSR(); 314 | 315 | /* No, not installed - establish and go resident */ 316 | asm cli 317 | 318 | /* Store old disk and timer handlers */ 319 | oldINT13 = getvect(0x13); 320 | oldINT08 = getvect(8); 321 | 322 | /* Install new interrupt handlers */ 323 | setvect(0xE9, oldINT08); 324 | setvect(0xEA, oldINT13); 325 | setvect(8, TimerInterrupt); 326 | /*setvect(0x13, DiskInterrupt); 327 | setvect(0xE8, TSRInterrupt); */ 328 | 329 | /* Compiler limitation: 330 | far DiskInterrupt() does not contain the "interrupt" keyword. Need to do my own setvect :) */ 331 | { 332 | unsigned int far* pSegment = (unsigned int far*)MK_FP(0, 0x13*4+2); 333 | unsigned int far* pOffset = (unsigned int far*)MK_FP(0, 0x13*4); 334 | 335 | *pSegment = FP_SEG(&DiskInterrupt); 336 | *pOffset = FP_OFF(&DiskInterrupt); 337 | } 338 | 339 | /* The same for TSRInterrupt (INT E8h) */ 340 | { 341 | unsigned int far* pSegment = (unsigned int far*)MK_FP(0, 0xE8*4+2); 342 | unsigned int far* pOffset = (unsigned int far*)MK_FP(0, 0xE8*4); 343 | 344 | *pSegment = FP_SEG(&TSRInterrupt); 345 | *pOffset = FP_OFF(&TSRInterrupt); 346 | } 347 | 348 | /* Mark as installed */ 349 | *pInstallationCheck = 0x38545352; /* ASCII '8TSR' */ 350 | *pTSRDataSegment = _DS; 351 | nTSRPSP = _psp; 352 | asm sti 353 | 354 | /* Terminate and stay resident, exitcode 0 + specify allocated resident memory space 355 | EXE file size in 16-byte DOS paragraphs. */ 356 | keep(0, (nTSRImageSize / 16) + 1); 357 | 358 | /* Won't get here :) */ 359 | return 0; 360 | } -------------------------------------------------------------------------------- /8TSR_A.ASM: -------------------------------------------------------------------------------- 1 | .8086 2 | .model small 3 | .code 4 | 5 | public _DiskInterrupt 6 | public _TSRInterrupt 7 | 8 | extrn _NTRACKS:byte:far 9 | extrn _NHEADS:byte:far 10 | extrn _NNEWSECTORSIZE:byte:far 11 | extrn _NNEWEOT:byte:far 12 | extrn _NNEWGAPLENGTH:byte:far 13 | extrn _NNEWDTL:byte:far 14 | extrn _NNEWGAP3LENGTH:byte:far 15 | 16 | ; Overriden AH=8 (Get drive parameters), 17 | ; 17h (Set disk type for format), 18 | ; 18h (Set media type for format) 19 | ; Only for floppies (DL=0 to 1 / 0 to 3) 20 | _DiskInterrupt proc far 21 | cli 22 | pushf 23 | cmp dl,3 24 | ja back 25 | cmp ah,8 26 | je get_drive_parameters 27 | cmp ah,17h 28 | je set_disk_format 29 | cmp ah,18h 30 | je set_media_format 31 | 32 | ; Call the old INT 13h handler if not any of these/not our drive 33 | back: 34 | popf 35 | sti 36 | int 0EAh 37 | 38 | ; Update FLAGS in the stack unwind and return back to caller 39 | back2: 40 | push bp 41 | mov bp,sp 42 | push ax 43 | lahf 44 | mov [bp+6],ah 45 | pop ax 46 | pop bp 47 | sti 48 | iret 49 | 50 | ; Fills CX and DX with tracks, sectors and sector size information 51 | ; New DPT in ES:DI 52 | get_drive_parameters: 53 | mov ch,[_NTRACKS] 54 | mov cl,[_NNEWEOT] 55 | mov dh,[_NHEADS] 56 | mov dl,1 57 | xor ax,ax 58 | xor bx,bx 59 | mov es,ax 60 | les di,[es:78h] 61 | popf 62 | clc 63 | jmp back2 64 | 65 | ; Does not do anything, just returns success 66 | set_disk_format: 67 | mov ah,0 68 | popf 69 | clc 70 | jmp back2 71 | 72 | ; Only return success and the new DPT in ES:DI 73 | set_media_format: 74 | xor ax,ax 75 | mov es,ax 76 | les di,[es:78h] 77 | popf 78 | clc 79 | jmp back2 80 | _DiskInterrupt endp 81 | 82 | 83 | 84 | ; INT E8h Update TSR resident data 85 | ; AX: tracks and heads, BX: sector size and spt, CX: gap length and DTL, DL: gap3 length 86 | _TSRInterrupt proc far 87 | cli 88 | mov [_NTRACKS],al 89 | mov [_NHEADS],ah 90 | mov [_NNEWSECTORSIZE],bl 91 | mov [_NNEWEOT],bh 92 | mov [_NNEWGAPLENGTH],cl 93 | mov [_NNEWDTL],ch 94 | mov [_NNEWGAP3LENGTH],dl 95 | sti 96 | iret 97 | _TSRInterrupt endp 98 | 99 | 100 | END -------------------------------------------------------------------------------- /FAT.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "isadma.h" 6 | #include "8format.h" 7 | 8 | /* JMP SHORT, nop and the BIOS parameter block. Drive geometry to be modified (5,25" 360K originally) */ 9 | const unsigned char sBIOSParameterBlock[54] = 10 | { 11 | 0xEB, 0x34, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x33, 0x2E, 0x33, 0x00, 0x02, 0x02, 0x01, 0x00, 12 | 0x02, 0x70, 0x00, 0xD0, 0x02, 0xFD, 0x02, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 14 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 15 | }; 16 | 17 | /* 128byte bootstrapper (74 bytes, excl. the BPB) + separate 384 byte bootcode */ 18 | unsigned char s128ByteBootstrapper[458] = 19 | { 20 | 0xFA, 0xFC, 0x31, 0xDB, 0x8E, 0xD3, 0xBC, 0x00, 0x7C, 0x0E, 0x1F, 0x1E, 0x07, 0xFB, 0xBE, 0x63, 21 | 0x7C, 0xB4, 0x0E, 0xAC, 0x3C, 0x55, 0x74, 0x08, 0x56, 0x55, 0xCD, 0x10, 0x5D, 0x5E, 0xEB, 0xF1, 22 | 0xB8, 0x03, 0x02, 0xB9, 0x02, 0x00, 0xBB, 0x80, 0x7C, 0xCD, 0x13, 0xEB, 0x1D, 0x38, 0x22, 0x20, 23 | 0x64, 0x69, 0x73, 0x6B, 0x3A, 0x20, 0x42, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 24 | 0x69, 0x6E, 0x67, 0x2E, 0x2E, 0x2E, 0x0D, 0x0A, 0x55, 0xAA, 0xFA, 0x31, 0xC0, 0x90, 0x88, 0x16, 25 | 0xD7, 0x7D, 0xBB, 0x78, 0x00, 0x36, 0xC5, 0x37, 0xBF, 0x2B, 0x7C, 0xB9, 0x0B, 0x00, 0xFC, 0xAC, 26 | 0x26, 0x80, 0x3D, 0x00, 0x74, 0x03, 0x26, 0x8A, 0x05, 0xAA, 0x88, 0xE0, 0xE2, 0xF1, 0x06, 0x1F, 27 | 0x89, 0x47, 0x02, 0xC7, 0x07, 0x2B, 0x7C, 0xFB, 0xCD, 0x13, 0x72, 0x6B, 0xA0, 0x10, 0x7C, 0x98, 28 | 0xF7, 0x26, 0x16, 0x7C, 0x03, 0x06, 0x1C, 0x7C, 0x03, 0x06, 0x0E, 0x7C, 0xA3, 0x3F, 0x7C, 0xA3, 29 | 0x37, 0x7C, 0xB8, 0x20, 0x00, 0xF7, 0x26, 0x11, 0x7C, 0x8B, 0x1E, 0x0B, 0x7C, 0x01, 0xD8, 0x48, 30 | 0xF7, 0xF3, 0x01, 0x06, 0x37, 0x7C, 0xBB, 0x00, 0x05, 0xA1, 0x3F, 0x7C, 0xE8, 0xA3, 0x00, 0xB8, 31 | 0x01, 0x02, 0xE8, 0xB7, 0x00, 0x72, 0x1D, 0x89, 0xDF, 0xB9, 0x0B, 0x00, 0xBE, 0xC1, 0x7D, 0xF3, 32 | 0xA6, 0x75, 0x11, 0x8D, 0x7F, 0x20, 0xEB, 0x02, 0x55, 0xAA, 0xBE, 0xCC, 0x7D, 0xB9, 0x0B, 0x00, 33 | 0xF3, 0xA6, 0x74, 0x18, 0xBE, 0xD8, 0x7D, 0xE8, 0x6A, 0x00, 0x30, 0xE4, 0xCD, 0x16, 0xEA, 0x00, 34 | 0xF0, 0xFF, 0xFF, 0x90, 0x90, 0x90, 0x90, 0xBE, 0xF1, 0x7D, 0xEB, 0xEB, 0xA1, 0x1C, 0x05, 0x31, 35 | 0xD2, 0xF7, 0x36, 0x0B, 0x7C, 0xFE, 0xC0, 0xA2, 0x3C, 0x7C, 0xA1, 0x37, 0x7C, 0xA3, 0x3D, 0x7C, 36 | 0xBB, 0x00, 0x07, 0xA1, 0x37, 0x7C, 0xE8, 0x49, 0x00, 0xA1, 0x18, 0x7C, 0x2A, 0x06, 0x3B, 0x7C, 37 | 0x40, 0x38, 0x06, 0x3C, 0x7C, 0x73, 0x03, 0xA0, 0x3C, 0x7C, 0x50, 0xE8, 0x4E, 0x00, 0x58, 0x72, 38 | 0xC6, 0x28, 0x06, 0x3C, 0x7C, 0x74, 0x0C, 0x01, 0x06, 0x37, 0x7C, 0xF7, 0x26, 0x0B, 0x7C, 0x01, 39 | 0xC3, 0xEB, 0xD0, 0x8A, 0x2E, 0x15, 0x7C, 0x8A, 0x16, 0xD7, 0x7D, 0x8B, 0x1E, 0x3D, 0x7C, 0xEA, 40 | 0x00, 0x00, 0x70, 0x00, 0xAC, 0x08, 0xC0, 0x74, 0x22, 0xB4, 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 41 | 0xEB, 0xF2, 0x31, 0xD2, 0xF7, 0x36, 0x18, 0x7C, 0xFE, 0xC2, 0x88, 0x16, 0x3B, 0x7C, 0x31, 0xD2, 42 | 0xF7, 0x36, 0x1A, 0x7C, 0x88, 0x16, 0x2A, 0x7C, 0xA3, 0x39, 0x7C, 0xC3, 0xB4, 0x02, 0x8B, 0x16, 43 | 0x39, 0x7C, 0xB1, 0x06, 0xD2, 0xE6, 0x0A, 0x36, 0x3B, 0x7C, 0x89, 0xD1, 0x86, 0xE9, 0x8A, 0x16, 44 | 0xD7, 0x7D, 0x8A, 0x36, 0x2A, 0x7C, 0xCD, 0x13, 0xC3, 0x90, 0x90, 0x49, 0x4F, 0x20, 0x20, 0x20, 45 | 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x53, 0x59, 46 | 0x53, 0x00, 0x0D, 0x0A, 0x49, 0x4F, 0x2F, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x2E, 0x53, 0x59, 0x53, 47 | 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x66, 0x6F, 0x75, 0x6E, 0x64, 0x00, 0x0D, 0x0A, 0x52, 0x65, 0x61, 48 | 0x64, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x00, 0x00, 0x55, 0xAA 49 | }; 50 | 51 | /* Regular "512byte"-compatible FAT12 boot code (to be put in a 512/1024B sector). Searches for IO.SYS/MSDOS.SYS */ 52 | const unsigned char sFAT12BootCode[439] = 53 | { 54 | 0xFA, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0x16, 0x07, 0xBB, 0x78, 0x00, 0x36, 0xC5, 0x37, 55 | 0x1E, 0x56, 0x16, 0x53, 0xBF, 0x2B, 0x7C, 0xB9, 0x0B, 0x00, 0xFC, 0xAC, 0x26, 0x80, 0x3D, 0x00, 56 | 0x74, 0x03, 0x26, 0x8A, 0x05, 0xAA, 0x8A, 0xC4, 0xE2, 0xF1, 0x06, 0x1F, 0x89, 0x47, 0x02, 0xC7, 57 | 0x07, 0x2B, 0x7C, 0xFB, 0xCD, 0x13, 0x72, 0x67, 0xA0, 0x10, 0x7C, 0x98, 0xF7, 0x26, 0x16, 0x7C, 58 | 0x03, 0x06, 0x1C, 0x7C, 0x03, 0x06, 0x0E, 0x7C, 0xA3, 0x3F, 0x7C, 0xA3, 0x37, 0x7C, 0xB8, 0x20, 59 | 0x00, 0xF7, 0x26, 0x11, 0x7C, 0x8B, 0x1E, 0x0B, 0x7C, 0x03, 0xC3, 0x48, 0xF7, 0xF3, 0x01, 0x06, 60 | 0x37, 0x7C, 0xBB, 0x00, 0x05, 0xA1, 0x3F, 0x7C, 0xE8, 0x9F, 0x00, 0xB8, 0x01, 0x02, 0xE8, 0xB3, 61 | 0x00, 0x72, 0x19, 0x8B, 0xFB, 0xB9, 0x0B, 0x00, 0xBE, 0xD6, 0x7D, 0xF3, 0xA6, 0x75, 0x0D, 0x8D, 62 | 0x7F, 0x20, 0xBE, 0xE1, 0x7D, 0xB9, 0x0B, 0x00, 0xF3, 0xA6, 0x74, 0x18, 0xBE, 0x77, 0x7D, 0xE8, 63 | 0x6A, 0x00, 0x32, 0xE4, 0xCD, 0x16, 0x5E, 0x1F, 0x8F, 0x04, 0x8F, 0x44, 0x02, 0xCD, 0x19, 0xBE, 64 | 0xC0, 0x7D, 0xEB, 0xEB, 0xA1, 0x1C, 0x05, 0x33, 0xD2, 0xF7, 0x36, 0x0B, 0x7C, 0xFE, 0xC0, 0xA2, 65 | 0x3C, 0x7C, 0xA1, 0x37, 0x7C, 0xA3, 0x3D, 0x7C, 0xBB, 0x00, 0x07, 0xA1, 0x37, 0x7C, 0xE8, 0x49, 66 | 0x00, 0xA1, 0x18, 0x7C, 0x2A, 0x06, 0x3B, 0x7C, 0x40, 0x38, 0x06, 0x3C, 0x7C, 0x73, 0x03, 0xA0, 67 | 0x3C, 0x7C, 0x50, 0xE8, 0x4E, 0x00, 0x58, 0x72, 0xC6, 0x28, 0x06, 0x3C, 0x7C, 0x74, 0x0C, 0x01, 68 | 0x06, 0x37, 0x7C, 0xF7, 0x26, 0x0B, 0x7C, 0x03, 0xD8, 0xEB, 0xD0, 0x8A, 0x2E, 0x15, 0x7C, 0x8A, 69 | 0x16, 0xFD, 0x7D, 0x8B, 0x1E, 0x3D, 0x7C, 0xEA, 0x00, 0x00, 0x70, 0x00, 0xAC, 0x0A, 0xC0, 0x74, 70 | 0x22, 0xB4, 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2, 0x33, 0xD2, 0xF7, 0x36, 0x18, 0x7C, 71 | 0xFE, 0xC2, 0x88, 0x16, 0x3B, 0x7C, 0x33, 0xD2, 0xF7, 0x36, 0x1A, 0x7C, 0x88, 0x16, 0x2A, 0x7C, 72 | 0xA3, 0x39, 0x7C, 0xC3, 0xB4, 0x02, 0x8B, 0x16, 0x39, 0x7C, 0xB1, 0x06, 0xD2, 0xE6, 0x0A, 0x36, 73 | 0x3B, 0x7C, 0x8B, 0xCA, 0x86, 0xE9, 0x8A, 0x16, 0xFD, 0x7D, 0x8A, 0x36, 0x2A, 0x7C, 0xCD, 0x13, 74 | 0xC3, 0x0D, 0x0A, 0x4E, 0x6F, 0x6E, 0x2D, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x20, 0x64, 0x69, 75 | 0x73, 0x6B, 0x20, 0x6F, 0x72, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 76 | 0x0D, 0x0A, 0x52, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x73, 0x74, 77 | 0x72, 0x69, 0x6B, 0x65, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x20, 0x77, 0x68, 0x65, 78 | 0x6E, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x44, 0x69, 0x73, 0x6B, 79 | 0x20, 0x42, 0x6F, 0x6F, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x75, 0x72, 0x65, 0x0D, 0x0A, 0x00, 80 | 0x49, 0x4F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4D, 0x53, 0x44, 0x4F, 0x53, 81 | 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x00 82 | }; 83 | 84 | /* To be used from the BPB, locally */ 85 | unsigned int nReservedSectors = 0; 86 | unsigned int nRootDirEntries = 0; 87 | unsigned int nSectorsPerFAT = 0; 88 | unsigned int nBytesPerCluster = 0; 89 | 90 | /* Total disk capacity and free space */ 91 | unsigned long nTotalDiskCapacity = 0; 92 | unsigned long nTotalDiskSpace = 0; 93 | 94 | /* FAT signature */ 95 | unsigned char sFATSignature[3] = 96 | { 97 | 0xFE, 0xFF, 0xFF 98 | }; 99 | 100 | void PrepareBPB() 101 | { 102 | /* Initialize pointers to values that need to be redefined */ 103 | unsigned int* pBytesPerSector = (unsigned int*)(&(pDMABuffer)[0x0b]); 104 | unsigned char* pSectorsPerCluster = (unsigned char*)(&(pDMABuffer)[0x0d]); 105 | unsigned int* pReservedSectors = (unsigned int*)(&(pDMABuffer)[0x0e]); 106 | unsigned int* pRootDirEntries = (unsigned int*)(&(pDMABuffer)[0x11]); 107 | unsigned int* pTotalSectors = (unsigned int*)(&(pDMABuffer)[0x13]); 108 | unsigned char* pMediaDescriptor = (unsigned char*)(&(pDMABuffer)[0x15]); 109 | unsigned int* pSectorsPerFAT = (unsigned int*)(&(pDMABuffer)[0x16]); 110 | unsigned int* pSectorsPerTrack = (unsigned int*)(&(pDMABuffer)[0x18]); 111 | unsigned int* pNumberOfHeads = (unsigned int*)(&(pDMABuffer)[0x1a]); 112 | 113 | /* Initialize with default values (5,25" 360K) and start defining the 8" geometry */ 114 | memcpy(pDMABuffer, sBIOSParameterBlock, sizeof(sBIOSParameterBlock)); 115 | 116 | /* Common values for 77-track 8"s */ 117 | *pNumberOfHeads = (unsigned int)nHeads; 118 | *pSectorsPerTrack = (unsigned int)nSectorsPerTrack; 119 | *pBytesPerSector = nLogicalSectorSize; 120 | *pTotalSectors = (unsigned int)nLogicalSectorsPerTrack * nHeads * (unsigned int)nTracks; 121 | 122 | /* 250kB SSSD (TYPE DSSD /1) */ 123 | if ((strcmp(sFormatType, "DSSD") == 0) && (nHeads == 1)) 124 | { 125 | *pSectorsPerCluster = 4; 126 | *pReservedSectors = 4; 127 | *pMediaDescriptor = 0xfe; 128 | *pSectorsPerFAT = 4; 129 | } 130 | 131 | /* 500kB TYPE DSSD */ 132 | else if ((strcmp(sFormatType, "DSSD") == 0) && (nHeads == 2)) 133 | { 134 | *pSectorsPerCluster = 4; 135 | *pReservedSectors = 4; 136 | *pMediaDescriptor = 0xfd; 137 | *pSectorsPerFAT = 4; 138 | } 139 | 140 | /* 1.2MB TYPE DSDD, DSDD /1 */ 141 | else if (strcmp(sFormatType, "DSDD") == 0) 142 | { 143 | *pSectorsPerCluster = 1; 144 | *pReservedSectors = 1; 145 | *pMediaDescriptor = 0xfe; 146 | *pSectorsPerFAT = 2; 147 | } 148 | 149 | /* 1.2MB EXT1, EXT1 /1, 1.3MB CRAM UNDOCUMENTED */ 150 | else if ((strcmp(sFormatType, "EXT1") == 0) || (strcmp(sFormatType, "CRAM") == 0)) 151 | { 152 | *pSectorsPerCluster = 1; 153 | *pReservedSectors = 1; 154 | *pMediaDescriptor = 0xf9; 155 | *pSectorsPerFAT = 8; 156 | } 157 | 158 | /* 1.0MB EXT2, EXT2 /1 */ 159 | else if (strcmp(sFormatType, "EXT2") == 0) 160 | { 161 | *pSectorsPerCluster = 2; 162 | *pReservedSectors = 2; 163 | *pMediaDescriptor = 0xf0; 164 | *pSectorsPerFAT = 4; 165 | } 166 | 167 | /* 693kB EXT3, EXT3 /1 */ 168 | else 169 | { 170 | *pSectorsPerCluster = 2; 171 | *pReservedSectors = 1; 172 | *pMediaDescriptor = 0xf9; 173 | *pSectorsPerFAT = 4; 174 | } 175 | 176 | /* Compute the maximum number of 32-byte root directory entries */ 177 | *pRootDirEntries = (nLogicalSectorsPerTrack - *pReservedSectors - *pSectorsPerFAT + 1) * nLogicalSectorSize / 32; 178 | 179 | nReservedSectors = *pReservedSectors; 180 | nRootDirEntries = *pRootDirEntries; 181 | nSectorsPerFAT = *pSectorsPerFAT; 182 | nBytesPerCluster = *pSectorsPerCluster * nLogicalSectorSize; 183 | nTotalDiskCapacity = (unsigned long)(*pTotalSectors) * nLogicalSectorSize; 184 | 185 | /* 0xf0 "Superfloppy" media descriptor if /512PH was used */ 186 | if (nPhysicalSectorSize != nLogicalSectorSize) 187 | { 188 | *pMediaDescriptor = 0xf0; 189 | } 190 | 191 | /* Change FAT ID to media descriptor from BPB */ 192 | sFATSignature[0] = *pMediaDescriptor; 193 | } 194 | 195 | void WriteBootCode() 196 | { 197 | /* Write a single 512/1024-byte bootsector, four 128-byte sectors or two 256-byte sectors. */ 198 | unsigned char* pAfterBPB = &(pDMABuffer)[0x36]; 199 | 200 | /* Prepare the BIOS parameter block (drive geometry) */ 201 | PrepareBPB(); 202 | 203 | /* 128B or 256B-per-sector floppy require a special "bootstrapper" 204 | This is pre-signed with 0xaa55 on the 127th and 255th bytes, to be able to be booted from. */ 205 | if (nPhysicalSectorSize < 512) 206 | { 207 | unsigned char nSectorIdx = 1; 208 | unsigned int nBytesWritten = 0; 209 | 210 | /* Experimental 256byte sector support */ 211 | if (nPhysicalSectorSize == 256) 212 | { 213 | /* Modify the boot code to load one sector to 7d00h 214 | defaultly: load three sectors to 7c80h (3x 128B) */ 215 | s128ByteBootstrapper[33] = 0x01; /* was 0x03 (INT 0x13, AH=02 Read Sectors, AL=1 instead of 3) */ 216 | s128ByteBootstrapper[40] = 0x7D; /* was 0x7c (Offset to load to: 0x7d00 instead of 0x7c80) */ 217 | s128ByteBootstrapper[39] = 0x00; /* was 0x80 */ 218 | } 219 | 220 | /* The BIOS only loads one sector CHS 0/0/1 upon boot... So load 3 more sectors (128B size) 221 | or 1 more sector (256B size), to get a complete 512B boot routine. */ 222 | memcpy(pAfterBPB, s128ByteBootstrapper, nPhysicalSectorSize-sizeof(sBIOSParameterBlock) /* 54 */); 223 | 224 | /* On track 0, head 0 */ 225 | FDDSeek(0, 0); 226 | 227 | for(;;) 228 | { 229 | /* Complete boot routine */ 230 | int nRemainingBytes; 231 | 232 | /* Write one 128B/256B sector */ 233 | FDDWrite(nSectorIdx++); 234 | 235 | /* Length of first part: 54 bytes BPB + the rest of the sector size */ 236 | if (nBytesWritten == 0) 237 | { 238 | nBytesWritten += nPhysicalSectorSize-sizeof(sBIOSParameterBlock); 239 | } 240 | else 241 | { 242 | nBytesWritten += nPhysicalSectorSize; /* 128 or 256 */ 243 | } 244 | 245 | nRemainingBytes = sizeof(s128ByteBootstrapper) - nBytesWritten; 246 | if (nRemainingBytes <= 0) 247 | { 248 | /* Done */ 249 | break; 250 | } 251 | 252 | /* Next 128/256byte part */ 253 | memcpy(pDMABuffer, 254 | &(s128ByteBootstrapper)[nBytesWritten], 255 | (nRemainingBytes > nPhysicalSectorSize) ? nPhysicalSectorSize : nRemainingBytes); 256 | } 257 | } 258 | 259 | /* Classic bootsector code (fits a >=512-byte sector) */ 260 | else 261 | { 262 | /* Needs to be signed manually at the end of the sector */ 263 | unsigned int* pSignatureBPB = (unsigned int*)(&(pDMABuffer)[nPhysicalSectorSize-2]); 264 | 265 | /* Copy classic bootsector */ 266 | memcpy(pAfterBPB, sFAT12BootCode, sizeof(sFAT12BootCode)); 267 | 268 | /* Bootable sector signature for BIOS */ 269 | *pSignatureBPB = 0xaa55; 270 | 271 | /* Write one sector on track 0, head 0, sector no: 1 */ 272 | FDDSeek(0, 0); 273 | FDDWrite(1); 274 | } 275 | } 276 | 277 | void WriteRootDir() 278 | { 279 | /* Start of the FAT region */ 280 | unsigned char nSectorIdx = (unsigned char)nReservedSectors + 1; 281 | 282 | unsigned int nReservedLength = 0; 283 | unsigned int nBytesWritten = 0; 284 | unsigned char nHead = 0; 285 | unsigned char nTrack = 0; 286 | 287 | /* Disk write buffer length: 2 FATs + root directory length. */ 288 | const unsigned int nBufferLen = (2 * nSectorsPerFAT * nLogicalSectorSize) + (nRootDirEntries * 32); 289 | 290 | /* Allocate disk write buffer */ 291 | unsigned char* pBuffer = (unsigned char*)calloc(nBufferLen, sizeof(unsigned char)); 292 | if (!pBuffer) 293 | { 294 | printf("\nMemory allocation error\n"); 295 | Quit(EXIT_FAILURE); 296 | } 297 | 298 | /* Physical sector size not equivalent to logical? This means that 512B physical sectors are forced. 299 | As such, set that the FAT region starts at sector 2 (one bootsector is reserved). */ 300 | if (nPhysicalSectorSize != nLogicalSectorSize) 301 | { 302 | nSectorIdx = 2; 303 | nReservedLength = 512; 304 | } 305 | else 306 | { 307 | /* Compute bootsector/reserved sectors length in bytes */ 308 | nReservedLength = (unsigned int)nReservedSectors * nLogicalSectorSize; 309 | } 310 | 311 | /* Sign two FATs */ 312 | memcpy(&pBuffer[0], sFATSignature, sizeof(sFATSignature)); 313 | memcpy(&pBuffer[nSectorsPerFAT*nLogicalSectorSize], sFATSignature, sizeof(sFATSignature)); 314 | 315 | /* Write logical format to disk */ 316 | while (nBytesWritten < nBufferLen) 317 | { 318 | /* Copy memory for the whole sector size, or just the remainder */ 319 | unsigned int nCopyCount = abs(nBytesWritten-nBufferLen); 320 | if (nCopyCount > nPhysicalSectorSize) 321 | { 322 | nCopyCount = nPhysicalSectorSize; 323 | } 324 | 325 | /* DMA buffer filled with format byte (to mark "unwritten" stuff, instead of zeros) */ 326 | memset(pDMABuffer, nFormatByte, 1024); 327 | 328 | /* Need to advance to the next head (or track) ? */ 329 | if (nSectorIdx > nSectorsPerTrack) 330 | { 331 | nSectorIdx = 1; 332 | nHead++; 333 | 334 | if (nHead > nHeads-1) 335 | { 336 | nHead = 0; 337 | FDDSeek(++nTrack, nHead); 338 | } 339 | else 340 | { 341 | FDDSeek(nTrack, nHead); 342 | } 343 | } 344 | 345 | /* Copy memory for writing */ 346 | memcpy(&pDMABuffer[0], &pBuffer[nBytesWritten], nCopyCount); 347 | 348 | /* Write FATs/root directory in nPhysicalSectorSize chunks */ 349 | FDDWrite(nSectorIdx++); 350 | nBytesWritten += nCopyCount; 351 | } 352 | 353 | free(pBuffer); 354 | 355 | /* Compute free disk space (total capacity minus reserved length/bootsector(s), FATs and root dir) */ 356 | nTotalDiskSpace = nTotalDiskCapacity - nReservedLength - nBytesWritten; 357 | } 358 | 359 | void DisplayDiskInformation() 360 | { 361 | /* FAT information */ 362 | printf("\nUsing a %uB logical sector size on %uB-sectored physical media (ID: 0x%02X)\n" 363 | "with 2 FATs, a FAT cluster size of %u bytes, %u reserved logical sector(s),\n" 364 | "%u logical sectors per FAT and %u root directory entries.\n", 365 | nLogicalSectorSize, nPhysicalSectorSize, sFATSignature[0], 366 | nBytesPerCluster, nReservedSectors, 367 | nSectorsPerFAT, nRootDirEntries); 368 | 369 | /* Disk space */ 370 | printf("\n %7lu bytes (%luK) total disk capacity,\n %7lu bytes (%luK) usable space.\n\n", 371 | nTotalDiskCapacity, nTotalDiskCapacity / 1024, nTotalDiskSpace, nTotalDiskSpace / 1024); 372 | } 373 | 374 | void WriteFAT12() 375 | { 376 | /* Clear the 8K DMA buffer */ 377 | memset(pDMABuffer, 0, 8*1024); 378 | 379 | /* Write FAT12 bootsector */ 380 | WriteBootCode(); 381 | 382 | /* Write an empty root directory */ 383 | WriteRootDir(); 384 | 385 | /* Inform about free space */ 386 | DisplayDiskInformation(); 387 | } -------------------------------------------------------------------------------- /FAT.H: -------------------------------------------------------------------------------- 1 | #ifndef _FAT_H_ 2 | #define _FAT_H_ 3 | 4 | extern void WriteFAT12(); 5 | 6 | #endif -------------------------------------------------------------------------------- /ISADMA.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "8format.h" 5 | #include "isadma.h" 6 | 7 | unsigned char* pDMABuffer = NULL; /* Public pointer to the 8K DMA buffer*/ 8 | unsigned char* pDMABufferPrivate = NULL; /* Our private pointer to an xK DMA buffer */ 9 | 10 | unsigned int nDMATransferLength = 0; /* length of transfer in bytes, minus one */ 11 | unsigned long nDMAAddress = 0; /* physical address */ 12 | 13 | void InitializeDMABuffer() 14 | { 15 | /* Allocate 8K DMA buffer */ 16 | unsigned int nSize = 7*1024; 17 | 18 | /* For computing physical address in the 16bit segmentation model */ 19 | unsigned long nDataSegment = (unsigned long)_DS << 4; 20 | 21 | /* Initialize 64K boundary aligned DMA buffer */ 22 | for(;;) 23 | { 24 | /* 1K increments */ 25 | nSize += 1024; 26 | 27 | /* Was the buffer already allocated beforehand? */ 28 | if (pDMABufferPrivate != NULL) 29 | { 30 | free(pDMABufferPrivate); 31 | } 32 | 33 | /* Allocate buffer */ 34 | pDMABufferPrivate = (unsigned char*)malloc(nSize); 35 | if ((pDMABufferPrivate == NULL) || (nSize == 64512)) 36 | { 37 | printf("\nNot enough memory or 64K-boundary problem\n"); 38 | Quit(EXIT_FAILURE); 39 | } 40 | 41 | /* Compute physical address of the buffer. 42 | Do this only once (the very first iteration), or if the address of the bigger buffer has changed. */ 43 | if (nDMAAddress != nDataSegment + (unsigned int)pDMABufferPrivate) 44 | { 45 | nDMAAddress = nDataSegment + (unsigned int)pDMABufferPrivate; 46 | } 47 | 48 | /* It is the 2nd iteration (or greater). 49 | The buffer grows in 1024B increments to overcome the boundary. Only the last 1024B are used. */ 50 | else 51 | { 52 | nDMAAddress += nSize-1024; 53 | } 54 | 55 | /* Check for 64K boundary crossing of the address alone && address+bufsize (1024B) */ 56 | if ((nDMAAddress >> 16) == ((nDMAAddress+1024) >> 16)) 57 | { 58 | /* It doesn't cross - we're all set. Set the public 8K DMA buffer pointer */ 59 | pDMABuffer = (unsigned char*)(nDMAAddress - nDataSegment); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | void FreeDMABuffer() 66 | { 67 | if(pDMABufferPrivate == NULL) 68 | { 69 | return; 70 | } 71 | 72 | free(pDMABufferPrivate); 73 | } 74 | 75 | void PrepareDMABufferForTransfer(unsigned char nToMemory, unsigned int nBytes) 76 | { 77 | /* nToMemory 1 to read from drive to memory, 0 to write from memory */ 78 | unsigned char nDMAMode = nToMemory ? 0x44 : 0x48; /* Without autoinit bit, to support old systems */ 79 | unsigned char nDMABase = nUseDMA * 2; /* DMA base register */ 80 | 81 | unsigned char nDMAPage; /* DMA page register */ 82 | switch(nUseDMA) 83 | { 84 | case 0: 85 | nDMAPage = 0x87; 86 | break; 87 | case 1: 88 | nDMAPage = 0x83; 89 | break; 90 | case 2: 91 | default: 92 | nDMAPage = 0x81; 93 | break; 94 | case 3: 95 | nDMAPage = 0x82; 96 | } 97 | 98 | nDMATransferLength = nBytes - 1; /* DMA transfer length (in bytes minus one) */ 99 | 100 | outportb(0x0a, nUseDMA | 4); /* Mask chosen channel */ 101 | 102 | /* Program the address, low 8bits, high 8bits and higher 4bit nibble into the page register (20bit) */ 103 | outportb(0x0c, 0xff); /* Reset flip-flop */ 104 | outportb(nDMABase, (unsigned char)nDMAAddress); 105 | outportb(nDMABase, (unsigned char)(nDMAAddress >> 8)); 106 | outportb(nDMAPage, (unsigned char)(nDMAAddress >> 16)); 107 | 108 | /* Program the count; low 8bits, high 8bits */ 109 | outportb(0x0c, 0xff); /* Reset flip-flop */ 110 | outportb(nDMABase+1, (unsigned char)nDMATransferLength); 111 | outportb(nDMABase+1, (unsigned char)(nDMATransferLength >> 8)); 112 | 113 | /* Program transfer mode for read/write */ 114 | outportb(0x0b, nUseDMA | nDMAMode); 115 | 116 | outportb(0x0a, nUseDMA); /* Unmask chosen channel */ 117 | } 118 | -------------------------------------------------------------------------------- /ISADMA.H: -------------------------------------------------------------------------------- 1 | #ifndef _ISADMA_H_ 2 | #define _ISADMA_H_ 3 | 4 | extern unsigned char* pDMABuffer; 5 | 6 | void InitializeDMABuffer(); 7 | void FreeDMABuffer(); 8 | void PrepareDMABufferForTransfer(unsigned char, unsigned int); 9 | 10 | #endif -------------------------------------------------------------------------------- /MAIN.C: -------------------------------------------------------------------------------- 1 | /* 8FORMAT (c) J. Bogin 2 | Compile for DOS (unsigned char: byte, unsigned int: word) */ 3 | 4 | #include 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | PrintSplash(); 9 | ParseCommandLine(argc, argv); 10 | 11 | DoOperations(); 12 | 13 | /* The program quits before here, but main() shall return something on paper */ 14 | return 0; 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 8FORMAT 2 | Format and use 8 inch floppy disks with a PC. 3 | 4 | # More information 5 | Check out my [website article](http://boginjr.com/it/sw/dev/8format/) to learn more! 6 | -------------------------------------------------------------------------------- /TESTREAD.C: -------------------------------------------------------------------------------- 1 | /* Read first 4 sectors of track 0 to check 8ISR applied sector size 2 | 1KB buffer filler byte 0xCC for unused/unread data 3 | Memory model: TINY */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | unsigned char nDriveNumber = 0; 12 | const unsigned char nSectorInfo[] = "\nTrack 0, sector %u likely contains %s"; 13 | 14 | void ReadSector(unsigned char nSector) 15 | { 16 | union REGS regs; 17 | struct SREGS sregs; 18 | FILE* pFile; 19 | char sFileName[12] = {0}; 20 | unsigned char* pBuffer = malloc(1024); 21 | 22 | if (!pBuffer) 23 | { 24 | printf("\nMemory allocation error\n"); 25 | exit(-1); 26 | } 27 | memset(pBuffer, 0xCC, 1024); 28 | 29 | regs.x.ax = 0; 30 | regs.h.dl = nDriveNumber; 31 | int86(0x13, ®s, ®s); 32 | 33 | regs.h.ah = 2; 34 | regs.h.al = 1; 35 | regs.h.ch = 0; 36 | regs.h.cl = nSector; 37 | regs.h.dh = 0; 38 | regs.h.dl = nDriveNumber; 39 | regs.x.bx = FP_OFF(pBuffer); 40 | sregs.es = FP_SEG(pBuffer); 41 | int86x(0x13, ®s, ®s, &sregs); 42 | 43 | if (regs.x.cflag > 0) 44 | { 45 | printf("\nINT 13h AH=02 Read sector %d failed for drive %u (%c:)", 46 | nSector, nDriveNumber, nDriveNumber+0x41); 47 | free(pBuffer); 48 | return; 49 | } 50 | 51 | /* Filler byte 0xCC for unread data */ 52 | sprintf(sFileName, "SECTOR%u.BIN", nSector); 53 | pFile = fopen(sFileName, "wb"); 54 | if (!pFile) 55 | { 56 | printf("\nCannot create file %s", sFileName); 57 | free(pBuffer); 58 | return; 59 | } 60 | 61 | if ((pBuffer[0] == 0xCC) && (pBuffer[1023] == 0xCC)) 62 | { 63 | printf(nSectorInfo, nSector, "no valid data"); 64 | } 65 | else if (pBuffer[128] == 0xCC) 66 | { 67 | printf(nSectorInfo, nSector, "a 128-byte sector"); 68 | printf(" (%s)", sFileName); 69 | fwrite(pBuffer, 128, 1, pFile); 70 | } 71 | else if (pBuffer[256] == 0xCC) 72 | { 73 | printf(nSectorInfo, nSector, "a 256-byte sector"); 74 | printf(" (%s)", sFileName); 75 | fwrite(pBuffer, 256, 1, pFile); 76 | } 77 | else if (pBuffer[512] == 0xCC) 78 | { 79 | printf(nSectorInfo, nSector, "a 512-byte sector"); 80 | printf(" (%s)", sFileName); 81 | fwrite(pBuffer, 512, 1, pFile); 82 | } 83 | else 84 | { 85 | printf(nSectorInfo, nSector, "a 1024-byte sector"); 86 | printf(" (%s)", sFileName); 87 | fwrite(pBuffer, 1024, 1, pFile); 88 | } 89 | 90 | fclose(pFile); 91 | free(pBuffer); 92 | } 93 | 94 | int main(int argc, char* argv[]) 95 | { 96 | const unsigned char* pArgument; 97 | if (argc != 2) 98 | { 99 | printf("\nSpecify floppy drive (A: to D:)\n"); 100 | return 0; 101 | } 102 | 103 | pArgument = strupr(argv[1]); 104 | if ((strlen(pArgument) > 2) || (pArgument[0] < 0x41) || (pArgument[0] > 0x44)) 105 | { 106 | printf("\nInvalid drive specified\n"); 107 | return -1; 108 | } 109 | 110 | nDriveNumber = pArgument[0] - 0x41; 111 | 112 | ReadSector(1); 113 | ReadSector(2); 114 | ReadSector(3); 115 | ReadSector(4); 116 | 117 | printf("\n"); 118 | return 0; 119 | } -------------------------------------------------------------------------------- /_BUILD.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM *** Assumes DOS Turbo C compiler in C:\TC *** 4 | REM *** Place Turbo Assembler (TASM) into %TCBIN% if it's not there *** 5 | REM *** For Turbo C 2.01, set TCBIN=%TC% *** 6 | 7 | set TC=C:\TC 8 | set TCBIN=%TC%\BIN 9 | set PATH=%TCBIN%;%PATH% 10 | 11 | if exist 8format.exe del 8format.exe 12 | if exist 8tsr.exe del 8tsr.exe 13 | if exist testread.com del testread.com 14 | del *.obj >nul 15 | 16 | TCC -I%TC%\INCLUDE -c -f- -1- -G -ms -K MAIN.C 8FLOPPY.C 8FORMAT.C ISADMA.C FAT.C 17 | TLINK /x %TC%\LIB\C0S.OBJ MAIN.OBJ 8FLOPPY.OBJ 8FORMAT.OBJ ISADMA.OBJ FAT.OBJ,8FORMAT.EXE,,%TC%\LIB\CS.LIB 18 | 19 | TCC -I%TC%\INCLUDE -c -f- -1- -G -mt -K TESTREAD.C 20 | TLINK /x /t %TC%\LIB\C0T.OBJ TESTREAD.OBJ,TESTREAD.COM,,%TC%\LIB\CS.LIB 21 | 22 | TASM 8TSR_A.ASM 23 | TCC -I%TC%\INCLUDE -c -f- -1- -G -ms -K 8TSR.C 24 | TLINK /x %TC%\LIB\C0S.OBJ 8TSR.OBJ 8TSR_A.OBJ,8TSR.EXE,,%TC%\LIB\CS.LIB 25 | 26 | del *.obj >nul 27 | del *.dsk >nul 28 | del *.map >nul --------------------------------------------------------------------------------