// -*- c++ -*-
//
// mips.cc:
//
#include <iostream>
#include "mips.h"
#include "breakcode.h"

using namespace std;

//
// constructor
//
mips::mips()
{
}

//
// destructor
//
mips::~mips()
{
}

//
// mips class member functions
//

void
mips::connect_memory(memory *mem)
{
    this->mem = mem;
}

void
mips::connect_icache(cache *icache)
{
    this->icache = icache;
}

void
mips::connect_dcache(cache *dcache)
{
    this->dcache = dcache;
}

void
mips::reset()
{
    running_f = true;
    stall_cond_f = false;

    phase = 0;

    PC = 0;

    Regs.reset();
    MulDivUnit.reset();

#ifdef	DELAYED_BRANCH
    delayed_f = false;
#endif
}

void
mips::set_pc(sim_addr pc)
{
    PC = pc;
}

void
mips::set_reg(int regno, sim_word val)
{
    Regs[regno] = val;
}

sim_addr
mips::get_pc()
{
    return PC;
}

sim_word
mips::get_reg(int regno)
{
    return Regs[regno];
}

void
mips::IF()
{
    cout << "---------- IF ----------" << endl;;

    icache->request_read_word(PC);
    NPC = PC + 4;

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
}

void
mips::IS()
{
    cout << "---------- IS ----------" << endl;;

    if (icache->is_done()) {
        icache->reply_read_word(PC, IR);
    }
    else {
        IR = 0x00000000;		// IR set to NOP
        make_stall("*** IS stalled ***");
    }

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
    cout << "IR=" << hex << IR << endl;

}

void
mips::RF()
{
    cout << "---------- RF ----------" << endl;;

    A = Regs[rs(IR)];
    B = Regs[rt(IR)];
    Imm = imm(IR);
    UImm = uimm(IR);

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
    cout << "IR=" << hex << IR << endl;

    cout << "Opcode=" << hex << opcode(IR) << endl;
    cout << "RS=" << dec << rs(IR) << endl;
    cout << "RT=" << dec << rt(IR) << endl;
    cout << "RD=" << dec << rd(IR) << endl;

    cout << "A=" << hex << A << endl;
    cout << "B=" << hex << B << endl;
    cout << "Imm=" << hex << Imm << endl;
    cout << "UImm=" << hex << UImm << endl;
}

