//===-- EmulationStateARM.cpp -----------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "EmulationStateARM.h"

#include "lldb/Core/RegisterValue.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Interpreter/OptionValueArray.h"
#include "lldb/Interpreter/OptionValueDictionary.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/RegisterContext.h"

#include "Utility/ARM_DWARF_Registers.h"

using namespace lldb;
using namespace lldb_private;

EmulationStateARM::EmulationStateARM () :
    m_gpr (),
    m_vfp_regs (),
    m_memory ()
{
    ClearPseudoRegisters();
}

EmulationStateARM::~EmulationStateARM ()
{
}

bool
EmulationStateARM::LoadPseudoRegistersFromFrame (StackFrame &frame)
{
    RegisterContext *reg_ctx = frame.GetRegisterContext().get();
    bool success = true;
    uint32_t reg_num;
    
    for (int i = dwarf_r0; i < dwarf_r0 + 17; ++i)
    {
        reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindDWARF, i);
        const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num);
        RegisterValue reg_value;
        if (reg_ctx->ReadRegister (reg_info, reg_value))
        {
            m_gpr[i - dwarf_r0] = reg_value.GetAsUInt32();
        }
        else
            success = false;
    }
    
    for (int i = dwarf_d0; i < dwarf_d0 + 32; ++i)
    {
        reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindDWARF, i);
        RegisterValue reg_value;
        const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num);

        if (reg_ctx->ReadRegister (reg_info, reg_value))
        {
            uint32_t idx = i - dwarf_d0;
            if (i < 16)
                m_vfp_regs.sd_regs[idx].d_reg = reg_value.GetAsUInt64();
            else
                m_vfp_regs.d_regs[idx - 16] = reg_value.GetAsUInt64();
        }
        else
            success = false;
    }
    
    return success;
}
    
bool
EmulationStateARM::StorePseudoRegisterValue (uint32_t reg_num, uint64_t value)
{
    if ((dwarf_r0 <= reg_num) && (reg_num <= dwarf_cpsr))
        m_gpr[reg_num  - dwarf_r0] = (uint32_t) value;
    else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31))
    {
        uint32_t idx = reg_num - dwarf_s0;
        m_vfp_regs.sd_regs[idx / 2].s_reg[idx % 2] = (uint32_t) value;
    }
    else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31))
    {
        if ((reg_num - dwarf_d0) < 16)
        {
            m_vfp_regs.sd_regs[reg_num - dwarf_d0].d_reg = value;
        }
        else
            m_vfp_regs.d_regs[reg_num - dwarf_d16] = value;
    }
    else
        return false;
        
    return true;
}
    
uint64_t
EmulationStateARM::ReadPseudoRegisterValue (uint32_t reg_num, bool &success)
{
    uint64_t value = 0;
    success = true;
    
    if ((dwarf_r0 <= reg_num) && (reg_num <= dwarf_cpsr))
        value = m_gpr[reg_num  - dwarf_r0];
    else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31))
    {
        uint32_t idx = reg_num - dwarf_s0;
        value = m_vfp_regs.sd_regs[idx / 2].s_reg[idx % 2];
    }
    else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31))
    {
        if ((reg_num - dwarf_d0) < 16)
            value = m_vfp_regs.sd_regs[reg_num - dwarf_d0].d_reg;
        else
            value = m_vfp_regs.d_regs[reg_num - dwarf_d16];
    }
    else
        success = false;
        
    return value;
}
    
void
EmulationStateARM::ClearPseudoRegisters ()
{
    for (int i = 0; i < 17; ++i)
        m_gpr[i] = 0;
    
    for (int i = 0; i < 16; ++i)
        m_vfp_regs.sd_regs[i].d_reg = 0;
    
    for (int i = 0; i < 16; ++i)
        m_vfp_regs.d_regs[i] = 0;
}

void
EmulationStateARM::ClearPseudoMemory ()
{
    m_memory.clear();
}
    
