/*======================================================================*/
/*	EXPAND2.C	- Mid level routines for EXPAND, where the work */
/*			  gets done.					*/
/*======================================================================*/

#include "expand.os"
#include "switch.os"
#include "hardio.h"
#include "expand.h"
#include "expand2.h"


/*======================================================================*/
/*			CHANGE HISTORY					*/
/*======================================================================*/
/* 0.0a	- 12/29/83 -	Initially written			  - jjc	*/
/*======================================================================*/


#define	Banner2	"EXPAND2   version 0.0e   02/09/84 - 10:00"	

/*page*/
/*======================================================*/
/*	Things related to disk and partition size	*/
/*======================================================*/
#define DspCnt	"      Tracks left to move"

#define	KPerTracks	16

#define PartDelete(p)	(p->Part_Name [0] == 'd')
#define PartExists(p)	(p->size != 0 && p->size != 0xE5)
#define	PartSize(p)	(sizeinK [(p)->size])
#define SizeName(s)	(sizename [(s)])
/* indexed by DAT size byte */
#define SIZEMAX		7
int	sizeinK [SIZEMAX] = {
	0,
	256,
	2 * 256,
	4 * 256,
	8 * 256,
	16 * 256,
	32 * 256
	};

CHAR	*sizename [SIZEMAX] = {
	"Unknown",
	"256K",
	"512K",
	"1M",
	"2M",
	"4M",
	"8M"
	};


#define		MAXvol		4	/* Only 4 volumes allowed.	     */
#define		UNUSED22_31size	10	/* Unused bytes in vol_info struct.  */
#define		VOLlabelSIZE	10	/* 10 chars in volume label.	     */
#define		MAXhdTypes	10	/* 10 types of hard disks allowed.   */
#define		BADtrackSPACE	2	/* Two tracks reserved to reallocate
					  space lost by finding bad sectors. */
#define		HDnameLEN	35	/* Length of hard disk type descript.*/
#define		FIRST8or14disk	5	/* Sixth entry into hd_types is the

/*...........................................................................*/

/* Each hard disk can be described by the following fields of hd_description.*/

struct hd_description
{
	CHAR	diameter;
	int	size_in_Kbytes;
	CHAR	platters;
	CHAR	RW_heads;
	CHAR	tracks;
	CHAR	sect_per_track;
	char	name[HDnameLEN];
};


/*...........................................................................*/

struct vol_info
{
	CHAR	vol_present;
	CHAR	cur_track;
	CHAR	tracks;
	CHAR	sect_per_track;
	CHAR	head_mask;
	int	addr_part_offset_table;
	int	addr_bad_sector_table;
	int	addr_dirty_bad_sector_flag;
	CHAR	open_error;
	char	vol_label[VOLlabelSIZE];
	char	unused22_31[UNUSED22_31size];
};

/*...........................................................................*/

/* DMS hard disk information returned by hard disk controller call 
    "get volume information" = 18h = 24 decimal.			*/

struct hd_stat_info
{
	CHAR	command_echo;
	CHAR	vers_rom;
	CHAR	rev_rom;
	CHAR	vers_firmware;
	CHAR	rev_firmware;
	CHAR	Xebec;
	CHAR	unused6;
	CHAR	status;
	struct vol_info		volume[MAXvol];
};

/*...........................................................................*/
/* NOTE: Keep the first five entries (all the 5.25 drives) in this order
  because index into hd_types will match code returned by hard disk 
  controller - 1 (in 'head mask' byte).
   i.e.:
	hard disk type = hd_types[ head_mask - 1 ]
*/

struct hd_description
hd_types[MAXhdTypes+1] =
{
{  5, 14680, 0, 0,                 0,  0, "5 inch   CMI         (15 meg)"},
{  5, 15352, 0, 0,                 0,  0, "5 inch   MiniScribe  (16 meg)"},
{  5, 12280, 0, 0,                 0,  0, "5 inch   RMS         (13 meg)"},
{  5,  4888, 0, 0,                 0,  0, "5 inch   Syquest     ( 5 meg)"},
{  5, 15544, 0, 0,                 0,  0, "5 inch   Rodime      (16 meg)"},
{  8, 10648, 2, 4, 242+BADtrackSPACE, 11, "8 inch   Fujitsu     (11 meg)"},
{  8, 21296, 4, 8, 242+BADtrackSPACE, 11, "8 inch   Fujitsu     (23 meg)"},
{  8, 44528, 4, 8, 242+BADtrackSPACE, 23, "8 inch   Fujitsu     (46 meg)"},
{ 14, 13600, 1, 4, 202+BADtrackSPACE, 17, "14 inch  Shugart     (14 meg)"},
{ 14, 27200, 2, 8, 202+BADtrackSPACE, 17, "14 inch  Shugart     (28 meg)"},
{  0,     0, 0, 0,                 0,  0, "Unknown hard disk type       "},
};



