Getting drive structure information in DOS 7.x

I wrote service information for the reference book and (because I and the people I wrote for collecting and using vintage equipment) made it compatible with DOS and Windows 9x, as well as with 64-bit Windows XP / Vista / 7/8 (because we also use them.) The problem I encountered was with Windows 9x and FAT32 disks. I managed to get it working until Windows 9x actually booted up, but if I load only the command line or reboot in MS-DOS mode, I will lose access to the Windows API, which allowed me to get data about a large disk, and this is the default returned to the usual DOS procedure. They are limited to 2 GB restrictive procedures. Studying how DOS 7.x programs are handled (mostly chkdsk), (since they have no problem reporting the correct disk sizes), it seems like they use DOS interrupts (mostly INT 21h) to do this. I think it's not a problem, I will do a quick version check, and if it's DOS 7 or higher, I just run a quick build to get the drive structure and calculate the total and free space this way. Only, the procedure (although it does not return an error) does not fill my buffer with anything.

Here is the code:

#include <stdio.h> #include <dos.h> void main(void) { unsigned short hes,hdi,sectors,bytes; unsigned long tclusters,fclusters; unsigned char far *drivedata; char test = '\0'; char display[17] = "0123456789ABCDEF"; int count; drivedata = new unsigned char [63]; for (count = 0; count < 63; count++) drivedata[count] = '\0'; drivedata[0] = '\x3d'; drivedata[1] = '\x00'; hes = FP_SEG(drivedata); hdi = FP_OFF(drivedata); asm { push ax push es push di push ds push dx push cx mov ax,0x440d mov bx,0x0003 mov cx,0x484a int 21h jnc _GOOD mov ax,0x7302 mov es,[hes] mov di,[hdi] mov dx,0x0003 mov cx,0x003f int 21h jnc _GOOD } test = '\1'; _GOOD: asm { mov ax,0x440d mov bl,0x03 mov cx,0x486a int 21h pop cx pop dx pop ds pop di pop es pop ax } if (test == '\1') { printf("There was an error.\r\n"); return; } tclusters = (unsigned long) drivedata[48]; tclusters = (tclusters * 256) + (unsigned long)drivedata[47]; tclusters = (tclusters * 256) + (unsigned long)drivedata[46]; tclusters = (tclusters * 256) + (unsigned long)drivedata[45]; ++tclusters; fclusters = (unsigned long)drivedata[36]; fclusters = (fclusters * 256) + (unsigned long)drivedata[35]; fclusters = (fclusters * 256) + (unsigned long)drivedata[34]; fclusters = (fclusters * 257) + (unsigned long)drivedata[33]; bytes = (unsigned int)drivedata[5]; bytes = (bytes * 256) + (unsigned int)drivedata[4]; sectors = (unsigned long)drivedata[6]; ++sectors; printf("Drive C has:\r\n"); printf(" Total Clusters: %u\r\n",tclusters); printf(" Free Clusters: %u\r\n",fclusters); printf(" Sectors: %u\r\n",sectors); printf(" Bytes: %u\r\n",bytes); printf("\r\n"); printf(" | 0 1 2 3 4 5 6 7 8 9 ABCDEF\r\n"); printf("---------------------------------------------------------------------"); for (count = 0; count < 63; count++) { if ((count % 16) == 0) printf("\r\n %c | ",display[(count / 16)]); printf("%03u ",drivedata[count]); } printf("\r\n"); return; } 

This is the last bit that I tried to figure out what was going wrong. I got strange results and could not understand the pattern. Initially, I was not worried about flushing the buffer, since the INT call should fill it with its own values ​​(except for the first 2 bytes, which should be filled with the size of the EDB data buffer.) After receiving so many, obviously random results, I added in the loop at the beginning to fill the buffer with zeros, and then add the buffer size. The results ceased to be random at this point, they were always zero, which means that the INT call does not fill the buffer. In various tests, I confirmed that hes and hdi are correctly assigned a segment and a buffer address offset. I also tried using es and di for the address of the pointer instead of the buffer address. I did not think that this would work, because everything I read said to set it to the address, not the pointer, but I tried everything I could think of. In all cases, the buffer is not filled with anything.

As you can probably tell, this is just a test program that I write to figure out the exact procedure before adding it to my main program (which works fine except for this one problem.) FP_ lines are just macros that can be specified as (unsigned long) (x and 0xffff0000) β†’ 16 for the segment and (unsigned long) (x and 0x0000ffff) for the offset. You should usually pass a pointer (& drivedata), but drivedata is already a pointer.

Actual conclusion:

 Drive C has: Total Clusters: 1 Free Clusters: 0 Sectors: 1 Bytes: 0 | 0 1 2 3 4 5 6 7 8 9 ABCDEF --------------------------------------------------------------------- 0 | 061 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 1 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 2 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 3 | 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 

So what am I missing? Like chkdsk, I lock the drive before and unlock it after the call (although I'm not sure if necessary.) How can I make this work correctly? Alternatively, is there a better way to get the drive structure (clusters, sectors per cluster, bytes per sector) than using INT 21h? Everything that I find in searches only indicates functions of the Windows API that the user will not have access to if they load on the command line, etc.

+5
source share
2 answers

Wow, using DOS, this old school! Not like an old school, like using punch cards, but still ...

Apparently, FreeDOS has FAT 32 support , you can try to install it on those machines that don’t even have Windows 95 installed.

+1
source

For your vintage hobby, you should equip yourself with the LBA and FAT32 specifications. Wikipedia: The file distribution table seems to be a good reference.

One thing you may find is that these legacy systems (and the software written for them) cannot gracefully handle large disks (disk size> 2 ^ (32-1) ).

Other materials that I think will also be very important:

The "best way" that should work for you in all cases is to use BIOS calls to find out all the basics, and then replicate the algorithms to calculate sizes, etc. in your own code. In the old days, non-Microsoft DOS APIs were not reusable. Programs that were supposed to do advanced things needed to know how to do this on their own.

+1
source

Source: https://habr.com/ru/post/1202047/


All Articles