// -*- c++ -*-
//
// arm.h:
//
#ifndef	ARM_H
#define	ARM_H

#include <iostream>
#include "sim.h"
#include "regfile.h"
#include "cache.h"
#include "memory.h"
#include "arm_inst.h"

using namespace std;


class arm_psr {
    sim_word    CPSR;
public:
    arm_psr(sim_word psr = 0) { CPSR = psr; }

    sim_word raw() { return CPSR; }

    void set_N(bool v = true) { CPSR = v? CPSR|0x80000000 : CPSR&~0x80000000; }
    void set_Z(bool v = true) { CPSR = v? CPSR|0x40000000 : CPSR&~0x40000000; }
    void set_C(bool v = true) { CPSR = v? CPSR|0x20000000 : CPSR&~0x20000000; }
    void set_V(bool v = true) { CPSR = v? CPSR|0x10000000 : CPSR&~0x10000000; }

    bool N() { return (CPSR & 0x80000000)? true: false; }
    bool Z() { return (CPSR & 0x40000000)? true: false; }
    bool C() { return (CPSR & 0x20000000)? true: false; }
    bool V() { return (CPSR & 0x10000000)? true: false; }

    sim_word flags() { return CPSR >> 28; }
};


class arm {
    bool	running_f;	// running flag
    bool        multicycle_exe_f; // multi-cycle execution 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
#define	PC	(Regs[15])
    sim_addr	IADDR;		// instruction address

    regfile	Regs;	        // general-purpose registers
                                // (16 regs, no zero reg)
    arm_psr	CPSR;		// current program status word

    //
    // temporary states
    //
    arm_inst	IR;		// instruction register

    sim_word	A;	        // reg. read port 1 (left input of ALU)
    sim_word	B;	        // reg. read port 2 (right input of ALU via shifter)
    sim_word	C;	        // reg. read port 3 (shifter ctrl./store data)

    bool        CondOK;         // condition for execution is OK

    sim_word	ALUOutput;	// output of ALU
    sim_word	ALUOutput2;	// output of ALU 2
    sim_word	LMD;		// load memory data


    //
    // some helper functions
    //
    int carry() { return CPSR.C()? 1: 0; } // carry flag

    // helper for shift operation
    sim_word LSL(sim_word v, int n, bool &cy);	        // logical shift left
    sim_word LSR(sim_word v, int n, bool &cy);	        // logical shift right
    sim_word ASR(sim_word v, int n, bool &cy);	        // arithmetic shift right
    sim_word ROR(sim_word v, int n, bool &cy);	        // rotate right

    // helper for register list check
    int count_bits(unsigned int n);
    int next_regno(unsigned int reglist, bool asending);

    // calculation of operand 2
    sim_word Operand2_val(arm_inst IR, sim_word B, sim_word C, bool flag_set);
    sim_word Operand2_reg(arm_inst IR, sim_word Rm, sim_word Rs, bool flag_set);
    sim_word Operand2_imm(arm_inst IR, sim_word uimm8, sim_word rot, bool flag_set);

    // check condition codes
    bool check_condition_codes(arm_inst IR, arm_psr CPSR);

    // check overflow
    int sign(sim_word v);
    bool overflow_S(sim_word a, sim_word b, sim_word c);
    bool overflow_U(sim_word a, sim_word b, sim_word c);

public:
    arm();
    ~arm();

    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);		// set program counter
    void set_lr(sim_addr link_addr);	// set link register
    void set_sp(sim_addr sp);		// set stack pointer
    void set_reg(int regno, sim_word val);

    sim_addr get_pc();			// get program counter
    sim_addr get_lr();			// get link register
    sim_addr get_sp();			// get stack pointer
    sim_word get_reg(int regno);

    bool is_running() { return running_f; }
    bool is_multicycle_execution() { return multicycle_exe_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/pipeline stage
    //
    void IF();		// Instruction Fetch
    void ID();		// Instruction Decode
    void EX();		// EXecution
    void MA();		// Memory Access
    void WB();		// Write Back

    //
    // service call
    //
    void do_swi(int fcode);
};

#endif
// end of arm.h