#include <stdio.h>

/* SET TABS TO 4 !!!! */

/* ST2Amiga  Format-convert ST Executables to Amiga Executables.
 *
 * (C) Copyright, Dave Campbell 90/91
 *
 * This software is freely distributable.  Modified versions may be
 * redistributed provided that the modifications made are fully
 * documented as such.
 *
 * Its purpose is to aid the binary conversion of ST executables into
 * Amiga executables using the ReSource disassembler.  To do this
 * you'll need some good books such as "Advanced MSDOS Programming" by
 * Ray Duncan, and other books on GEM (books on PC-GEM are best).
 * The ST's system call TRAPs map directly to PC and GEM calls.  Eg
 * TRAP #1 is INT 21h.
 *
 * Don't forget to send me whatever you've converted (PD only)!!
 *
 * Many ST programs use very large BSS sections making them particularly
 * memory-hungry.  The ST's memory allocation scheme is totally stuffed
 * and so many programs do their own memory allocation from their huge
 * BSS section.  The people who designed the ST's OS certainly made
 * many more mistakes than Commodore did with the Amiga.
 *
 * There is a program called Dos-2-Dos which provides copying of files
 * to/from ST format disks.
 * Older ST disks are the same format as MSDOS 720k 3.5 inch, but the
 * bootblock and possibly a few other small things are slightly different.
 * All newer standard ST formatted disks will be identical to MSDOS
 * formatted disks.
 *
 * The ST does not support scatter-loading.  All sections must be
 * consecutive; CODE, DATA, BSS.  For this reason I merge the code
 * and data sections, and besides, it makes the job easy for me.
 * BSS comes out as a separate hunk -> its your responsibility to fix
 * up the dependencies.
 *
 * Why don't I space my source out more?  I feel sorry for my space bar.
 *
 * Contact address:
 *
 * David Campbell
 * 36 Hemsworth Street
 * Acacia Ridge QLD 4110
 * AUSTRALIA
 *
 * email: dave@csis.dit.csiro.au
 *
 * If you haven't heard of ReSource, its the ultimate disassembler.
 * For ReSource contact:
 *
 * In OZ: Glen McDiarmid                    In the US: The Puzzle Factory
 *        28 Marginson Street               PO Box 986
 *        Ipswich QLD 4305                  Veneta OR 97487
 *        AUSTRALIA                         USA
 *
 *        (07) 812-2963                        (503) 935 3709
 */

struct st_header {
	unsigned short branch;
	unsigned long code_size;
	unsigned long data_size;
	unsigned long bss_size;
	unsigned long sym_size;
	unsigned long xx;
	unsigned long flag0;
	unsigned short xxxx;
};

struct symbol {
	unsigned long sym_name;
	unsigned long sym_name2;									/* 8 character symbols */
	unsigned short flags;
	unsigned long offset;
};

