/*
	 Copyright 2001 (c) by Authors,
	 Swiss Federal Institute of Technology,
	 Computer Engineering and Networks Laboratory.
 
	 TOPSY -- A Teachable Operating System.
	 Implementation of a tiny and simple
	 micro kernel for teaching purposes.
 
	 For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
	 This software is provided under the terms of the GNU General Public 
	 Licence. A full copy of the GNU GPL is provided in the file COPYING 
	 found in the development root of Topsy.
 */
/*
 
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/sa1100/Drivers/Attic/IXPuCode.c,v $
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 2922 $
	Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $  by: $Author: baderma $
 
*/

// this code is based upon parts of vera from Princeton


#include "IXPuCode.h"
#include "IXPuEInit.h"
#include "Syscall.h"
#include "Support.h"
#include "types.h"
#include "kalloc_api.h"


/* The type of the microword instruction. */
typedef enum {
  NONE        = 0,
  BR_ALU      = 2,
  BR_BYTE     = 3,
  ALU         = 4,
  EXT_2_SRC   = 5,
  IMMED       = 6,
  EXT_1_SRC   = 7

} UW_TYPE;

/* The type of command. */
typedef enum {
  CSR         = 0,
  FAST_WR     = 2,
  HASH        = 3,
  CONTEXT     = 4,
  BRANCH_BIT  = 5,
  USTORE      = 6,
  BRANCH      = 7,
  FBOX_CSR    = 8
} EXT_1_OP_CMD;


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_isBranch                                             */
/* Parameters: uint32  ucode   The word of microcode to query.               */
/* Returns: TRUE if the microcode is a relocatable branch instruction        */
/* Effects: <none>                                                           */
/*                                                                           */
/*****************************************************************************/

static inline Boolean
uCode_isBranch(uint32 ucode)
{
  uint8 ext_1_op_cmd = ucode & 0xF;
  uint8 uw_type = ucode >> 29;

  return ( ((uw_type == EXT_1_SRC) &&
            ((ext_1_op_cmd == BRANCH) || (ext_1_op_cmd == BRANCH_BIT)))
           ||
           (uw_type == BR_ALU)
           ||
           (uw_type == BR_BYTE));
}

/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_RelocateBranches                                     */
/* Parameters:                                                               */
/*      uEng_uCodeInfo  info    The microcode to relocate.                   */
/*      uint32             offset  The offset by which to relocate branches. */
/* Returns: <none>                                                           */
/* Effects: Relocates any branch instruction in info by offset.              */
/*                                                                           */
/*****************************************************************************/

