Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting large drive structure information in DOS 7.x

Tags:

c++

assembly

dos

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...

like image 254
user3399848 Avatar asked Sep 08 '14 06:09

user3399848


1 Answers

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.

like image 121
zmbq Avatar answered Nov 01 '22 11:11

zmbq