/*  :ts=8 bk=0 
 * Disk mapper.  Uses trackdisk.device to grab and read sector bitmap 
 * to discover what's allocated, then displays a (hopefully) pretty picture 
 * showing disk useage. 
 * 
 * Crufted together by Leo Schwab while desperately bored.  8606.8 
 * Turned into something working for the Manx compiler.  8607.23 
 */ 
 
#include <exec/types.h> 
#include <exec/memory.h> 
#include <intuition/intuition.h> 
#include <libraries/dos.h> 
#include <devices/trackdisk.h> 
 
#define REV             0L 
#define BLOCKSIZE       TD_SECTOR 
#define NUMBLOCKS       (NUMCYLS * NUMHEADS * NUMSECS) 
#define ROOTBLOCK       (NUMBLOCKS / 2) 
#define BITMAPINDEX     79 
#define NUMLONGS        (NUMBLOCKS / 32) 
#define XX              6L 
#define YY              6L 
#define XOFF            30L 
#define YOFF            25L 
#define BRKOVER         91L     /*  11 * YY + YOFF  */ 
#define SEP             6 
 
extern void     *OpenLibrary(), *OpenDevice(), *OpenWindow(), *AllocMem(), 
                *Lock(), *Info(), *CreatePort(), *CreateExtIO(); 
extern long     TextLength(); 
 
 
struct NewWindow windef = { 
        0, 0, 640, 180, 
        -1, -1, 
        CLOSEWINDOW, 
        WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SMART_REFRESH | ACTIVATE, 
        NULL, NULL, 
        (UBYTE *) "Disk Allocation Map ", 
        NULL, NULL, 0, 0, 0, 0, 
        WBENCHSCREEN 
}; 
 
struct IntuiText ok = { 
        AUTOFRONTPEN, AUTOBACKPEN, 
        AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, 
        AUTOITEXTFONT, 
        (UBYTE *) "OK", 
        AUTONEXTTEXT 
}; 
 
struct IntuiText errmsg = { 
        AUTOFRONTPEN, AUTOBACKPEN, 
        AUTODRAWMODE, AUTOLEFTEDGE, AUTOTOPEDGE, 
        AUTOITEXTFONT, 
        NULL,   /*  Gets filled in later  */ 
        AUTONEXTTEXT 
}; 
 
