/*

Freely Distributable C30 Simulator Package

Copyright (c) 1996-1998 The University of Texas
All Rights Reserved.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
The GNU Public License is available in the file LICENSE, or you
can write to the Free Software Foundation, Inc., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA, or you can find it on the
World Wide Web at http://www.fsf.org.

Authors: Chi Duong, Brian Evans, and Chris Moy
Version: @(#)simmisc.cc	1.25	01/19/98 

Department of Electrical and Computer Engineering
The University of Texas, Austin, TX 78712-1084

*/

#include <string.h>

#include "pipeline.h"
#include "state.h"
#include "simmisc.h"
#include "memmap.h"
#include "execute.h"
#include "assm_fun.h"

#define C30SIM_DSK_INIT_STACK_POINTER 0x00809f00
#define C30SIM_INIT_EXT_REG_VALUE     0x80000000
#define C30SIM_INIT_REG_VALUE         0

#ifndef __cpluscplus
#define inline
#endif

/*
Returns the number of c30 cycles executed since the last reset.
*/
int C30SimGetCycle(pipeline *pipe) {
        return pipe->cycleCount;
}

/*
Initializes the simulator's data structures by flushing
the pipeline, clearing the status register, resetting the cycle count,
setting up the register pointer array in the pipeline struct, and
building the disassembler hash table.
*/
void C30SimInit(state *st, pipeline *pipe, c30ProcType processor) {
        /* Initialize the disassember hash table */
        BuildFastLook();

        /* Initialize the state registers */
        memset(st, 0, sizeof(state));

        /* Set the default state and pipeline values */
        C30SimReset(st, pipe);

        /* Allocate memory for the particular processor in the C3x family */
        C30SimInitMemoryMap(st, processor);
}

void C30SimReset(state *st, pipeline *pipe) {
        /* Initialize the pipeline */
        memset(pipe, 0, sizeof(pipeline));

        pipe->regs[0] = &st->Sregs[__r0];
        pipe->regs[1] = &st->Sregs[__r1];
        pipe->regs[2] = &st->Sregs[__r2];
        pipe->regs[3] = &st->Sregs[__r3];
        pipe->regs[4] = &st->Sregs[__r4];
        pipe->regs[5] = &st->Sregs[__r5];
        pipe->regs[6] = &st->Sregs[__r6];
        pipe->regs[7] = &st->Sregs[__r7];
        pipe->regs[8] = &st->ar0;
        pipe->regs[9] = &st->ar1;
        pipe->regs[10] = &st->ar2;
        pipe->regs[11] = &st->ar3;
        pipe->regs[12] = &st->ar4;
        pipe->regs[13] = &st->ar5;
        pipe->regs[14] = &st->ar6;
        pipe->regs[15] = &st->ar7;
        pipe->regs[16] = &st->dp;
        pipe->regs[17] = &st->ir0;
        pipe->regs[18] = &st->ir1;
        pipe->regs[19] = &st->bk;
        pipe->regs[20] = &st->sp;
        pipe->regs[21] = &st->st;
        pipe->regs[22] = &st->ie;
        pipe->regs[23] = &st->if_;
        pipe->regs[24] = &st->iof;
        pipe->regs[25] = &st->rs;
        pipe->regs[26] = &st->re;
        pipe->regs[27] = &st->rc;

        pipe->flushed = 1;
        pipe->finalOpcode1 = S_nop;
        pipe->opcode1 = S_nop;
        pipe->inst = C30_NOP_INST;
        pipe->source1 = pipe->source2 = pipe->source3 =
                        pipe->source4 = &pipe->hold;

        /* Reset the register values to zero */
        int regNum;
        for (regNum = 0; regNum < C30_NUMBER_OF_REGISTERS; regNum++) {
          *pipe->regs[regNum] = C30SIM_INIT_REG_VALUE;
        }

        /* Initialize the extended registers rxn to 0x80000000 as in dsk3d */
        st->Sregs[__rx0] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx1] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx2] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx3] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx4] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx5] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx6] = C30SIM_INIT_EXT_REG_VALUE;
        st->Sregs[__rx7] = C30SIM_INIT_EXT_REG_VALUE;

        /* Initialize sp as in the dsk3d */
        st->sp = C30SIM_DSK_INIT_STACK_POINTER;
}

