#include	<stdio.h>
#include	<libraries/dos.h>
#include	<hardware/custom.h>
#include	<hardware/dmabits.h>
#include	<exec/types.h>
#include	<exec/memory.h>
#include	<exec/execbase.h>
#include	<functions.h>
#include	<exec/lists.h>
#include	<graphics/gfxmacros.h>


#define PREFERENCES_OFFSET	0x2   /* offset to an index to PREFS */
#define EMULATOR_ENTRY_POINT	0x298 /* actual start of the emulator */
#define DOSSIZE_OFFSET		0x3b  /* offset into ATPREFS for dossize */
#define PRIVHNDLRSIZE		0x38
#define DATASIZE		0xa3ce

#define HUNK_CODE		0x3e9L

char	*em = 0;		/* pointer to loaded emulator code */
struct Segment	*em_seg = 0;	/* pointer to loaded emulator segment */
char	*text = 0;		/* pointer to emulator text */
char	*data = 0;		/* pointer to emulator data */
char	*dos = 0;		/* pointer to area for dos */
char	*total_chip = 0;	/* pointer to a temp area for sizing */

long	tsize;			/* amount of memory for emulator text */
long	dsize = DATASIZE;	/* amount of memory for emulator data */
long	dossize;		/* amount of memory allocated to dos */ 
long	totalsize;		/* total text+data */

long	fsize;			/* amount of available fast memory */
long	csize;			/* amount of available chip memory */
long	size;			/* larger of free chip or fast mem */

long	tattributes;		/* AllocMem attributes for emulator text */
long	dattributes;		/* AllocMem attributes for user DOS space */

long	preferences_offset;	/* offset to data area for ATPREFS */
long	dossize_offset;		/* offset to ATPREFS requested dos size */
int	preferences_loaded;	/* flag to indicate that we read ATPREFS */

struct FileHandle *handle = 0;	/* file handle, general use */
char	pref_buf[200];		/* buffer to hold preferences data */

extern struct ExecBase	*SysBase;

