/**
 * Sparc V9 Solaris/GNU版 固有の変換規則
 * <p>TargetMethodインタフェースを実装している。
 */

class SparcVer9Method implements TargetMethod {

  /*****************/
  /* PRIVATE FIELD */
  /*****************/
  /**
   * 汎用レジスタに対応するアトムの配列
   */
  private static Cell[] r64Cell = {
    List.atom("g0"), List.atom("g1"), List.atom("g2"), List.atom("g3"),
    List.atom("g4"), List.atom("g5"), List.atom("g6"), List.atom("g7"),
    List.atom("o0"), List.atom("o1"), List.atom("o2"), List.atom("o3"),
    List.atom("o4"), List.atom("o5"), List.atom("o6"), List.atom("o7"),
    List.atom("l0"), List.atom("l1"), List.atom("l2"), List.atom("l3"),
    List.atom("l4"), List.atom("l5"), List.atom("l6"), List.atom("l7"),
    List.atom("i0"), List.atom("i1"), List.atom("i2"), List.atom("i3"),
    List.atom("i4"), List.atom("i5"), List.atom("i6"), List.atom("i7")
  };
  /**
   * 汎用レジスタ名称の配列
   */
  private static String[] r64 = {
    "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"
  };

  /**
   * 浮動小数点レジスタ(64ビット)に対応するアトムの配列
   */
  private static Cell[] f64Cell = {
    List.atom("f0"), List.atom("f2"), List.atom("f4"), List.atom("f6"),
    List.atom("f8"), List.atom("f10"), List.atom("f12"), List.atom("f14"),
    List.atom("f16"), List.atom("f18"), List.atom("f20"), List.atom("f22"),
    List.atom("f24"), List.atom("f26"), List.atom("f28"), List.atom("f30"),
    List.atom("f32"), List.atom("f34"), List.atom("f36"), List.atom("f38"),
    List.atom("f40"), List.atom("f42"), List.atom("f44"), List.atom("f46"),
    List.atom("f48"), List.atom("f50"), List.atom("f52"), List.atom("f54"),
    List.atom("f56"), List.atom("f58"), List.atom("f60"), List.atom("f62")
  };
  /**
   * 浮動小数点レジスタ(64ビット)名称の配列
   */
  private static String[] f64 = {
    "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14",
    "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30",
    "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46",
    "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62"
  };

  /**
   * 浮動小数点レジスタ(上位32ビット)名称の配列
   */
  private static String[] g32 = {
    "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14",
    "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30"
  };
  /**
   * 浮動小数点レジスタ(下位32ビット)名称の配列
   */
  private static String[] G32={
    "f1", "f3", "f5", "f7", "f9", "f11", "f13", "f15",
    "f17", "f19", "f21", "f23", "f25", "f27", "f29", "f31"
  };

  /* 定数定義 */
  private static Cell labelCell = List.atom("LABEL");
  private static Cell addCell = List.atom("ADD");
  private static Cell subCell = List.atom("SUB");
  private static Cell intconstCell = List.atom("INTCONST");

  /* デバッグ */
  private static final int debMode = 0;

  /***************/
  /* INITIALIZER */
  /***************/
  /**
   * 中間形式ファイル(バイナリ形式)読み込み時の事前準備を行う。
   */
  public void cleanUp() {
  }

  /**
   * 中間形式ファイル(バイナリ形式)読み込み後の再初期化処理を行う。
   */
  public void init() {
    for ( int i=0; i<r64.length; i++ )
      r64Cell[i] = List.atom(r64[i]);
    for ( int i=0; i<f64.length; i++ )
      f64Cell[i] = List.atom(f64[i]);
    labelCell = List.atom("LABEL");
    addCell = List.atom("ADD");
    subCell = List.atom("SUB");
    intconstCell = List.atom("INTCONST");
  }