void
uCode_RelocateBranches(uEng_uCodeInfo info, uint16 offset)
{
  if ((info != NULL) && (offset < 1024))
  {// There can only be 1024
    //                                  // instructions.
    int i = 0;

    for (i = 0; i < info->length; i++)
    {
      if (uCode_isBranch(info->code[i]))
      {
        info->code[i] = uCode_RelocateBranchField(info->code[i],
                        offset);

      }
    }
  }
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_copyExpand                                           */
/* Parameters:                                                               */
/*      uEng_uCodeInfo *info        The microcode structure to copy.         */
/*      uint16             addedsize   The additional number of slots to add.*/
/* Returns: uEng_uCodeInfo *        A newly allocated microcode structure.   */
/* Effects: Creates a new microcode structure with a few additional slots.   */
/*                                                                           */
/*****************************************************************************/

static inline uEng_uCodeInfo
uCode_copyExpand(uEng_uCodeInfo info, uint16 addedsize)
{
  uEng_uCodeInfo retval = NULL;

  if (info)
  {

    // XXX maybe i should clear the memory whic i return?
    retval = km_alloc((long)sizeof (*retval), KM_MEMORY );
    retval->length = info->length + addedsize;
    // XXX maybe i should clear the memory whic i return?
    retval->code = km_alloc(retval->length * sizeof(uint32), KM_MEMORY );
  }

  return retval;
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_RelocateBranchField                                  */
/* Parameters:                                                               */
/*      uint32     code    The longword of microcode to modify.              */
/*      uint16  offset  The offset by which to modify the branch instruction.*/
/* Returns: uint32: The relocated branch instruction.                        */
/* Effects: Relocates the target address of the branch by offset             */
/*          instructions. The original code is not modified.                 */
/*****************************************************************************/

static inline uint32
uCode_RelocateBranchField(uint32 code, uint16 offset)
{
  uint32 retval =  code & ~(0x3FF << 7); // all the other bits.
  uint32 addr = (code >> 7) & 0x3FF;     // The 10 address bits

  addr += offset;     // Retarget the branch

  retval |= (addr << 7);      // Change the bits.

#if DEBUG_UCODE_RELOCATE_BRANCH_FIELD

  printf("%s(%x, %4d) = %x\n", __FUNCTION__, code, offset, retval);
#endif

  return retval;
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_immed_wx                                             */
/* Parameters:                                                               */
/*      uint16     data    The value to which to set the given word.         */
/*      uint8      dest    The destination register.                         */
/*      int     x       The word to set.                                     */
/* Returns: uint32: A microword instruction to set word x of register dest to*/
/*               data.                                                       */
/* Effects: <none>                                                           */
/*                                                                           */
/*****************************************************************************/

static inline uint32
uCode_immed_wx(uint16 data, uint8 dest, int x)
{
  uint32 retval = 0;

  retval = (((((((((((((6                     /* UW_TYPE = IMMED      */
                        << 8) | dest)         /* ABS_DEST = dest      */
                      << 1) | 0x1)            /* LOAD_WORD = TRUE     */
                    << 1) | 0x0)              /* LOAD_BYTE = FALSE    */
                  << 2) | ((x == 1)? 0x2 : 0x0)) /* IMMED_SHF */
                << 10) | (data & 0x3FF))      /* IMMED_DATA_16_BIT 16:7*/
              << 1) | 0x0)                    /* INVERT               */
            << 6) | (data >> 10);             /* IMMED_DATA_16_BIT 5:0 */

#if DEBUG_UCODE_IMMED

  Fmt_print("%s(%04x, %02x, %d) = %08x\n", __FUNCTION__, data, dest, x,
            retval);
#endif

  return retval;
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_Add_immed_data                                       */
/* Parameters:                                                               */
/*      uEng_uCodeInfo  info    The microcode to modify.                     */
/*      uint32          data    The value to set in the register. (19 bits.) */
/* Returns: A newly allocated microcode array with additional instructions.  */
/* Effects: Copies info to a new microcode array and adds two instructions   */
/*          to set the address portion of VRP_FORWARDER_STATE_REGISTER to    */
/*          data.                                                            */
/*****************************************************************************/

uEng_uCodeInfo
uCode_Add_immed(uEng_uCodeInfo info, uint32 data)
{
  uEng_uCodeInfo retval = NULL;
  uint16 immed[2];


  immed[0] = 0;
  immed[1] = 0;

  retval = uCode_copyExpand(info, 2);

  byteCopy(&retval->code[2], info->code, info->length * sizeof(uint32));

  data &= 0x7FFFF;                    // Mask off the high bits.

  /* To fit in the register, the data needs to be shifted left 10 bits. Hi
   * contains the top 16 bits of the shifted number. low contains the lower
   * 16 bits. */
  immed[1] = data >> 6;                       // (data << 10) >> 16
  immed[0] = (data & 0x3F) << 10;             // (data << 10) & 0xFFFF

#if DEBUG_UCODE_IMMED

  printf("%s: data = %08x\n\thi = %04x\n\tlow = %04x\n",
         __FUNCTION__, data, immed[1], immed[0]);
#endif

  retval->code[0] = uCode_immed_wx(immed[0], VRP_FORWARDER_STATE_REGISTER,0);
  retval->code[1] = uCode_immed_wx(immed[1], VRP_FORWARDER_STATE_REGISTER,1);

  return retval;
}


/*****************************************************************************/
/*                                                                           */
/* Function Name: uCode_Add_rtn                                              */
/* Parameters: uEng_uCodeInfo info                                           */
/* Returns: A newly allocated array of microcode with new instructions.      */
/* Effects: Copies info to a new microcode array and adds an instruction to  */
/*          jump to the address stored in VRP_FORWARDER_STATE_REGISTER.      */
/*****************************************************************************/

uEng_uCodeInfo
uCode_Add_rtn(uEng_uCodeInfo info)
{
  uEng_uCodeInfo retval = uCode_copyExpand(info, 1);

  byteCopy(retval->code, info->code, retval->length * sizeof(uint32));

  retval->code[retval->length - 1] = ((2 << 8) |
                                      VRP_FORWARDER_STATE_REGISTER) << 20;

  return retval;
}
