/***********************************************************************

	kicktrix.c - Kickstartmanipulation

	by Martin Brenner Feb-88

 ***********************************************************************/

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

#define getlong(l) {l = 0; for (i=0;i<4;i++) l = l*256+getc(fp);}

long DoIO();
void *AllocMem();
void FreeMem();
struct MsgPort *CreatePort();
void DeletePort();
struct IOStdReq *CreateStdIO();
void DeleteStdIO();
long OpenDevice();
void CloseDevice();

#define BLOCKSIZE TD_SECTOR

struct MsgPort *diskport;
struct IOStdReq *diskreq;
UBYTE *diskbuffer;
UBYTE *filemem;
long error = -9999;
long adress, length;

         
ReadBlocks(start, end)
long start;
long end;
	{
        diskreq->io_Length = (end-start)*BLOCKSIZE;
        diskreq->io_Data = (APTR)(diskbuffer + (start*BLOCKSIZE));
        diskreq->io_Offset = (start+1)*BLOCKSIZE;
        diskreq->io_Command = CMD_READ;
        DoIO(diskreq);
	}
WriteBlocks(start, end)
long start;
long end;
	{
        diskreq->io_Length = (end-start)*BLOCKSIZE;
        diskreq->io_Data = (APTR)(diskbuffer + (start*BLOCKSIZE));
        diskreq->io_Offset = (start+1)*BLOCKSIZE;
        diskreq->io_Command = CMD_WRITE;
        DoIO(diskreq);
	}
Update()
	{
        diskreq->io_Command = CMD_UPDATE;
	DoIO(diskreq);
	}
  
Motor(state)
int state;
	{
        diskreq->io_Length = state;
        diskreq->io_Command = TD_MOTOR;
        DoIO(diskreq);
	}
 
main(argc, argv)   
int argc;
char *argv[];
{
	long i;
        SHORT c;
	long drive;
	UBYTE *diskdata;
	UBYTE *filedata;
	ULONG *longptr, lng, sum;
	UWORD matchword;
	UBYTE input[256];
	FILE *fp, *fopen();
        
	printf("KickTrix\n");
	if (argc != 3 || strncmp("df",argv[1],2)) {
		printf("\23333mUsage: %s <drive> <file>\2330m\n", argv[0]);
		printf("- drive: df0:, df1:, df2:, df3:\n");
		printf("- file:  to write \n");
		exit(1);
		}
	if ((drive = argv[1][2] - 48) < 0 || drive > 3) {
		printf("\23333mWrong drive number %ld !\2330m\n", drive);
		exit(1);
		}

	/* Puffer fuer Kickstart im CHIP-Memory alocieren */
	diskbuffer = (UBYTE *)AllocMem (0x40000L, MEMF_CHIP|MEMF_PUBLIC);
	if (diskbuffer == NULL)
		cleanup("Kein Speicher fuer Kickstart!");

	/* Programm-Datei oeffnen und nach Matchword suchen */
	fp = fopen(argv[2], "r");
	if (fp == NULL) {
		printf("Datei %s nicht gefunden!\n",argv[2]);
		cleanup(NULL);
		}
	do	{
		if ((c = getc(fp)) == EOF) {
			printf("%s ist kein Kickstart-Programm!\n", argv[2]);
			cleanup(NULL);
			}
		}
	while ((matchword = c * 256 + getc(fp)) != 0x4afc);

	/* Programmadresse lesen */
	getlong(adress);
	adress &= 0x3ffff;
	getlong(length);

	/* Speicher fuer Datei holen */
	filemem = AllocMem(length, MEMF_PUBLIC);
	if (filemem == 0)
		cleanup("Kein Speicher fuer Datei!");

	/* Datei einlesen */
	filedata = filemem;
	for (i = 0; i < length; i++)
		*filedata++ = getc(fp);

	fclose(fp);

	printf("Kickstart Diskette einlegen und RETURN druecken\n");
	gets(input);

	/* Trackdisk-Device oeffnen */
	diskport = CreatePort(0L,0L);
	if(diskport == 0) cleanup("CreatePort ging schief");
	diskreq = CreateStdIO(diskport);
	if(diskreq == 0) cleanup("CreateStdIO ging schief");
	error = OpenDevice(TD_NAME,drive,diskreq,0L);
	if (error) cleanup("OpenDevice ging schief");

	/* Kickstart einlesen */
	Motor(1);
	ReadBlocks(0L, 512L);
	if(diskreq->io_Error) {
		printf("Error Read Blocks, Error %d\n", diskreq->io_Error);
		cleanup(NULL);
		}

	/* Datei in Kickstart-Speicher kopieren */
	filedata = filemem;
	diskdata = diskbuffer + adress;
	for (i = 0; i < length; i++)
		*diskdata++ = *filedata++;

	/* Kickstart-Checksumme berechnen */
	longptr = (ULONG *)diskbuffer;
	longptr[0x14] = 0;
	sum = 0;
	for (i = 0; i < 0x10000; i++) {
		lng = sum + *longptr++;
		sum = lng + (lng < sum);
		}
	longptr = (ULONG *)diskbuffer;
	longptr[0x14] = ~sum;

	/* Kickstart auf Disk schreiben */
	WriteBlocks(adress/BLOCKSIZE, (adress+length)/BLOCKSIZE+1);
	if(diskreq->io_Error) {
		printf("Error Write Blocks, Error %d\n", diskreq->io_Error);
		cleanup(NULL);
		}
	WriteBlocks(0L, 1L);
	if(diskreq->io_Error) {
		printf("Error Write Blocks, Error %d\n", diskreq->io_Error);
		cleanup(NULL);
		}
	Update();
	Motor(0);
	cleanup(NULL);
	}

/* Aufraeumen */
cleanup(mes)
char *mes;
	{
	if (mes) puts(mes);

	if (!error) CloseDevice(diskreq);
        if (diskreq) DeleteStdIO(diskreq);
        if (diskport) DeletePort(diskport);
	if (filemem) FreeMem(filemem, length);
	if (diskbuffer) FreeMem(diskbuffer, 0x40000L);
	exit(0);
	}       /* end of main */
