// $Id: MIPSGenerator.java,v 1.1.1.1 1999/12/05 22:19:52 mpp Exp $

package java6035.tools.ASM;
import java.io.*;


/* MODIFIED BY LE04 FOR ONE REASON:
   We wanted the constructor to accept an OutputStream instead of a 
   FileOutputStream. It's a one word change. Whee. */

/**
 * MIPSGenerator 
 *
 * is a map on ASM that generates MIPS code, in either SPIM or native
 * format.
 */
public final class MIPSGenerator 
{
    public static int SPIM_FORMAT = 0;
    public static int NATIVE_FORMAT = 1;

    private int format;
    private PrintWriter output;


    private void checkOperand(Object o, int index, String instr)
    {
	if (o == null) 
	    throw new ASMException
		("null object supplied as operand "+index+" of "+instr);
    }

    /**
     * Opens a MIPSGenerator output to the file output given.
     *
     * @param format	Format of the MIPS output: SPIM_FORMAT/NATIVE_FORMAT.
     * @param output	Output file stream where the assembly is dumped.
     */
    public MIPSGenerator(int format, OutputStream output)
    {
	if (format < 0 || format > 1 || output == null)
	    throw new ASMException
		("MIPSGenerator: format invalid, or garbage output stream.");
	this.format = format;
	this.output = new PrintWriter(output);

	this.output.print("    # MIPS assembly: ");
	if (format == SPIM_FORMAT)
	    this.output.println("SPIM format");
	else
	    this.output.println("native format");
	this.output.println
	    ("    # Automatically generated by ASM.MIPSGenerator");
	this.output.println("");
    }

    /**
     * closes a MIPSGenerator output
     */
    public void done()
    {
	this.output.println("");
	this.output.close();
    }


    public void gen(ASM asm)
    {
	for(int asm_iter = 0; asm_iter < asm.size(); asm_iter++)
	{
	    ASMEntry elm = asm.getEntryAt(asm_iter);

	    if (elm instanceof MIPSInstruction) {

	        MIPSInstruction instr = (MIPSInstruction)elm;
	        int op = instr.getOpcode();
    
	        MIPSOperand dest = instr.getDest();
	        MIPSOperand src1 = instr.getSrc1();
	        MIPSOperand src2 = instr.getSrc2();
                String comment = instr.getComment();
    
	        String d_rep = null, s1_rep = null, s2_rep = null;
    
	        if (dest != null)
	        {
		    d_rep = dest.toString();
		    if (format == NATIVE_FORMAT)
		    {
		        if (dest instanceof MIPSRegister)
			    d_rep = "$"+((MIPSRegister)dest).which();
		        else if (dest instanceof MIPSAddress)
			    d_rep = ((MIPSAddress)dest).toNative();
		    }
	        }
    
	        if (src1 != null)
	        {
		    s1_rep = src1.toString();
		    if (format == NATIVE_FORMAT)
		    {
		        if (src1 instanceof MIPSRegister)
			    s1_rep = "$"+((MIPSRegister)src1).which();
		        else if (src1 instanceof MIPSAddress)
			    s1_rep = ((MIPSAddress)src1).toNative();
		    }
	        }
	        
	        if (src2 != null)
	        {
		    s2_rep = src2.toString();
		    if (format == NATIVE_FORMAT)
		    {
		        if (src2 instanceof MIPSRegister)
			    s2_rep = "$"+((MIPSRegister)src2).which();
		        else if (src2 instanceof MIPSAddress)
			    s2_rep = ((MIPSAddress)src2).toNative();
		    }
	        }
    
	        switch(op) {
    
	            /* first, all the Rdest,src1 instructions */
	            case MIPSOpcode.LA:
	            case MIPSOpcode.LW:
	            case MIPSOpcode.LI:
	            case MIPSOpcode.MOVE:
	            case MIPSOpcode.NOT:
    
		        checkOperand(dest,0,MIPSOpcode.getStringRep(op));
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)+" "+
			                    d_rep+","+s1_rep);
		        break;
    
	            /* all the src1,src2 instructions */
	            case MIPSOpcode.SW:
    
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
		        checkOperand(src2,2,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)+" "+
			                    s1_rep+","+s2_rep);
		        break;
    
		    /* all the label instructions (uses dest operand) */
		    case MIPSOpcode.B:
		    case MIPSOpcode.J:
    
		        checkOperand(dest,0,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)
			                    +" "+d_rep);
		        break;
    
