/* Chrysalide - Outil d'analyse de fichiers binaires
 * pseudo.c - implémentation des pseudo-fonctions de spécification
 *
 * Copyright (C) 2014-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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.
 *
 *  Chrysalide 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 "pseudo.h"


#include <stddef.h>


#include "../../../common/bconst.h"



/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                carry = retenue enventuelle à constituer. [OUT]              *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'LSL_C'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_lsl_c(uint32_t x, unsigned int n, unsigned int shift, bool *carry, uint32_t *value)
{
    if (n > 32) return false;
    if (shift == 0) return false;

    if (carry != NULL)
        *carry = x & (1 << (n - 1));

    *value = x << shift;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'LSL'.                                   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_lsl(uint32_t x, unsigned int n, unsigned int shift, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */

    if (shift == 0)
        result = true;

    else
        result = armv7_lsl_c(x, n, shift, NULL, value);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                carry = retenue enventuelle à constituer. [OUT]              *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'LSR_C'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_lsr_c(uint32_t x, unsigned int n, unsigned int shift, bool *carry, uint32_t *value)
{
    if (n > 32) return false;
    if (shift == 0) return false;

    if (carry != NULL)
        *carry = x & (1 << (shift - 1));

    *value = x >> shift;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'LSR'.                                   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_lsr(uint32_t x, unsigned int n, unsigned int shift, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */

    if (shift == 0)
        result = x;

    else
        result = armv7_lsr_c(x, n, shift, NULL, value);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                carry = retenue enventuelle à constituer. [OUT]              *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ASR_C'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_asr_c(uint32_t x, unsigned int n, unsigned int shift, bool *carry, uint32_t *value)
{
    if (n > 32) return false;
    if (shift == 0) return false;

    if (carry != NULL)
        *carry = x & (1 << (shift - 1));

    *value = ((int32_t)x) >> shift;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ASR'.                                   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_asr(uint32_t x, unsigned int n, unsigned int shift, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */

    if (shift == 0)
        result = true;

    else
        result = armv7_asr_c(x, n, shift, NULL, value);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                carry = retenue enventuelle à constituer. [OUT]              *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ROR_C'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_ror_c(uint32_t x, unsigned int n, unsigned int shift, bool *carry, uint32_t *value)
{
    if (n > 32) return false;
    if (shift == 0) return false;

    *value = (x >> shift) | (x << (32 - shift));

    if (carry != NULL)
        *carry = *value & (1 << (n - 1));

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                shift = nombre de décallages visés.                          *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ROR'.                                   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_ror(uint32_t x, unsigned int n, unsigned int shift, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */

    if (shift == 0)
        result = true;

    else
        result = armv7_ror_c(x, n, shift, NULL, value);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                carry = retenue enventuelle à utiliser puis constituer. [OUT]*
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'RRX_C'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_rrx_c(uint32_t x, unsigned int n, bool *carry, uint32_t *value)
{
    bool new_c;                             /* Nouvelle retenue à retenir  */

    new_c = x & 0x1;

    *value = (*carry ? 1 : 0) << (n - 1) | x >> 1;

    *carry = new_c;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x     = valeur sur 32 bits maximum à traiter.                *
*                n     = nombre de bits à prendre en compte.                  *
*                carry = retenue enventuelle à utiliser.                      *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'RRX'.                                   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_rrx(uint32_t x, unsigned int n, bool carry, uint32_t *value)
{
    return armv7_rrx_c(x, n, &carry, value);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : imm12 = valeur sur 32 bits maximum à traiter.                *
*                carry = retenue enventuelle à utiliser / constituer. [OUT]   *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ARMExpandImm_C'.                        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_arm_expand_imm_c(uint32_t imm12, bool *carry, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */
    uint32_t unrotated;                     /* Transformation à décaller   */

    /**
     * Selon les spécifications, x contient toujours 12 bits utiles seulement.
     */

    unrotated = armv7_zero_extend(imm12 & 0xff, 8, 32);

    result = armv7_shift(unrotated, 32, SRType_ROR, 2 * ((imm12 >> 8) & 0xf), carry, value);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : imm12 = valeur sur 32 bits maximum à traiter.                *
*                carry = retenue enventuelle à utiliser / constituer. [OUT]   *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ARMExpandImm'.                          *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_arm_expand_imm(uint32_t imm12, uint32_t *value)
{
    return armv7_arm_expand_imm_c(imm12, (bool []) { false /* FIXME : APSR.C */ }, value);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : imm12 = valeur sur 32 bits maximum à traiter.                *
*                carry = retenue enventuelle à utiliser / constituer. [OUT]   *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ThumbExpandImm_C'.                      *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_thumb_expand_imm_c(uint32_t imm12, bool *carry, uint32_t *value)
{
    bool result;                            /* Conclusion à faire remonter */
    uint8_t byte;                           /* Octet à reproduire          */
    uint32_t unrotated;                     /* Transformation à décaller   */

    result = true;

    if (((imm12 >> 10) & b11) == b00)
    {
        byte = imm12 & 0xff;

        switch ((imm12 >> 8) & b11)
        {
            case b00:
                *value = armv7_zero_extend(byte, 8, 32);
                break;

            case b01:
                if (byte == 0)
                    result = false;
                else
                    *value = byte << 16 | byte;
                break;

            case b10:
                if (byte == 0)
                    result = false;
                else
                    *value = byte << 24 | byte << 8;
                break;

            case b11:
                if (byte == 0)
                    result = false;
                else
                    *value = byte << 24 | byte << 16 | byte << 8 | byte;
                break;

        }

    }
    else
    {
        unrotated = 1 << 7 | (imm12 & 0x3f);
        result = armv7_ror_c(unrotated, 32, (imm12 >> 7) & 0x1f, carry, value);
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : imm12 = valeur sur 32 bits maximum à traiter.                *
*                carry = retenue enventuelle à utiliser / constituer. [OUT]   *
*                value = nouvelle valeur calculée. [OUT]                      *
*                                                                             *
*  Description : Traduit la fonction 'ThumbExpandImm'.                        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_thumb_expand_imm(uint32_t imm12, uint32_t *value)
{
    return armv7_thumb_expand_imm_c(imm12, (bool []) { false /* FIXME : APSR.C */ }, value);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : type2 = type de décallage encodé sur 2 bits.                 *
*                imm5  = valeur de décallage entière sur 5 bits.              *
*                type  = type de décallage à constituer. [OUT]                *
*                value = valeur pleine et entière à utiliser. [OUT]           *
*                                                                             *
*  Description : Traduit la fonction 'DecodeImmShift'.                        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_decode_imm_shift(uint8_t type2, uint8_t imm5, SRType *type, uint32_t *value)
{
    bool result;                            /* Bilan à retourner           */

    result = true;

    switch (type2)
    {
        case b00:
            *type = SRType_LSL;
            *value = imm5;
            break;

        case b01:
            *type = SRType_LSR;
            *value = (imm5 == 0 ? 32 : imm5);
            break;

        case b10:
            *type = SRType_ASR;
            *value = (imm5 == 0 ? 32 : imm5);
            break;

        case b11:
            if (imm5 == 0)
            {
                *type = SRType_RRX;
                *value = 1;
            }
            else
            {
                *type = SRType_ROR;
                *value = imm5;
            }
            break;

        default:
            result = false;
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : type2 = type de décallage encodé sur 2 bits.                 *
*                type  = type de décallage à constituer. [OUT]                *
*                                                                             *
*  Description : Traduit la fonction 'DecodeRegShift'.                        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_decode_reg_shift(uint8_t type2, SRType *type)
{
    bool result;                            /* Bilan à retourner           */

    result = true;

    switch (type2)
    {
        case b00:
            *type = SRType_LSL;
            break;

        case b01:
            *type = SRType_LSR;
            break;

        case b10:
            *type = SRType_ASR;
            break;

        case b11:
            *type = SRType_ROR;
            break;

        default:
            result = false;
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x      = valeur sur 32 bits maximum à traiter.               *
*                n      = nombre de bits à prendre en compte.                 *
*                type   = type d'opération à mener.                           *
*                amount = quantité liée à l'opération à mener.                *
*                carry  = retenue enventuelle à utiliser / constituer. [OUT]  *
*                value  = nouvelle valeur calculée. [OUT]                     *
*                                                                             *
*  Description : Traduit la fonction 'Shift_C'.                               *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_shift_c(uint32_t x, unsigned int n, SRType type, unsigned int amount, bool *carry, uint32_t *value)
{
    bool result;                            /* Bilan final à retourner     */

    if (type == SRType_RRX && amount != 1) return false;

    if (amount == 0)
    {
        *value = x;
        return true;
    }

    result = true;     /* Pour GCC... */

    switch (type)
    {
        case SRType_LSL:
            result = armv7_lsl_c(x, n, amount, carry, value);
            break;

        case SRType_LSR:
            result = armv7_lsr_c(x, n, amount, carry, value);
            break;

        case SRType_ASR:
            result = armv7_asr_c(x, n, amount, carry, value);
            break;

        case SRType_ROR:
            result = armv7_ror_c(x, n, amount, carry, value);
            break;

        case SRType_RRX:
            result = armv7_rrx_c(x, n, carry, value);
            break;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x      = valeur sur 32 bits maximum à traiter.               *
*                n      = nombre de bits à prendre en compte.                 *
*                type   = type d'opération à mener.                           *
*                amount = quantité liée à l'opération à mener.                *
*                carry  = retenue enventuelle à utiliser.                     *
*                value  = nouvelle valeur calculée. [OUT]                     *
*                                                                             *
*  Description : Traduit la fonction 'Shift'.                                 *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool armv7_shift(uint32_t x, unsigned int n, SRType type, unsigned int amount, bool carry, uint32_t *value)
{
    return armv7_shift_c(x, n, type, amount, &carry, value);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : x = valeur sur 32 bits maximum à traiter.                    *
*                n = nombre de bits à prendre en compte.                      *
*                i = taille finale à obtenir.                                 *
*                                                                             *
*  Description : Traduit la fonction 'ZeroExtend'.                            *
*                                                                             *
*  Retour      : Nouvelle valeur calculée.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

uint32_t armv7_zero_extend(uint32_t x, unsigned int n, unsigned int i)
{
    return x;

}