/*
DPBTEST.C -- uses undocumented INT 21h Function 32h (Get DPB)
    to display bytes per drive; but first walks the DPB chain, 
    showing the difference between the two access methods
*/

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>

#pragma pack(1)

typedef unsigned char BYTE;

typedef struct dpb {
    BYTE drive;
    BYTE unit;
    unsigned bytes_per_sect;
    BYTE sectors_per_cluster;       // plus 1
    BYTE shift;                     // for sectors per cluster
    unsigned boot_sectors;          
    BYTE copies_fat;
    unsigned max_root_dir;
    unsigned first_data_sector;
    unsigned highest_cluster;
    union {
        struct {
            unsigned char sectors_per_fat;
            unsigned first_dir_sector;
            void far *device_driver;
            BYTE media_descriptor;
            BYTE access_flag;
            struct dpb far *next;
            unsigned long reserved;
            } dos3;
        struct {
            unsigned sectors_per_fat;       // WORD, not BYTE!
            unsigned first_dir_sector;
            void far *device_driver;
            BYTE media_descriptor;
            BYTE access_flag;
            struct dpb far *next;
            unsigned long reserved;
            } dos4;
        } vers;
    } DPB;

#ifndef MK_FP
#define MK_FP(seg,ofs) \
    ((void far *)(((unsigned long)(seg) << 16) | (ofs)))
#endif

void fail(char *s) { puts(s); exit(1); }

void display(DPB far *dpb)
{
    unsigned long bytes_per_clust = 
        dpb->bytes_per_sect * (dpb->sectors_per_cluster + 1);

    printf("Drive %c: ", 'A' + dpb->drive);
    printf("%u bytes/sector * ", dpb->bytes_per_sect);
    printf("%u sectors/cluster = \n", 
        dpb->sectors_per_cluster + 1);
    printf("         %lu bytes/cluster * ", bytes_per_clust);
    printf("%u clusters = ", dpb->highest_cluster - 1);
    printf("%lu bytes\n\n",
        bytes_per_clust * (dpb->highest_cluster - 1));
}

main()
{
    DPB far *dpb;
    union REGS r;
    struct SREGS s;
    
    /* floppy = single disk drive logical drive indicator 0=a 1=b */
    unsigned char far *pfloppy = (BYTE far *) 0x504L;
    
    int i;
    
#ifdef __TURBOC__
    unsigned lastdrive = setdisk(getdisk());
#else
    unsigned lastdrive;
    unsigned curdrv;
    _dos_getdrive(&curdrv);
    _dos_setdrive(curdrv, &lastdrive);
#endif

    puts("Using DPB linked list");
    
    s.es = r.x.bx = 0;
    r.h.ah = 0x52;
    intdosx(&r, &r, &s);
    /* pointer to first DPB at offset 0h in List of Lists */
    if (! (dpb = *((void far * far *) MK_FP(s.es, r.x.bx))))
        return 1;
    do {
        /* skip either drive A: or drive B: */
        if (((*pfloppy == 1) && (dpb->drive != 0)) ||
            ((*pfloppy == 0) && (dpb->drive != 1)))
                display(dpb);
        if (_osmajor < 4) 
            dpb = dpb->vers.dos3.next;
        else
            dpb = dpb->vers.dos4.next;
    } while (FP_OFF(dpb) != -1);

    
    puts("Using INT 21h Function 32h");
    
    segread(&s);
    for (i=1; i<=lastdrive; i++)
    {
        /* skip either drive A: or drive B: */
        if ((*pfloppy == 1) && (i == 1)) continue;
        else if ((*pfloppy == 0) && (i == 2)) continue;

        r.h.ah = 0x32;
        r.h.dl = i;
        intdosx(&r, &r, &s);
        if (r.h.al != 0xFF)
            display((DPB far *) MK_FP(s.ds, r.x.bx));
    }

    return 0;
}
