/* Chrysalide - Outil d'analyse de fichiers binaires
 * register.c - aides auxiliaires relatives aux registres x86
 *
 * Copyright (C) 2009-2012 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  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 "register.h"


#include <stdio.h>


#include "../operand-int.h"



/* Liste des registres 8 bits */
typedef enum _X868bRegister
{
    X86_REG8_AL = 0,                        /* Registre AL                 */
    X86_REG8_CL = 1,                        /* Registre AL                 */
    X86_REG8_DL = 2,                        /* Registre AL                 */
    X86_REG8_BL = 3,                        /* Registre AL                 */
    X86_REG8_AH = 4,                        /* Registre AH                 */
    X86_REG8_CH = 5,                        /* Registre AH                 */
    X86_REG8_DH = 6,                        /* Registre AH                 */
    X86_REG8_BH = 7,                        /* Registre AH                 */

    X86_REG8_NONE                           /* Aucun registre              */

} X868bRegister;

/* Liste des registres 16 bits */
typedef enum _X8616bRegister
{
    X86_REG16_AX = 0,                       /* Registre AX                 */
    X86_REG16_CX = 1,                       /* Registre AX                 */
    X86_REG16_DX = 2,                       /* Registre AX                 */
    X86_REG16_BX = 3,                       /* Registre AX                 */
    X86_REG16_SP = 4,                       /* Registre SP                 */
    X86_REG16_BP = 5,                       /* Registre BP                 */
    X86_REG16_SI = 6,                       /* Registre SI                 */
    X86_REG16_DI = 7,                       /* Registre DI                 */

    X86_REG16_NONE                          /* Aucun registre              */

} X8616bRegister;

/* Liste des registres 32 bits */
typedef enum _X8632bRegister
{
    X86_REG32_EAX = 0,                      /* Registre EAX                */
    X86_REG32_ECX = 1,                      /* Registre EAX                */
    X86_REG32_EDX = 2,                      /* Registre EAX                */
    X86_REG32_EBX = 3,                      /* Registre EAX                */
    X86_REG32_ESP = 4,                      /* Registre ESP                */
    X86_REG32_EBP = 5,                      /* Registre EBP                */
    X86_REG32_ESI = 6,                      /* Registre ESI                */
    X86_REG32_EDI = 7,                      /* Registre EDI                */

    X86_REG32_NONE                          /* Aucun registre              */

} X8632bRegister;


/* Représentation d'un registre x86 (instance) */
struct _GX86Register
{
    GArchOperand parent;                    /* Instance parente            */

    MemoryDataSize size;                    /* Taille de ce registre       */

    union
    {
        X868bRegister reg8;                 /* Registre 8 bits             */
        X8616bRegister reg16;               /* Registre 16 bits            */
        X8632bRegister reg32;               /* Registre 32 bits            */

    } reg;

};


/* Représentation d'un registre x86 (classe) */
struct _GX86RegisterClass
{
    GArchOperandClass parent;               /* Classe parente              */

};


#define MAX_REGNAME_LEN 5


/* Initialise la classe des registres x86. */
static void g_x86_register_class_init(GX86RegisterClass *);

/* Initialise une instance de registre x86. */
static void g_x86_register_init(GX86Register *);



