// -*- c++ -*-
//
// mips.h:
//
#ifndef	MIPS_H
#define	MIPS_H

#define	DELAYED_BRANCH		// define if you want to use DELAYED BRANCH

#include "sim.h"
#include "regfile.h"
#include "cache.h"
#include "memory.h"

class mips {
    bool	running_f;	// running flag
    bool        stall_cond_f;	// stall condition flag

    memory	*mem;	        // main memory
    cache	*icache;	// I-cache
    cache	*dcache;	// D-cache

    sim_addr	PC;		// program counter

    regfile	Regs;		// general-purpose registers
    sim_word	Hi, Lo;		// HI/LO registers (for Mul/Div)

    //
    // temporary values
    //
    sim_inst	IR;		// instruction register
    sim_addr	NPC;		// next program counter
    sim_word	A, B;		// inputs for ALU
    sim_word	Imm;		// immediate value (from IR)
    sim_word	UImm;		// unsigned immediate value (from IR)
    sim_word	ALUOutput;	// output of ALU
    bool	Cond;		// condition for branch
    sim_word	LMD;		// load memory data

#ifdef	DELAYED_BRANCH
    //
    // delayed branch handling
    //
    bool	delayed_f;
    sim_addr	delayed_pc;

#endif

    //
    // helper functions
    //
    int opcode(sim_inst ir) { return (ir >> 26) & 0x3f; }
    int rs(sim_inst ir) { return (ir >> 21) & 0x1f; }
    int rt(sim_inst ir) { return (ir >> 16) & 0x1f; }
    int rd(sim_inst ir) { return (ir >> 11) & 0x1f; }
    int shamt(sim_inst ir) { return (ir >> 6) & 0x1f; }
    int funct(sim_inst ir) { return ir & 0x3f; }

    sim_word uimm(sim_inst ir) {	// immediate value (zero-extended)
	return ir & 0xffff;
    }
    sim_word imm(sim_inst ir) {		// immediate value (signed-extended)
	sim_word v = ir & 0xffff;
	return (v & 0x8000)? (0xffff0000|v): v;
    }

    void check_overflow(sim_word dest, sim_word src1, sim_word src2) {
	if ((src1 & 0x80000000) == (src2 & 0x80000000) &&
	    (src1 & 0x80000000) != (dest & 0x80000000)) {

	    cout << "overflow detected!" << endl;

	    running_f = false;		// program should be stopped
	}
    }

public:
    mips();
    ~mips();

    void connect_memory(memory *mem);
    void connect_icache(cache *icache);
    void connect_dcache(cache *dcache);
    void reset();		// reset all internal states

    void set_pc(sim_addr pc);
    void set_reg(int regno, sim_word val);

    sim_addr get_pc();
    sim_word get_reg(int regno);

    bool is_running() { return running_f; }

    bool is_stalled() { return stall_cond_f; }
    void reset_stall_condition() { stall_cond_f = false; }
    void set_stall_condition() { stall_cond_f = true; }

    //
    // instruction cycle
    //
    void IF();		// Instruction Fetch (First)
    void IS();		// Instruction Fetch (Second)
    void RR();		// Instruction Decode and Register file Read
    void EX();		// EXecution
    void MA();		// Memory Access
    void WB();		// Write Back

    //
    // service call
    //
    void do_break();
    void do_syscall();
};

#endif
// end of mips.h