/*  Code generator for SAB c16x microcontrollers.               */

#include "supp.h"

static char FILE_[]=__FILE__;

/*  Public data that MUST be there.                             */

/* Name and copyright. */
char cg_copyright[]="vbcc code-generator for c16x V0.0 (c) in 1998 by Volker Barthelmann";

/*  Commandline-flags the code-generator accepts                */
int g_flags[MAXGF]={VALFLAG,VALFLAG,0,0,
                    0,0,0,0};
char *g_flags_name[MAXGF]={"cpu","fpu","no-delayed-popping","const-in-data",
                           "merge-constants","--","--","--"};
union ppi g_flags_val[MAXGF];

/*  Alignment-requirements for all types in bytes.              */
zlong align[16];

/*  Alignment that is sufficient for every object.              */
zlong maxalign;

/*  CHAR_BIT of the target machine.                             */
zlong char_bit;

/*  Sizes of all elementary types in bytes.                     */
zlong sizetab[16];

/*  Minimum and Maximum values each type can have.              */
/*  Must be initialized in init_cg().                           */
zlong t_min[32];
zulong t_max[32];

/*  Names of all registers.                                     */
char *regnames[MAXR+1]={"noreg","R0","R1","R2","R3","R4","R5","R6","R7",
                        "R8","R9","R10","R11","R12","R13","R14","R15"};

/*  The Size of each register in bytes.                         */
zlong regsize[MAXR+1];

/*  Type which can store each register. */
struct Typ *regtype[MAXR+1];

/*  regsa[reg]!=0 if a certain register is allocated and should */
/*  not be used by the compiler pass.                           */
int regsa[MAXR+1];

/*  Specifies which registers may be scratched by functions.    */
int regscratch[MAXR+1]={0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1};

struct reg_handle empty_reg_handle={0};

/****************************************/
/*  Some private data and functions.    */
/****************************************/

static char *bregnames[]={"nobreg","RL0","RL1","RL2","RL3","RL4","RL5","RL6","RL7",
 "e8","e9","e10","e11","e12","e13","e14","e15","e16"
};

static long malign[16]=  {1,1,2,2,2,2,2,1,2,1,1,1,2,1};
static long msizetab[16]={0,1,2,2,4,4,8,0,2,0,0,0,2,0};

struct Typ ltyp={INT};

#define DATA 0
#define BSS 1
#define CODE 2
#define CDATA 3

static int section=-1,newobj,scnt;
static char *codename="SCODE\tSECTION CODE WORD PUBLIC 'CPROGRAM'\n",*ecode="SCODE\tends\n",
            *dataname="SDATA\tSECTION DATA WORD PUBLIC\n",*edata="SDATA\tends\n",
            *bssname="SBSS\tSECTION DATA WORD PUBLIC\n",*ebss="SBSS\tends\n",
            *cdataname="SCDATA\tSECTION DATA WORD PUBLIC\n",*ecdata="SCDATA\tends\n";

static char sec_end[32];
static int is_const(struct Typ *);
/* (user)stack-pointer, pointer-tmp, int-tmp; reserved for compiler */
static const int sp=1,tp=2,ti=6;
static int is_const(struct Typ *);
static void pr(FILE *,struct IC *);
static void function_top(FILE *,struct Var *,long);
static void function_bottom(FILE *f,struct Var *,long);

#define isreg(x) ((p->x.flags&(REG|DREFOBJ))==REG)

static long loff,stackoffset,notpopped,dontpop;

static char *x_t[]={"?","b","","","","","","","","","","","","","",""};
static char *ccs[]={"z","nz","lt","ge","le","gt"};
static char *logicals[]={"or","xor","and"};
static char *arithmetics[]={"shl","shr","add","sub","mul","div","mod"};
static char *dct[]={"","db","dw","dw","ddw","ddw","ddw","ddw","ddw"};
static pushedsize,pushorder=2;
static char *idprefix="_",*labprefix="l";

static struct fpconstlist {
    struct fpconstlist *next;
    int label,typ;
    union atyps val;
} *firstfpc;

