#include <stdio.h>
#include "basics.h"
#include "sparc.h"

uint32 gen_call(int32 disp) {
  return (1 << 30) | (disp & 0x3fffffff);
}
uint32 gen_sethi(reg rd, int32 imm) {
  return ((rd & 0x1f) << 25) | (4 << 22) | (imm & 0x3fffff);
}
uint32 gen_bicc(uint8 cond,bool annul,int32 disp) {
  return (annul << 29) | ((cond & 0xf) << 25) | (2 << 22) | (disp & 0x3fffff);
}
uint32 gen_mop(uint8 op,reg rd,reg rs1,reg rs2) {
  return (3 << 30) | ((rd & 0x1f) << 25) | ((op & 0x3f) << 19) | ((rs1 & 0x1f) << 14) | (rs2 & 0x1f);
}
uint32 gen_mop_imm(uint8 op,reg rd,reg rs1,int16 imm) {
  return (3 << 30) | ((rd & 0x1f) << 25) | ((op & 0x3f) << 19) | ((rs1 & 0x1f) << 14) | (1 << 13) | (imm & 0x1fff);
}
uint32 gen_op(uint8 op,reg rd,reg rs1,reg rs2) {
  return (2 << 30) | ((rd & 0x1f) << 25) | ((op & 0x3f) << 19) | ((rs1 & 0x1f) << 14) | (rs2 & 0x1f);
}
uint32 gen_op_imm(uint8 op,reg rd,reg rs1,int16 imm) {
  return (2 << 30) | ((rd & 0x1f) << 25) | ((op & 0x3f) << 19) | ((rs1 & 0x1f) << 14) | (1 << 13) | (imm & 0x1fff);
}

char *regname[] = 
 {"%g0","%g1","%g2","%g3","%g4","%g5","%g6","%g7",
  "%o0","%o1","%o2","%o3","%o4","%o5","%o6","%o7",
  "%l0","%l1","%l2","%l3","%l4","%l5","%l6","%l7",
  "%i0","%i1","%i2","%i3","%i4","%i5","%i6","%i7"};

#define SIGN_EXTEND(x,b) (((int)(x)) << (32-(b)) >> (32-(b)))

void dump(uint32 *scode, int length) {
  while (length-- > 0) {
    uint32 inst = *scode;
    fprintf (stderr,"%.8x:\t%.8x\t", scode,inst);
    int op1 = inst >> 30;
    switch (op1) {
    case 0: 
      {
	int op2 = (inst >> 22) & 0x7; 
	switch (op2) {
	case 2: 
	  {
	    int annul = (inst >> 29) & 1;
	    int cond = (inst >> 25) & 0xf;
	    int disp = SIGN_EXTEND(inst & 0x3fffff,22);
	    static char *condnames[] =  
	      {"bn","be","ble","bl",
	       "bleu","blu","bneg","bvs",
	       "ba","bne","bg","bge",
	       "bgu","bgeu","bpos","bvc"};
	    fprintf(stderr,"%s%s %d [%.8x]",condnames[cond],(annul ? ",a" : ""),disp,scode+disp);
	    break;
	  }
	case 4:
	  {
	    reg rd = (inst >> 25) & 0x1f;
	    int imm = inst & 0x3fffff;
	    fprintf (stderr,"sethi %.8x,%s",imm,regname[rd]);
	    break;
	  }
	default: 
	  fprintf(stderr,"?");
	  break;
	}
	break;
      }
    case 1: 
      {
	int disp = SIGN_EXTEND(inst & 0x3fffffff, 30); 
	fprintf(stderr,"call %.8x [%.8x]", disp, scode+disp);
	break;
      }
    case 2:
    case 3:
      {
	int has_imm = (inst >> 13) & 0x1;
	reg rd = (inst >> 25) & 0x1f;
	reg rs1 = (inst >> 14) & 0x1f;
	reg rs2 = inst & 0x1f;    // if !has_imm
	int imm = SIGN_EXTEND(inst & 0x1fff,13);  // if has_imm
	int op2 = (inst >> 19) & 0x3f; 
	static char *opnames[] = 
	  {"add","and","or","xor","sub","andn","orn","xnor",
	   "addx","?","umul","smul","subx","?","udiv","sdiv",
	   "addcc","andcc","orcc","xorcc","subcc","andncc","orncc","xnorcc",
	   "addxcc","?","umulcc","smulcc","subxcc","?","udivcc","sdivcc",
	   "taddcc","tsubcc","taddcctv","tsubcctv","mulscc","sll","srl","sra",
	   "rd?","rdpsr","rdwim","rdtbr","?","?","?","?",
	   "wry(?)","wrpsr","wrwim","wrtbr","fpop1?","fpop2?","cpop1?","cpop2",
	   "jmpl","rett","ticc?","flush","save","restore","?","?"};
	static char *memopnames[] = 
          {"ld","ldub","lduh","ldd","st","stb","sth","std",
	   "?","ldsb","ldsh","?","?","ldstub","?","swap",
	   "lda","lduba","lduha","ldda","sta","stba","stha","stda",
	   "?","ldsba","ldsha","?","?","lstub","?","swapa",
	   "ldf","ldfsr","?","lddf","stf","stfsr","stdfq","stdf",
	   "?","?","?","?","?","?","?","?",
	   "ldc","ldcsr","?","lddc","stc","stcsr","stdcq","stdc",
	   "?","?","?","?","?","?","?","?"};
	char *opname = (op1 == 2) ? opnames[op2] : memopnames[op2];
	if (has_imm)
	  fprintf(stderr,"%s %s,%d,%s", opname,regname[rs1],imm,regname[rd]);
	else
	  fprintf(stderr,"%s %s,%s,%s", opname,regname[rs1],regname[rs2],regname[rd]);
	break;
      }	
    }
    fprintf(stderr,"\n");
    scode++;
  }
}
