/**************************************************
 
 	Format.c
 	Standard-Formatierprogramm
 	mit Trackdisk.device
 
 	-=mb=- Sep-87
 
 **************************************************/

#include <exec/types.h>
#include <exec/exec.h>
#include <devices/trackdisk.h>
#include <libraries/dos.h>
#include <stdio.h>

/*
 *	Funktionsdeklarationen aus <functions.h> 
 * 	Diese Deklarationen sind nur noetig, wenn
 *	ihr Include-Directory die Datei
 *	"functions.h" nicht enthaelt, sonst reicht
 *	ein #include <functions.h>
 */
long DoIO();
void *AllocMem();
void FreeMem();
struct MsgPort *CreatePort();
void DeletePort();
struct IOStdReq *CreateStdIO();
void DeleteStdIO();
long OpenDevice();
void CloseDevice();

#define BLOCKSIZE TD_SECTOR
#define	CYLSIZE	BLOCKSIZE*NUMHEADS*NUMSECS
#define MOTOR_ON 1
#define MOTOR_OFF 0

struct MsgPort *diskport;
struct IOStdReq *diskreq;
int openerror = -999;
/* hier lesen wir einen Block hinein */
unsigned char *diskbuffer;
 
/* hier lesen wir einen Block von Diskette */
ReadBlock(block, buffer)	
int block;
unsigned char *buffer;
	{
	diskreq->io_Length = BLOCKSIZE;
	diskreq->io_Data = (APTR)buffer;
	diskreq->io_Command = CMD_READ;
	diskreq->io_Offset = block*BLOCKSIZE;
	DoIO(diskreq);
	if (diskreq->io_Error != 0) {
		printf("Fehler beim Lesen von Block ");
		printf("%d, Fehlernummer: %d\n", block,
			diskreq->io_Error);
		cleanup(NULL);
		}
	}

/* hier schreiben wir einen Block auf Diskette */
WriteBlock(block, buffer)	
int block;
unsigned char *buffer;
	{
	diskreq->io_Length = BLOCKSIZE;
	diskreq->io_Data = (APTR)buffer;
	diskreq->io_Command = CMD_WRITE;
	diskreq->io_Offset = block*BLOCKSIZE;
	DoIO(diskreq);
	if (diskreq->io_Error != 0) {
		printf("Fehler beim Schreiben von Block ");
		printf("%d, Fehlernummer: %d\n", block,
			diskreq->io_Error);
		cleanup(NULL);
		}
	}

/* alle Puffer im Speicher auf Disk zurueckschreiben */
Update()
	{
        diskreq->io_Command = CMD_UPDATE;
	DoIO(diskreq);
	if (diskreq->io_Error != 0) {
		printf("Fehler bei Update, Fehlernummer: %d\n", 
			diskreq->io_Error);
		cleanup(NULL);
		}
	}

/* hier formatieren wir einen Cylinder */
FormatCylinder(cyl, buffer)
int cyl;
unsigned char *buffer;
	{
	diskreq->io_Length = CYLSIZE;
	diskreq->io_Data = (APTR)buffer;
	diskreq->io_Command = TD_FORMAT;
	diskreq->io_Offset = cyl*CYLSIZE;
	DoIO(diskreq);
	if (diskreq->io_Error != 0) {
		printf("Fehler beim Formatieren von Cylinder ");
		printf("%d, Fehlernummer: %d\n", cyl, 
			diskreq->io_Error);
		cleanup(NULL);
		}
	}

/* Drivemotor an- bzw. ausschalten */
SwitchMotor(onoffswitch)
/* onoffswitch ist MOTOR_ON oder MOTOR_OFF */
int onoffswitch;
	{
	diskreq->io_Length = onoffswitch;
	diskreq->io_Command = TD_MOTOR;
	DoIO(diskreq);
	}

CorrectBlockChecksum(buffer)
unsigned char *buffer;
	{
	ULONG checksum, *longbufptr;
	int i;

	longbufptr = (ULONG *)buffer;
	checksum = 0;
	for (i = 0; i < BLOCKSIZE/4; i++)
		checksum += longbufptr[i];
	longbufptr[5] -= checksum;
	}

AllocBlock(block, buffer)
int block;
unsigned char *buffer;
	{
	ULONG *longbufptr = (ULONG *)buffer;
	int longword, bit, i;
	ULONG checksum;

	longword = (block-2)/32;
	bit = block-2 - longword*32;
	longbufptr[longword+1] &= ~(1L << bit);

	/* BAM-Checksumme ausrechnen und setzen */
	for (i = 1, checksum = 0; i < BLOCKSIZE/4; i++)
		checksum += longbufptr[i];
	longbufptr[0] = -checksum;
	}