  /*****************/
  /* PUBLIC METHOD */
  /*****************/
  /**
   * S式を指定の方法で文字列に変換する。
   * @param arg		HOLEにマッチするS式
   * @param spec	変換指定文字
   * @return		変換後の文字列
   */
  public String convertHole(Cell arg, char spec) {
    if ( debMode>0 ) {
      System.out.println("convertHole: arg "+arg+" spec "+spec);
    }
    switch ( spec ) {
    case 'r':			// 32ビット整数レジスタ
      return "%"+r64[r64num(arg)];

    case 'f':			// 64ビット浮動小数点レジスタ
      return "%"+f64[f64num(arg)];
    case 'g':			// 64ビット浮動小数点レジスタ(上位32ビット)
      return "%"+g32[f64num(arg)];
    case 'G':			// 64ビット浮動小数点レジスタ(下位32ビット)
      return "%"+G32[f64num(arg)];

    case 'i':			// 13ビット・アセンブラ定数(即値)
    case 'I':			// 32ビット・アセンブラ定数(即値)
    case 'h':			// 22ビット・アセンブラ定数(即値)
      return immediate(arg);

    case 'l':
      return arg.toString();	// アセンブラ定数(絶対番地、変位)(LABEL)
// 2001/07/10: 削除
//   ASMCONSTはマクロ・テンプレートで、LABEL式とINTCONST式の演算の変更した。
//    case 'a':
//      return asmconst(arg);	// アセンブラ定数(絶対番地、変位)(ASMCONST)

    default:
      Util.abort("SparcVer9Method: bad conversion specifier: "+spec);
    }
    return "";
  }

  /******************/
  /* PRIVATE METHOD */
  /******************/
  /**
   * S式表現からレジスタ番号に変換する。
   */
  private static int r64num(Cell arg) {
    for(int i=0; i<r64Cell.length; i++) if( r64Cell[i]==arg ) return i;
    Util.abort("SparcVer9Method: bad general register: "+arg);
    return -1;
  }

  /**
   * S式表現から浮動小数点レジスタ(64ビット、32ビット)番号に変換する。
   */
  private static int f64num(Cell arg) {
    for(int i=0; i<f64Cell.length; i++) if( f64Cell[i]==arg ) return i;
    Util.abort("SparcVer9Method: bad floating point register: "+arg);
    return -1;
  }

  /**
   * S式表現からアセンブラ定数(即値)に変換する。
   */
  private static String immediate(Cell arg) {
    return "("+arg.toString()+")";
  }

// 2001/07/10: 削除
//   ASMCONSTはマクロ・テンプレートで、LABEL式とINTCONST式の演算の変更した。
//  /**
//   * S式表現からアセンブラ定数(絶対番地、変位)に変換する。
//   */
//  private static String asmconst(Cell arg) {
//    if ( arg.isAtom() ) {
//      Util.abort("SparcVer9Method: illegal ASMCONST expression: "+arg);
//      return null;
//    } else {
//      String v = asmcSub(arg);
//      if ( v==null ) {
//	Util.abort("SparcVer9Method: illegal ASMCONST expression: "+arg);
//      }
//      return v;
//    }
//  }
//
//  /**
//   * アセンブラ定数の変換
//   */
//  private static String asmcSub(Cell arg) {
//    if ( arg.isAtom() ) {
//      return arg.toString();
//    } else {
//      if ( arg.car().isAtom() ) {
//	if ( arg.car()==labelCell ) {
//	  return arg.nth(2).toString();
//	} else if ( arg.car()==addCell ) {
//	  if ( arg.nth(2).car()!=labelCell || arg.nth(3).car()!=intconstCell ) return null;
//	  return asmcSub(arg.nth(2))+"+"+asmcSub(arg.nth(3));
//	} else if ( arg.car()==subCell ) {
//	  if ( arg.nth(2).car()!=labelCell ||
//	       (arg.nth(3).car()!=intconstCell && arg.nth(3).car()!=labelCell) ) return null;
//	  return asmcSub(arg.nth(2))+"-"+asmcSub(arg.nth(3));
//	} else if ( arg.car()==intconstCell ) {
//	  return arg.nth(2).toString();
//	} else {
//	  return null;
//	}
//      } else {
//	return null;
//      }
//    }
//  }

}

