I wrote a directory information utility and (because I, and the people I wrote this for collect & use vintage hardware,) made it compatible with DOS and Windows 9x as well as Windows XP/Vista/7/8 64-bit (because we also use those.) The problem I ran into was with Windows 9x and FAT32 drives. I managed to get it to work as long as Windows 9x was actually loaded, but if I boot to command prompt only, or restart in MS-DOS mode I lose access to the Windows API that allowed me to get the large drive data and it defaulted back to the DOS routine I have. These are limited to the 2GB limit routines. Examining how the DOS 7.x programs (chkdsk mainly,) handle this (as they have no problem reporting the correct drive sizes,) it seems they use DOS interrupts (primarily INT 21h,) to do this. Thinking, no problem, I'll do a quick version check, and if it's DOS 7 or higher I'll just run a quick assembly routing to get the drive structure and calculate the total & free space that way. Only, the routine (though it doesn't return an error,) doesn't 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 A B C D E F\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;
}
That last bit was me trying to figure out what was going wrong. I was getting strange results, and couldn't figure out a pattern. Originally, I wasn't worried about clearing the buffer as the INT call should have been filling it with it's own values (except the first 2 bytes, which is supposed to be filled with the EDB data buffer size.) After getting so many apparently random results, I added in the loop at the beginning to fill the buffer with zeroes, then add in the buffer size. The results stopped being random at that point, they were invariably all zeroes, meaning the INT call isn't filling the buffer. With a variety of tests, I've confirmed that hes & hdi are correctly being assigned the segment and offset of the buffer address. I've also tried es & di to the pointer address instead of the buffer address. I didn't think it would work as everything I read said to set it to the address and not to a pointer, but I was trying everything I could think of. In all cases, the buffer isn't getting filled with anything.
As you can probably tell, this is just a test program I'm writing to figure out the exact procedure before adding it to my main program (which works just fine except for this one issue.) The FP_ lines are just macros that could be stated as (unsigned long)(x & 0xffff0000) >> 16 for the segment and (unsigned long)(x & 0x0000ffff) for the offset. Normally you would pass the pointer (&drivedata,) but drivedata is already a pointer.
The actual output:
Drive C has:
Total Clusters: 1
Free Clusters: 0
Sectors: 1
Bytes: 0
| 0 1 2 3 4 5 6 7 8 9 A B C D E F
---------------------------------------------------------------------
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'm locking the drive before and unlocking it after the call (though I'm not sure of the necessity.) How can I get this to work right? Alternately, is there a better way of getting the drive structure (clusters, sectors per cluster, bytes per sector,) than using INT 21h? Everything I find in searches only point me to Windows API functions, which the user won't have access to, if they do a boot to command prompt etc...
Wow, using DOS, that's old-school! Not as old-school as using punch cards, but still...
Apparently, FreeDOS has FAT 32 support. You might try to install it on those machines that don't even have Windows 95 installed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With