/*	Alloc module 4.				#4
    "PFORMAT.C"
    "AL4.C"

    This module contains all the Formatting code, mostly MSDOS stuff.
*/
/*
#define		debug4		0


#define		dbg4		TRUE
*/
#define		EXTERNal	0
#include	"alloc.dat"
#include	"fcntl.h"

/*=============================================================================
				HISTORY

date		7 March 1984
person		tpl

ver	0
rev	0
mod	c
patch	5

 mod-patch
-----------
    c5		7 March 1984		tpl
	.Make Command.com bigger. Change size from 3C78 to 4000 hex.

    c3		2 March 1984		tpl
	.Close alloc.sys after use so that C's open file table does not
	overflow when formatting many msdos partitions.
	.Also tell user which partition is being formatted.

    c2		23 February 1984	tpl
	.'free' all 'alloc'ed space.

    c1		22 February 1984	tpl
	.Make this module an overlay for Alloc.
	.Rename this module "PFORMAT.C".

   c0		16 February 1984	jjc
	.Changed to use HARDIO disk drivers rather than BIOS calls.

   b13		30 January 1984		tpl
	.RELEASED TO SOFTWARE TEST.

   b7		17 January 1984		tpl
	.Take out "if ( debug_mode )" and replace with
	"#ifdef debug" and "#endif" to save space.


   b2		9 January 1984		tpl
	.Enter format code now that reset hard disk call is avaliable.

  b		3 January 1984		tpl
	.Update constants to reflect USERS type standard.
	.Update to new include file structure.

  a		September 23, 1983	tpl
	.Make this module from alloc1/g, alloc2/g, alloc3/f, alloc4/e.

*/

/*page*/
/*=============================================================================
		DEFINES, VARIABLES and DATA STRUCTURES
*/
#ifdef dbg4
#define	isprint(c) (c > 0x19 && c < 0x7F)
WriteSect(t,s,b)
int	t, s;
CHAR	*b;
{
int	i;

	hd_xfer (READ, t, s, b, 1);
	printf ("Would have overwritten T %0x S %0x\n", t, s);
	for (i = 0; i < 128; ++i) {
		printf ("%02x%c ", b [i], isprint (b [i]) ? b [i] : '_');
		if ((i % 16) == 0) printf ("\n");
		}
	}
#else
#define	WriteSect(t,s,b)	hd_xfer (WRITE, t, s, b, 1)
#endif

/* Masks to set attribute byte in MSDOS dir entry.			*/
#define		MSroMASK	0x01
#define		MShiddenMASK	0x02
#define		MSsystemMASK	0x04
#define		MSVOLlabelMASK	0x08
#define		MSsubDirMASK	0x10
#define		MSarchiveMASK	0x20

#define		MSDIRnameSIZE		8
#define		MSDIRextNameSIZE	3
#define		MSDIRreservedSIZE	10

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

struct ms_dir_entry
{
	CHAR	name[MSDIRnameSIZE];
	CHAR	name_extension[MSDIRextNameSIZE];
	CHAR	attribute;
	CHAR	reserved[MSDIRreservedSIZE];
	CHAR	time[2];
	CHAR	date[2];
	int	start_cluster;
	int	size_in_bytes[2];
};

struct ms_dir_entry
ms_comm_entry =
{
{ 'C', 'O', 'M', 'M', 'A', 'N', 'D', ' '} , {'C','O','M'}, /* name	*/
{ 0 },						/* attribute		*/
{0,0,0,0,0,0,0,0,0,0},				/* reserved		*/
{0,0},{0,0},					/* No date/time yet	*/
2,						/* begin with cluster 2	*/
{0x4000,0}					/* 16K hex bytes	*/
};

struct BPB	*temp_BPB;
struct DPB	*save_dpb;

/*page*/
/*=============================================================================
				ROUTINES
*/
/*---------------------------------------------------------------------------*/

ovmain()
{
  register CHAR i;

    putz("\nPlease wait. Now formatting partitions.\n\n");

    for ( i=0; i<frmt_p_index; i++ )
	/* If partition to format was not deleted in same run of ALLOC */
	if ( parts_to_format[i] != NallocEntries )
	    FormatPartition( parts_to_format[i] );

} /* ovmain */

/*---------------------------------------------------------------------------*/

FormatPartition (partition) 
  CHAR partition;
{

#ifdef debug4
    putz("..FormatPartition\n");
#endif

    printz("Formatting partition %.8s    O.S. type is ", alloc_table[partition].name );

/* Now do the actual formatting.	*/
    temp_BPB = &bpb_types [alloc_table [partition].size - 1];

    if ((alloc_table[partition].control & OSMASK) == CPM)
    {
	putz("CPM\n");
	Init_CPM_partition (partition);
    }
    else
    {
	putz("MSDOS\n");
	Init_MS_partition (partition);
    }

} /* FormatPartition */