/* Programmsteuerung */
main()   
	{
	/* wir benutzen Drive 0 */
	char input[256];
	long drive = 0;
	int namelength;
	int i;
	ULONG *longbuffer;
        
	/* Disknamen einlesen */
	printf("Diskettenname ? ");
	gets(input);
	if (strlen(input) > 30) 
		cleanup("Diskname zu lang (> 30 Zeichen)");

	printf("Diskette in Laufwerk 0 einlegen und RETURN druecken\n");
	/* auf <RETURN> warten */
	while (getchar() != '\n')
		;

	/* CHIP-memory fuer Puffer holen und loeschen */
	diskbuffer = AllocMem(CYLSIZE, MEMF_CHIP | MEMF_CLEAR);
	if (diskbuffer == 0) cleanup("No memory!");

	/* Port zur Kommunikation mit trackdisk.device aufbauen */
	diskport = CreatePort("mydisk.port", 0L);
	if (diskport == 0) cleanup("CreatePort failed!");

	/* StdIO-Block zur Kommunikation ueber 'diskport' aufbauen */
	diskreq = CreateStdIO(diskport);
	if (diskreq == 0) cleanup("CreateStdIO failed!");
 
	/* wir reservieren uns trackdisk.device df0: */
	openerror = OpenDevice(TD_NAME,drive,diskreq,0L);
	if (openerror) cleanup("OpenDevice failed!");
    
	/* Drive-Motor einschalten */
	SwitchMotor(MOTOR_ON);

	/* Alle (NUMCYLS == 80) Cylinder formatieren */
	for (i = 0; i < NUMCYLS; i++) {
		printf("\033[F Formatiere Cylinder %d\n", i);
		FormatCylinder(i, diskbuffer);
		}

	/* Kennzeichnung fuer DOS im Boot-Block setzen */
	for (i = 0; i < 4; i++)
		diskbuffer[i] = "DOS\0"[i];
	/* Boot-Block (Block 0) schreiben */
	WriteBlock(0, diskbuffer);

	/* Root-Block initialisieren */
	longbuffer = (ULONG *)diskbuffer;	/* wir rechnen in */
						/* Langworten */
	longbuffer[0] = 2;		/* Blocktyp = Directoryblock */
	longbuffer[3] = 72;		/* Hashtabellengroesse */
	longbuffer[78] = 0xffffffff;	/* BAM gueltig */
	longbuffer[79] = 881;		/* BAM steht in Block 881 */
	DateStamp(&longbuffer[121]);	/* Datum der Formatierung */
	DateStamp(&longbuffer[105]);	/* Datum der letzten Aenderung */
	diskbuffer[432] = strlen(input);	/* Diskettenname */
	for (i = 0; i < strlen(input); i++)
		diskbuffer[433+i] = input[i];
	longbuffer[127] = 1;		/* Sekundaertyp = ROOT-Block */
	/* Block-Checksumme neu berechnen und aktualisieren */
	CorrectBlockChecksum(diskbuffer);
	/* Root-Block (Block 880) schreiben */
	WriteBlock(880, diskbuffer);

	/* BAM initialisieren */
	/* Alle Blocks deallocieren */
	for (i = 1; i < 56; i++)
		longbuffer[i] = 0xffffffff;
	/* Block 880 und 881 allocieren */
	AllocBlock(880, diskbuffer);
	AllocBlock(881, diskbuffer);
	/* BAM-Block (Block 881) schreiben */
	WriteBlock(881, diskbuffer);

	Update();

	/* Drive-Motor ausschalten */
	SwitchMotor(MOTOR_OFF);
	cleanup(NULL);
	}
 
/* Aufraeumarbeiten erledigen */
cleanup(mes)
char *mes;
	{
	/* evtl. Fehlermeldung */
	if (mes) puts(mes);

	/* Drive df0: wieder freigeben */
	if (!openerror) CloseDevice(diskreq);
	/* StdIo-Block loeschen */
	if (diskreq) DeleteStdIO(diskreq);
	/* DiskPort entfernen */
	if (diskport) DeletePort(diskport);
	/* Pufferspeicher freigeben */
	if (diskbuffer) FreeMem(diskbuffer, BLOCKSIZE);
	exit(0);
	}