bool
EmulationStateARM::StoreToPseudoAddress (lldb::addr_t p_address, uint64_t value, uint32_t size)
{
    if (size > 8)
        return false;
    
    if (size <= 4)
        m_memory[p_address] = value;
    else if (size == 8)
    {
        m_memory[p_address] = (value << 32) >> 32;
        m_memory[p_address + 4] = value << 32;
    }
    return true;
}
    
uint32_t
EmulationStateARM::ReadFromPseudoAddress (lldb::addr_t p_address, uint32_t size, bool &success)
{
    std::map<lldb::addr_t,uint32_t>::iterator pos;
    uint32_t ret_val = 0;
    
    success = true;
    pos = m_memory.find(p_address);
    if (pos != m_memory.end())
        ret_val = pos->second;
    else
        success = false;
        
    return ret_val;
}

size_t
EmulationStateARM::ReadPseudoMemory (EmulateInstruction *instruction,
                                     void *baton,
                                     const EmulateInstruction::Context &context,
                                     lldb::addr_t addr,
                                     void *dst,
                                     size_t length)
{
    if (!baton)
        return 0;
        
    bool success = true;
    EmulationStateARM *pseudo_state = (EmulationStateARM *) baton;
    if (length <= 4)
    {
        uint32_t value = pseudo_state->ReadFromPseudoAddress (addr, length, success);
        if (!success)
            return 0;
            
        *((uint32_t *) dst) = value;
    }
    else if (length == 8)
    {
        uint32_t value1 = pseudo_state->ReadFromPseudoAddress (addr, 4, success);
        if (!success)
            return 0;
            
        uint32_t value2 = pseudo_state->ReadFromPseudoAddress (addr + 4, 4, success);
        if (!success)
            return 0;
            
        uint64_t value64 = value2;
        value64 = (value64 << 32) | value1;
        *((uint64_t *) dst) = value64;
    }
    else
        success = false;
    
    if (success)
        return length;
    
    return 0;
}
    
size_t
EmulationStateARM::WritePseudoMemory (EmulateInstruction *instruction,
                                      void *baton,
                                      const EmulateInstruction::Context &context,
                                      lldb::addr_t addr,
                                      const void *dst,
                                      size_t length)
{
    if (!baton)
        return 0;
        
    bool success;
    EmulationStateARM *pseudo_state = (EmulationStateARM *) baton;
    uint64_t value = *((const uint64_t *) dst);
    success = pseudo_state->StoreToPseudoAddress (addr, value, length);
    if (success)
        return length;
        
    return 0;
}
    
bool
EmulationStateARM::ReadPseudoRegister (EmulateInstruction *instruction,
                                       void *baton,
                                       const lldb_private::RegisterInfo *reg_info,
                                       lldb_private::RegisterValue &reg_value)
{
    if (!baton || !reg_info)
        return false;
        
    bool success = true;
    EmulationStateARM *pseudo_state = (EmulationStateARM *) baton;
    const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
    assert (dwarf_reg_num != LLDB_INVALID_REGNUM);
    uint64_t reg_uval = pseudo_state->ReadPseudoRegisterValue (dwarf_reg_num, success);
    
    if (success)
        success = reg_value.SetUInt(reg_uval, reg_info->byte_size);
    return success;
    
}
    
bool
EmulationStateARM::WritePseudoRegister (EmulateInstruction *instruction,
                                        void *baton,
                                        const EmulateInstruction::Context &context,
                                        const lldb_private::RegisterInfo *reg_info,
                                        const lldb_private::RegisterValue &reg_value)
{
    if (!baton || !reg_info)
        return false;

    EmulationStateARM *pseudo_state = (EmulationStateARM *) baton;
    const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF];
    assert (dwarf_reg_num != LLDB_INVALID_REGNUM);
    return pseudo_state->StorePseudoRegisterValue (dwarf_reg_num, reg_value.GetAsUInt64());
}
                         