/*---------------------------------------------------------------------------*/
/*page*/

Init_MS_partition(partition)
  int	partition;
{
  int	sector, track;

#ifdef debug4
	putz("..Init_MS_partition\n");
#endif

    track = Part_Offset (partition);
    sector = 1;
    MS_BPB_init( &track, &sector);	/* Write BPB onto drive.     */

    sector++;

    MS_FAT_init( &track, &sector);/* Write 2 FATs onto drive.  */

    MS_Dir_init( &track, &sector, partition );
			/* Write directory onto drive, with COMMAND.COM and */
			/*volume name in first directory sector.	    */

    MS_Com_File_Write( &track, &sector);	/* Write COMMAND.COM to disk.*/


}/* Init_MS_partition */

/*---------------------------------------------------------------------------*/
/*page*/

MS_BPB_init( track, sector)
  int	*track, *sector;
{
  struct BPB_sector	*bpb_sect;

#ifdef debug4
    putz("..MS_BPB_init\n");
#endif

    if ( (bpb_sect = alloc( sizeof( struct BPB_sector) )) == 0 ) {
	putz("Not enough room to allocate bpb_sect in MS_BPB_init.\n");
	zabort();
    }
    clear( bpb_sect, sizeof( struct BPB_sector), 0);

    bpb_sect->name[0] = 'D';
    bpb_sect->name[1] = 'M';
    bpb_sect->name[2] = 'S';

    blockmv( &(bpb_sect->bpb), temp_BPB, sizeof( struct BPB ) );
    
#ifdef debug4
	printz("    bios write     track: %x    sector: %x\n",
		*track, *sector);
#endif
	WriteSector (*track, *sector, bpb_sect);

#ifdef debug4
	    putz("\nBPB sector: %x hex\n", bpb_sect);
#endif

    free( bpb_sect );

}/* MS_BPB_init */

/*---------------------------------------------------------------------------*/
/*page*/

MS_FAT_init(track,sector)

  int	*track, *sector;    /* Beginning track and sector of the partition.*/
{
  CHAR	*fat;
  register CHAR		first_sector, j, i;

#ifdef debug4
    putz("..MS_FAT_init\n");
#endif

    if ( (fat = alloc( WRbuffSIZE )) == 0 )
    {
	putz("Not enough room to allocate fat in MS_FAT_init.\n");
	zabort();
    }

#ifdef debug4
	printz("MS_FAT_init.     fat: %x\n",fat);
#endif

    clear( fat, WRbuffSIZE, 0);

    for ( j=0; j < temp_BPB->num_FATs; j++) {
		first_sector = TRUE;
		FirstFATsectorInit(fat);
		WriteSector (*track, *sector, fat);
#ifdef debug4
		printz("    track: %x    sector: %x\n",
			*track, *sector);
#endif
		if ((*sector)++ == temp_BPB->SectPerTrack) {
			*track += *sector / temp_BPB->SectPerTrack;
			*sector = 1;
			}

	clear( fat, WRbuffSIZE, 0);

	for ( i=1; i < temp_BPB->SectPerFAT; i++) { 
		WriteSector (*track, *sector, fat);
#ifdef debug4
		printz("    track: %x    sector: %x\n",
			*track, *sector);
#endif
		if ((*sector)++ == temp_BPB->SectPerTrack) {
			*track += *sector / temp_BPB->SectPerTrack;
			*sector = 1;
			}

	}/*for #sects/FAT*/

    }/*for #FATs*/

    free( fat );

}/* MS_FAT_init */

/*---------------------------------------------------------------------------*/
/*page*/

MS_Dir_init (track, sector, partition)

  int	*track, *sector;	/* track and sector of the directory.*/
  int	partition;
{
  register CHAR	i, *first_sector;

#ifdef debug4
	putz("..MS_Dir_init.");
#endif

	/* Enter COMMAND.COM and volume name into directory. */
	first_sector = MSdir_command_init( partition );
	WriteSector (*track, *sector, first_sector);

#ifdef debug4
	    printz("    track: %x    sector: %x\n",
			*track, *sector);
#endif
	if ((*sector)++ == temp_BPB->SectPerTrack) {
		*track += *sector / temp_BPB->SectPerTrack;
		*sector = 1;
		}

    for ( i=1; i < temp_BPB->num_Root_Dir_Entries
					  / DIRentriesPERsector; i++) {
	WriteSector (*track, *sector, dir_init);
#ifdef debug4
	    printz("    track: %x    sector: %x\n",
			*track, *sector);
#endif
	if ((*sector)++ == temp_BPB->SectPerTrack) {
		*track += *sector / temp_BPB->SectPerTrack;
		*sector = 1;
		}
    }

}/* MS_Dir_init */

/*---------------------------------------------------------------------------*/
/*page*/

