/*
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1997, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and either the original sources or derived sources 
 * are distributed along with any executables derived from the originals.
 *
 * The author is not responsible for any damages that may arise from use
 * of this software, either idirect or consequential.
 *
 * V2.05 June 2002
 * David Lindauer, camille@bluegrass.net
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
#include        <stdio.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include        "gen386.h"
#include				"diag.h"

/*
 *      this module handles the allocation and de-allocation of
 *      temporary registers. when a temporary register is allocated
 *      the stack depth is saved in the field deep of the address
 *      mode structure. when validate is called on an address mode
 *      structure the stack is popped until the register is restored
 *      to it's pre-push value.
 */
extern int cf_freedata,cf_freeaddress,cf_freefloat;

	AMODE push[1],pop[1];
int             max_data;			/* Max available */
int regs[3];
int pushcount;
char pushedregs[40];
char readytopop[40];
int lastreg1,lastreg2;
void regini(void)
{
	initstack();
}
void gen_push(int reg, int rmode, int flag)
/*
 *      this routine generates code to push a register onto the stack
 */
{       AMODE    *ap1;
        ap1 = xalloc(sizeof(AMODE));
        ap1->preg = reg;
        ap1->mode = rmode;
				ap1->length = 4;
				if (rmode == am_freg) {
			FLOAT;
				}
				else {
        	OCODE *new = xalloc(sizeof(OCODE));
        	new->opcode = op_push;
        	new->oper1 = ap1;
        	add_peep(new);
				}
}

void gen_pop(int reg, int rmode, int flag)
/*
 *      generate code to pop the primary register in ap from the
 *      stack.
 */
{       AMODE    *ap1;
        ap1 = xalloc(sizeof(AMODE));
        ap1->preg = reg;
        ap1->mode = rmode;
				ap1->length = 4;
				if (rmode == am_freg) {
			FLOAT;
				}
				else {
        	OCODE *new = xalloc(sizeof(OCODE));
        	new->opcode = op_pop;
        	new->oper1 = ap1;
        	add_peep(new);
				}
}
void pushregs(unsigned mask)
{
	int i;
	int umask = 0x08000;
	for (i=0; i < 4; i++) {
		if (umask & mask)
			gen_push(i,am_dreg,1);
		umask >>=1;
	}
	umask = 0x080;
	for (i=4; i < 8; i++) {
		if (umask & mask)
			gen_push(i,am_dreg,1);
		umask >>=1;
	}
}
/* This is ONLY called from the return.  Calling from any other place
 * will leave the stack depth unpredictable... */
void popregs(unsigned mask)
{
	int i;
	int umask = 0x800;
	for (i=7; i >= 4; i--) {
		if (umask & mask) {
			gen_pop(i,am_dreg,1);
		}
		umask >>=1;
	}
	umask = 0x8;
	for (i=3; i >= 0; i--) {
		if (umask & mask) {
			gen_pop(i,am_dreg,1);
		}
		umask >>=1;
	}
}
int dregbase;
void initstack(void)
/*
 *      this routine should be called before each expression is
 *      evaluated to make sure the stack is balanced and all of
 *      the registers are marked free.
 */
{
			if (pushcount)
				gen_code(op_add,makedreg(ESP),make_immed(pushcount * 4));
			pushcount = 0;
      max_data = cf_freedata-1;
			regs[0]=regs[1]=regs[2]=0;
}
int next_dreg(void)
{
	int reg;
	if (!regs[0])
		return regs[0]*3;
	else
		if (!regs[1])
			return regs[1]*3+1;
		else
			if (!regs[2])
				return regs[2]*3+2;
			else 
				if (lastreg1 == 0)
					if (lastreg2 == 1)
						return regs[2]*3+2;
					else
						return regs[1]*3+1;
				else if (lastreg1 == 1)
					if (lastreg2 == 0)
						return regs[2]*3+2;
					else
						return regs[0]*3;
				else if (lastreg1 == 2)
					if (lastreg2 == 0)
						return regs[1]*3+1;
					else
						return regs[0]*3;						
}
AMODE    *temp_data(void)
/*
 *      allocate a temporary data register and return it's
 *      addressing mode.
 */
{       AMODE    *ap = xalloc(sizeof(AMODE));
				int reg = next_dreg();
				int rp3 = reg %3;
				if (regs[rp3]++) {
					gen_push(rp3,am_dreg,0);
					pushedregs[pushcount] = rp3;
					readytopop[pushcount++] = 0;
				}
				lastreg2 = lastreg1;
				lastreg1 = rp3;
    	  ap->mode = am_dreg;
			  ap->preg = rp3;
				ap->tempflag = TRUE;
				ap->length = 4;
        return ap;
}

void tempaxdx(void)
{
   if (regs[2]++) {
      gen_push(EDX,am_dreg,0) ;
      pushedregs[pushcount] = EDX ;
      readytopop[pushcount++] = 0 ;
   }
   if (regs[0]++) {
      gen_push(EAX,am_dreg,0) ;
      pushedregs[pushcount] = EAX ;
      readytopop[pushcount++] = 0 ;
   }
}
AMODE *tempcx(void)
{
   AMODE *ap = xalloc(sizeof(AMODE)) ;
   if (regs[1]++) {
      gen_push(ECX,am_dreg,0) ;
      pushedregs[pushcount] = ECX ;
      readytopop[pushcount++] = 0 ;
   }
    	  ap->mode = am_dreg;
           ap->preg = ECX;
				ap->tempflag = TRUE;
				ap->length = 4;
        return ap;
}
/*
 *
 */
void freedata(int dreg)
{
	if (dreg < cf_freedata && dreg >=0) {
		if (regs[dreg]) {
			int i;
			for (i=pushcount-1; i >= 0; i--)
				if (pushedregs[i] == dreg)
					break;
			if (i >= 0) {
				readytopop[i] = 1;
				while (readytopop[pushcount-1]) {
					pushcount--;
					gen_pop(pushedregs[pushcount],am_dreg,0);
				}
			}
			regs[dreg]--;
			
		}
	}
}
void freeaxdx(void)
{
   if (pushedregs[pushcount-1] == EAX && pushedregs[pushcount-2] == EDX) {
      regs[EAX]--;
      regs[EDX]-- ;
      pushcount -= 2 ;
      gen_code(op_add,makedreg(ESP),make_immed(8)) ;
   } else {
      freedata(EDX) ;
      freedata(EAX);
   }
}
void freeop(AMODE *ap)
/*
 *      release any temporary registers used in an addressing mode.
 */
{
        if( ap->mode == am_dreg)
					freedata(ap->preg);
		 		else if (ap->mode == am_indisp)		
					freedata(ap->preg);
		 		else if (ap->mode == am_indispscale) {
					freedata(ap->preg);
					freedata(ap->sreg);
            } else if (ap->mode == am_axdx) {
               freeaxdx() ;
            }
}