/*
End the simulation and deallocate memory map.
*/
int C30SimEnd(state* st, pipeline* /*pipe*/) {
	return C30SimFreeMemoryMap(st);
}

static inline uint32 reverseBit(int n, uint32 num) {
        uint32 tmp=0;
        for (int i=0; i<n; i++) {
                tmp >>= 1; // no sign-extended
                if (num & 0x80000000L)
              tmp |= 0x80000000L;
                num <<= 1;
        }
          return tmp;
}
/*
The following are the indirect address forming routines,
which are called in the decode stage. ar is the AR register
number. disp is the displacement. And src is a pointer to a
pointer to the operand source.
*/
static inline void modifyAddress(int m, c30reg* addrReg, uint32 index, state* st) {
        uint32 size, base;
        uint32 temp = st->bk;
        int i, n;

        // Look for location of the most significant 1 of st->bk, with n<=15
        for (n = 15; n >= 0; n--) {
                if (temp & 0x00008000L) break;
                else temp <<= 1;
        }

        // Create mask for base
        temp = 0;
        for (i = n; i >= 0; i--) {
                temp <<= 1;
                temp |= 1;
        }

        size = (st->bk) & temp;
        base = (*addrReg & ~temp);

        if ((base + size) >= *addrReg) {
        //Arn must point to an element in the circular queue
                if (index < base)
                        st->updateArn[st->saveArn].arnVal[m] = index + size;
                else if (index >= (base +  size))
                        st->updateArn[st->saveArn].arnVal[m] = index  - size;
                else
                        st->updateArn[st->saveArn].arnVal[m] = index;
        }
        /* update arx_s */
        *(addrReg-8) = st->updateArn[st->saveArn].arnVal[m];
        st->updateArn[st->saveArn].flag = 1;
}


/* Predisplacement add */
void C30SimPredisadd(int /*n*/, state *st, unsigned char ar,
                     unsigned char disp, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar)+disp);
}

/* Predisplacement subtract */
void C30SimPredissub(int /*n*/, state *st, unsigned char ar,
                     unsigned char disp, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar)-disp);
}

/* Predisplacement add and modify */
void C30SimPredisaddmod(int n, state *st, unsigned char ar,
                        unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + disp;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Predisplacement subtract and modify */
void C30SimPredissubmod(int n, state *st, unsigned char ar,
                        unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - disp;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Postdisplacement add and modify */
void C30SimPostdisaddmod(int n, state *st, unsigned char ar,
                         unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + disp;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postdisplacement subtract and modify */
void C30SimPostdissubmod(int n, state *st, unsigned char ar,
                         unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - disp;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postdisplacement add and circular modify */
void C30SimPostdisaddcirc(int n, state *st, unsigned char ar,
                          unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) + disp;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* Postdisplacement subtract and circular modify */
void C30SimPostdissubcirc(int n, state *st, unsigned char ar,
                          unsigned char disp, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) - disp;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* Preindex (IR0) add */
void C30SimPreir0add(int /*n*/, state *st, unsigned char ar,
                     unsigned char /*disp*/, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar) + st->ir0);
}

/* Preindex (IR0) subtract */
void C30SimPreir0sub(int /*n*/, state *st, unsigned char ar,
                     unsigned char /*disp*/, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar) - st->ir0);
}

