/*
 * Genfunc: Generates the assembler to put the C parameters on the
 * stack to the required registers, and call the RKM procedure.
 *
 * Copyright 1988 by J.A. Lydiatt.  Permission is given to freely
 * distribute and/or modify this code for non-commmercial use.
 * Maintenance Notes:
 *   18Jun88 - V1.0 Created by Jal.
 */

#include "genstubs.h"
#define PERMREGS	(~(0x0303))
#define REGA6		  (0x4000)

typedef struct xdefnode {
	struct xdefnode *next;
	char   name[1];
	} XDEFNODE;


/*
 * External variables:
 */

extern char base[];			/* ##base _SysBase for example	*/
extern int  bias;			/* ##bias 30			*/
extern char Section[];			/* SECTION "<Section>",CODE	*/
extern FUNCINFO function;		/* The current function.	*/

static XDEFNODE *xdeflist = NULL;
static FILE *fo;

/*
 * genfunc: support code to generate the function.
 */

static int getrange( mask, frombit, tobit )
register unsigned int mask;
int frombit, tobit;
{
	register int bit;

	for (bit=frombit; bit <= tobit; ++bit ){
		if ( !(mask & 1 << bit ))
			break;
	}

	return --bit;
}

static int makelist( list, mask, frombit, tobit, idchar )
char list[];
register unsigned int mask;
int frombit, tobit;
char idchar;
{
	int bit, endbit, nperm, endrange;
	char tmp[32];
	extern int sprintf();
	extern char *strcat();

	nperm = 0;
	for ( bit=frombit; bit <= tobit; ++bit ){
		if ( mask & 1 << bit ){
			endrange = getrange( mask, bit, tobit );
			if ( endrange > bit )
				(void) sprintf( tmp, "/%c%d-%c%d",
					idchar, bit % 8,
					idchar, endrange % 8);
			else
				(void) sprintf( tmp, "/%c%d",
					idchar, bit % 8 );
			(void)strcat( list, tmp ); 
			nperm += endrange - bit + 1;
			bit = endrange;
		}
	}
	return nperm;
}

static int getperm( mask, list )
unsigned int mask;
char *list;
{
	char tmplist[64];
	int nperm;
	extern char *strcpy();

	tmplist[0] = '\0';
	nperm =  makelist( tmplist, mask, 0, 7, 'D' );
	nperm += makelist( tmplist, mask, 8, 15, 'A' );
	if ( tmplist[0] != '\0' )
		strcpy( list, &tmplist[1] );
	else
		list[0] = '\0';
	return nperm;
}

static void gencomment( txt )
char *txt;
{
	fprintf( fo, "* %s\n", txt );
}

static void gen( code, p1, p2, p3, p4 )
char *code;
long p1, p2, p3, p4;
{
	putc( '\t', fo );
	fprintf( fo, code, p1, p2, p3, p4);
	putc( '\n', fo );
}

static void genlabel( label )
char *label;
{
	(void)fputs( label, fo );
	(void)putc( '\n', fo );
}

void genfunc()
{
	int  nperm;		/* # Permanent registers used */
	int  permmask;		/* Mask for permanent regs used. */
	int  offset;
	int  j;
	FUNCINFO *f;		/* Pointer to function info struct */ 
	char permlist[128];
	void assemble();

	/*
	 * print the current line as a comment.
	 */

	f = &function;
	sprintf( permlist, "ram:%s.a", &f->name[1] );
	if ( (fo = fopen( permlist, "w")) == NULL ) {
		fprintf( stderr, "Can't open: %s\n", permlist );
		exit( 12 );
	}

	gencomment("");
	gencomment( f->givenline );
	gencomment( "" );

	/*
	 * Start a new Section Name.
	 */

	gen( "SECTION\t%s,CODE", Section );
	gen( "XDEF\t%s", f->name );
	gen( "XREF\t%s", base );
	genlabel( f->name );

	/*
	 * Save any permanent registers used.
	 */

	nperm = getperm( f->regmask & PERMREGS | REGA6 , permlist );
	if ( nperm )
		gen( "MOVEM.L\t%s,-(a7)", permlist );

	/*
	 * Generate a move instruction for each list.
	 */

	offset = 4 + nperm * 4;
	for ( j=0; j<f->nlists; ++j ) {
		gen( "MOVEM.L\t%d(A7),%s", offset, f->reglist[j] );
		offset += f->nregs[j] * 4;
	}

	/*
	 * Generate the jump instruction, restore perm and exit.
	 */

	gen( "MOVEM.L\t%s,A6", base );
	gen( "JSR\t%d(A6)", -bias );
	if ( nperm )
		gen( "MOVEM.L\t%(a7)+,%s", permlist );
	gen( "RTS" );
	gen( "END" );

	fclose( fo );
	assemble( &f->name[1] );

}


/*
 * assemble a generated stub.
 */

#define ASSEMBLER "a68k"
#define AOPTION   "-d"

static void
assemble( stub )
char *stub;
{
	register XDEFNODE *p, *q;
	char tmp1[128];
	char tmp2[128];
	int fexecl();
	void unlink();

	sprintf( tmp1, "ram:%s.a", stub);
	sprintf( tmp2, "-oram:%s.o", stub);

	printf( "%s %s %s %s\n", ASSEMBLER, AOPTION, tmp1, tmp2); 
	fexecl( ASSEMBLER,
		AOPTION,
		tmp1,
		tmp2,
		NULL);		
	unlink( tmp1 );			
}
