/*
 * quicken() -- replace *, /, % MC68010 function calls with in-line MC68020 code
 *
 * The function of quicken is to replace silly MC68010 function calls with
 * in-line MC68020 code that accomplishes the same thing.  Of course, this
 * is very difficult in general, but we are only interested in some obvious
 * cases here.  For example, the 68010 C library contains a set of routines
 * for doing 32 bit integer arithmetic, because the 68010 isn't REALLY a 32
 * bit processor (despite what Motorola might say).  Most of these routines
 * are called with the following code sequence:
 *
 * <push operand onto stack>
 * <push operand onto stack>
 * jsr <function>
 * addql #8,sp
 *
 * where the call and stack adjustment occupy 4 words.  In this program each
 * such routine to be fixed is called a 'task'.  We seek to replace those 4
 * words with the following code:
 *
 * movl sp@+,d0		| get second arg off stack, adjusting stack
 * <appropriate instruction> sp@+,d0	| <op> it with first argument
 * nop			| one word left over !
 *
 * where <appropriate instruction is a MC68020 instruction that does what
 * <function> did before.
 */

/*
 * History:
 * ========
 * 860602 jht -- Initial adaptation of Roger Scott's a.out quickener.
 */

#include "../h/param.h"
#include "../s32/cpu.h"		/* K_A */

#ifdef	QUICKEN
#ifdef	M68020

/*
 * Defined constants for various MC68020 instructions
 */
#define	JSR			0x4eb9

/*
 * addql #8,sp
 */
#define	ADDQL			0x508f

/*
 * movl sp@+,d0
 */
#define	MOVL_SP_AT_PLUS_D0	0x201f

/*
 * mulul sp@+,d0:d0
 */
#define	MULUL_1			0x4c1f
#define	MULUL_2			0x000

/*
 * mulsl sp@+,d0:d0
 */
#define	MULSL_1			0x4c1f
#define	MULSL_2			0x800

/*
 * divul sp@+,d1:d0
 */
#define	DIVUL_1			0x4c5f
#define	DIVUL_2			0x1

/*
 * divsl sp@+,d1:d0
 */
#define	DIVSL_1			0x4c5f
#define	DIVSL_2			0x801

#define	NOP			0x4e71
#define	MOVL_D1_D0		0x2001

/*
 * A 'task' is a subroutine call to be replaced with in-line code.
 * The task structure contains the target address of the subroutine call
 * and the instruction words with which to replace the call.
 * The routine field is used to look up the target addresses in the symbol
 * table.
 */

struct task
{
	int		count;		/* # that have we fixed		  */
	unsigned short	jsr_target_h,	/* 2nd and 3rd words of JSR-inst. */
			jsr_target_l;
	unsigned short	in_line_0,	/* Replacement machine-code	  */
			in_line_1,
			in_line_2,
			in_line_3;
	char		routine[65];	/* Name of routine in symbol table */
} tasks[] = {

 /*
  * We constrain ourselves to SIMPLE arithmetic functions.
  */
 {0, 0, 0, MOVL_SP_AT_PLUS_D0, MULSL_1, MULSL_2, NOP,		 "lmul"},
 {0, 0, 0, MOVL_SP_AT_PLUS_D0, MULUL_1, MULUL_2, NOP,		"ulmul"},

 {0, 0, 0, MOVL_SP_AT_PLUS_D0, DIVSL_1, DIVSL_2, NOP,		 "ldiv"},
 {0, 0, 0, MOVL_SP_AT_PLUS_D0, DIVUL_1, DIVUL_2, NOP,		"uldiv"},
 {0, 0, 0, MOVL_SP_AT_PLUS_D0, DIVSL_1, DIVSL_2, MOVL_D1_D0,	 "lrem"},
 /*
  * This line will break the smoct driver on power-up, this needs to
  * be investigated....
  *
  */
 {0, 0, 0, MOVL_SP_AT_PLUS_D0, DIVUL_1, DIVUL_2, MOVL_D1_D0,	"ulrem"}
};

#define	NUM_TASKS	(sizeof(tasks) / sizeof(struct task))

#define	SYM_SIZE	sizeof(struct sym_entry)

/*
 * getSymVal  --  (Quietly) return the value associated
 *		  with the debugger's symbol table entry.
 */
 u_long