/* Preindex (IR0) add and modify */
void C30SimPreir0addmod(int n, state *st, unsigned char ar,
                        unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir0;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Preindex (IR0) subtract and modify */
void C30SimPreir0submod(int n, state *st, unsigned char ar,
                        unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        st->updateArn[st->saveArn].arnVal[n] = 
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir0;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR0) add and modify */
void C30SimPostir0addmod(int n, state *st, unsigned char ar,
                         unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir0;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR0) subtract and modify */
void C30SimPostir0submod(int n, state *st, unsigned char ar,
                         unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir0;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR0) add and circular modify */
void C30SimPostir0addcirc(int n, state *st, unsigned char ar,
                          unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir0;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* Postindex (IR0) subtract and circular modify */
void C30SimPostir0subcirc(int n, state *st, unsigned char ar,
                          unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir0;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* Preindex (IR1) add */
void C30SimPreir1add(int /*n*/, state *st, unsigned char ar,
                     unsigned char /*disp*/, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar)+st->ir1);
}

/* Preindex (IR1) subtract */
void C30SimPreir1sub(int /*n*/, state *st, unsigned char ar,
                     unsigned char /*disp*/, uint32 **src) {
        *src = C30SimMemMap(st, *(&(st->ar0)+ar)-st->ir1);
}

/* Preindex (IR1) add and modify */
void C30SimPreir1addmod(int n, state *st, unsigned char ar,
                        unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir1;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) = 
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Preindex (IR1) subtract and modify */
void C30SimPreir1submod(int n, state *st, unsigned char ar,
                        unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir1;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        *src = C30SimMemMap(st, st->updateArn[st->saveArn].arnVal[n]);
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR1) add and modify */
void C30SimPostir1addmod(int n, state *st, unsigned char ar,
                         unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir1;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR1) subtract and modify */
void C30SimPostir1submod(int n, state *st, unsigned char ar,
                         unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        st->updateArn[st->saveArn].arnVal[n] =
                *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir1;
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}

/* Postindex (IR1) add and circular modify */
void C30SimPostir1addcirc(int n, state *st, unsigned char ar,
                          unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) + st->ir1;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* Postindex (IR1) subtract and circular modify */
void C30SimPostir1subcirc(int n, state *st, unsigned char ar,
                          unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar; 
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 index = *(st->updateArn[st->saveArn].arnAddr[n]-8) - st->ir1;
        modifyAddress(n,st->updateArn[st->saveArn].arnAddr[n],index,st);
}

/* indirect */
void C30SimIndirect(int /*n*/, state *st, unsigned char ar,
                    unsigned char /*disp*/, uint32 **src) {
        *src = C30SimMemMap(st,*(&(st->ar0)+ar));
}

/* Postindex (IR0) add and bit-reversed modify */
void C30SimBitrev(int n, state *st, unsigned char ar,
                  unsigned char /*disp*/, uint32 **src) {
        st->updateArn[st->saveArn].arnAddr[n] = &(st->ar0) + ar;
        *src = C30SimMemMap(st, *(st->updateArn[st->saveArn].arnAddr[n]-8));
        uint32 ir0 = st->ir0;
        //looking for the location j of the most significant 1 of st->ir0
        int j = 0;
        for (j = 32; j > 0; j--) {
                if (ir0 & 0x80000000L) break;
                else ir0 <<= 1;
        }
        uint32 addrR=((*st->updateArn[st->saveArn].arnAddr[n]) << (32-j));
        addrR=reverseBit(j,addrR);
        ir0=(st->ir0 << (32-j));
        ir0=reverseBit(j,ir0);
        addrR += ir0;
        addrR=reverseBit(j,addrR);
        st->updateArn[st->saveArn].arnVal[n] =
                (addrR >> (32-j)) | ((*(st->updateArn[st->saveArn].arnAddr[n]-8) >> j) << j);// no sign-extend
        /* update arx_s */
        *(st->updateArn[st->saveArn].arnAddr[n]-8) =
                st->updateArn[st->saveArn].arnVal[n];
        st->updateArn[st->saveArn].flag = 1;
}
