/* OpenIDA - Outil d'analyse de fichiers binaires
 * registers.c - aides auxiliaires relatives aux registres MIPS
 *
 * Copyright (C) 2009 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  OpenIDA 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenIDA 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "registers.h"


#include <malloc.h>
#include <stdio.h>



/* Registre MIPS */
enum _mips_register
{
    MIPS_REG_ZERO   = 0,                    /* Constante zéro              */
    MIPS_REG_AT     = 1,                    /* Temporaire assemblage       */
    MIPS_REG_V_0    = 2,                    /* Retours et évaluations      */
    MIPS_REG_V_1    = 3,                    /* Retours et évaluations      */
    MIPS_REG_ARG_0  = 4,                    /* Arguments de fonction       */
    MIPS_REG_ARG_1  = 5,                    /* Arguments de fonction       */
    MIPS_REG_ARG_2  = 6,                    /* Arguments de fonction       */
    MIPS_REG_ARG_3  = 7,                    /* Arguments de fonction       */
    MIPS_REG_TMP_0  = 8,                    /* Stockage temporaire         */
    MIPS_REG_TMP_1  = 9,                    /* Stockage temporaire         */
    MIPS_REG_TMP_2  = 10,                   /* Stockage temporaire         */
    MIPS_REG_TMP_3  = 11,                   /* Stockage temporaire         */
    MIPS_REG_TMP_4  = 12,                   /* Stockage temporaire         */
    MIPS_REG_TMP_5  = 13,                   /* Stockage temporaire         */
    MIPS_REG_TMP_6  = 14,                   /* Stockage temporaire         */
    MIPS_REG_TMP_7  = 15,                   /* Stockage temporaire         */
    MIPS_REG_SAV_0  = 16,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_1  = 17,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_2  = 18,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_3  = 19,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_4  = 20,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_5  = 21,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_6  = 22,                   /* Sauvegarde des registres    */
    MIPS_REG_SAV_7  = 23,                   /* Sauvegarde des registres    */
    MIPS_REG_TMP_8  = 24,                   /* Stockage temporaire         */
    MIPS_REG_TMP_9  = 25,                   /* Stockage temporaire         */
    MIPS_REG_KERN_0 = 26,                   /* Réservé pour le noyau       */
    MIPS_REG_KERN_1 = 27,                   /* Réservé pour le noyau       */
    MIPS_REG_GLOBAL = 28,                   /* Pointeur global             */
    MIPS_REG_STACK  = 29,                   /* Pointeur de pile            */
    MIPS_REG_FRAME  = 30,                   /* Pointeur de frame           */
    MIPS_REG_RET    = 31                    /* Adresse de retour           */

};



/******************************************************************************
*                                                                             *
*  Paramètres  : value = valeur correspondant au registre.                    *
*                                                                             *
*  Description : Récupère l'indentifiant interne d'un registre.               *
*                                                                             *
*  Retour      : Registre définit ou NULL.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

mips_register *get_mips_register(bin_t value)
{
    mips_register *result;                   /* Représentation à renvoyer   */

    if (value > 31) return NULL;

    result = (mips_register *)calloc(1, sizeof(mips_register));

    *result = value;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : reg = registre à supprimer.                                  *