void
mips::EX()
{
    cout << "---------- EX ----------" << endl;;

    //
    // dispatch
    //
    switch (opcode(IR)) {
    case 0x00:		// SPECIAL instructions
	switch (funct(IR)) {
	case 0x00:	// SLL
	    ALUOutput = B << shamt(IR);
	    break;
	case 0x02:	// SRL
	    ALUOutput = B >> shamt(IR);
	    break;
	case 0x03:	// SRA
	    ALUOutput = (sim_word)(((signed long)B) >> shamt(IR));
	    break;

	case 0x04:	// SLLV
	    ALUOutput = A << (B & 0x1f);
	    break;
	case 0x06:	// SRLV
	    ALUOutput = A >> (B & 0x1f);
	    break;
	case 0x07:	// SRAV
	    ALUOutput = (sim_word)(((signed long)A) >> (B & 0x1f));
	    break;

	case 0x08:	// JR
	    ALUOutput = A;
	    break;
	case 0x09:	// JALR
	    ALUOutput = A;
	    break;

	case 0x0c:	// SYSCALL
	    do_syscall();
	    break;
	case 0x0d:	// BREAK
	    do_break();
	    break;

	case 0x10:	// MFHI
            if (MulDivUnit.is_done()) {
                ALUOutput = MulDivUnit.HI();
            }
            else {
                make_stall("*** Mul/Div unit is in operation ***");
            }
	    break;
	case 0x11:	// MTHI
            if (MulDivUnit.is_done()) {
                MulDivUnit.HI() = ALUOutput;
            }
            else {
                make_stall("*** Mul/Div unit is in operation ***");
            }
	    break;
	case 0x12:	// MFLO
            if (MulDivUnit.is_done()) {
                ALUOutput = MulDivUnit.LO();
            }
            else {
                make_stall("*** Mul/Div unit is in operation ***");
            }
	    break;
	case 0x13:	// MTLO
            if (MulDivUnit.is_done()) {
                MulDivUnit.LO() = ALUOutput;
            }
            else {
                make_stall("*** Mul/Div unit is in operation ***");
            }
	    break;

	case 0x18:	// MULT
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MUL);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x19:	// MULTU
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MULU);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x1a:	// DIV
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_DIV);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x1b:	// DIVU
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_DIVU);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x20:	// ADD
	    ALUOutput = A + B;
	    /* check overflow */
	    check_overflow(ALUOutput, A, B);
	    break;
	case 0x21:	// ADDU
	    ALUOutput = A + B;
	    break;
	case 0x22:	// SUB
	    ALUOutput = A - B;
	    /* check overflow */
	    check_overflow(ALUOutput, A, -B);
	    break;
	case 0x23:	// SUBU
	    ALUOutput = A - B;
	    break;
	case 0x24:	// AND
	    ALUOutput = A & B;
	    break;
	case 0x25:	// OR
	    ALUOutput = A | B;
	    break;
	case 0x26:	// XOR
	    ALUOutput = A ^ B;
	    break;
	case 0x27:	// NOR
	    ALUOutput = ~(A | B);
	    break;

	case 0x2a:	// SLT
	    ALUOutput = (signed long)A < (signed long)B? 1: 0;
	    break;
	case 0x2b:	// SLTU
	    ALUOutput = A < B? 1: 0;
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// jump/branch instructions
	//
    case 0x02:		// J
	ALUOutput = (NPC & 0xf0000000)|((IR & 0x03ffffff) << 2);
	break;
    case 0x03:		// JAL
	ALUOutput = (NPC & 0xf0000000)|((IR & 0x03ffffff) << 2);
	break;

    case 0x04:		// BEQ
	ALUOutput = NPC + (Imm << 2);
	Cond = A == B? true: false;

	cout << "Cond=" << Cond << endl;
	break;

    case 0x05:		// BNE
	ALUOutput = NPC + (Imm << 2);
	Cond = A != B? true: false;

	cout << "Cond=" << Cond << endl;
	break;

    case 0x06:		// BLEZ
	ALUOutput = NPC + (Imm << 2);
	Cond = (signed long)A <= 0? true: false;

	cout << "Cond=" << Cond << endl;
	break;

    case 0x07:		// BGTZ
	ALUOutput = NPC + (Imm << 2);
	Cond = (signed long)A > 0? true: false;

	cout << "Cond=" << Cond << endl;
	break;

	//
	// ALU + immediate instructions
	//
    case 0x08:		// ADDI
	ALUOutput = A + Imm;
	/* check overflow */
	check_overflow(ALUOutput, A, Imm);
	break;
    case 0x09:		// ADDIU
	ALUOutput = A + Imm;
	break;
    case 0x0a:		// SLTI
	ALUOutput = (signed long)A < (signed long)Imm? 1: 0;
	break;
    case 0x0b:		// SLTIU
	ALUOutput = A < UImm? 1: 0;
	break;
    case 0x0c:		// ANDI
	ALUOutput = A & UImm;
	break;
    case 0x0d:		// ORI
	ALUOutput = A | UImm;
	break;
    case 0x0e:		// XORI
	ALUOutput = A ^ UImm;
	break;
    case 0x0f:		// LUI
	ALUOutput = UImm << 16;
	break;

	//
	// co-processor instructions
	//
    case 0x10:		// COP0
	cout << "cop0 instruction not implemented" << endl;
	break;
    case 0x11:		// COP1
	cout << "cop1 instruction not implemented" << endl;
	break;
    case 0x12:		// COP2
	cout << "cop2 instruction not implemented" << endl;
	break;

    case 0x1c:		// SPECIAL2 instructions
	switch (funct(IR)) {
	case 0x02:	// MUL (MULtiply) (this is 3 operand multiply)
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MUL, muldiv::NO_ACC_MODE);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

            //
            // multiply and add instructions
            //
	case 0x00:	// MADD (Mul-and-ADD)
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MUL, muldiv::ACC_ADD_MODE);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x01:	// MADDU (Mul-and-ADD Unsigned)
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MULU, muldiv::ACC_ADD_MODE);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x04:	// MSUB (Mul-and-SUB)
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MUL, muldiv::ACC_SUB_MODE);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;

	case 0x05:	// MSUBU (Mul-and-SUb Unsigned)
            if (MulDivUnit.is_ready()) {
                MulDivUnit.initiate(A, B, muldiv::OP_MULU, muldiv::ACC_SUB_MODE);
            }
            else {
                make_stall("*** Mul/Div unit is busy ***");
            }
	    break;


	case 0x20:	// CLZ (Count Leading Zero)
            cout << "CLZ instruction not implemented" << endl;
	    break;
	case 0x21:	// CLO (Count Leading One)
            cout << "CLO instruction not implemented" << endl;
	    break;
	case 0x3F:	// SDBBP (Software Debug Breakpoint)
            cout << "SDBBP instruction not implemented" << endl;
	    break;
	default:
	    cout << "undefined instruction opcode=SPECIAL2("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// load/store instructions
	// (effective address calculation)
        //
    case 0x20:		// LB
    case 0x21:		// LH
    case 0x22:		// LWL
    case 0x23:		// LW
    case 0x24:		// LBU
    case 0x25:		// LHU
    case 0x26:		// LWR
    case 0x28:		// SB
    case 0x29:		// SH
    case 0x2a:		// SWL
    case 0x2b:		// SW
    case 0x2e:		// SWR
	ALUOutput = A + Imm;
	break;

    default:
	cout << "undefined instruction opcode="
	     << hex << opcode(IR) << endl;
	break;
    }

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
    cout << "IR=" << hex << IR << endl;

    cout << "Opcode=" << hex << opcode(IR) << endl;
    cout << "RS=" << dec << rs(IR) << endl;
    cout << "RT=" << dec << rt(IR) << endl;
    cout << "RD=" << dec << rd(IR) << endl;

    cout << "A=" << hex << A << endl;
    cout << "B=" << hex << B << endl;
    cout << "Imm=" << hex << Imm << endl;
    cout << "UImm=" << hex << UImm << endl;

    cout << "ALUOutput=" << hex << ALUOutput << endl;
}