struct Window   *win; 
struct RastPort *rp; 
struct InfoData *id; 
struct IOExtTD  *diskreq; 
struct MsgPort  *diskport; 
ULONG           diskchangecount, lok, *diskbuffer; 
void            *IntuitionBase, *GfxBase; 
 
 
main (ac, av) 
char *av[]; 
{ 
        long k, x, y; 
        int unit, bmsect, free = NUMBLOCKS-2; 
        short i, n, l; 
        char buf[80]; 
 
        if (!ac) {      /*  Run from workbench (ick!)  */ 
                /*  I'll make this part better someday.....  */ 
                strcpy (buf, "df0:"); 
        } else {        /*  Run from CLI (thank you)  */ 
                if (ac == 1) { 
                        printf ("Drive specifier: "); 
                        gets (buf); 
                } else 
                        strcpy (buf, av[1]); 
        } 
 
        openstuff (); 
 
        if (!(lok = Lock (buf, ACCESS_READ))) 
                die ("Can't obtain lock for specified device."); 
 
        if (!(id = AllocMem ((long) sizeof (*id), MEMF_CLEAR))) 
                die ("Can't get InfoData memory."); 
 
        if (!Info (lok, id)) 
                die ("Call to Info() failed."); 
 
        if (id -> id_DiskType == ID_NO_DISK_PRESENT) 
                die ("No disk in drive."); 
 
        unit = id -> id_UnitNumber; 
        FreeMem (id, (long) sizeof (*id));  id = NULL; 
        opendisk (unit); 
        MotorOn (); 
        GetSector ((long) ROOTBLOCK); 
        bmsect = diskbuffer[BITMAPINDEX]; 
        GetSector ((long) bmsect); 
        MotorOff (); 
 
        /* 
         * At this point, we now have the bitmap in the disk buffer. 
         * This is what the first longword in the bitmap represents: 
         * 
         *      333322222222221111111111 
         *      32109876543210987654321098765432        Sector # 
         *      -------------------------------- 
         *      10100101001010010010010001010011        Longword 
         *      MSB                          LSB 
         * 
         * If the bit is set, the sector is free. 
         * 
         * Sectors 0 and 1 contain the boot block.  Thus, the DOS is not 
         * allowed to walk on them, and they are permanently allocated. 
         * However, the bitmap does not reflect this i.e. the bitmap starts 
         * at sector 2, which is indicated by bit 0, longword 1.  This means 
         * we have to offset everything by two.  Can you say "crock?" 
         * 
         * Actually, I suppose this is good, since it prevents some would-be 
         * smart person from un-allocating those sectors and messing things 
         * up badly. 
         * 
         * Bitmap starts at longword 1; longword 0 appears to be some kind 
         * of significant garbage (checksum?) 
         * 
         * For more info on how I found the bitmap sector, consult the DOS 
         * Technical Reference Manual, p. 1-1 - 1-2 
         */ 
 
        SetAPen (rp, 3L); 
        RectFill (rp, XOFF, YOFF, XX+XOFF, YY+YY+YOFF); /*  Show first two  */ 
        for (i=1; i<=NUMLONGS; i++) { 
                k = diskbuffer[i]; 
                for (n=0; n<32; n++) 
                        /*  Bits progress from low to high order  */ 
                        if (i<NUMLONGS || n<30) {       /*  Ignore last two  */ 
                                if (~k & 1) { 
                                        /*  Perform icky conversion  */ 
                                        free--; 
                                        l = (i-1 << 5) + n + 2; 
                                        x = (l / 22) * XX + XOFF; 
                                        y = (l % 22) * YY + YOFF; 
                                        if (y >= BRKOVER) 
                                                y += SEP; 
                                        RectFill (rp, x, y, x+XX-1, y+YY-1); 
                                } 
                                k >>= 1; 
                        } 
        } 
 
        /*  Draw grid so we can see  */ 
        SetAPen (rp, 1L); 
        for (x=XOFF; x<=80*XX+XOFF; x += XX) { 
                Move (rp, x, YOFF); 
                Draw (rp, x, YOFF+11*YY); 
                Move (rp, x, BRKOVER+SEP); 
                Draw (rp, x, BRKOVER+SEP+11*YY); 
        } 
        for (y=0; y<=11*YY; y += YY) { 
                Move (rp, XOFF, y+YOFF); 
                Draw (rp, XOFF+80*XX, y+YOFF); 
                Move (rp, XOFF, y+SEP+BRKOVER); 
                Draw (rp, XOFF+80*XX, y+SEP+BRKOVER); 
        } 
 
        /*  Draw map markings  */ 
        Move (rp, XOFF+XX/2, YOFF);       Draw (rp, XOFF+XX/2, YOFF-3); 
        Move (rp, XOFF+80*XX-XX/2, YOFF); Draw (rp, XOFF+80*XX-XX/2, YOFF-3); 
        Move (rp, XOFF, YOFF+YY/2);       Draw (rp, XOFF-3, YOFF+YY/2); 
        Move (rp, XOFF, YOFF+YY*10+YY/2); Draw (rp, XOFF-3, YOFF+YY*10+YY/2); 
        Move (rp, XOFF-1, YOFF-3);        Text (rp, "0", 1L); 
        Move (rp, XOFF+79*XX-1, YOFF-3);  Text (rp, "79", 2L); 
        Move (rp, XOFF-12, YOFF+6);       Text (rp, "0", 1L); 
        Move (rp, XOFF-20, YOFF+11*YY);   Text (rp, "10", 2L); 
 
        /*  Do labels  */ 
        sprintf (buf, "Bitmap on sector %d", bmsect); 
        Move (rp, XOFF, 176L); 
        Text (rp, buf, (long) strlen (buf)); 
        sprintf (buf, "Sectors free: %d", free); 
        Move (rp, 250L, 176L); 
        Text (rp, buf, (long) strlen (buf)); 
        sprintf (buf, "Allocated: %d", (int) NUMBLOCKS-free); 
        Move (rp, 450L, 176L); 
        Text (rp, buf, (long) strlen (buf)); 
        Move (rp, 520L, 60L); 
        Text (rp, "Surface 0", 9L); 
        Move (rp, 520L, 132L); 
        Text (rp, "Surface 1", 9L); 
 
        /*  Wait for close gadget  */ 
        Wait (1L << win -> UserPort -> mp_SigBit); 
        closestuff (); 
} 
 
 
openstuff () 
{ 
        if (!(IntuitionBase = OpenLibrary ("intuition.library", REV))) { 
                /* 
                 * If we can't open Intuition, then we can't use 
                 * AutoRequest () 
                 */ 
                printf ("Intuition failed; you'll have to use logic.\n"); 
                closestuff (); 
                exit (100); 
        } 
 
        if (!(GfxBase = OpenLibrary ("graphics.library", REV))) { 
                printf ("Art shop closed.\n"); 
                closestuff (); 
                exit (100); 
        } 
 
        if (!(win = OpenWindow (&windef))) { 
                printf ("Window painted shut.\n"); 
                closestuff (); 
                exit (100); 
        } 
        rp = win -> RPort; 
} 
 