static int addfpconst(struct obj *o,int t)
{
    struct fpconstlist *p=firstfpc;
    t&=NQ;
    if(g_flags[4]&USEDFLAG){
        for(p=firstfpc;p;p=p->next){
            if(t==p->typ){
                eval_const(&p->val,t);
                if(t==FLOAT&&zdeqto(vdouble,zf2zd(o->val.vfloat))) return(p->label);
                if(t==DOUBLE&&zdeqto(vdouble,o->val.vdouble)) return(p->label);
            }
        }
    }
    p=mymalloc(sizeof(struct fpconstlist));
    p->next=firstfpc;
    p->label=++label;
    p->typ=t;
    p->val=o->val;
    firstfpc=p;
    return(p->label);
}
static long voff(struct obj *p)
{
  if(p->v->offset<0) return loff-zl2l(p->v->offset)+zl2l(p->val.vlong)-stackoffset+pushedsize-2;
  return zl2l(p->v->offset)+zl2l(p->val.vlong)-stackoffset;
}

static void probj2(FILE *f,struct obj *p,int t)
/*  Gibt Objekt auf Bildschirm aus                      */
{
  if((p->flags&(DREFOBJ|REG))==(DREFOBJ|REG)) fprintf(f,"[");
  if(p->flags&VARADR) fprintf(f,"#");
  if((p->flags&(VAR|REG))==VAR){
    if(p->v->storage_class==AUTO||p->v->storage_class==REGISTER){
      fprintf(f,"[%s+#%ld]",regnames[sp],voff(p));
    }else{
      if(!zleqto(l2zl(0L),p->val.vlong)){printval(f,&p->val,LONG,0);fprintf(f,"+");}
      if(p->v->storage_class==STATIC&&(p->v->vtyp->flags&NQ)!=FUNKT){
	fprintf(f,"%sl%ld",labprefix,zl2l(p->v->offset));
      }else{
	fprintf(f,"%s%s",idprefix,p->v->identifier);
      }
    }
  }
  if(p->flags&REG){
    fprintf(f,"%s",regnames[p->reg]);
  }
  if(p->flags&KONST){
    if((t&NQ)==FLOAT||(t&NQ)==DOUBLE){
      fprintf(f,"%sl%d",labprefix,addfpconst(p,t));
    }else{
      fprintf(f,"#");printval(f,&p->val,t&NU,0);
    }
  }
  if((p->flags&(DREFOBJ|REG))==(DREFOBJ|REG)) fprintf(f,"]");
}

static void pr(FILE *f,struct IC *p)
{

}
static void function_top(FILE *f,struct Var *v,long offset)
/*  erzeugt Funktionskopf                       */
{
    int i;
    if(section!=CODE){fprintf(f,sec_end);fprintf(f,codename);section=CODE;strcpy(sec_end,ecode);}
    if(v->storage_class==EXTERN) fprintf(f,"\tpublic\t%s%s\n",idprefix,v->identifier);
    fprintf(f,"%s%s\tproc\tfar\n",idprefix,v->identifier);
    for(pushedsize=0,i=1;i<=MAXR;i++){
        if(regused[i]&&!regscratch[i]&&!regsa[i]){
            fprintf(f,"\tmov\t[-%s],%s\n",regnames[sp],regnames[i]);
            pushedsize+=2;
        }
    }
    if(offset) fprintf(f,"\tsub\t%s,#%ld\n",regnames[sp],offset);
}
static void function_bottom(FILE *f,struct Var *v,long offset)
/*  erzeugt Funktionsende                       */
{
    int i;
    if(offset) fprintf(f,"\tadd\t%s,#%ld\n",regnames[sp],offset);
    for(i=MAXR;i>0;i--){
        if(regused[i]&&!regscratch[i]&&!regsa[i]){
            fprintf(f,"\tmov\t%s,[%s+]\n",regnames[i],regnames[sp]);
        }
    }
    fprintf(f,"\trets\n");
    fprintf(f,"%s%s\tendp\n",idprefix,v->identifier);
    if(!strcmp("main",v->identifier)) fprintf(f,"\textern\t__CSTART:far\n");
}
static int is_const(struct Typ *t)
/*  Tests if a type can be placed in the code-section.  */
{
    if(!(t->flags&(CONST|STRINGCONST))){
        do{
            if(t->flags&(CONST|STRINGCONST)) return(1);
            if((t->flags&NQ)!=ARRAY) return(0);
            t=t->next;
        }while(1);
    }else return(1);
}
static int compare_objects(struct obj *o1,struct obj *o2)
{
    if(o1->flags==o2->flags&&o1->am==o2->am){
        if(!(o1->flags&VAR)||(o1->v==o2->v&&zleqto(o1->val.vlong,o2->val.vlong))){
            if(!(o1->flags&REG)||o1->reg==o2->reg){
                return(1);
            }
        }
    }
    return(0);
}

