/*BEGIN_LEGAL 
Intel Open Source License 

Copyright (c) 2002-2014 Intel Corporation. All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.  Redistributions
in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.  Neither the name of
the Intel Corporation nor the names of its contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
END_LEGAL */
/// @file xed-ex-agen.c  

// decoder example with agen callbacks.

#include "xed-interface.h"
#include "xed-examples-util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //strcmp
#include <assert.h> 

int main(int argc, char** argv);


/* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */
/* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */
/* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */
/* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */

xed_uint64_t register_callback(xed_reg_enum_t reg, void* context, xed_bool_t* error) {
    (void) context;   // pacify compiler
    (void) error;

    /* these are all the registers you are going to have to provide values
    for in this callback. Note that AL is required for the XLAT
    instruction. That is the only byte reg needed. Also note, that for real
    mode, raw segement selectors are returned by this function.  */

    /* in reality, you'd have to return valid valies for each case */

   /* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */
    
    switch(reg) {
      case XED_REG_RAX:
      case XED_REG_EAX:
      case XED_REG_AX:
        return 0xAABBCC00;
        break;
      case XED_REG_AL: // FOR XLAT
        break;

      case XED_REG_RCX:
      case XED_REG_ECX:
      case XED_REG_CX:
        return 0xAABBCCDD;
        break;
      case XED_REG_RDX:
      case XED_REG_EDX:
      case XED_REG_DX:
        break;
      case XED_REG_RBX:
      case XED_REG_EBX:
      case XED_REG_BX:
        return 0x11223344;
        break;
      case XED_REG_RSP:
      case XED_REG_ESP:
      case XED_REG_SP:
        break;
      case XED_REG_RBP:
      case XED_REG_EBP:
      case XED_REG_BP:
        break;
      case XED_REG_RSI:
      case XED_REG_ESI:
      case XED_REG_SI:
        return 0x1122334455;
        break;
      case XED_REG_RDI:
      case XED_REG_EDI:
      case XED_REG_DI:
        return 0x6655443322;
        break;
      case XED_REG_R8:
      case XED_REG_R8D:
      case XED_REG_R8W:
        break;
      case XED_REG_R9:
      case XED_REG_R9D:
      case XED_REG_R9W:
        break;
      case XED_REG_R10:
      case XED_REG_R10D:
      case XED_REG_R10W:
        break;
      case XED_REG_R11:
      case XED_REG_R11D:
      case XED_REG_R11W:
        break;
      case XED_REG_R12:
      case XED_REG_R12D:
      case XED_REG_R12W:
        break;
      case XED_REG_R13:
      case XED_REG_R13D:
      case XED_REG_R13W:
        break;
      case XED_REG_R14:
      case XED_REG_R14D:
      case XED_REG_R14W:
        break;
      case XED_REG_R15:
      case XED_REG_R15D:
      case XED_REG_R15W:
        break;
      case XED_REG_RIP:
      case XED_REG_EIP:
      case XED_REG_IP:
        break;
      case XED_REG_CS:
      case XED_REG_DS:
      case XED_REG_ES:
      case XED_REG_SS:
      case XED_REG_FS:
      case XED_REG_GS:
        break;
      default:
        assert(0);
    }
    return 0;
}

xed_uint64_t segment_callback(xed_reg_enum_t reg, void* context, xed_bool_t* error) {
    /* for protected mode, this function returns the valid segment base values */

    /* in reality, you'd have to return valid valies for each case */
    /* THIS IS JUST AN EXAMPLE FOR TESTING AND DOES NOT RETURN REAL DATA */

    (void) context;   // pacify compiler
    (void) error;
    
    switch(reg) {
      case XED_REG_CS:
      case XED_REG_DS:
      case XED_REG_ES:
      case XED_REG_SS:
        return 0;
      case XED_REG_FS:
        return 0;
        break;
      case XED_REG_GS:
        return 0;
        break;
      default:
        assert(0);
        break;
    }
    return 0;
}