bool
EmulationStateARM::CompareState (EmulationStateARM &other_state)
{
    bool match = true;
   
    for (int i = 0; match && i < 17; ++i)
    {
        if (m_gpr[i] != other_state.m_gpr[i])
            match = false;
    }
    
    for (int i = 0; match && i < 16; ++i)
    {
        if (m_vfp_regs.sd_regs[i].s_reg[0] != other_state.m_vfp_regs.sd_regs[i].s_reg[0])
            match = false;

        if (m_vfp_regs.sd_regs[i].s_reg[1] != other_state.m_vfp_regs.sd_regs[i].s_reg[1])
            match = false;
    }
    
    for (int i = 0; match && i < 32; ++i)
    {
        if (i < 16)
        {
            if (m_vfp_regs.sd_regs[i].d_reg != other_state.m_vfp_regs.sd_regs[i].d_reg)
                match = false;
        }
        else
        {
            if (m_vfp_regs.d_regs[i - 16] != other_state.m_vfp_regs.d_regs[i - 16])
                match = false;
        }
    }
    
    return match;
}

bool
EmulationStateARM::LoadStateFromDictionary (OptionValueDictionary *test_data)
{
    static ConstString memory_key ("memory");
    static ConstString registers_key ("registers");
    
    if (!test_data)
        return false;
    
    OptionValueSP value_sp = test_data->GetValueForKey (memory_key);
    
    // Load memory, if present.
    
    if (value_sp.get() != NULL)
    {
        static ConstString address_key ("address");
        static ConstString data_key ("data");
        uint64_t start_address = 0;
        
        OptionValueDictionary *mem_dict = value_sp->GetAsDictionary();
        value_sp = mem_dict->GetValueForKey (address_key);
        if (value_sp.get() == NULL)
            return false;
        else
            start_address = value_sp->GetUInt64Value ();
        
        value_sp = mem_dict->GetValueForKey (data_key);
        OptionValueArray *mem_array = value_sp->GetAsArray();
        if (!mem_array)
            return false;

        uint32_t num_elts = mem_array->GetSize();
        uint32_t address = (uint32_t) start_address;
        
        for (uint32_t i = 0; i < num_elts; ++i)
        {
            value_sp = mem_array->GetValueAtIndex (i);
            if (value_sp.get() == NULL)
                return false;
            uint64_t value = value_sp->GetUInt64Value();
            StoreToPseudoAddress (address, value, 4);
            address = address + 4;
        }
    }
    
    value_sp = test_data->GetValueForKey (registers_key);
    if (value_sp.get() == NULL)
        return false;

        
    // Load General Registers
   
    OptionValueDictionary *reg_dict = value_sp->GetAsDictionary ();
   
    StreamString sstr;
    for (int i = 0; i < 16; ++i)
    {
        sstr.Clear();
        sstr.Printf ("r%d", i);
        ConstString reg_name (sstr.GetData());
        value_sp = reg_dict->GetValueForKey (reg_name);
        if (value_sp.get() == NULL)
            return false;
        uint64_t reg_value = value_sp->GetUInt64Value();
        StorePseudoRegisterValue (dwarf_r0 + i, reg_value);
    }
    
    static ConstString cpsr_name ("cpsr");
    value_sp = reg_dict->GetValueForKey (cpsr_name);
    if (value_sp.get() == NULL)
        return false;
    StorePseudoRegisterValue (dwarf_cpsr, value_sp->GetUInt64Value());
    
    // Load s/d Registers
    for (int i = 0; i < 32; ++i)
    {
        sstr.Clear();
        sstr.Printf ("s%d", i);
        ConstString reg_name (sstr.GetData());
        value_sp = reg_dict->GetValueForKey (reg_name);
        if (value_sp.get() == NULL)
            return false;
        uint64_t reg_value = value_sp->GetUInt64Value();
        StorePseudoRegisterValue (dwarf_s0 + i, reg_value);
    }

    return true;
}