static void move(FILE *f,struct obj *q,int qr,struct obj *z,int zr,int t)
/*  Generates code to move object q (or register qr) into object z (or  */
/*  register zr).                                                       */
{
  int tr;
  t&=NQ;
  if(q&&(q->flags&(REG|DREFOBJ))==REG) qr=q->reg;
  if(z&&(z->flags&(REG|DREFOBJ))==REG) zr=z->reg;
  if(qr)
    tr=qr;
  else{
    if(zr) tr=zr; else tr=ti;
    fprintf(f,"\tmov%s\t%s,",x_t[t],(t==CHAR)?bregnames[tr]:regnames[tr]);
    if(qr) fprintf(f,"%s",(t==CHAR)?bregnames[qr]:regnames[qr]); else probj2(f,q,t);
    fprintf(f,"\n");
  }
  if(tr!=zr){
    fprintf(f,"\tmov%s\t",x_t[t]);
    if(zr) fprintf(f,"%s",(t==CHAR)?bregnames[zr]:regnames[zr]); else probj2(f,z,t);
    fprintf(f,",%s\n",(t==CHAR)?bregnames[tr]:regnames[tr]);
  }
}

static void save_result(FILE *f,int r,struct obj *o,int t)
/*  Saves result in register r!=tp to object o. May use tp. */ 
{
  if((o->flags&(REG|DREFOBJ))==DREFOBJ){
    o->flags&=~DREFOBJ;
    move(f,o,0,0,tp,POINTER);
    o->flags|=(DREFOBJ|REG);
    o->reg=tp;
  }
  move(f,0,r,o,0,t);
}

static long pof2(zulong x)
/*  Yields log2(x)+1 oder 0. */
{
    zulong p;int ln=1;
    p=ul2zul(1L);
    while(ln<=32&&zulleq(p,x)){
        if(zuleqto(x,p)) return(ln);
        ln++;p=zuladd(p,p);
    }
    return(0);
}

/****************************************/
/*  End of private fata and functions.  */
/****************************************/


int init_cg(void)
/*  Does necessary initializations for the code-generator. Gets called  */
/*  once at the beginning and should return 0 in case of problems.      */
{
    int i;
    /*  Initialize some values which cannot be statically initialized   */
    /*  because they are stored in the target's arithmetic.             */
    maxalign=l2zl(2L);
    char_bit=l2zl(8L);
    for(i=0;i<16;i++){
        sizetab[i]=l2zl(msizetab[i]);
        align[i]=l2zl(malign[i]);
    }
    for(i=1;i<=MAXR;i++){regsize[i]=l2zl(2L);regtype[i]=&ltyp;}

    /*  Initialize the min/max-settings. Note that the types of the     */
    /*  host system may be different from the target system and you may */
    /*  only use the smallest maximum values ANSI guarantees if you     */
    /*  want to be portable.                                            */
    /*  That's the reason for the subtraction in t_min[INT]. Long could */
    /*  be unable to represent -2147483648 on the host system.          */
    t_min[UNSIGNED|CHAR]=t_min[UNSIGNED|SHORT]=t_min[UNSIGNED|INT]=t_min[UNSIGNED|LONG]=l2zl(0L);
    t_min[CHAR]=l2zl(-128L);
    t_min[SHORT]=l2zl(-32768L);
    t_min[LONG]=zlsub(l2zl(-2147483647L),l2zl(1L));
    t_min[INT]=t_min[SHORT];
    t_max[CHAR]=ul2zul(127L);
    t_max[SHORT]=ul2zul(32767UL);
    t_max[LONG]=ul2zul(2147483647UL);
    t_max[INT]=t_max[SHORT];
    t_max[UNSIGNED|CHAR]=ul2zul(255UL);
    t_max[UNSIGNED|SHORT]=ul2zul(65535UL);
    t_max[UNSIGNED|LONG]=ul2zul(4294967295UL);
    t_max[UNSIGNED|INT]=t_max[UNSIGNED|SHORT];
    /*  Reserve a few registers for use by the code-generator.      */
    regsa[sp]=regsa[tp]=regsa[ti]=1;
    regscratch[sp]=regscratch[tp]=regscratch[ti]=0;
    return(1);
}