main(argc,argv)
int argc;
char **argv;
{
	struct st_header ah;
	unsigned long codedata_size,bss_size;						/* in longwords, not bytes */
	unsigned long first_long,count;
	unsigned char b;
	long apos;
	int r;
	unsigned long l,num_hunks;
	FILE *inf,*outf;
	char *mem;
	if(argc!=3) {
usage:
		printf(	"\2335;33mST2Amiga\2330;31m 1.1"
				"    Format-convert executables from ST -> Amiga.\n"
				"                For use with the ReSource disassembler.\n"
				"                by Dave Campbell (dave@csis.dit.csiro.au)."
				"\n\n\2335;33mUSAGE: ST2Amiga <ST-Exe> <Amiga-Exe>"
				"\2330;31m\n\n");
		exit(0);
	}
	inf=fopen(argv[1],"r");
	if(!inf) {
		printf("Error reading file %s.\n",argv[1]);
		exit(0);
	}
	outf=fopen(argv[2],"w");
	if(!outf) {
		printf("Error writing file %s.\n",argv[2]);
		exit(0);
	}
	r=fread((char *)&ah,1,sizeof(ah),inf);
	if(r!=sizeof(ah)) {
inv_atari_exe:
		printf("Invalid atari executable %s.\n",argv[1]);
		exit(0);
	}
	if(ah.branch!=0x601a)
		goto inv_atari_exe;										/* anti-goto people go jump */

	printf("Code size: $%lx\n",ah.code_size);
	printf("Data size: $%lx\n",ah.data_size);
	printf("Bss  size: $%lx\n",ah.bss_size);
	if(!(ah.flag0&1))
		printf("Warning: This Atari executable assumes a cleared heap.\n");

	codedata_size=(ah.code_size+ah.data_size+3)>>2;				/* up to next longword */
	bss_size=(ah.bss_size+3)>>2;

	l=0x03f3; fwrite((char *)&l,sizeof(long),1,outf);			/* hunk_header */
	l=0x0000; fwrite((char *)&l,sizeof(long),1,outf);			/* end of name list */
	num_hunks=0;
	if(codedata_size) ++num_hunks;
	if(bss_size)  ++num_hunks;
	fwrite((char *)&num_hunks,sizeof(long),1,outf);				/* table size */
	l=0x0000; fwrite((char *)&l,sizeof(long),1,outf);			/* first hunk */
	--num_hunks;
	fwrite((char *)&num_hunks,sizeof(long),1,outf);				/* last hunk */
	++num_hunks;
	if(codedata_size)											/* size of each hunk */
		fwrite((char *)&codedata_size,sizeof(long),1,outf);
	if(bss_size)
		fwrite((char *)&bss_size,sizeof(long),1,outf);
	if(codedata_size) {
		l=0x03e9; fwrite((char *)&l,sizeof(long),1,outf);		/* hunk_code */
		fwrite((char *)&codedata_size,sizeof(long),1,outf);
		mem=(char *)malloc(codedata_size<<2);
		if(!mem) {
			printf("Cannot allocate %d bytes of memory.\n",codedata_size<<2);
			exit(0);
		}
		r=fread(mem,1,ah.code_size+ah.data_size,inf);
		if(r!=ah.code_size+ah.data_size) {
			free(mem);
			goto inv_atari_exe;
		}
		fwrite(mem,1,codedata_size<<2,outf);
		free(mem);
	}
	if(ah.sym_size) {											/* symbols are present */
		struct symbol sym;
		int hunk_written=0;
		count=ah.sym_size/sizeof(sym);
		while(count) {
			r=fread((char *)&sym,1,sizeof(sym),inf);
			if(r!=sizeof(sym))
				goto inv_atari_exe;
			if(!(sym.flags&0x4000)) {
				if(!hunk_written) {
					l=0x03f0; fwrite((char *)&l,sizeof(long),1,outf);
																/* hunk_symbol */
					hunk_written=1;								/* never again */
				}
				l=0x0002; fwrite((char *)&l,sizeof(long),1,outf);
																/* length of symbols in longs */
/*				printf("%04x %s\n",sym.flags,
					(sym.flags=0,(char *)&sym.sym_name)); */
				fwrite((char *)&sym.sym_name,1,8,outf);
				fwrite((char *)&sym.offset,sizeof(long),1,outf);
			}
			count--;
		}
		l=0x0000; fwrite((char *)&l,sizeof(long),1,outf);		/* terminator */
	}
	r=fread((char *)&first_long,1,sizeof(long),inf);			/* read first reloc long */
	if(r!=sizeof(long))
		goto inv_atari_exe;
	if(first_long) {
		count=1;
		l=0x03ec; fwrite((char *)&l,sizeof(long),1,outf);		/* hunk_reloc32 */

		apos=ftell(inf);
		r=fread((char *)&b,1,1,inf);							/* first pass to count */
		if(r!=1)
			goto inv_atari_exe;
		while(b) {
			if(b>=2 && b<255)									/* only count the relocs */
				++count;
			fread((char *)&b,1,1,inf);
		}
		fwrite((char *)&count,1,sizeof(long),outf);
		l=0; fwrite((char *)&l,sizeof(long),1,outf);			/* reloc on hunk 0 */
		fwrite((char *)&first_long,1,sizeof(long),outf);
		fseek(inf,apos,0);										/* back to where we were */
		r=fread((char *)&b,1,1,inf);
		if(r!=1)
			goto inv_atari_exe;

		/* ST relocation scheme */

		while(b) {												/* 0 -> end of relocation */
			if(b==1)
				first_long+=254;								/* 1 -> skip 254 bytes */
			else if(b&1)
				goto inv_atari_exe;								/* other odds undefined */
			else { 												/* other evens skip bytes and relocate */
				first_long+=b;
				fwrite((char *)&first_long,sizeof(long),1,outf);
			}
			r=fread((char *)&b,1,1,inf);
			if(r!=1)
				goto inv_atari_exe;
		}
		l=0x0000; fwrite((char *)&l,sizeof(long),1,outf);		/* end of reloc */
		l=0x03f2; fwrite((char *)&l,sizeof(long),1,outf);		/* hunk_end */
	}
	if(bss_size) {
		l=0x03eb; fwrite((char *)&l,sizeof(long),1,outf);		/* hunk_bss */
		fwrite((char *)&bss_size,sizeof(long),1,outf);
		fseek(inf,ah.bss_size,1);
	}
	l=0x03f2; fwrite((char *)&l,sizeof(long),1,outf);			/* hunk_end */

	/* CLOSE UP SHOP */

	fclose(inf);
	fclose(outf);
}
