1

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?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
MendaxRox
  • 101
  • 1
  • 7
  • 2
    To help prevent the question from being closed, please edit in the code that you wrote and tried, but isn't working. – Cody Gray - on strike Jan 05 '17 at 16:16
  • Fixed, thanks. @Joshua I do wanna learn assembly, I love to program. – MendaxRox Jan 05 '17 at 16:31
  • @MendaxRox: Start with an easier problem. Formatting floppies is hard in any language. – Joshua Jan 05 '17 at 16:35
  • 1
    Formatting is a bit ambiguous here: Do you mean low-level formatting or you just want to write a FS on the floppy? – Margaret Bloom Jan 05 '17 at 16:38
  • Back in the time when I was doing such things, the ultimate reference was the [Ralf Brown's Interrupt List](http://www.cs.cmu.edu/~ralf/files.html). I don't know how well it is maintained in the last 15 years or so since I last looked at it. – AProgrammer Jan 05 '17 at 16:40
  • Do a web search for BIOS call descriptions. Use INT 13H, AH = 17H to select floppy type, then INT 13H, AH = 05H to format a track. I have old code to format a floppy disk (to get a custom sector interleave factor), but it's a combination of assembly and C. – rcgldr Jan 05 '17 at 16:43
  • I mean low-level format, write 00h in all sectors. Oh my, I thought that format something would very simples, just put the letter of drive and a loop writing zeros on it... – MendaxRox Jan 05 '17 at 18:09
  • 2
    @MendaxRox lol ... almost nothing from that era works like this. Usually all the HW-design related things are soaking into SW design (to keep things simple on the controller/API side), so many of the "why did they do it like this" would be obvious if you consider the HW design itself, but when you see only SW side, it's often on the verge of anti-logic. It was (correctly) assumed, it will be much easier+cheaper to resolve any unfortunate complexity on the code side, than adding further wires/chips/memory cells. On other machines you had to turn on/off the motors and calculate timing of R/W. :) – Ped7g Jan 05 '17 at 18:16
  • You can still do the "turn on/off the motors and calculate the timing of R/W" dance on the IBM PC. There are even lower level APIs that control the physical disk drive directly. By calling `int 13h`, which is the BIOS, you're actually doing a higher level programming! :-) But yeah, formatting is a lot more complicated than writing zeroes. You could trivially write all zeroes by doing a simple write operation. The tricky part is initializing the sectors and tracks so that the system can read it. You then need to initialize a file system (FAT), and possibly also copy system files. Not simple! – Cody Gray - on strike Jan 06 '17 at 02:05
  • @Ped7g oh how often did the hardware guys say "we can let the software deal with that" and go off for a beer? – Weather Vane Jan 06 '17 at 20:44

1 Answers1

9

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:

  • drive number: DL
  • head number: DH
  • cylinder number: CH
  • sector number: CL (not used for format!)
  • number of sectors: AL
  • address of buffer: ES:BX

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:

  • AX == 440Dh
  • CL == 42h
  • CH == 08h (block device category)
  • BX == drive number (0 = default, 1 = A:, 2 = B:, etc.)
  • DS:DX == address of an IoctlFmtVrfyTrackRec structure that indicates the head and cylinder number to be formatted

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.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • There's some place on Internet that can I find an example of a program that formats diskette? I'm to new on Assembly, I read all it but I can't understand :(. Also english isn't my first language, so it's more hard yet. Thank you... – MendaxRox Jan 05 '17 at 17:13
  • 1
    I looked briefly using Google, but I didn't see any examples of pre-written code. Should be pretty straightforward code, setting up the parameters in the registers and calling the interrupt. The tricky part would be setting up the table. I don't have time at the moment to write up the code, and I don't have a VM that I can test it on. – Cody Gray - on strike Jan 05 '17 at 17:15
  • I took a look to the source of format.com from MS-DOS. HOW CAN FORMAT HD AND DISKETTE WITHOUT USING A SINGLE INTERRUPTION? :o – MendaxRox Jan 05 '17 at 17:44
  • I believe format.com actually delegates to lower level APIs to do the work. At least, that's how it worked back in DOS 2 (which, granted, isn't even modern by DOS standards), but that's the only one I've seen the source code for (MS released it for public viewing a few years back). I haven't disassembled any more recent versions to see what's changed. In DOS 2, format.com invokes functionality that was provided by the OEM system vendor. I'm guessing that's changed in later versions, as Microsoft took firmer control over the source code. I'm betting there's an interrupt in there somewhere... – Cody Gray - on strike Jan 06 '17 at 02:06
  • Here's [Ralph Brown's Interrupt List](http://www.ctyme.com/rbrown.htm). It's a seriously gigantic list of functions he collected over years. You might need it. – z0rberg's Jan 06 '17 at 10:50