int freturn(struct Typ *t)
/*  Returns the register in which variables of type t are returned. */
/*  If the value cannot be returned in a register returns 0.        */
{
    int typ=t->flags&NQ;
    if(typ<=POINTER) return 5;
    return 0;
}

int regok(int r,int t,int mode)
/*  Returns 0 if register r cannot store variables of   */
/*  type t. If t==POINTER and mode!=0 then it returns   */
/*  non-zero only if the register can store a pointer   */
/*  and dereference a pointer to mode.                  */
{
    t&=NQ;
    if(r==0) return(0);
    if(mode){
      if(r<=4) return 1; else return 0;
    }
    if(t==CHAR&&r>8) return 0;
    if(t<LONG||t==POINTER) return 1;
    return 0;
}

int dangerous_IC(struct IC *p)
/*  Returns zero if the IC p can be safely executed     */
/*  without danger of exceptions or similar things.     */
/*  vbcc may generate code in which non-dangerous ICs   */
/*  are sometimes executed although control-flow may    */
/*  never reach them (mainly when moving computations   */
/*  out of loops).                                      */
/*  Typical ICs that generate exceptions on some        */
/*  machines are:                                       */
/*      - accesses via pointers                         */
/*      - division/modulo                               */
/*      - overflow on signed integer/floats             */
{
    int c=p->code;
    if((p->q1.flags&DREFOBJ)||(p->q2.flags&DREFOBJ)||(p->z.flags&DREFOBJ))
        return(0);
    if((c==DIV||c==MOD)&&!(p->q2.flags&KONST))
        return(1);
    return(0);
}

int must_convert(np p,int t)
/*  Returns zero if code for converting np to type t    */
/*  can be omitted.                                     */
/*  In this generic 32bit RISC cpu pointers and 32bit   */
/*  integers have the same representation and can use   */
/*  the same registers.                                 */
{
    int o=p->ntyp->flags,op=o&NQ,tp=t&NQ;
    if(op==tp) return 0;
    if((op==SHORT||op==INT||op==POINTER)&&(tp==SHORT||tp==INT||tp==POINTER))
      return 0;
    return(1);
}

void gen_ds(FILE *f,zlong size,struct Typ *t)
/*  This function has to create <size> bytes of storage */
/*  initialized with zero.                              */
{
  fprintf(f,"\tds\t%ld\n",zl2l(size));
}

void gen_align(FILE *f,zlong align)
/*  This function has to make sure the next data is     */
/*  aligned to multiples of <align> bytes.              */
{
    if(!zlleq(align,l2zl(1L))) fprintf(f,"\teven\n");
}

void gen_var_head(FILE *f,struct Var *v)
/*  This function has to create the head of a variable  */
/*  definition, i.e. the label and information for      */
/*  linkage etc.                                        */
{
    int constflag;
    if(v->clist) constflag=is_const(v->vtyp);
    if(v->storage_class==STATIC){
        if((v->vtyp->flags&NQ)==FUNKT) return;
        if(v->clist&&(!constflag||(g_flags[3]&USEDFLAG))&&section!=DATA){
          fprintf(f,sec_end);strcpy(sec_end,edata);
          fprintf(f,dataname);section=DATA;
        }
        if(v->clist&&constflag&&!(g_flags[3]&USEDFLAG)&&section!=CDATA){
          fprintf(f,sec_end);strcpy(sec_end,ecdata);
          fprintf(f,cdataname);section=CDATA;
        }
        if(!v->clist&&section!=BSS){
           fprintf(f,sec_end);strcpy(sec_end,ebss);
           fprintf(f,bssname);section=BSS;
        }
        fprintf(f,"\teven\n%sl%ld\n",labprefix,zl2l(v->offset));
    }
    if(v->storage_class==EXTERN){
        if(v->flags&(DEFINED|TENTATIVE)){
            fprintf(f,"\tpublic\t%s%s\n",idprefix,v->identifier);
            if(v->clist&&(!constflag||(g_flags[3]&USEDFLAG))&&section!=DATA){
              fprintf(f,sec_end);strcpy(sec_end,edata);
              fprintf(f,dataname);section=DATA;
            }
            if(v->clist&&constflag&&!(g_flags[3]&USEDFLAG)&&section!=CDATA){
              fprintf(f,sec_end);strcpy(sec_end,ecdata);
              fprintf(f,cdataname);section=CDATA;
            }
            if(!v->clist&&section!=BSS){
              fprintf(f,sec_end);strcpy(sec_end,ebss);
              fprintf(f,bssname);section=BSS;
            }
            fprintf(f,"\teven\n%s%s\n",idprefix,v->identifier);
        }else{
          fprintf(f,"\textern\t%s%s:far\n",idprefix,v->identifier);
        }
    }
}