getSymVal(symNamep)
	char	* symNamep;
{
	u_long		showsym();	/* C.f., s32/debugger.c */

	return (u_long) showsym(symNamep, 0);
}

extern	short	okToQuicken;	/* 0==>Disable; 1==>Enable Quicken	      */
extern	short	printSymVal;	/* 0==>Disable; 1==>Enable Quicken-blab	      */
extern	short	quickenBlab;	/* 0==> No blab; !0==> Blab; Cf, s32/startup.c*/
extern	short	quickenSummary;	/* 0==>No blab; !0==>Blab; Cf, s32/startup.c  */

/*
 * quicken  --  update the kernel TEXT to 68020 code, if appropriate.
 *
 * NOTE:  We presume that the kernel TEXT is writable!
 */
quicken()
{
/*D7*/	register int		  i;
/*D6*/	register int		  j;
/*D5*/	register u_long		  symVal;
/*A5*/	register short		* p;
/*A4*/	register char		* s;
/*A3*/	register struct task	* t;
/*D4*/	register int		  text_size;
	extern	char		  etext;


	if (!okToQuicken)
		return 1;	/* Request denied:  not an error */

	if (quickenBlab)
		printf("quicken/I-0:  Symbol lookup for target routines\n");

	/*
	 * See if the task-symbol is a kernel routine.
	 * If we have a match, save the value
	 * for the JSR target address.
	 */
	for (j = 0, t = tasks; j < NUM_TASKS; j++, t++)
	{
		/*
		 * Consult the debugger's kernel symbol table.
		 */
		if (symVal = getSymVal(t->routine))
		{
			/*
			 * Got a match...
			 * Insert the target address into our task table.
			 */
			t->jsr_target_h = symVal >> 16;
			t->jsr_target_l = symVal;
		}
	}
#if 1
	/*
	 * Enunciate the routines found (for the sake of an audit trail.)
	 */
	if (printSymVal) {
		printf("quicken/I-1:  Target routines + 68020 substitutions over range 0x%X-0x%X:\n",
			K_A, (u_long)&etext);
		for (j = 0, t = tasks; j < NUM_TASKS; j++, t++)
			printf("    %7s (@ %x%04x) --> %04x %04x %04x %04x\n",
				t->routine,
				t->jsr_target_h,
				t->jsr_target_l,
				t->in_line_0,
				t->in_line_1,
				t->in_line_2,
				t->in_line_3);
	}
#endif 1

	/*
	 * Scan the kernel TEXT, looking
	 * for pattern matches upon the <function> calls.
	 *
	 * NOTE:  We presume that the kernel TEXT
	 * is (temporarily) writable!
	 */
	if (quickenBlab)
		printf("quicken/I-2:  Substituting inline code for function calls\n");

	text_size = (u_long)&etext - K_A;

	for (p = (short *)K_A, i = text_size / 2; --i;)
	{
		/* Template is:
		 *
		 *	jsr
		 *	xxx
		 *	xxx
		 *	addql #8,sp
		 */
		if (p[0] == JSR && p[3] == ADDQL)
		{
		    /*
		     * See if any of the tasks match
		     */
		    for (j = 0, t = tasks; j < NUM_TASKS; j++, t++)
			if (p[1] == t->jsr_target_h && p[2] == t->jsr_target_l)
			{
			    /*
			     * Replace with 68020 code.
			     */
			    *p++ = t->in_line_0;
			    *p++ = t->in_line_1;
			    *p++ = t->in_line_2;
			    *p++ = t->in_line_3;
			    t->count++;		/* Tally updates */
			    asm("movl	#8,d0");	/* Clear cache */
			    asm(".word	0x4e7a,0x0002"); /* movec d0,cacr */
			    break;
			}
		    if (j == NUM_TASKS) p++;	/* Did't find anything */

		} else p++;
	}

	/*
	 * Sumarize the changes
	 */
	if (quickenSummary) {
		printf("quicken/I-3: Summary of function calls that are now inline 68020 instructions:\n");
		for (i = 0; i < NUM_TASKS; i++)
			if (tasks[i].count > 0)
			      printf("    %4d %7ss\n",
				tasks[i].count,
				tasks[i].routine);
	}
	return 0;	/* Say all is well... */
}
#endif	M68020
#endif	QUICKEN