void
mips::DF()
{
    cout << "---------- DF ----------" << endl;;

    PC = NPC;		// jump/branch instructions override PC

#ifdef	DELAYED_BRANCH
    if (delayed_f) {
	PC = delayed_pc;
	delayed_f = false;
	cout << "PC=" << hex << PC << endl;
    }
#endif

    switch (opcode(IR)) {
    case 0x00:		// SPECIAL instructions
	switch (funct(IR)) {
	case 0x00:	// SLL
	case 0x02:	// SRL
	case 0x03:	// SRA
	case 0x04:	// SLLV
	case 0x06:	// SRLV
	case 0x07:	// SRAV
	    break;

	case 0x08:	// JR
#ifdef	DELAYED_BRANCH
	    delayed_f = true;
	    delayed_pc = ALUOutput;
#else
	    PC = ALUOutput;
	    cout << "PC=" << hex << PC << endl;
#endif
	    break;
	case 0x09:	// JALR
#ifdef	DELAYED_BRANCH
	    delayed_f = true;
	    delayed_pc = ALUOutput;
#else
	    PC = ALUOutput;
	    cout << "PC=" << hex << PC << endl;
#endif
	    break;

	case 0x0c:	// SYSCALL
	    break;
	case 0x0d:	// BREAK
	    break;

	case 0x10:	// MFHI
	case 0x11:	// MTHI
	case 0x12:	// MFLO
	case 0x13:	// MTLO
	    break;

	case 0x18:	// MULT
	case 0x19:	// MULTU
	case 0x1a:	// DIV
	case 0x1b:	// DIVU
	    break;

	case 0x20:	// ADD
	case 0x21:	// ADDU
	case 0x22:	// SUB
	case 0x23:	// SUBU
	case 0x24:	// AND
	case 0x25:	// OR
	case 0x26:	// XOR
	case 0x27:	// NOR
	    break;

	case 0x2a:	// SLT
	case 0x2b:	// SLTU
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// jump/branch instructions
	//
    case 0x02:		// J
#ifdef	DELAYED_BRANCH
	delayed_f = true;
	delayed_pc = ALUOutput;
#else
	PC = ALUOutput;
	cout << "PC=" << hex << PC << endl;
#endif
	break;
    case 0x03:		// JAL
#ifdef	DELAYED_BRANCH
	delayed_f = true;
	delayed_pc = ALUOutput;
#else
	PC = ALUOutput;
	cout << "PC=" << hex << PC << endl;
#endif
	break;

    case 0x04:		// BEQ
    case 0x05:		// BNE
    case 0x06:		// BLEZ
    case 0x07:		// BGTZ
#ifdef	DELAYED_BRANCH
	if (Cond) {
	    delayed_f = true;
	    delayed_pc = ALUOutput;
	}
#else
	if (Cond) {
	    PC = ALUOutput;
	    cout << "PC=" << hex << PC << endl;
	}
#endif
	break;

	//
	// ALU + immediate instructions
	//
    case 0x08:		// ADDI
    case 0x09:		// ADDIU
    case 0x0a:		// SLTI
    case 0x0b:		// SLTIU
    case 0x0c:		// ANDI
    case 0x0d:		// ORI
    case 0x0e:		// XORI
    case 0x0f:		// LUI
	break;

	//
	// co-processor instructions
	//
    case 0x10:		// COP0
    case 0x11:		// COP1
    case 0x12:		// COP2
	break;

    case 0x1c:		// SPECIAL2 instructions
	switch (funct(IR)) {
	case 0x02:	// MUL (MULtiply)
            if (MulDivUnit.is_done()) {
                ALUOutput = MulDivUnit.LO();
            }
            else {
                make_stall("*** Mul/Div unit is in operation ***");
            }
            break;

            //
            // multiply and add instructions
            //
	case 0x00:	// MADD (Mul-and-ADD)
	case 0x01:	// MADDU (Mul-and-ADD Unsigned)
	case 0x04:	// MSUB (Mul-and-SUB)
	case 0x05:	// MSUBU (Mul-and-SUb Unsigned)
	case 0x20:	// CLZ (Count Leading Zero)
	case 0x21:	// CLO (Count Leading One)
	case 0x3F:	// SDBBP (Software Debug Breakpoint)
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL2("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// load instructions
	//
    case 0x20:		// LB
        dcache->request_read_byte(ALUOutput);
	break;
    case 0x21:		// LH
        dcache->request_read_hword(ALUOutput);
	break;
    case 0x22:		// LWL
        dcache->request_read_word(ALUOutput);
	// T.B.D.
        assert(0);
	break;
    case 0x23:		// LW
        dcache->request_read_word(ALUOutput);
	break;
    case 0x24:		// LBU
        dcache->request_read_byte(ALUOutput);
	break;
    case 0x25:		// LHU
        dcache->request_read_hword(ALUOutput);
	break;
    case 0x26:		// LWR
        dcache->request_read_word(ALUOutput);
	// T.B.D.
        assert(0);
	break;

	//
	// store instructions
	//
    case 0x28:		// SB
        dcache->request_write_byte(ALUOutput, B);
	break;
    case 0x29:		// SH
        dcache->request_write_hword(ALUOutput, B);
	break;
    case 0x2a:		// SWL
        // T.B.D.
        assert(0);
        dcache->request_write_word(ALUOutput, B);
	break;
    case 0x2b:		// SW
        dcache->request_write_word(ALUOutput, B);
	break;
    case 0x2e:		// SWR
        // T.B.D.
        assert(0);
        dcache->request_write_word(ALUOutput, B);
	break;

    default:
	cout << "undefined instruction opcode="
	     << hex << opcode(IR) << endl;
	break;
    }

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
    cout << "IR=" << hex << IR << endl;

    cout << "Opcode=" << hex << opcode(IR) << endl;
    cout << "RS=" << dec << rs(IR) << endl;
    cout << "RT=" << dec << rt(IR) << endl;
    cout << "RD=" << dec << rd(IR) << endl;

    cout << "A=" << hex << A << endl;
    cout << "B=" << hex << B << endl;
    cout << "Imm=" << hex << Imm << endl;
    cout << "UImm=" << hex << UImm << endl;

    cout << "ALUOutput=" << hex << ALUOutput << endl;
}

void
mips::DS()
{
    cout << "---------- DS ----------" << endl;;

    switch (opcode(IR)) {
    case 0x00:		// SPECIAL instructions
	switch (funct(IR)) {
	case 0x00:	// SLL
	case 0x02:	// SRL
	case 0x03:	// SRA
	case 0x04:	// SLLV
	case 0x06:	// SRLV
	case 0x07:	// SRAV
	case 0x08:	// JR
	case 0x09:	// JALR
	    break;

	case 0x0c:	// SYSCALL
	case 0x0d:	// BREAK
	    break;

	case 0x10:	// MFHI
	case 0x11:	// MTHI
	case 0x12:	// MFLO
	case 0x13:	// MTLO
	    break;

	case 0x18:	// MULT
	case 0x19:	// MULTU
	case 0x1a:	// DIV
	case 0x1b:	// DIVU
	    break;

	case 0x20:	// ADD
	case 0x21:	// ADDU
	case 0x22:	// SUB
	case 0x23:	// SUBU
	case 0x24:	// AND
	case 0x25:	// OR
	case 0x26:	// XOR
	case 0x27:	// NOR
	    break;

	case 0x2a:	// SLT
	case 0x2b:	// SLTU
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// jump/branch instructions
	//
    case 0x02:		// J
    case 0x03:		// JAL
    case 0x04:		// BEQ
    case 0x05:		// BNE
    case 0x06:		// BLEZ
    case 0x07:		// BGTZ
	break;

	//
	// ALU + immediate instructions
	//
    case 0x08:		// ADDI
    case 0x09:		// ADDIU
    case 0x0a:		// SLTI
    case 0x0b:		// SLTIU
    case 0x0c:		// ANDI
    case 0x0d:		// ORI
    case 0x0e:		// XORI
    case 0x0f:		// LUI
	break;

	//
	// co-processor instructions
	//
    case 0x10:		// COP0
    case 0x11:		// COP1
    case 0x12:		// COP2
	break;

    case 0x1c:		// SPECIAL2 instructions
	switch (funct(IR)) {
	case 0x02:	// MUL (MULtiply)
            break;

            //
            // multiply and add instructions
            //
	case 0x00:	// MADD (Mul-and-ADD)
	case 0x01:	// MADDU (Mul-and-ADD Unsigned)
	case 0x04:	// MSUB (Mul-and-SUB)
	case 0x05:	// MSUBU (Mul-and-SUb Unsigned)
	case 0x20:	// CLZ (Count Leading Zero)
	case 0x21:	// CLO (Count Leading One)
	case 0x3F:	// SDBBP (Software Debug Breakpoint)
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL2("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// load instructions
	//
    case 0x20:		// LB
        if (dcache->is_done()) {
	    sim_byte v;
	    dcache->reply_read_byte(ALUOutput, v);
            LMD = v;
	    LMD = 0x80 & LMD? 0xffffff00|LMD: LMD;   // sign-extend
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x21:		// LH
        if (dcache->is_done()) {
	    sim_hword v;
	    dcache->reply_read_hword(ALUOutput, v);
            LMD = v;
	    LMD = 0x8000 & LMD? 0xffff0000|LMD: LMD; // sign-extend
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x22:		// LWL
        if (dcache->is_done()) {
	    dcache->reply_read_word(ALUOutput, LMD);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	// T.B.D.
	break;
    case 0x23:		// LW
        if (dcache->is_done()) {
	    dcache->reply_read_word(ALUOutput, LMD);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x24:		// LBU
        if (dcache->is_done()) {
	    sim_byte v;
	    dcache->reply_read_byte(ALUOutput, v);
            LMD = v;
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x25:		// LHU
        if (dcache->is_done()) {
	    sim_hword v;
	    dcache->reply_read_hword(ALUOutput, v);
            LMD = v;
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x26:		// LWR
        if (dcache->is_done()) {
	    dcache->reply_read_word(ALUOutput, LMD);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	// T.B.D.
	break;

	//
	// store instructions
	//
    case 0x28:		// SB
        if (dcache->is_done()) {
	    dcache->reply_write_byte(ALUOutput);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x29:		// SH
        if (dcache->is_done()) {
	    dcache->reply_write_hword(ALUOutput);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x2a:		// SWL
        if (dcache->is_done()) {
	    dcache->reply_write_word(ALUOutput);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x2b:		// SW
        if (dcache->is_done()) {
	    dcache->reply_write_word(ALUOutput);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;
    case 0x2e:		// SWR
        if (dcache->is_done()) {
	    dcache->reply_write_word(ALUOutput);
        }
	else {
            make_stall("*** DS stalled ***");
        }
	break;

    default:
	cout << "undefined instruction opcode="
	     << hex << opcode(IR) << endl;
	break;
    }

    cout << "PC=" << hex << PC << endl;
    cout << "NPC=" << hex << NPC << endl;
    cout << "IR=" << hex << IR << endl;

    cout << "Opcode=" << hex << opcode(IR) << endl;
    cout << "RS=" << dec << rs(IR) << endl;
    cout << "RT=" << dec << rt(IR) << endl;
    cout << "RD=" << dec << rd(IR) << endl;

    cout << "A=" << hex << A << endl;
    cout << "B=" << hex << B << endl;
    cout << "Imm=" << hex << Imm << endl;
    cout << "UImm=" << hex << UImm << endl;

    cout << "ALUOutput=" << hex << ALUOutput << endl;

    cout << "LMD=" << hex << LMD << endl;
}

void
mips::WB()
{
    cout << "---------- WB ----------" << endl;;

    switch (opcode(IR)) {
    case 0x00:		// SPECIAL instructions
	switch (funct(IR)) {
	case 0x00:	// SLL
	case 0x02:	// SRL
	case 0x03:	// SRA
	case 0x04:	// SLLV
	case 0x06:	// SRLV
	case 0x07:	// SRAV
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
	    break;

	case 0x08:	// JR
	    break;
	case 0x09:	// JALR
#ifdef	DELAYED_BRANCH
	    Regs[rd(IR)] = NPC + 4;
#else
	    Regs[rd(IR)] = NPC;
#endif
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << Regs[rd(IR)] << endl;
	    break;

	case 0x0c:	// SYSCALL
	    break;
	case 0x0d:	// BREAK
	    break;

	case 0x10:	// MFHI
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
	    break;
	case 0x11:	// MTHI
	    break;
	case 0x12:	// MFLO
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
	    break;
	case 0x13:	// MTLO
	    break;

	case 0x18:	// MULT
	case 0x19:	// MULTU
	case 0x1a:	// DIV
	case 0x1b:	// DIVU
	    break;

	case 0x20:	// ADD
	case 0x21:	// ADDU
	case 0x22:	// SUB
	case 0x23:	// SUBU
	case 0x24:	// AND
	case 0x25:	// OR
	case 0x26:	// XOR
	case 0x27:	// NOR
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
	    break;

	case 0x2a:	// SLT
	case 0x2b:	// SLTU
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// jump/branch instructions
	//
    case 0x02:		// J
	break;
    case 0x03:		// JAL
#ifdef	DELAYED_BRANCH
	Regs[31] = NPC + 4;
#else
	Regs[31] = NPC;
#endif
	cout << "regs[31]=" << hex << Regs[31] << endl;
	break;

    case 0x04:		// BEQ
    case 0x05:		// BNE
    case 0x06:		// BLEZ
    case 0x07:		// BGTZ
	break;

	//
	// ALU + immediate instructions
	//
    case 0x08:		// ADDI
    case 0x09:		// ADDIU
    case 0x0a:		// SLTI
    case 0x0b:		// SLTIU
    case 0x0c:		// ANDI
    case 0x0d:		// ORI
    case 0x0e:		// XORI
    case 0x0f:		// LUI
	Regs[rt(IR)] = ALUOutput;
	cout << "regs[" << dec << rt(IR) << "]="
	     << hex << ALUOutput << endl;
	break;

	//
	// co-processor instructions
	//
    case 0x10:		// COP0
    case 0x11:		// COP1
    case 0x12:		// COP2
	break;

    case 0x1c:		// SPECIAL2 instructions
	switch (funct(IR)) {
	case 0x02:	// MUL (MULtiply)
	case 0x20:	// CLZ (Count Leading Zero)
	case 0x21:	// CLO (Count Leading One)
	    Regs[rd(IR)] = ALUOutput;
	    cout << "regs[" << dec << rd(IR) << "]="
		 << hex << ALUOutput << endl;
            break;

	case 0x00:	// MADD (Mul-and-ADD)
	case 0x01:	// MADDU (Mul-and-ADD Unsigned)
	case 0x04:	// MSUB (Mul-and-SUB)
	case 0x05:	// MSUBU (Mul-and-SUb Unsigned)
	case 0x3F:	// SDBBP (Software Debug Breakpoint)
	    break;

	default:
	    cout << "undefined instruction opcode=SPECIAL2("
		 << hex << funct(IR) << ")" << endl;
	    break;
	}
	break;

	//
	// load instructions
	//
    case 0x20:		// LB
    case 0x21:		// LH
    case 0x22:		// LWL
    case 0x23:		// LW
    case 0x24:		// LBU
    case 0x25:		// LHU
    case 0x26:		// LWR
        Regs[rt(IR)] = LMD;
	cout << "regs[" << dec << rt(IR) << "]="
	     << hex << LMD << endl;
	break;

	//
	// store instructions
	//
    case 0x28:		// SB
    case 0x29:		// SH
    case 0x2a:		// SWL
    case 0x2b:		// SW
    case 0x2e:		// SWR
	break;

    default:
	cout << "undefined instruction opcode="
	     << hex << opcode(IR) << endl;
	break;
    }
}

void
mips::update()
{
    reset_stall_condition();

    switch (phase) {
    case 0:
        IF(); phase++; break;
    case 1:
        IS(); phase++; break;
    case 2:
        RF(); phase++; break;
    case 3:
        EX(); phase++; break;
    case 4:
        DF(); phase++; break;
    case 5:
        DS(); phase++; break;
    case 6:
        WB(); phase = 0; break;
    }

    // if stall condition occurs, phase must be rollback-ed
    if (is_stalled())
        phase = (phase == 0)? 6: phase-1;

    // update mul/div unit
    MulDivUnit.update();
}

void
mips::do_break()
{
    cout << "break exception occurred!" << endl;

    //
    // break handler interface
    //
    // input
    // r2: function code
    // r4,r5,r6,r7: arg1,arg2,arg3,arg4
    //
    // output
    // r2: return value
    //

    // you can add your new break handler routines here
    int fcode = Regs[2];
    switch (fcode) {

	// program termination
    case BREAK_PROGRAM_EXIT:
	running_f = false;
	break;

	// read data
    case BREAK_READ_INT:
        {
	    int v;
	    cin >> v;
	    Regs[2] = v;

	    cout << "read value(int) = " << v << endl;
	}
	break;
    case BREAK_READ_CHAR:
        {
	    char c;
	    cin >> c;
	    Regs[2] = c;

	    cout << "read value(char) = " << c << endl;
	}
	break;
    case BREAK_READ_STRING:
	{
	    char tmpbuf[2048];
	    cin >> tmpbuf;
	    for (int i = 0; tmpbuf[i] != '\0'; i++)
		mem->write_byte(Regs[4] + i, tmpbuf[i]);

	    cout << "read value(string) = " << tmpbuf << endl;
	}
	break;

	// print data
    case BREAK_PRINT_INT:
	cout << dec << Regs[4] << endl;
	break;
    case BREAK_PRINT_CHAR:
	cout << (char)(Regs[4]) << endl;
	break;
    case BREAK_PRINT_STRING:
	for (int i = 0; ; i++) {
	    char c = mem->read_byte(Regs[4] + i);
	    if (c == '\0')
		break;

	    cout << c;
	}
	cout << endl;
	break;

    default:
        cout << "unknown break fcode " << fcode << endl;
        break;
    }
}

void
mips::do_syscall()
{
    cout << "syscall issued!" << endl;

    // you can add your new syscall routines here
}

// end of mips.cc