void gen_dc(FILE *f,int t,struct const_list *p)
/*  This function has to create static storage          */
/*  initialized with const-list p.                      */
{
    fprintf(f,"\t%s\t",dct[t&NQ]);
    if(!p->tree){
        if((t&NQ)==FLOAT||(t&NQ)==DOUBLE){
        /*  auch wieder nicht sehr schoen und IEEE noetig   */
            unsigned char *ip;
            ip=(unsigned char *)&p->val.vdouble;
            fprintf(f,"0x%02x%02x%02x%02x",ip[3],ip[2],ip[1],ip[0]);
            if((t&NQ)==DOUBLE){
                fprintf(f,",0x%02x%02x%02x%02x",ip[7],ip[6],ip[5],ip[4]);
            }
        }else{
            printval(f,&p->val,t&NU,0);
        }
    }else{
        int m=p->tree->o.flags;
        p->tree->o.flags&=~VARADR;
        probj2(f,&p->tree->o,t&NU);
        p->tree->o.flags=m;
    }
    fprintf(f,"\n");
}

/*  The main code-generation routine.                   */
/*  f is the stream the code should be written to.      */
/*  p is a pointer to a doubly linked list of ICs       */
/*  containing the function body to generate code for.  */
/*  v is a pointer to the function.                     */
/*  offset is the size of the stackframe the function   */
/*  needs for local variables.                          */
void gen_code(FILE *f,struct IC *p,struct Var *v,zlong offset)
{
    int c,t,lastcomp=0,reg;
    if(DEBUG&1) printf("gen_code()\n");
    for(c=1;c<=15;c++) regs[c]=regsa[c];
    regs[16]=0;
    loff=((zl2l(offset)+1)/2)*2;
    function_top(f,v,loff);
    stackoffset=notpopped=dontpop=0;
    for(;p;pr(f,p),p=p->next){
        c=p->code;t=p->typf;
        if(c==NOP) continue;
        if(c==SUBPFP) c=SUB;
        if(c==SUBIFP) c=SUB;
        if(c==ADDI2P) c=ADD;
        if(c==ALLOCREG){
            regs[p->q1.reg]=1;
            continue;
        }
        if(c==FREEREG){
            regs[p->q1.reg]=0;
            continue;
        }
        if(notpopped&&!dontpop){
            int flag=0;
            if(c==LABEL||c==COMPARE||c==TEST||c==BRA){
                fprintf(f,"\tadd\t%s,#%ld\n",regnames[sp],notpopped);
                stackoffset+=notpopped;notpopped=0;
            }
        }
        if(c==LABEL) {fprintf(f,"%s%d:\n",labprefix,t);continue;}
        if(c==BRA){fprintf(f,"\tjmpr\tcc_uc,%s%d\n",labprefix,t);continue;}
        if(c==BEQ||c==BNE){
          fprintf(f,"\tjmpr\tcc_%s,%s%d\n",ccs[c-BEQ],labprefix,t);
          continue;
        }
        if(c>BNE&&c<BRA){            
            if(lastcomp&UNSIGNED) fprintf(f,"\tjmpr\tcc_u%s,%s%d\n",ccs[c-BEQ],labprefix,t);
                else              fprintf(f,"\tjmpr\tcc_s%s,%s%d\n",ccs[c-BEQ],labprefix,t);
            continue;
        }
        if(c==MOVETOREG){
            move(f,&p->q1,0,0,p->z.reg,INT);
            continue;
        }
        if(c==MOVEFROMREG){
            move(f,0,p->q1.reg,&p->z,0,INT);
            continue;
        }

        if((t&NQ)==LONG) {pric2(stdout,p);ierror(0);}
        if((t&NQ)==FLOAT) {pric2(stdout,p);ierror(0);}
        if((t&NQ)==DOUBLE) {pric2(stdout,p);ierror(0);}

        if(isreg(z)) reg=p->z.reg; else reg=ti;
        if(p->q1.flags&DREFOBJ){
          if(p->q1.flags&REG){
	    if(p->q2.flags)
	      fprintf(f,"\tmov%s\t%s,[%s]\n",x_t[t&NQ],regnames[reg],regnames[p->q1.reg]);
          }else{
	    p->q1.flags&=~DREFOBJ;
	    move(f,&p->q1,0,0,tp,POINTER);
	    p->q1.flags|=(DREFOBJ|REG);
	    p->q1.reg=tp;
	    if(p->q2.flags)
	      fprintf(f,"\tmov\t%s,[%s]\n",regnames[reg],regnames[tp]);
          }
        }else{
	  if(p->q2.flags){
	    if(!p->z.flags&&isreg(q1)) reg=p->q1.reg;
	    move(f,&p->q1,0,0,reg,t);
	  }
        }
        if(p->q2.flags&DREFOBJ){
          if(!(p->q2.flags&REG)||p->q2.reg>4){
            p->q2.flags&=~DREFOBJ;
            move(f,&p->q2,0,0,tp,POINTER);
            p->q2.flags|=(REG|DREFOBJ);
            p->q2.reg=tp;
          }
        }else if((p->q2.flags&(VAR|REG))==VAR){
	  if(p->q2.v->storage_class==AUTO||p->q2.v->storage_class==REGISTER){
	    move(f,&p->q2,0,0,tp,t);
	    p->q2.flags|=REG;
	    p->q2.reg=tp;
	  }
	}
	
        if(c>=CONVCHAR&&c<=CONVULONG){
            int to;
            if(c==CONVCHAR) to=CHAR;
            if(c==CONVUCHAR) to=(UNSIGNED|CHAR);
            if(c==CONVSHORT) to=SHORT;
            if(c==CONVUSHORT) to=(UNSIGNED|SHORT);
            if(c==CONVINT) to=SHORT;
            if(c==CONVUINT) to=(UNSIGNED|SHORT);
            if(c==CONVLONG) to=LONG;
            if(c==CONVULONG) to=(UNSIGNED|LONG);
            if(c==CONVFLOAT) to=FLOAT;
            if(c==CONVDOUBLE) to=DOUBLE;
            if(c==CONVPOINTER) to=(UNSIGNED|INT);
            if((t&NU)==INT) t=SHORT;
            if((t&NU)==(UNSIGNED|INT)||(t&NU)==POINTER) t=(UNSIGNED|INT);
            if((to&NQ)<=SHORT&&(t&NQ)<=SHORT){
                if((to&NQ)==CHAR){
		  if(isreg(q1)||((p->q1.flags&(VAR|DREFOBJ))==VAR&&(p->q1.v->storage_class==STATIC||p->q1.v->storage_class==EXTERN))){
                    fprintf(f,"\tmovb%c\t%s,",(to&UNSIGNED)?'z':'s',regnames[reg]);
                    if(isreg(q1)) fprintf(f,"%s",bregnames[p->q1.reg]); else probj2(f,&p->q1,to);
                    fprintf(f,"\n");
                    save_result(f,reg,&p->z,t);
                    continue;
                  }
                  move(f,&p->q1,0,0,reg,to);
                  if(isreg(z)||((p->z.flags&(VAR|DREFOBJ))==VAR&&(p->z.v->storage_class==STATIC||p->z.v->storage_class==EXTERN))){
                    fprintf(f,"\tmovb%c\t",(to&UNSIGNED)?'z':'s');
                    probj2(f,&p->z,t);fprintf(f,",%s\n",bregnames[reg]);
                    continue;
                  }
		  fprintf(f,"\tmovb%c\t%s,%s\n",(to&UNSIGNED)?'z':'s',regnames[reg],bregnames[reg]);
                  save_result(f,reg,&p->z,t);
                }else{
		  if(isreg(q1)&&regok(p->q1.reg,t,0)) reg=p->q1.reg;
		  move(f,&p->q1,0,0,reg,to);
		  save_result(f,reg,&p->z,t);
		}
                continue;
            }
            ierror(0);
        }
        if(c==MINUS||c==KOMPLEMENT){
	  move(f,&p->q1,0,0,reg,t);
	  fprintf(f,"\t%s%s\t%s\n",(c==MINUS?"neg":"cpl"),x_t[t&NQ],regnames[reg]);
	  save_result(f,reg,&p->z,t);
	  continue;
        }
        if(c==SETRETURN){
            if(p->z.reg){
	      move(f,&p->q1,0,0,p->z.reg,t);
	    }
            continue;
        }
        if(c==GETRETURN){
            if(p->q1.reg){
	      save_result(f,p->q1.reg,&p->z,t);
	    }
            continue;
        }
        if(c==CALL){
            int reg;
	    if((p->q1.flags&VAR)&&p->q1.v->fi&&p->q1.v->fi->inline_asm){
	      fprintf(f,"%s\n",p->q1.v->fi->inline_asm);
	    }else{
	      if(p->q1.flags&DREFOBJ){
                fprintf(f,"\tcalli\tcc_uc");probj2(f,&p->q1,t);
		fprintf(f,"\n");
	      }else{
                fprintf(f,"\tcalls\t");probj2(f,&p->q1,t);
                fprintf(f,"\n");
	      }
	    }
            if(!zleqto(l2zl(0L),p->q2.val.vlong)){
	      notpopped+=zl2l(p->q2.val.vlong);
	      dontpop-=zl2l(p->q2.val.vlong);
	      if(!(g_flags[2]&USEDFLAG)&&stackoffset==-notpopped){
                /*  Entfernen der Parameter verzoegern  */
	      }else{
		fprintf(f,"\tadd\t%s,#%ld\n",regnames[sp],zl2l(p->q2.val.vlong));
		stackoffset+=zl2l(p->q2.val.vlong);
		notpopped-=zl2l(p->q2.val.vlong);
	      }
            }
            continue;
        }
        if(c==ASSIGN||c==PUSH){
            if(c==PUSH) dontpop+=zl2l(p->q2.val.vlong);
            if((t&NQ)>POINTER||!zleqto(p->q2.val.vlong,sizetab[t&NQ])||!zlleq(p->q2.val.vlong,l2zl(4L))){
	      ierror(0);
            }
	    if(isreg(q1)) reg=p->q1.reg;
            if(c==PUSH){
	      move(f,&p->q1,0,0,reg,t);
	      fprintf(f,"\tmov%s\t[-%s],%s\n",x_t[t&NQ],regnames[sp],regnames[reg]);
	      stackoffset-=zl2l(p->q2.val.vlong);
	      continue;
            }
            if(c==ASSIGN){
	      move(f,&p->q1,0,0,reg,t);
              if((p->z.flags&DREFOBJ)&&!(p->z.flags&REG)){
                if(reg==tp) ierror(0);
                p->z.flags&=~DREFOBJ;
                move(f,&p->z,0,0,tp,POINTER);
                p->z.flags|=(REG|DREFOBJ);
                p->z.reg=tp;
              }
	      move(f,0,reg,&p->z,0,t);
	      continue;
            }
            ierror(0);
        }
        if(c==ADDRESS){
	  fprintf(f,"\tmov\t%s,%s\n",regnames[reg],regnames[sp]);
	  if(voff(&p->q1)) fprintf(f,"\tadd\t%s,#%ld\n",regnames[reg],voff(&p->q1));
	  save_result(f,reg,&p->z,POINTER);
	  continue;
        }
        if(c==TEST){
            lastcomp=t;
	    if(isreg(q1)) reg=p->q1.reg;
	    move(f,&p->q1,0,0,reg,t);
	    fprintf(f,"\tcmp%s\t%s,#0\n",x_t[t&NQ],regnames[reg]);
	    continue;
	}
        if(c==COMPARE){
            lastcomp=t;
	    p->q1.flags=REG;p->q1.reg=reg;
            if(isreg(q2)||(p->q1.flags&KONST)){
                struct IC *b=p->next;
                struct obj o;
                o=p->q1;p->q1=p->q2;p->q2=o;
                while(b&&b->code==FREEREG) b=b->next;
                if(!b) ierror(0);
                if(b->code==BLT) b->code=BGT;
                else if(b->code==BLE) b->code=BGE;
                else if(b->code==BGT) b->code=BLT;
                else if(b->code==BGE) b->code=BLE;
            }
            fprintf(f,"\tcmp%s\t",x_t[t&NQ]);
	    probj2(f,&p->q1,t);fprintf(f,",");
            probj2(f,&p->q2,t);fprintf(f,"\n");
            continue;
        }
        if((c==MULT||c==DIV||(c==MOD&&(p->typf&UNSIGNED)))&&(p->q2.flags&KONST)){
            long ln;
            eval_const(&p->q2.val,t);
            if(zlleq(l2zl(0L),vlong)&&zulleq(ul2zul(0UL),vulong)){
                if(ln=pof2(vulong)){
                    if(c==MOD){
                        vlong=zlsub(vlong,l2zl(1L));
                        p->code=AND;
                    }else{
                        vlong=l2zl(ln-1);
                        if(c==DIV) p->code=RSHIFT; else p->code=LSHIFT;
                    }
                    c=p->code;
                    if((t&NU)==CHAR) p->q2.val.vchar=zl2zc(vlong);
                    if((t&NU)==SHORT) p->q2.val.vshort=zl2zs(vlong);
                    if((t&NU)==INT) p->q2.val.vint=zl2zi(vlong);
                    if((t&NU)==LONG) p->q2.val.vlong=vlong;
                    vulong=zl2zul(vlong);
                    if((t&NU)==(UNSIGNED|CHAR)) p->q2.val.vuchar=zul2zuc(vulong);
                    if((t&NU)==(UNSIGNED|SHORT)) p->q2.val.vushort=zul2zus(vulong);
                    if((t&NU)==(UNSIGNED|INT))  p->q2.val.vuint=zul2zui(vulong);
                    if((t&NU)==(UNSIGNED|LONG)) p->q2.val.vulong=vulong;
                }
            }
        }
        if(c==MOD||c==DIV){
	  ierror(0);
	}
        if((c>=LSHIFT&&c<=MOD)||(c>=OR&&c<=AND)){
            char *s;
            if(c>=OR&&c<=AND) s=logicals[c-OR];
                else s=arithmetics[c-LSHIFT];
            if(c==RSHIFT&&!(t&UNSIGNED)) s="ashr";
	    if((c==MULT&&!isreg(q2))){
	      move(f,&p->q2,0,0,tp,t);
	      p->q2.flags=REG;
	      p->q2.reg=tp;
	    }
	    if((c==LSHIFT||c==RSHIFT)&&!(p->q2.flags&KONST)&&!isreg(q2)){
	      move(f,&p->q2,0,0,tp,t);
	      p->q2.flags=REG;
	      p->q2.reg=tp;
	    }
	    fprintf(f,"\t%s%s\t%s,",s,x_t[t&NQ],regnames[reg]);
	    probj2(f,&p->q2,t);fprintf(f,"\n");
            if(c==MULT) fprintf(f,"\tmov\t%s,mdl\n",regnames[reg]);
            save_result(f,reg,&p->z,t);
            continue;
        }
        ierror(0);
    }
    if(notpopped){
        fprintf(f,"\tadd\t%s,#%ld\n",regnames[sp],notpopped);
        stackoffset+=notpopped;notpopped=0;
    }
    function_bottom(f,v,loff);
}