CHAR
*MSdir_command_init( partition )
  int	partition;
{
  struct ms_dir_entry	*msdir;
  CHAR	*first_sector;
  register CHAR	j, i, prnt_char;

#ifdef debug4
    putz("..MSdir_command_init\n");
#endif

    if ( (msdir = alloc( sizeof( struct ms_dir_entry ))) == 0 )
    {
	putz("Not enough room to allocate msdir in MSdir_command_init.\n");
	zabort();
    }

    if ( (first_sector = alloc( WRbuffSIZE )) == 0 )
    {
	putz("Not enough room to alocate first_sector in MSdir_command_init.\n");
	zabort();
    }

    blockmv( first_sector, dir_init, WRbuffSIZE);

/* Set up volume label.	*/
    clear( msdir, sizeof( struct ms_dir_entry), 0);
    for ( i=0; i<MSDIRnameSIZE; i++)
	msdir->name[i] = alloc_table[ partition ].name[i];
    for ( i=0; i<MSDIRextNameSIZE; i++)
	msdir->name_extension[i] = SPACE;
    msdir->attribute = MSVOLlabelMASK;
    blockmv( first_sector, msdir, sizeof( struct ms_dir_entry));

/* Set up COMMAND.COM */
    blockmv( first_sector + sizeof( struct ms_dir_entry), &ms_comm_entry,
		sizeof( struct ms_dir_entry) );

#ifdef debug4
	printz("Directory first sector.... after COMMAND.COM init: %x hex\n",
				first_sector);
#endif

    free( msdir );
    free ( first_sector );

    return( first_sector );

}/*MSdir_command_init*/

/*---------------------------------------------------------------------------*/
/*page*/

FirstFATsectorInit(fat)
  CHAR	*fat;
{
  register CHAR		prnt_char, i, j;
  int			num_clusters;

#ifdef debug4
    putz("..FirstFATsectorInit\n");
#endif
    *fat = 0xf8;
    *(fat+1) = 0xff;
    *(fat+2) = 0xff;

    num_clusters = NumCommandComClusters();

    for ( i=0; i<num_clusters-1; i++)	 /* -1 so last cluster can be 0xfff. */
	MSallocateCluster(i+2,i+3,fat);   /* Start with cluster 2 in FAT.*/

    MSallocateCluster(i+2,0xfff,fat);     /* 0xfff signals end of file.  */

#ifdef debug4
	    printz("First FAT sector: %x hex\n", fat);
#endif

}/*FirstFATsectorInit*/

/*---------------------------------------------------------------------------*/
/*page*/

int
NumCommandComClusters()
{
  int	num_clusters, num_sectors;
  long	num_bytes;

/*    ms_comm_entry.size_in_bytes[1];   WHAT TO DO IF THIS IS > 0 ???
*/
    num_bytes = ms_comm_entry.size_in_bytes[0];
    num_sectors = ( num_bytes + (temp_BPB->BytesPerSect-1))
			/ temp_BPB->BytesPerSector;
    num_clusters = (num_sectors + (temp_BPB->SectPerCluster-1))
			/ temp_BPB->SectPerCluster;
#ifdef debug4
	    putz(".NumCommandComClusters.\n");
	    printz("  num_bytes: %ld    num_sectors: %d    num_clusters: %d\n",
			num_bytes, num_sectors, num_clusters);
#endif
    return(num_clusters);

}/*NumCommandComClusters*/

/*---------------------------------------------------------------------------*/
/*page*/

MSallocateCluster(prev_cluster,next_cluster,fat)
  int	next_cluster, prev_cluster;
  CHAR	*fat;
{
  int	offset;
  int	*new;

#ifdef debug4
    printz(".MSallocateCluster. prev_cluster: %x   next_cluster: %x\n",
					prev_cluster,	   next_cluster);
#endif

    offset = prev_cluster + (prev_cluster/2);
    new = fat + offset;

#ifdef debug4
	    printz("new: %x    fat: %x    offset: %x\n",
			new, fat, offset);
	    printz("....before allocation, *new: %x\n",*new);
#endif

    if ( (prev_cluster % 2) == 0)	/* if even */ {
	*new &= 0xf000;
	*new |= next_cluster;
    }
    else {
	*new &= 0x000f;
	*new |= (next_cluster<<4);
    }

#ifdef debug4
	    printz(".....after allocation, *new: %x\n",*new);
#endif

}/*MSallocateCluster*/

/*---------------------------------------------------------------------------*/
/*page*/

