// -*- c++ -*-
//
// arm_inst.cc:
//

using namespace std;
#include <iostream>
#include <cstdlib>
#include <cassert>
#include "arm_inst.h"

//
// arm_inst class member functions
//

void
arm_inst::decode_opcode()
{
    if (IR == 0) {
        inst_code = ARM_INST_NOP;
        return;
    }

    switch (Optype()) {         // dispatch using IR[27:25] (3bit)
    case 0:			// Data Processing (register-register)
	if ((IR & 0x0ffffff0) == 0x012fff10) { // case of BX
            inst_code = ARM_INST_BX;
	}
	else if ((IR & 0x0f8000f0) == 0x00000090) { // case of MUL
            inst_code = ARM_INST_MUL;
	}
	else if ((IR & 0x0f8000f0) == 0x00800090) { // case of MULL
            inst_code = ARM_INST_MULL;
	}
	else if ((IR & 0x0fbf0fff) == 0x010f0000) { // case of MRS
	    cerr << "MRS is not implemented" << endl;
//            inst_code = ARM_INST_MRS;
            inst_code = ARM_INST_UNDEF;
	}
	else if ((IR & 0x0fbffff0) == 0x0129f000) { // case of MSR
	    cerr << "MSR is not implemented" << endl;
//            inst_code = ARM_INST_MSR;
            inst_code = ARM_INST_UNDEF;
	}
	else if ((IR & 0x0e000090) == 0x00000090) { // case of LDRH/STRH
            if (Ld()) {      // load instruction
		switch (SH()) {
		case 1:		// Unsigned halfword
                    inst_code = B()? ARM_INST_LDRH: ARM_INST_LDRH_RR;
                    break;
		case 2:		// Signed byte
                    inst_code = B()? ARM_INST_LDRSB: ARM_INST_LDRSB_RR;
		    break;
		case 3:		// Signed halfword
                    inst_code = B()? ARM_INST_LDRSH: ARM_INST_LDRSH_RR;
		    break;
		default:
                    cerr << "unknown instruction " << hex << IR << endl;
                    inst_code = ARM_INST_UNDEF;
		    break;
		}
            }
            else {              // store instruction
		switch (SH()) {
		case 1:		// Unsigned halfword
                    inst_code = B()? ARM_INST_STRH: ARM_INST_STRH_RR;
                    break;
		case 2:		// Signed byte
                    inst_code = B()? ARM_INST_STRSB: ARM_INST_STRB_RR;
		    break;
		case 3:		// Signed halfword
                    inst_code = B()? ARM_INST_STRSH: ARM_INST_STRSH_RR;
		    break;
		default:
                    cerr << "unknown instruction " << hex << IR << endl;
                    inst_code = ARM_INST_UNDEF;
		    break;
		}
            }
	}
	else {			// normal data processing
            static int optbl[] = {
                ARM_INST_AND, ARM_INST_EOR, ARM_INST_SUB, ARM_INST_RSB,
                ARM_INST_ADD, ARM_INST_ADC, ARM_INST_SBC, ARM_INST_RSC,
                ARM_INST_TST, ARM_INST_TEQ, ARM_INST_CMP, ARM_INST_CMN,
                ARM_INST_ORR, ARM_INST_MOV, ARM_INST_BIC, ARM_INST_MVN,
            };
            inst_code = optbl[Opcode()];
	}
	break;

    case 1:			// Data Processing (register-immediate)
	if ((IR & 0x0fb00ff0) == 0x01000090) { // case of SWP
            inst_code = B()? ARM_INST_SWPB: ARM_INST_SWP;
	}
	else {			// normal data processing
            static int optbl[] = {
                ARM_INST_AND_I, ARM_INST_EOR_I, ARM_INST_SUB_I, ARM_INST_RSB_I,
                ARM_INST_ADD_I, ARM_INST_ADC_I, ARM_INST_SBC_I, ARM_INST_RSC_I,
                ARM_INST_TST_I, ARM_INST_TEQ_I, ARM_INST_CMP_I, ARM_INST_CMN_I,
                ARM_INST_ORR_I, ARM_INST_MOV_I, ARM_INST_BIC_I, ARM_INST_MVN_I,
            };
            inst_code = optbl[Opcode()];
	}
	break;

    case 2:			// Single Data Transfer (base+offset)
        inst_code = Ld()?
            (B()? ARM_INST_LDRB: ARM_INST_LDR):
            (B()? ARM_INST_STRB: ARM_INST_STR);
	break;

    case 3:			// Single Data Transfer (base+index)
        inst_code = Ld()?
            (B()? ARM_INST_LDRB_RR: ARM_INST_LDR_RR):
            (B()? ARM_INST_STRB_RR: ARM_INST_STR_RR);
	break;

    case 4:			// Block Data Transfer
        inst_code = Ld()? ARM_INST_LDM: ARM_INST_STM;
	break;

    case 5:			// Branch
        inst_code = L()? ARM_INST_BL: ARM_INST_B;
	break;

    case 6:			// Coprocessor Data Transfer
        cerr << "coprocessor instruction not implemented" << endl;
        inst_code = ARM_INST_UNDEF;
	break;

    case 7:		
	if ((Opcode() & 8)) { // SWI (SoftWare Interrupt)
            inst_code = ARM_INST_SWI;
	}
	else {			// Coprocessor Data Operation/Register Transfer
	    cerr << "coprocessor instruction not implemented" << endl;
            inst_code = ARM_INST_UNDEF;
	}
	break;

    default:
	cerr << "unknown instruction " << hex << IR << endl;
        inst_code = ARM_INST_UNDEF;
	break;
    }
}