int shortcut(int code,int typ)
{
    return(0);
}

void cleanup_cg(FILE *f)
{
    struct fpconstlist *p;
    unsigned char *ip;
    while(p=firstfpc){
        if(f){
            if(section!=CDATA){
              fprintf(f,sec_end);strcpy(sec_end,ecdata);
              fprintf(f,cdataname);section=CDATA;
            }
	    fprintf(f,"\teven\n");
            fprintf(f,"%sl%d\n\tddw\t",labprefix,p->label);
            ip=(unsigned char *)&p->val.vdouble;
            fprintf(f,"0x%02x%02x%02x%02x",ip[3],ip[2],ip[1],ip[0]);
            if((p->typ&NQ)==DOUBLE){
                fprintf(f,",0x%02x%02x%02x%02x",ip[7],ip[6],ip[5],ip[4]);
            }
            fprintf(f,"\n");
        }
        firstfpc=p->next;
        free(p);
    }
    if(f) fprintf(f,"%s\tREGDEF R0-R15\n\tend\n",sec_end);
}

int reg_parm(struct reg_handle *p,struct Typ *t,int mode)
{
  if((t->flags&NQ)>LONG&&(t->flags&NQ)!=POINTER) return 0;
  if(p->gpr>3||mode) return 0;
  return 13+p->gpr++;
}

