/* Chrysalide - Outil d'analyse de fichiers binaires
 * operand.c - gestion des operandes de l'architecture x86
 *
 * Copyright (C) 2008-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 "operand.h"



/******************************************************************************
*                                                                             *
*  Paramètres  : instr = instruction dont la définition est à compléter. [OUT]*
*                data  = flux de données à analyser.                          *
*                pos   = position courante dans ce flux. [OUT]                *
*                len   = taille totale des données à analyser.                *
*                count = quantité d'opérandes à lire.                         *
*                ...   = éventuelle(s) information(s) complémentaire(s).      *
*                                                                             *
*  Description : Procède à la lecture de trois opérandes donnés.              *
*                                                                             *
*  Retour      : Bilan de l'opération : true en cas de succès, false sinon.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool _x86_read_operands(GArchInstruction *instr, const bin_t *data, off_t *pos, off_t len, unsigned int count, ...)
{
    bool result;                            /* Bilan à retourner           */
    va_list ap;                             /* Liste des compléments       */
    X86OperandType types[MAX_OPERANDS];     /* Type des opérandes          */
    unsigned int i;                         /* Boucle de parcours          */
    bool op1_first;                         /* Position de l'opérande  #1  */
    bool op2_first;                         /* Position de l'opérande  #2  */
    MemoryDataSize oprsize;                 /* Taille des opérandes        */
    off_t op_pos[MAX_OPERANDS];             /* Position après lecture      */
    vmpa_t offset;                          /* Adresse courante            */
    bin_t base;                             /* Indice du premier registre  */
    GArchOperand *op;                       /* Opérande unique décodé      */

    if (count > MAX_OPERANDS) return false;

    result = true;

    va_start(ap, count);

    /* Types à charger */

    for (i = 0; i < count; i++)
        types[i] = va_arg(ap, MemoryDataSize);

    for ( ; i < MAX_OPERANDS; i++)
        types[i] = X86_OTP_NONE;

    /* Initialisations */

    if (types[0] & X86_OTP_RM_TYPE)
    {
        op1_first = true;
        op2_first = false;
    }
    else if (types[1] & X86_OTP_RM_TYPE)
    {
        op1_first = false;
        op2_first = true;
    }
    else
    {
        op1_first = true;
        op2_first = false;
    }

    oprsize = MDS_UNDEFINED;

    /* Lecture des opérandes */

    for (i = 0; i < count && result; i++)
    {
        /* Tête de lecture */
        switch (i)
        {
            case 0:
                op_pos[0] = *pos;
                break;

            case 1:
                if ((types[0] & X86_OTP_REG_TYPE || types[0] & X86_OTP_RM_TYPE) && (types[1] & X86_OTP_IMM_TYPE))
                    op_pos[1] = op_pos[0];
                else op_pos[1] = *pos;
                *pos = op_pos[0];
                break;

            case 2 ... MAX_OPERANDS:
                *pos = MAX(*pos, op_pos[i - 1]);
                op_pos[i] = *pos;

        }

        /* Lecture */
        switch (types[i])
        {
            case X86_OTP_IMM8:
                op = g_imm_operand_new_from_data(MDS_8_BITS, data, &op_pos[i], len, SRE_LITTLE /* FIXME */);
                break;

            case X86_OTP_IMM16:
                op = g_imm_operand_new_from_data(MDS_16_BITS, data, &op_pos[i], len, SRE_LITTLE /* FIXME */);
                break;

            case X86_OTP_IMM1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_imm_operand_new_from_data(oprsize == MDS_32_BITS ? MDS_32_BITS : MDS_16_BITS, data, &op_pos[i], len, SRE_LITTLE /* FIXME */);
                break;

            case X86_OTP_MOFFS8:
                op = g_x86_moffs_operand_new(data, &op_pos[i], len, MDS_8_BITS);
                break;

            case X86_OTP_MOFFS1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_moffs_operand_new(data, &op_pos[i], len, oprsize);
                break;

            case X86_OTP_REL8:
                offset = va_arg(ap, vmpa_t);
                op = g_x86_relative_operand_new(data, &op_pos[i], len, MDS_8_BITS, offset + 1);
                break;

            case X86_OTP_REL1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                offset = va_arg(ap, vmpa_t);
                op = g_x86_relative_operand_new(data, &op_pos[i], len, oprsize, offset + 1);
                break;

            case X86_OTP_R8:
                op = g_x86_register_operand_new_from_mod_rm(data, &op_pos[i], len, MDS_8_BITS, i == 0 ? op1_first : op2_first);
                break;

            case X86_OTP_R1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_register_operand_new_from_mod_rm(data, &op_pos[i], len, oprsize, i == 0 ? op1_first : op2_first);
                break;

            case X86_OTP_OP_R8:
                base = (bin_t)va_arg(ap, int);
                op = g_x86_register_operand_new_from_opcode(data, &op_pos[i], len, MDS_8_BITS, base);
                break;

            case X86_OTP_OP_R1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                base = (bin_t)va_arg(ap, int);
                op = g_x86_register_operand_new_from_opcode(data, &op_pos[i], len, oprsize, base);
                break;

            case X86_OTP_RM8:
                op = g_x86_mod_rm_operand_new(data, &op_pos[i], len, MDS_8_BITS);
                break;

            case X86_OTP_RM16:
                op = g_x86_mod_rm_operand_new(data, &op_pos[i], len, MDS_16_BITS);
                break;

            case X86_OTP_RM1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_mod_rm_operand_new(data, &op_pos[i], len, oprsize);
                break;

            case X86_OTP_DST_8:
                op = g_x86_data_operand_new(MDS_8_BITS, true);
                break;

            case X86_OTP_DST_1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_data_operand_new(oprsize == MDS_32_BITS ? MDS_32_BITS : MDS_16_BITS, true);
                break;

            case X86_OTP_SRC_8:
                op = g_x86_data_operand_new(MDS_8_BITS, false);
                break;

            case X86_OTP_SRC_1632:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_data_operand_new(oprsize == MDS_32_BITS ? MDS_32_BITS : MDS_16_BITS, false);
                break;

            case X86_OTP_ONE:
                op = g_imm_operand_new_from_value(MDS_8_BITS, 1);
                break;

            case X86_OTP_CL:
                op = g_x86_register_operand_new_from_index(0x01, MDS_8_BITS);
                break;

            case X86_OTP_AL:
                op = g_x86_register_operand_new_from_index(0x00, MDS_8_BITS);
                break;

            case X86_OTP_E_AX:
                if (oprsize == MDS_UNDEFINED) oprsize = va_arg(ap, MemoryDataSize);
                op = g_x86_register_operand_new_from_index(0x00, oprsize);
                break;

        }

        if (op == NULL) result = false;
        else g_arch_instruction_attach_extra_operand(instr, op);

    }

    *pos = MAX(*pos, op_pos[i - 1]);

    return result;

}