void
arm_inst::decode_operand()
{
    A_reg = B_reg = C_reg = D_reg = E_reg = OPERAND_NOT_USED;

    if (inst_code == ARM_INST_NOP || inst_code == ARM_INST_UNDEF)
        return;

    switch (inst_code) {
    // branch/control transfer
    case ARM_INST_BX:                // branch and exchange
        A_reg = Rm();
        break;

    case ARM_INST_B:                 // branch
        A_reg = 15;
        break;

    case ARM_INST_BL:                // branch with link
        A_reg = 15;
//        C_reg = 15;
        E_reg = 14;                  // LR will be updated
        break;

    // data processing (register-register)
    case ARM_INST_AND:               // bitwise AND
    case ARM_INST_EOR:               // bitwise Exculsive-OR
    case ARM_INST_SUB:               // subtract
    case ARM_INST_RSB:               // subtract reverse
    case ARM_INST_ADD:               // add
    case ARM_INST_ADC:               // add with carry
    case ARM_INST_SBC:               // subtract with carry
    case ARM_INST_RSC:               // subtract reverse with carry
    case ARM_INST_ORR:               // bitwise OR
    case ARM_INST_BIC:               // bit clear
        A_reg = Rn();
        B_reg = Rm();
        C_reg = shRm()? Rs(): OPERAND_NOT_USED;
        D_reg = Rd();
        break;

    case ARM_INST_TST:               // test (bitwise AND)
    case ARM_INST_TEQ:               // test (bitwise EOR)
    case ARM_INST_CMP:               // compare
    case ARM_INST_CMN:               // compare negative
        A_reg = Rn();
        B_reg = Rm();
        C_reg = shRm()? Rs(): OPERAND_NOT_USED;
        //D_reg = Rd();                // (result is not written)
        break;

    case ARM_INST_MOV:               // move
        //A_reg = Rn();                // (operand 1 is ignored)
#if 0
        // shift operation might be applied
        // even when the source and destination register are same
	if (Rm() != Rd()) {
	    B_reg = Rm();
	    C_reg = shRm()? Rs(): OPERAND_NOT_USED;
	    D_reg = Rd();
	}
#endif
        B_reg = Rm();
        C_reg = shRm()? Rs(): OPERAND_NOT_USED;
        D_reg = Rd();
        break;

    case ARM_INST_MVN:               // move with negate
        //A_reg = Rn();                // (operand 1 is ignored)
        B_reg = Rm();
        C_reg = shRm()? Rs(): OPERAND_NOT_USED;
        D_reg = Rd();
        break;

    // data processing (register-immediate)
    case ARM_INST_AND_I:             // bitwise AND
    case ARM_INST_EOR_I:             // bitwise Exculsive-OR
    case ARM_INST_SUB_I:             // subtract
    case ARM_INST_RSB_I:             // subtract reverse
    case ARM_INST_ADD_I:             // add
    case ARM_INST_ADC_I:             // add with carry
    case ARM_INST_SBC_I:             // subtract with carry
    case ARM_INST_RSC_I:             // subtract reverse with carry
    case ARM_INST_ORR_I:             // bitwise OR
    case ARM_INST_BIC_I:             // bit clear
        A_reg = Rn();
        D_reg = Rd();
        break;

    case ARM_INST_TST_I:             // test (bitwise AND)
    case ARM_INST_TEQ_I:             // test (bitwise EOR)
    case ARM_INST_CMP_I:             // compare
    case ARM_INST_CMN_I:             // compare negative
        A_reg = Rn();
        //D_reg = Rd();                // (result is not written)
        break;

    case ARM_INST_MOV_I:             // move
    case ARM_INST_MVN_I:             // move with negate
        //A_reg = Rn();                // (operand 1 is ignored)
        D_reg = Rd();
        break;

    // single data transfer (base+offset)
    case ARM_INST_LDR:               // load word
    case ARM_INST_LDRB:              // load byte
    case ARM_INST_LDRSB:             // load byte (signed)
    case ARM_INST_LDRH:              // load halfword
    case ARM_INST_LDRSH:             // load halfword (signed)
        A_reg = Rn();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        E_reg = Rd();
        break;

    case ARM_INST_STR:               // store word
    case ARM_INST_STRB:              // store byte
    case ARM_INST_STRSB:             // store byte (signed)
    case ARM_INST_STRH:              // store halfword
    case ARM_INST_STRSH:             // store halfword (signed)
        A_reg = Rn();
        C_reg = Rd();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        break;

    // single data transfer (base+index)
    case ARM_INST_LDR_RR:            // load word
    case ARM_INST_LDRB_RR:           // load byte
    case ARM_INST_LDRSB_RR:          // load byte (signed)
    case ARM_INST_LDRH_RR:           // load halfword
    case ARM_INST_LDRSH_RR:          // load halfword (signed)
        A_reg = Rn();
        B_reg = Rm();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        E_reg = Rd();
        break;

    case ARM_INST_STR_RR:            // store word
    case ARM_INST_STRB_RR:           // store byte
    case ARM_INST_STRSB_RR:          // store byte (signed)
    case ARM_INST_STRH_RR:           // store halfword
    case ARM_INST_STRSH_RR:          // store halfword (signed)
        A_reg = Rn();
        B_reg = Rm();
        C_reg = Rd();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        break;

    // multiple data transfer
    case ARM_INST_LDM:               // load multiple
        A_reg = Rn();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        break;

    case ARM_INST_STM:               // store multiple
        A_reg = Rn();
        D_reg = W()? Rn(): OPERAND_NOT_USED;
        break;

    // swap
    case ARM_INST_SWP:               // swap word
    case ARM_INST_SWPB:              // swap byte
        A_reg = Rn();
        C_reg = Rm();
        E_reg = Rd();
        break;

    // multiplication
    case ARM_INST_MUL:               // multiply
        A_reg = A()? Rd(): OPERAND_NOT_USED;
        B_reg = Rm();
        C_reg = Rs();
        D_reg = Rn();
        break;

    case ARM_INST_MULL:              // multiply long
        A_reg = Rs();
        B_reg = Rm();
        D_reg = Rd();
        E_reg = Rn();
        break;

    // coprocessor
    case ARM_INST_CDP:               // coprocessor data processing
    case ARM_INST_LDC:               // coprocessor load data
    case ARM_INST_STC:               // coprocessor store data
    case ARM_INST_MCR:               // coprocessor move from register
    case ARM_INST_MRC:               // coprocessor move to register
        break;

    // PSR transfer
    case ARM_INST_MRS:               // move PSR to register
    case ARM_INST_MSR:               // move PSR from register
        break;

    // software interrupt/system call
    case ARM_INST_SWI:               // software interrupt
        break;

    default:
        assert(0);
        break;
    }
}


bool
arm_inst::is_memory_load()
{
    switch (inst_code) {
    case ARM_INST_LDR:
    case ARM_INST_LDRB:
    case ARM_INST_LDRSB:
    case ARM_INST_LDRH:
    case ARM_INST_LDRSH:

    case ARM_INST_LDR_RR:
    case ARM_INST_LDRB_RR:
    case ARM_INST_LDRSB_RR:
    case ARM_INST_LDRH_RR:
    case ARM_INST_LDRSH_RR:

    case ARM_INST_LDM:

    case ARM_INST_SWP:
    case ARM_INST_SWPB:

        return true;
    }
    return false;
}

bool
arm_inst::is_memory_store()
{
    switch (inst_code) {
    case ARM_INST_STR:
    case ARM_INST_STRB:
    case ARM_INST_STRSB:
    case ARM_INST_STRH:
    case ARM_INST_STRSH:

    case ARM_INST_STR_RR:
    case ARM_INST_STRB_RR:
    case ARM_INST_STRSB_RR:
    case ARM_INST_STRH_RR:
    case ARM_INST_STRSH_RR:

    case ARM_INST_STM:

    case ARM_INST_SWP:
    case ARM_INST_SWPB:

        return true;
    }
    return false;
}

// end of arm_inst.cc