*                                                                             *
*  Description : Efface de la mémoire l'indentifiant interne d'un registre.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void free_mips_register(mips_register *reg)
{
    free(reg);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : reg = registre à consulter.                                  *
*                                                                             *
*  Description : Indique si le registre correspond à ra.                      *
*                                                                             *
*  Retour      : true si la correspondance est avérée, false sinon.           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool is_mips_register_return_address(const mips_register *reg)
{
    return (*reg == MIPS_REG_RET);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : reg    = registre à imprimer.                                *
*                syntax = type de représentation demandée.                    *
*                                                                             *
*  Description : Traduit un registre MIPS en texte.                           *
*                                                                             *
*  Retour      : Traduction en chaîne à libérer de la mémoire.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char *mips_register_as_text(const mips_register *reg, AsmSyntax syntax)
{
    char *result;                           /* Chaîne à renvoyer           */

    result = (char *)calloc(6, sizeof(char));

    switch (syntax)
    {
        case ASX_INTEL:
            switch (*reg)
            {
                case MIPS_REG_ZERO:
                    snprintf(result, 6, "zero");
                    break;
                case MIPS_REG_AT:
                    snprintf(result, 6, "at");
                    break;
                case MIPS_REG_V_0 ... MIPS_REG_V_1:
                    snprintf(result, 6, "v%c", '0' + *reg - MIPS_REG_V_0);
                    break;
                case MIPS_REG_ARG_0 ... MIPS_REG_ARG_3:
                    snprintf(result, 6, "a%c", '0' + *reg - MIPS_REG_ARG_0);
                    break;
                case MIPS_REG_TMP_0 ... MIPS_REG_TMP_7:
                    snprintf(result, 6, "t%c", '0' + *reg - MIPS_REG_TMP_0);
                    break;
                case MIPS_REG_SAV_0 ... MIPS_REG_SAV_7:
                    snprintf(result, 6, "s%c", '0' + *reg - MIPS_REG_SAV_0);
                    break;
                case MIPS_REG_TMP_8 ... MIPS_REG_TMP_9:
                    snprintf(result, 6, "t%c", '8' + *reg - MIPS_REG_TMP_8);
                    break;
                case MIPS_REG_KERN_0 ... MIPS_REG_KERN_1:
                    snprintf(result, 6, "k%c", '0' + *reg - MIPS_REG_KERN_0);
                    break;
                case MIPS_REG_GLOBAL:
                    snprintf(result, 6, "gp");
                    break;
                case MIPS_REG_STACK:
                    snprintf(result, 6, "sp");
                    break;
                case MIPS_REG_FRAME:
                    snprintf(result, 6, "fp");
                    break;
                case MIPS_REG_RET:
                    snprintf(result, 6, "ra");
                    break;
            }
            break;

        case ASX_ATT:
            switch (*reg)
            {
                case MIPS_REG_ZERO:
                    snprintf(result, 6, "%%zero");
                    break;
                case MIPS_REG_AT:
                    snprintf(result, 6, "%%at");
                    break;
                case MIPS_REG_V_0 ... MIPS_REG_V_1:
                    snprintf(result, 6, "%%v%c", '0' + *reg - MIPS_REG_V_0);
                    break;
                case MIPS_REG_ARG_0 ... MIPS_REG_ARG_3:
                    snprintf(result, 6, "%%a%c", '0' + *reg - MIPS_REG_ARG_0);
                    break;
                case MIPS_REG_TMP_0 ... MIPS_REG_TMP_7:
                    snprintf(result, 6, "%%t%c", '0' + *reg - MIPS_REG_TMP_0);
                    break;
                case MIPS_REG_SAV_0 ... MIPS_REG_SAV_7:
                    snprintf(result, 6, "%%s%c", '0' + *reg - MIPS_REG_SAV_0);
                    break;
                case MIPS_REG_TMP_8 ... MIPS_REG_TMP_9:
                    snprintf(result, 6, "%%t%c", '8' + *reg - MIPS_REG_TMP_8);
                    break;
                case MIPS_REG_KERN_0 ... MIPS_REG_KERN_1:
                    snprintf(result, 6, "%%k%c", '0' + *reg - MIPS_REG_KERN_0);
                    break;
                case MIPS_REG_GLOBAL:
                    snprintf(result, 6, "%%gp");
                    break;
                case MIPS_REG_STACK:
                    snprintf(result, 6, "%%sp");
                    break;
                case MIPS_REG_FRAME:
                    snprintf(result, 6, "%%fp");
                    break;
                case MIPS_REG_RET:
                    snprintf(result, 6, "%%ra");
                    break;
            }
            break;

        default:
            break;

    }

    return result;

}