/*page*/
/*======================================================================*/
/*			Move Table					*/
/*======================================================================*/
#define DoCompress	0
#define DoExpand	1
#define	KtoTracks(b)	((b) / KPerTracks)
struct MoveEntry {
	unsigned int	index;	/* index into the DAT			*/
	unsigned int	src;	/* starting Kbyte for source		*/
	unsigned int	dst;	/* starting Kbyte for destination	*/
	unsigned int	length;	/* size of partition in Kbytes		*/
	};



/*======================================================================*/
/*			Disk I/O stuff					*/
/*======================================================================*/
#define getDAT(b)	hd_xfer (READ, DATtrack, DATsector, b, DATlength)
#define putDAT(b)	hd_xfer (WRITE, DATtrack, DATsector, b, DATlength)
#define getTRACK(t, b)	hd_xfer (READ, t, 1, b, 128)
#define putTRACK(t, b)	hd_xfer (WRITE, t, 1, b, 128)


/*======================================================================*/
/*			GLOBAL VARIABLES				*/
/*======================================================================*/
unsigned int	allocated = 0;	/* Kbytes allocated to non-deleted partitions*/
unsigned int	deleted = 0;	/* Kbytes allocated to deleted partitions    */
unsigned int	disk_size = 0;	/* total disk size in Kbytes 		     */
unsigned int	needed = 0;	/* Kbytes needed to expand partition zero    */
int		Nparts = 0;	/* Number of partitions in DAT		     */
unsigned int	obtained = 0;	/* Kbytes obtained in partition move	     */
struct MoveEntry *start_expand;	/* spot to start expansion		     */
struct MoveEntry *start_compress; /* spot to start compression - should
				     be start_expand + 1		     */
unsigned int	total_tracks;	/* total number of tracks to move	     */

struct DATentry	DATable [DATMAX];	/* disk allocation table	     */
struct MoveEntry MoveTable [DATMAX];

/*page*/
/*======================================================================*/
/*	BuildMoveTab	- Builds a table of what partitions will move	*/
/*			  and where they will move			*/
/*======================================================================*/
BuildMoveTab () {
struct	MoveEntry	*m;
struct	DATentry	*p;
unsigned int			cur_src = 0;
unsigned int			cur_dst = 0;
int				i;

#ifndef NODEBUG
	printf ("======== Build Move Table =========\n");
#endif
	total_tracks = 0;
	p = DATable;
	cur_src = PartSize (p);
	cur_dst = cur_src + needed;

	/*===========================*/
	/* find partitions to expand */
	/*===========================*/
	for (++p, m = MoveTable, i = 1; 
	     obtained < needed	/* space needed found in deleted partitions */
	     && i < Nparts; 	
	     ++p, ++i) {
		if (!PartDelete (p)) {
			m->index = i;
			m->src = cur_src;
			m->dst = cur_dst;
			m->length = PartSize (p);
			if (m->src != m->dst)
				total_tracks += KtoTracks (m->length);
			cur_dst += PartSize (p);
			++m;
			}
		else
			obtained += PartSize (p);
		cur_src += PartSize (p);
		}


	start_expand = m - 1;
	start_compress = m;

	/*=============================*/
	/* find partitions to compress */
	/*=============================*/
	for (; i < Nparts; ++p, ++i) {
		if (!PartDelete (p)) {
			m->index = i;
			m->src = cur_src;
			m->dst = cur_dst;
			m->length = PartSize (p);
			if (m->src != m->dst)
				total_tracks += KtoTracks (m->length);
			cur_dst += PartSize (p);
			++m;
			}
		cur_src += PartSize (p);
		}

	m->length = 0;	/* end of table marked by lenght 0	*/

	}

/*page*/
/*======================================================================*/
/*	hd_size - return the size of the disk in Kbytes			*/
/*======================================================================*/
extern CHAR	h_cur_vol;
hd_size () {
struct vol_info		*key;
struct hd_description	*p;
struct vol_info		*volinfo;

	volinfo = ((struct hd_stat_info *) hd_init ())->volume;
	if (hd_controller () == XEBEC) {
		/* get size for 5" disk	*/
		hd_xfer (READ, 0, 1, volinfo, 1); /* get HD info */
		return (hd_types [volinfo [0].head_mask - 1].size_in_Kbytes);
		}
	else {
		for (p = &hd_types [FIRST8or14disk],
			key = &volinfo [h_cur_vol];
			p->diameter != 0 &&
			(key->tracks != p->tracks
			|| key->sect_per_track != p->sect_per_track
			|| key->head_mask+1 != p->RW_heads);
			p++)
			;
		return (p->size_in_Kbytes);
		}
	}



/*page*/
/*======================================================================*/
/*	initialize	- initialization				*/
/*======================================================================*/
initialize () {
struct DATentry	*p;

#ifndef NODEBUG
	printf ("======== INITIALIZE =======\n");
#endif

	disk_size = hd_size ();
#ifndef NODEBUG
	printf ("\tHard Disk Size = %uK\n", disk_size);
#endif
	
	getDAT (DATable);
	needed = PartSize ((p = DATable));
	}