		    /* all the src1 instructions */
		    case MIPSOpcode.JR:
    
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)
			                    +" "+s1_rep);
		        break;
    
		    /* all the src1,src2,label instructions (uses dest operand) */
		    case MIPSOpcode.BEQ:
		    case MIPSOpcode.BGE:
		    case MIPSOpcode.BGT:
		    case MIPSOpcode.BLE:
		    case MIPSOpcode.BLT:
		    case MIPSOpcode.BNE:
		    case MIPSOpcode.BGEU:
		    case MIPSOpcode.BGTU:
		    case MIPSOpcode.BLEU:
		    case MIPSOpcode.BLTU:
    
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
		        checkOperand(src2,2,MIPSOpcode.getStringRep(op));
		        checkOperand(dest,0,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)+" "+
			                    s1_rep+","+s2_rep+","+d_rep);
		        break;
    
		    /* special cases */
    
		    /* JAL could be two forms */
		    case MIPSOpcode.JAL:
		        if (dest != null)
			    this.output.print("\t"+MIPSOpcode.getStringRep(op)
				                +" "+d_rep);
		        else {
		            checkOperand(src1,1,MIPSOpcode.getStringRep(op));
			    this.output.print("\t"+MIPSOpcode.getStringRep(op)
				                +" "+s1_rep);
	 	        }
		        break;
		     
		    /* CALLOUT uses symbol differently */
	            case MIPSOpcode.CALLOUT:
    
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
		        checkOperand(src2,2,MIPSOpcode.getStringRep(op));
    
		        if (format == NATIVE_FORMAT)
		        {
                            this.output.print("\tjal "+s1_rep);
		        } 
    
		        else 
		        {
		            this.output.print("\t"+MIPSOpcode.getStringRep(op)
				                +" \""+s1_rep+"\" "+s2_rep);
		        }
    
		        break;
    
		    /* NOP */
	            case MIPSOpcode.NOP:
		        this.output.print("\tnop");
		        break;
    
		    /* by default: dest,src1,src2 instructions */
		    default:
    
		        checkOperand(src1,1,MIPSOpcode.getStringRep(op));
		        checkOperand(src2,2,MIPSOpcode.getStringRep(op));
		        checkOperand(dest,0,MIPSOpcode.getStringRep(op));
    
		        this.output.print("\t"+MIPSOpcode.getStringRep(op)+" "+
			                    d_rep+","+s1_rep+","+s2_rep);
		        break;
	        }
                
                // print the comment if there is one
                if (comment != null) {
                    this.output.println("\t\t# " + comment);
                } else {
                    this.output.println("");
                }
	    } 
    
	    else if (elm instanceof ASMLabel) {
	        if (((ASMLabel)elm).toString().equals("main"))
	        {
		    this.output.println("\t.globl main");
	        }
    
	        this.output.println(((ASMLabel)elm).toString()+":");
	    }
    
	    else if (elm instanceof ASMDirective) {
	        if (format == SPIM_FORMAT &&
		    (elm instanceof dCprestore ||
		     elm instanceof dEnt || 
		     elm instanceof dEnd))
	        {
		    this.output.println
		        ("\t# "+((ASMDirective)elm).toString());
	        }
    
	        else 
		    this.output.println("\t"+((ASMDirective)elm).toString());
    
	        if (elm instanceof dData)
	        {
		    ASMEntry next = asm.getEntryAt(asm_iter+1);
		    if (!(next instanceof dAlign))
		    {
		        this.output.println("\t.align 2");
		    }
	        }
	    }
    
	    else if (elm instanceof ASMComment) {
	        this.output.println(((ASMComment)elm).toString());
	    }
        }
    }

}


