[net.micro.amiga] Disk Sector Mapper

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (07/24/86)

[ This line eating food contains no articifial colors or preservatives. ]

	I can't believe I'm the first person to write this.  At least I
think I am.  If I'm wrong, let me know.

	This little crock displays the sector allocation on your floppy
disks.  If the box is colored in, the sector is allocated.  I cooked this up
in roughly a couple of evenings.

	This was written for the Manx compiler.  Conversion to Lettuce
should be trivial.  To make this beastie, you say:

1> cc dm.c
1> ln dm.o -lc -o dm

	Naturally, if you keep lots of stuff in RAM:, you'll do this
differently.

	The DOS manuals give *no* explanation on how the disk sector bitmap
works.  None.  Nada.  Zero.  Zilch.  Zippo.  NULL.  (read my lips: none!)
So I pulled out 'DiskEd' and stared at the raw bitmap in hex for a while
until I saw what was going on.  I've included a very brief comment on how it
works in the code.

	If someone at C-A sees this, could you please tell me if I got this
right?  Like I said, the manuals were no help at all.

	Have fun with your new crock.

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 ________                ___
           \            /___--__                Leo L. Schwab
  ___  ___ /\               ---##\              ihnp4!ptsfa!well!ewhac
      /   X  \_____    |  __ _---))                      ..or..
     /   /_\--    -----+==____\ // \  _         well ---\
___ (   o---+------------------O/   \/ \        dual ----> !unicom!ewhac
     \     /                ___ \_  (`o )       hplabs -/       ("AE-wack")
 ____ \___/                          \_/
              Recumbent Bikes:                  "Work FOR?  I don't work FOR
            The _O_n_l_y Way To Fly!                anybody!  I'm just having fun."

## This is not a shell archive, so you can cut anywhere you damn well please! #
## Although cutting here would be a good idea. ##
/*  :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;      
	iskreq -> 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);
}