/*page*/
/*======================================================================*/
/*	scan_DAT	- scan the Disk Allocation Table and sum up 	*/
/*			  space allocated and deleted			*/
/*======================================================================*/
scan_DAT () {
struct DATentry	*p;

#ifndef NODEBUG
	printf ("======== scan_DAT ========\n");
#endif

	deleted = allocated = 0;

	for (p = DATable, Nparts = 0; 
			p != &DATable [DATMAX] && PartExists (p); 
			++p, ++Nparts) {
#ifndef NODEBUG
		printf ("%d:%8.8s %s\n", Nparts, p->Part_Name, SizeName (p->size));
#endif
		if (PartDelete (p))
			deleted += PartSize (p);
		else
			allocated += PartSize (p);
		}

#ifndef NODEBUG
	printf ("\t%d partitions, deleted = %uK\tallocated = %uK\n",
		Nparts, deleted, allocated);
#endif
	}

/*page*/
/*======================================================================*/
/*	MovePartitions	- Use the MoveTable and actually move the	*/
/*			  partitions.					*/
/*======================================================================*/
MovePartitions () {
struct MoveEntry	*m;
int			i;

#ifndef NODEBUG
	printf ("======== MovePartitions ========\n");
#endif
	/* display message and get ready to start count down */
	putchar ('\n');
	printf (DspCnt);
	backup (strlen (DspCnt));

	for (m = start_expand; m >= MoveTable; --m) {
		if (m->src != m->dst) 
			Move1Part (DoExpand, m->src, m->dst, m->length);
#ifndef NODEBUG
		else
			printf ("\tpartition %d stays put.\n", m->index);
#endif
		}

	for (m = start_compress; m->length != 0; ++m) {
		if (m->src != m->dst)
			Move1Part (DoCompress, m->src, m->dst, m->length);
#ifndef NODEBUG
		else
			printf ("\tpartition %d stays put.\n", m->index);
#endif
		}

#ifndef NODEBUG
	printf ("Update Disk Allocation Table\n");
#endif
	for (i = 1, m = MoveTable; m->length != 0; ++i, ++m) {
		if (m->index != i)
			blockmv (&DATable [i], &DATable [m->index], 
				sizeof (struct DATentry));
		}

	/* change size of partition 0 */
	DATable [0].size += 1;

	clear (&DATable [i], (DATMAX - i) * sizeof (struct DATentry), '\0');
	putDAT (DATable);
	}

/*page*/
/*======================================================================*/
/*	Move1Part	- move a single partition			*/
/*======================================================================*/
Move1Part (expand, src, dst, len)
unsigned int	expand;	/* TRUE if expanding, else compressing	*/
unsigned int	src;	/* source of data in Kbytes		*/
unsigned int	dst;	/* destination of data in Kbytes	*/
unsigned int	len;	/* length of data in Kbytes		*/
{
int	i;
CHAR	b [128*SECTSIZE];

	/* convert from Kbytes to Tracks */
	len = KtoTracks (len);
	src = KtoTracks (src);
	dst = KtoTracks (dst);

	printf ("%5.5u", total_tracks);
	backup (5);
	if (expand == DoExpand) {
#ifndef NODEBUG
		printf ("\tEXPANDING (tracks): %d from %u to %u\n",len,src+len,dst+len);
#endif
		for (i = len; i > 0; --i) {
			getTRACK (src + i - 1, b);
			putTRACK (dst + i - 1, b);
			--total_tracks;
			printf ("%5.5u", total_tracks);
			backup (5);
			}
		}
	else /* compressing */ {
#ifndef NODEBUG
		printf  ("\n\tCOMPRESSING (tracks): %d from %u to %u\n",len,src,dst);
#endif
		for (i = 0; i < len; ++i) {
			getTRACK (src + i, b);
			putTRACK (dst + i, b);
			--total_tracks;
			printf ("%5.5u", total_tracks);
			backup (5);
			}
		}
	}

/*page*/
/*======================================================================*/
/*	nPart0Size	- returns a string with the size of partition   */
/*			  zero after expansion				*/ 
/*======================================================================*/
CHAR	*nPart0Size () {
	return (SizeName (DATable [0].size + 1));
	}

/*page*/
/*======================================================================*/
/*	oPart0Size	- returns a string with the size of partition   */
/*			  zero before expansion				*/ 
/*======================================================================*/
CHAR	*oPart0Size () {
	return (SizeName (DATable [0].size));
	}

/*page*/
/*======================================================================*/
/*	Part0atMax	- returns TRUE if partition zero is 8M		*/
/*======================================================================*/
Part0atMax () {
struct	DATentry	*p; /* Needed to aviod brain-damaged area of 
			       the AZTEK C compiler */
	p = DATable;	/* We should be able to use DATable in the following
			   macro expressions rather than 'p', but AZTEK 
			   won't compile with that */	
#ifndef NODEBUG
	printf ("======== Partition Zero is %s ========\n", SizeName (p->size));
#endif
	return (PartSize (p) == sizeinK [SIZEMAX - 1]);
	}


/*======================================================================*/