MS_Com_File_Write( track, sector)
  int	*track, *sector;
{
  CHAR	*name, *buffer;
  int	file_desc, ret_value;

#ifdef debug4
	printz(".MS_Com_File_Write....    track: %x    sector: %x\n",
					 *track, *sector);
#endif

/* Read each sector into buffer, write onto disk starting with *track,
	*sector.	*/

    if ( (buffer = alloc( WRbuffSIZE )) == 0 )
    {
	putz("Not enough room to allocate buffer in MS_Com_File_Write.\n");
	zabort();
    }

/* Open ALLOC.SYS file to read and then write to MSDOS partition as
	the COMMAND.COM file.	*/
/* Look for ALLOC.SYS on defualt ( user's login ) drive.	*/

    name = "ALLOC.SYS";
    file_desc = open( name, O_RDONLY);
    if (file_desc == -1) {	/* look on the 'A' drive.	*/
	name = "A:ALLOC.SYS";
	file_desc = open( name, O_RDONLY);
	if (file_desc == -1) {	/* Not found so zabort.	*/
	    printz("\n%c Can not find (open) ALLOC.SYS file.\n", BELL);
	    zabort();
	}
    }

    while ((ret_value = read( file_desc, buffer, WRbuffSIZE)) == WRbuffSIZE ) {
	if (ret_value == -1) {
	    printz("\n%cFatal Error reading ALLOC.SYS in MS_Com_File_Write.\n",BELL);
	    zabort();
	}
	WriteSector (*track, *sector, buffer);
#ifdef debug4
	    printz("    track: %x    sector: %x\n",
		*track, *sector);
#endif
	if((*sector)++ == temp_BPB->SectPerTrack) {
		*track += *sector / temp_BPB->SectPerTrack;
		*sector = 1;
		}

    }/* while reading alloc.sys = command.com */

#ifdef debug4
    printz("ret_value: %x\n",ret_value);
	printz("Last 128 byte record read from Command.Com: %x hex\n", buffer);
#endif

    close( file_desc );	/* To free from C's file list.	*/
    free ( buffer );

}/*MS_Com_File_Write*/

/*---------------------------------------------------------------------------*/
/*page*/
/*    Compute the track and sector of the first sector of the given partition.
   Start at the beginning of the hard disk. Thus this computes an absolute
   offset from the first track and sector of the hard disk.
*/
int	SizeTo256K [] ={
		0,	/* invalid size	*/
		1,	/* 256K		*/
		2,	/* 512K		*/
		4,	/* 1M		*/
		8,	/* 2M		*/
		16,	/* 4M		*/
		32	/* 8M		*/
		};

#define	TracksPer256k	(256 / 16)	/* 256K per (Tracks per K) */

int Part_Offset (partition) {
	int	i = 0;
	int	track;
	int	num_256k_chunks = 0;

	while (i != partition)
		num_256k_chunks += SizeTo256K [alloc_table [i++].size];

#ifdef debug4
	printz ("Part_Offset_Compute. track: %x\n",
		num_256k_chunks * TracksPer256k);
	printz ("           num_256k_chunks: %x     TracksPer256k: %x\n",
			    num_256k_chunks,        TracksPer256k);
#endif
	return (num_256k_chunks * TracksPer256k);

	}/* Part_Offset */


/*---------------------------------------------------------------------------*/
/*page*/

Init_CPM_parititon (partition)
  int	partition;
{
/*	from partition get size from aloc table.
	from size get dpb.
	from dpb get size of directory.
	fill directory with 0xe5's.
*/
  CHAR	*buffer;
  register int i;		/* Must be int since num_sectors does also.  */
  struct DPB	*dpb_ptr;
  unsigned int	num_sectors;
  int	track, sector;

#ifdef dbg4
	putz("..Init_CPM_partition\n");
#endif

    if ( (buffer = alloc( WRbuffSIZE ) ) == 0 ) {
	putz("Not enough room to allocate buffer in Init_CPM_partition.\n");
	zabort();
    }
    clear( buffer, WRbuffSIZE, 0xe5);

    dpb_ptr = &( dpb_types[ alloc_table[partition].size - 1 ] );

    num_sectors = (dpb_ptr->dir_max + 1) / DIRentriesPERsector;

    track = dpb_ptr->sys_offset + Part_Offset (partition);
    sector = 1;

#ifdef dbg4
    printz(" dpb_ptr: %x      &dpb_types[0]: %x\n", dpb_ptr, &(dpb_types[0]) );
    printz("  partition: %x   partition.size: %x   num_sectors: %x\n",
		partition, alloc_table[partition].size, num_sectors );
#endif

    for ( i=0; i<num_sectors; i++ ) {
	WriteSector (track, sector, buffer);

#ifdef dbg4
	printz("    track: %x    sector: %x\n",
			track, sector);
#endif

	if (sector++ == dpb_ptr->sect_per_track) {
		track += sector / dpb_ptr->sect_per_track;
		sector = 1;
		}
    }

#ifdef dbg4
	putz("\n..leave Init_CPM_partition\n");
#endif

    free ( buffer );

}/*Init_CPM_partition*/

/*---------------------------------------------------------------------------*/



/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