/* Indique le type défini pour une représentation d'un registre x86. */
G_DEFINE_TYPE(GX86Register, g_x86_register, G_TYPE_ARCH_OPERAND);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des registres x86.                      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_x86_register_class_init(GX86RegisterClass *klass)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : reg = instance à initialiser.                                *
*                                                                             *
*  Description : Initialise une instance de registre x86.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_x86_register_init(GX86Register *reg)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : size  = indique la taille du registre.                       *
*                value = valeur correspondant au registre.                    *
*                                                                             *
*  Description : Crée une réprésentation de registre x86.                     *
*                                                                             *
*  Retour      : Adresse de la structure mise en place.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GX86Register *g_x86_register_new(MemoryDataSize size, bin_t value)
{
    GX86Register *result;                   /* Structure à retourner       */

    result = g_object_new(G_TYPE_X86_REGISTER, NULL);

    result->size = size;

    switch (size)
    {
        case MDS_8_BITS:
            switch (value)
            {
                case 0 ... 7:
                    result->reg.reg8 = (X868bRegister)value;
                    break;
                default:
                    goto gxrn_error;
                    break;
            }
            break;

        case MDS_16_BITS:
            switch (value)
            {
                case 0 ... 7:
                    result->reg.reg16 = (X8616bRegister)value;
                    break;
                default:
                    goto gxrn_error;
                    break;
            }
            break;

        case MDS_32_BITS:
            switch (value)
            {
                case 0 ... 7:
                    result->reg.reg32 = (X8632bRegister)value;
                    break;
                default:
                    goto gxrn_error;
                    break;
            }
            break;

        default:
            goto gxrn_error;
            break;

    }

    return result;

 gxrn_error:

    g_object_unref(G_OBJECT(result));

    return NULL;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : a = premier opérande à consulter.                            *
*                b = second opérande à consulter.                             *
*                                                                             *
*  Description : Compare un registre avec un autre.                           *
*                                                                             *
*  Retour      : Bilan de la comparaison.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_x86_register_compare(const GX86Register *a, const GX86Register *b)
{
    bool result;                            /* Bilan à retourner           */

    if (a->size != b->size)
        return false;

    switch (a->size)
    {
        case MDS_8_BITS:
            result = (a->reg.reg8 == b->reg.reg8);
            break;

        case MDS_16_BITS:
            result = (a->reg.reg16 == b->reg.reg16);
            break;

        case MDS_32_BITS:
            result = (a->reg.reg32 == b->reg.reg32);
            break;

        default:
            result = false;
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : reg    = registre à transcrire.                              *
*                line   = ligne tampon où imprimer l'opérande donné.          *
*                syntax = type de représentation demandée.                    *
*                                                                             *
*  Description : Traduit un registre en version humainement lisible.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_x86_pool_operand_print(const GX86Register *reg, GBufferLine *line, AsmSyntax syntax)
{
    char key[MAX_REGNAME_LEN];              /* Mot clef principal          */
    size_t klen;                            /* Taille de ce mot clef       */

    switch (syntax)
    {
        case ASX_INTEL:
            switch (reg->size)
            {
                case MDS_8_BITS:
                    klen = 2;
                    switch (reg->reg.reg8)
                    {
                        case X86_REG8_AL:
                            snprintf(key, MAX_REGNAME_LEN, "al");
                            break;
                        case X86_REG8_CL:
                            snprintf(key, MAX_REGNAME_LEN, "cl");
                            break;
                        case X86_REG8_DL:
                            snprintf(key, MAX_REGNAME_LEN, "dl");
                            break;
                        case X86_REG8_BL:
                            snprintf(key, MAX_REGNAME_LEN, "bl");
                            break;
                        case X86_REG8_AH:
                            snprintf(key, MAX_REGNAME_LEN, "ah");
                            break;
                        case X86_REG8_CH:
                            snprintf(key, MAX_REGNAME_LEN, "ch");
                            break;
                        case X86_REG8_DH:
                            snprintf(key, MAX_REGNAME_LEN, "dh");
                            break;
                        case X86_REG8_BH:
                            snprintf(key, MAX_REGNAME_LEN, "bh");
                            break;
                        case X86_REG8_NONE:
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                case MDS_16_BITS:
                    klen = 2;
                    switch (reg->reg.reg16)
                    {
                        case X86_REG16_AX:
                            snprintf(key, MAX_REGNAME_LEN, "ax");
                            break;
                        case X86_REG16_CX:
                            snprintf(key, MAX_REGNAME_LEN, "cx");
                            break;
                        case X86_REG16_DX:
                            snprintf(key, MAX_REGNAME_LEN, "dx");
                            break;
                        case X86_REG16_BX:
                            snprintf(key, MAX_REGNAME_LEN, "bx");
                            break;
                        case X86_REG16_SP:
                            snprintf(key, MAX_REGNAME_LEN, "sp");
                            break;
                        case X86_REG16_BP:
                            snprintf(key, MAX_REGNAME_LEN, "bp");
                            break;
                        case X86_REG16_SI:
                            snprintf(key, MAX_REGNAME_LEN, "si");
                            break;
                        case X86_REG16_DI:
                            snprintf(key, MAX_REGNAME_LEN, "di");
                            break;
                        case X86_REG16_NONE:
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                case MDS_32_BITS:
                    klen = 3;
                    switch (reg->reg.reg32)
                    {
                        case X86_REG32_EAX:
                            snprintf(key, MAX_REGNAME_LEN, "eax");
                            break;
                        case X86_REG32_ECX:
                            snprintf(key, MAX_REGNAME_LEN, "ecx");
                            break;
                        case X86_REG32_EDX:
                            snprintf(key, MAX_REGNAME_LEN, "edx");
                            break;
                        case X86_REG32_EBX:
                            snprintf(key, MAX_REGNAME_LEN, "ebx");
                            break;
                        case X86_REG32_ESP:
                            snprintf(key, MAX_REGNAME_LEN, "esp");
                            break;
                        case X86_REG32_EBP:
                            snprintf(key, MAX_REGNAME_LEN, "ebp");
                            break;
                        case X86_REG32_ESI:
                            snprintf(key, MAX_REGNAME_LEN, "esi");
                            break;
                        case X86_REG32_EDI:
                            snprintf(key, MAX_REGNAME_LEN, "edi");
                            break;
                        case X86_REG32_NONE:
                            printf("null reg\n");
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                default:
                    klen = 0;
                    break;

            }
            break;

        case ASX_ATT:
            switch (reg->size)
            {
                case MDS_8_BITS:
                    klen = 3;
                    switch (reg->reg.reg8)
                    {
                        case X86_REG8_AL:
                            snprintf(key, MAX_REGNAME_LEN, "%%al");
                            break;
                        case X86_REG8_CL:
                            snprintf(key, MAX_REGNAME_LEN, "%%cl");
                            break;
                        case X86_REG8_DL:
                            snprintf(key, MAX_REGNAME_LEN, "%%dl");
                            break;
                        case X86_REG8_BL:
                            snprintf(key, MAX_REGNAME_LEN, "%%bl");
                            break;
                        case X86_REG8_AH:
                            snprintf(key, MAX_REGNAME_LEN, "%%ah");
                            break;
                        case X86_REG8_CH:
                            snprintf(key, MAX_REGNAME_LEN, "%%ch");
                            break;
                        case X86_REG8_DH:
                            snprintf(key, MAX_REGNAME_LEN, "%%dh");
                            break;
                        case X86_REG8_BH:
                            snprintf(key, MAX_REGNAME_LEN, "%%bh");
                            break;
                        case X86_REG8_NONE:
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                case MDS_16_BITS:
                    klen = 3;
                    switch (reg->reg.reg16)
                    {
                        case X86_REG16_AX:
                            snprintf(key, MAX_REGNAME_LEN, "%%ax");
                            break;
                        case X86_REG16_CX:
                            snprintf(key, MAX_REGNAME_LEN, "%%cx");
                            break;
                        case X86_REG16_DX:
                            snprintf(key, MAX_REGNAME_LEN, "%%dx");
                            break;
                        case X86_REG16_BX:
                            snprintf(key, MAX_REGNAME_LEN, "%%bx");
                            break;
                        case X86_REG16_SP:
                            snprintf(key, MAX_REGNAME_LEN, "%%sp");
                            break;
                        case X86_REG16_BP:
                            snprintf(key, MAX_REGNAME_LEN, "%%bp");
                            break;
                        case X86_REG16_SI:
                            snprintf(key, MAX_REGNAME_LEN, "%%si");
                            break;
                        case X86_REG16_DI:
                            snprintf(key, MAX_REGNAME_LEN, "%%di");
                            break;
                        case X86_REG16_NONE:
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                case MDS_32_BITS:
                    klen = 4;
                    switch (reg->reg.reg32)
                    {
                        case X86_REG32_EAX:
                            snprintf(key, MAX_REGNAME_LEN, "%%eax");
                            break;
                        case X86_REG32_ECX:
                            snprintf(key, MAX_REGNAME_LEN, "%%ecx");
                            break;
                        case X86_REG32_EDX:
                            snprintf(key, MAX_REGNAME_LEN, "%%edx");
                            break;
                        case X86_REG32_EBX:
                            snprintf(key, MAX_REGNAME_LEN, "%%ebx");
                            break;
                        case X86_REG32_ESP:
                            snprintf(key, MAX_REGNAME_LEN, "%%esp");
                            break;
                        case X86_REG32_EBP:
                            snprintf(key, MAX_REGNAME_LEN, "%%ebp");
                            break;
                        case X86_REG32_ESI:
                            snprintf(key, MAX_REGNAME_LEN, "%%esi");
                            break;
                        case X86_REG32_EDI:
                            snprintf(key, MAX_REGNAME_LEN, "%%edi");
                            break;
                        case X86_REG32_NONE:
                            /* Ne devrait jamais arriver */
                            break;
                    }
                    break;

                default:
                    klen = 0;
                    break;

            }
            break;

        default:
            klen = 0;
            break;

    }

    g_buffer_line_insert_text(line, BLC_ASSEMBLY, key, klen, RTT_REGISTER);

}


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

bool g_x86_register_is_base_pointer(const GX86Register *reg)
{
    bool result;                            /* Bilan à remonter            */

    switch (reg->size)
    {
        case MDS_8_BITS_UNSIGNED:
        case MDS_8_BITS_SIGNED:
            result = (reg->reg.reg8 == X86_REG8_CH);
            break;
        case MDS_16_BITS_UNSIGNED:
        case MDS_16_BITS_SIGNED:
            result = (reg->reg.reg16 == X86_REG16_BP);
            break;
        case MDS_32_BITS_UNSIGNED:
        case MDS_32_BITS_SIGNED:
            result = (reg->reg.reg32 == X86_REG32_EBP);
            break;
            /*
        case MDS_64_BITS_UNSIGNED:
        case MDS_64_BITS_SIGNED:
            result = (reg->reg.reg8 == X86_REG8_CH);
            break;
            */
        default:
            result = false;

    }

    return result;

}


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

bool g_x86_register_is_stack_pointer(const GX86Register *reg)
{
    bool result;                            /* Bilan à remonter            */

    switch (reg->size)
    {
        case MDS_8_BITS_UNSIGNED:
        case MDS_8_BITS_SIGNED:
            result = (reg->reg.reg8 == X86_REG8_AH);
            break;
        case MDS_16_BITS_UNSIGNED:
        case MDS_16_BITS_SIGNED:
            result = (reg->reg.reg16 == X86_REG16_SP);
            break;
        case MDS_32_BITS_UNSIGNED:
        case MDS_32_BITS_SIGNED:
            result = (reg->reg.reg32 == X86_REG32_ESP);
            break;
            /*
        case MDS_64_BITS_UNSIGNED:
        case MDS_64_BITS_SIGNED:
            result = (reg->reg.reg8 == X86_REG8_CH);
            break;
            */
        default:
            result = false;

    }

    return result;

}