opendisk (unit) 
int unit; 
{ 
        long err; 
        char *buf[80]; 
 
        if (!(diskport = CreatePort (NULL, NULL))) 
                die ("No port."); 
 
        if (!(diskreq = CreateExtIO (diskport, (long) sizeof (*diskreq)))) 
                die ("Can't make IO block."); 
 
        if (err = OpenDevice (TD_NAME, (long) unit, diskreq, NULL)) { 
                sprintf (buf, "Can't get at disk; err = %ld.", err); 
                DeleteExtIO (diskreq, (long) sizeof (*diskreq)); 
                diskreq = NULL; 
                die (buf); 
        } 
 
        if (!(diskbuffer = AllocMem (BLOCKSIZE, MEMF_CLEAR | MEMF_CHIP))) 
                die ("Can't allocate disk buffer."); 
 
        diskreq -> iotd_Req.io_Command = TD_CHANGENUM; 
        DoIO (diskreq); 
        diskchangecount = diskreq -> iotd_Req.io_Actual; 
} 
 
closestuff () 
{ 
        if (lok) 
                UnLock (lok); 
        if (diskreq) { 
                CloseDevice (diskreq); 
                DeleteExtIO (diskreq, (long) sizeof (*diskreq)); 
        } 
 
        if (diskbuffer) 
                FreeMem (diskbuffer, (long) BLOCKSIZE); 
        if (id) 
                FreeMem (id, (long) sizeof (*id)); 
        if (diskport) 
                DeletePort (diskport); 
        if (win) 
                CloseWindow (win); 
        if (GfxBase) 
                CloseLibrary (GfxBase); 
        if (IntuitionBase) 
                CloseLibrary (IntuitionBase); 
} 
 
die (str) 
UBYTE *str; 
{ 
        errmsg.IText = str; 
        AutoRequest (win, &errmsg, NULL, &ok, NULL, NULL, 
                     TextLength (rp, str, (long) strlen (str)) + 40, 46L); 
        closestuff (); 
        exit (100); 
} 
 
/* 
 * The following routines were stolen from an RKM example by Bob Peck and 
 * hacked up a bit. 
 */ 
 
GetSector (sector) 
long sector; 
{ 
        LONG offset = sector * BLOCKSIZE; 
 
        diskreq -> iotd_Req.io_Length = BLOCKSIZE;       
        diskreq -> iotd_Req.io_Data = (APTR) diskbuffer;  
                /* show where to put the data when read */ 
        diskreq -> iotd_Req.io_Command = ETD_READ; 
                /* check that disk not changed before reading */ 
        diskreq -> iotd_Count = diskchangecount; 
         
        /* convert from cylinder, head, sector to byte-offset value to get 
         * right one (as dos and everyone else sees it)...*/ 
         
        /* driver reads one CYLINDER at a time (head does not move for 
         * 22 sequential sector reads, or better-put, head doesnt move for 
         * 2 sequential full track reads.) 
         */ 
         
        diskreq -> iotd_Req.io_Offset = offset; 
        DoIO (diskreq); 
        return (0); 
} 
 
MotorOn()
{ 
        /* TURN ON DISK MOTOR ... old motor state is returned in io_Actual */ 
        diskreq -> iotd_Req.io_Length = 1; 
        /* this says motor is to be turned on */ 
        diskreq -> iotd_Req.io_Command = TD_MOTOR; 
        /* do something with the motor */ 
        DoIO (diskreq); 
        return (0); 
} 
 
MotorOff() 
{ 
        diskreq -> iotd_Req.io_Length = 0;       
        /* says that motor is to be turned on */ 
        diskreq -> iotd_Req.io_Command = TD_MOTOR;       
        /* do something with the motor */ 
        DoIO (diskreq); 
        return (0); 
} 
