assemblyx86dosx86-16floppy

How to format a floppy diskette in DOS using 16-bit assembly?


I was studying x86 assembly programming for DOS in my old book, and among a discussion on interrupts, I saw the 13h one. The book said that I can use it to format a drive. But unfortunately, there's no more information provided on how to do it in my book.

I became very curious, and tried to use it to myself, but it isn't working. How can I format the floppy diskette, in drive A:, using 16-bit x86 assembly? Is it simple to do? I'm using TASM to compile and link, and running in MS-DOS.

Perhaps there is a way other than using int 13h?


Solution

  • int 13h is the appropriate way to do it, but rather than calling a DOS service, you're actually using it to call a ROM BIOS service. I don't think DOS provides any service to format a disk. An application program generally leaves such low-level manipulation of the FAT to the operating system, using only the OS-provided services to do high-level read/write operations.

    Specifically, int 13h, service 05h formats a disk track. (The service number goes in the AH register when you invoke the interrupt.) Note that this service formats a single track, not the entire disk at once. You have to call this service multiple times to format an entire disk. The nice thing about this is that it allows you to specify different characteristics for each track—and even each sector on a track (some old-school copy-protection schemes used this by creating tracks with oddball formatting).

    The parameters for service 05h are basically the same as those for every other disk read/write service, except that you don't need to specify a sector number (normally passed in CL), since you cannot format individual sectors. Here's a list of the required parameters for floppy-disk services:

    If the interrupt returns with the carry flag (CF) clear, then no error occurred and the AH register will contain 0. If CF is set, then an error occurred and the AH register contains the error code.

    In the words of Peter Norton, from The New Programmer's Guide to the IBM PC and PS/2:

    Every sector on a diskette track has 4 descriptive bytes associated with it. You specify these 4 bytes for each sector to be formatted by creating a table of 4-byte groups and passing the table's address in the register pair ES:BX. When you format a disk track, the 4-byte groups are written to the diskette immediately in front of the individual sectors in the track. The 4 bytes of data associated with a sector on the disk are known as address marks and are used by the disk controller to identify individual sectors during the read, write, and verify operations. The 4 bytes are referred to as C for cylinder, H for head, R for record (or sector number), and N for number of bytes per sector (also called the size code).

    When a sector is being read or written, the diskette controller searches the diskette track for the sector's ID, the essential part of which is R, the record or sector number. The cylinder and head parameters are not actually needed in this address mark because the read/write head is positioned mechanically at the proper track and the side is selected electronically, but they are recorded and tested as a safety check.

    The size code (N) can take on any one of the four standard values shown below:

      N  | Sector Size (bytes) | Sector Size (KB)
    –––––|–––––––––––––––––––––|–––––––––––––––––
      0  |         128         |      1/8
      1  |         256         |      1/4
      2  |         512         |      1/2
      3  |        1024         |       1
    

    The normal setting is code 2 (512 bytes).

    The complete process of formatting a diskette track is rather complex and involves slightly more than just calling service 05h. You need to do the following:

    1. Call service 17h to set the type of the diskette to be formatted. (This only needs to be done once, before beginning an operation.)

    2. Call service 18h to set the media type for the format.

    3. Create a table of address marks for the track to be formatted, in the manner described in the quotation above. There must be a 4-byte entry in the table for each sector.

    4. Finally, call service 05h to format the track.

    5. Optionally, follow up by calling service 04h to verify the formatting process. This verifies that the sector can be found and read, and that the cyclical redundancy check (CRC) is correct. DOS's format.com does this to verify each track after it is formatted, but disk drives are typically reliable enough that verification is not really necessary.

    All of these disk I/O services use the same parameters as were listed above, although, as with service 05h, some of them may be ignored. Search online for an interrupt guide to obtain more detailed information. For example, here is a complete list of ROM BIOS disk I/O services. And here is another. The aforequoted guide by Peter Norton is also excellent, if you can find an old copy lying around somewhere, like maybe Amazon?

    (Note that things are slightly different for formatting hard disks, and for the ESDI drives in PS/2s, you have to use an entirely different service for formatting—1Ah.)


    Update: It turns out that there may be a DOS API to do this, after all. Unfortunately, I don't know that it really makes things all that much simpler. The key is to use IOCTL.

    The IOCTL API is defined by DOS, but it is actually implemented/handled by device drivers, which means that support is determined by the driver vendor and version, rather than the version of DOS. If you're using a VM environment, it should support this, but I haven't actually tested it.

    DOS function 44h is device I/O control (IOCTL), so you set AH to 44h before calling INT 21h (a DOS interrupt).

    To format, you want IOCTL for block devices. The block IOCTL functions require at least DOS 3.2 or later (some require even higher versions). They allow not only accessing entire tracks at a time, but also support a formatting function. They're accessed using the subfunction 0Dh, so you'd set AL to 0Dh.

    Putting this together, you would simply set AX to 440Dh and call INT 21h. The minor code for the format function is 42h, which is placed in CL.

    In summary, the DOS block IOCTL function to format a track on a logical drive would be called as follows:

    If the carry flag is set when the function returns, then AX contains an error code.

    Unfortunately, I can't find any documentation for IoctlFmtVrfyTrackRec online, aside from this page. This stuff predated the web and very little has been uploaded there. :-( You really need a book like Advanced MS-DOS Programming, which I don't have a copy of, either.

    I did manage to turn up this document on Scribd, which claims to be an implementation of format using IOCTL, written by Pierre Desloover. I haven't tested it.