main()
{
/* The declaration for PrivHndlr should really be a
 * function returning void, but we declare it this way because
 * we are copying the routine into a new place in RAM.
 */
	extern char	PrivHndlr[];	
	extern int	startup_text();

	char	s[10];	/* input buffer for testing for abort */

	/* The first thing we have to do is to ensure that the emulator
	 * loads into chip ram. This *could* have been done by 
	 * running "FixHunk" on the AT1 file, but since some people
	 * don't have that, we achieve the same result by writing 0x40 at 
	 * an offset of 0x14 bytes from the start of the file. This sets 
	 * bit 30 of the hunk size longword. 
	 *
	 * See page 283 of the Bantam AmigaDOS reference manual for more 
	 * details.
	 */
	handle = Open("AT1",(long)MODE_OLDFILE);
	if (!handle)
		errexit("Can't find AT1");

	if (Seek(handle,(long)0x14,(long)OFFSET_BEGINNING) == -1)
		errexit("File Seek error: AT1");

	/* we use the preferences buffer to hold the value to insert */
	pref_buf[0] = (char)0x40;
	if (Write(handle, &pref_buf[0], (long)1) == -1)
		errexit("File Write error: AT1");

	/* Now seek to the size of the text segment. */
	if (Seek(handle,(long)0x1c,(long)OFFSET_BEGINNING) == -1)
		errexit("File Seek error: AT1");

	/* See how big the text segment is. Use tsize as a 4 byte buffer,
	 * and read 4 bytes. This is the size of the segment in long 
	 * words. Adjust for a byte count, and add 8 bytes
	 * fudge factor for the segment overhead. (Size and 
	 * pointer to next)
	 */
	if (Read(handle,&tsize,(long)sizeof(long)) == -1)
		errexit("File Read error: AT1");
	Close(handle);
	handle = 0;

	tsize = (tsize << 2) + 8;	/* 8 bytes for segment overhead */


	/* Read the default Transformer preferences from ATPREFS */
	handle = Open("ATPREFS",(long)MODE_OLDFILE);
	if (!handle) {
		printf("Can't find ATPREFS. Using Default values.\n");
		dossize = 640 * 1024;
		preferences_loaded = 0;
	}
	else {
		Read(handle,pref_buf,200L);
		Close(handle);
		handle = 0;
		preferences_loaded = 1;

		/* see how much DOS space the user has requested */
		dossize = 1024 * (((pref_buf[DOSSIZE_OFFSET] & 0xff) << 8) +
				   (pref_buf[DOSSIZE_OFFSET+1] & 0xff)) + 62;
	}

	/* We don't allow interruptions from now on from other tasks */
	Forbid();

	/* make an unreasonably large memory allocation request to 
	 * flush any libraries out which are no longer in use.
	 */
	(void)AllocMem(0x1000000L, (long)MEMF_CHIP);
	(void)AllocMem(0x1000000L, (long)MEMF_FAST);


	/* We have to determine how much free memory we will
	 * have available for DOS. We need dsize + tsize bytes in 
	 * CHIP ram. Once that's obtained, we attempt to allocate the 
	 * amount of memory requested in ATPREFS for DOS. This can be 
	 * in either CHIP or FAST ram, depending on which we have more of.
	 */ 

	tsize = (tsize + 7) & ~7;	/* round up to 8 byte boundary */
	dsize = (dsize + 7) & ~7;	/* round up to 8 byte boundary */

	totalsize = tsize+dsize;

	/* allocate all we would need for both chip and text areas */
	total_chip = AllocMem(totalsize, (long)(MEMF_CHIP|MEMF_PUBLIC));

	/* we now need to see how much we have available for DOS */
	fsize = AvailMem((long)(MEMF_FAST|MEMF_LARGEST)) & ~07;
	csize = AvailMem((long)(MEMF_CHIP|MEMF_LARGEST)) & ~07;

	/* have to leave some chip ram for display use */
	csize -= 2048;

	/* If we have more FAST memory than we asked for for DOS use,
	 * or if we have more free FAST memory than CHIP, use FAST
	 * for DOS. Otherwise use CHIP.
	 */
	if ((fsize >= dossize) || (fsize >= csize)) {
		dattributes = MEMF_FAST|MEMF_PUBLIC;
		size = fsize;
	}
	else {
		dattributes = MEMF_CHIP|MEMF_PUBLIC;
		size = csize;
	}

	/* Free up the chunk we allocated above. We only allocated
	 * it to determine how much ram we would have left after
	 * allocation of space for emulator text and data.
	 */
	FreeMem(total_chip, totalsize);
	total_chip = NULL;

	/* Load the emulator segment */
	em_seg = LoadSeg("AT1");
	if (!em_seg)
		errexit("Can't load Emulator");

	/* Convert the BPTR to the loaded segment to a pointer,
	 * then increment it past the pointer to the "next" segment.
	 */
	em = (char *)BADDR(em_seg) + 4;


	/* If we were able to read ATPREFS, copy the preferences into 
	 * the appropriate place in the text area.
	 */
	if (preferences_loaded) {
		preferences_offset = *((long *)(&em[PREFERENCES_OFFSET]));
		movmem(pref_buf,&em[preferences_offset],(long)200);
	}

	/* We install a modified version of Scott Turner's DeciGel 
	 * program, which allows us to use a 68010 or 68020 processor.
	 */
	movmem(&PrivHndlr[0], 
	       &em[EMULATOR_ENTRY_POINT-PRIVHNDLRSIZE], (long)PRIVHNDLRSIZE);

	/* 'text' needs to point to the start of the emulator.
	 */
	text = &em[EMULATOR_ENTRY_POINT];

	/* now get some space for the emulator data */
	data = AllocMem(dsize, (long)MEMF_CHIP|MEMF_PUBLIC);
	if (!data)
		errexit("Can't get RAM for Emulator data\n");

	/* We'll only get the lesser of 'dossize' or 'size' ramspace
	 * for DOS.
	 */
	dossize = (dossize < size) ? dossize : size;
	dos = AllocMem(dossize,dattributes);

	/* Show us where we've allocated memory and how much of it there
	 * is. Also, a little ego boosting here.
	 */
	printf("\nPSTransformer, Copyright (c) 1987 by Phil Staub\n\n");
	printf("Startup module for AmigaTransformer to provide support ");
	printf("for extended memory,\n");
	printf("68010/68020, and multiple operating system versions.\n\n");
	printf("\nAmigaTransformer Memory allocations\n");
	printf("Emulator code : %10ld bytes at %08lx\n", tsize, text);
	printf("Emulator data : %10ld bytes at %08lx\n", dsize, data);
	printf("DOS User Space: %10ld bytes at %08lx\n\n", dossize, dos);

	/* now offer the chance to bail out */

	printf("Continue? [y/n] ");
	gets(&s[0]);
	if (s[0] == 'n' || s[0] == 'N')
		errexit("AmigaTransformer exiting\n");

	OFF_SPRITE;			/* don't need the mouse pointer */
	OFF_DISPLAY; 			/* turn off the display dma */

	/* now branch off to an assembly language routine which
	 * installs the Privileged instruction  handler, sets up the 
	 * registers the way the emulator wants them, then starts the 
	 * emulator.
	 */
	startup_text();
}

errexit(s)
char	*s;
{
	printf("%s\n",s);
	if (em_seg)
		(void)UnLoadSeg((struct Segment *)em_seg);
	if (total_chip)
		FreeMem(total_chip, totalsize);
	if (data)
		FreeMem(data, dsize);
	if (dos)
		FreeMem(dos, dossize);
	if (handle)
		Close(handle);
	Permit();
	exit(1);
}