int 
main(int argc, char** argv)
{
    xed_error_enum_t xed_error;

    xed_bool_t long_mode = 0;
    xed_bool_t real_mode = 0;
    xed_bool_t protected_16 = 0;
    xed_state_t dstate;
    unsigned int first_argv;
    unsigned int bytes = 0;
    unsigned char itext[XED_MAX_INSTRUCTION_BYTES];
    int i;
    unsigned int u;
    xed_decoded_inst_t xedd;
#define BUFLEN  1000
    char buffer[BUFLEN];
    xed_bool_t ok;
    unsigned int isyntax;
    xed_syntax_enum_t syntax;
    unsigned int memop_index = 0;    
    unsigned int memops = 0;
    xed_uint64_t out_addr = 0;

    xed_tables_init();
    xed_agen_register_callback( register_callback, segment_callback);

    xed_state_zero(&dstate);
    xed_set_verbosity( 99 );

    if (argc > 2 && strcmp(argv[1], "-64") == 0) 
        long_mode = 1;
    if (argc > 2 && strcmp(argv[1], "-r") == 0) 
        real_mode = 1;
    if (argc > 2 && strcmp(argv[1], "-16") == 0) 
        protected_16 = 1;

    if (long_mode) {
        first_argv = 2;
        dstate.mmode=XED_MACHINE_MODE_LONG_64;
    }
    else if (protected_16) {
        first_argv = 2;
        xed_state_init(&dstate,
                       XED_MACHINE_MODE_LEGACY_16,
                       XED_ADDRESS_WIDTH_16b, 
                       XED_ADDRESS_WIDTH_16b);
    }
    else if (real_mode) {
        first_argv = 2;
        /* we say that real mode uses 16b addressing even though the
           addresses returned are 20b long. */
        xed_state_init(&dstate,
                       XED_MACHINE_MODE_REAL_16,
                       XED_ADDRESS_WIDTH_16b,
                       XED_ADDRESS_WIDTH_16b);
    }
    else {
        first_argv=1;
        xed_state_init(&dstate,
                       XED_MACHINE_MODE_LEGACY_32, 
                       XED_ADDRESS_WIDTH_32b, 
                       XED_ADDRESS_WIDTH_32b);
    }

    xed_decoded_inst_zero_set_mode(&xedd, &dstate);
    for( i=first_argv ;i < argc; i++)    {
        xed_uint8_t x = (xed_uint8_t)(xed_atoi_hex(argv[i]));
        assert(bytes < XED_MAX_INSTRUCTION_BYTES);
        itext[bytes++] = x;
    }
    if (bytes == 0)    {
        fprintf(stderr, "Must supply some hex bytes\n");
        exit(1);
    }

    printf("PARSING BYTES: ");
    for( u=0;u<bytes; u++) 
        printf("%02x ", XED_STATIC_CAST(unsigned int,itext[u]));
    printf("\n");

    xed_error = xed_decode(&xedd, 
                           XED_REINTERPRET_CAST(const xed_uint8_t*,itext),
                           bytes);
    switch(xed_error)
    {
      case XED_ERROR_NONE:
        break;
      case XED_ERROR_BUFFER_TOO_SHORT:
        fprintf(stderr,"Not enough bytes provided\n");
        exit(1);
      case XED_ERROR_GENERAL_ERROR:
        fprintf(stderr,"Could not decode given input.\n");
        exit(1);
      default:
        fprintf(stderr,"Unhandled error code %s\n", xed_error_enum_t2str(xed_error));
        exit(1);
    }
        
    xed_decoded_inst_dump(&xedd,buffer, BUFLEN);
    printf("%s\n",buffer);

    for(isyntax=  XED_SYNTAX_XED; isyntax < XED_SYNTAX_LAST; isyntax++)    {
        syntax = XED_STATIC_CAST(xed_syntax_enum_t, isyntax);
        ok = xed_format(syntax, &xedd, buffer, BUFLEN, 0);
        if (ok)
            printf("%s syntax: %s\n", xed_syntax_enum_t2str(syntax), buffer);
        else
            printf("Error disassembling %s syntax\n", xed_syntax_enum_t2str(syntax));
    }


    memops = xed_decoded_inst_number_of_memory_operands(&xedd);
    printf("\nNumber of memory operands: %d\n", (int)memops);
    for(memop_index=0;memop_index<memops;memop_index++) {
        xed_error = xed_agen(&xedd, memop_index, 0, &out_addr);
        if (xed_error != XED_ERROR_NONE) {
            fprintf(stderr,"Agen error code %s\n", xed_error_enum_t2str(xed_error));
            exit(1);
        }
        printf("\tMemory agen%d: " XED_FMT_LX "\n", (int)memop_index, out_addr);
    }
    return 0;
}
