/*
 *      Interactive disassembler (IDA).
 *      Copyright (c) 1990-97 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              E-mail: ig@estar.msk.su
 *                              FIDO:   2:5020/209
 *
 */

#ifndef _FRAME_HPP
#define _FRAME_HPP
#include <funcs.hpp>
#pragma pack(push, 1)           // IDA uses 1 byte alignments!

//
//      This file contains routines to manipulate function stack frames, stack
//      variables, register variables and local labels.
//      The frame is represented as a structure:
//
//    +----------------------------------------+
//    | function arguments                     |
//    +----------------------------------------+
//    | return address (isn't stored in func_t)|
//    +----------------------------------------+
//    | saved registers (SI,DI,etc)            |
//    +----------------------------------------+        <- BP
//    | local variables                        |
//    +----------------------------------------+        <- SP
//
//      Also we need to trace value of SP register. For this we introduce
//      an array of SP register change points (stkpnt_t).
//

class struc_t;         // #include <struct.hpp>
class member_t;        // #include <struct.hpp>
class op_t;            // #include <ua.hpp>


// SP register change point:
// This structure is only for the kernel.
// Please use special functions to examine/manipulate the stack pointer.
// They are below in this file (stack pointer change points)

struct stkpnt_t
{
  ea_t ea;              // linear address
  sval_t spd;           // here we keep a cumulative difference from [BP-frsize]
};


//--------------------------------------------------------------------------
//      F R A M E   M A N I P U L A T I O N
//--------------------------------------------------------------------------

// Function frames are based on structures. In order to access structure
// of a function frame, use pfn->frame as ID of a structure.

// Add function frame.
//      pfn - pointer to function structure
//      frsize  - size of function local variables
//      frregs  - size of saved registers
//      argsize - size of function arguments area which will be purged upon return
//                This parameter is used for __stdcall and __pascal calling conventions
//                For other calling conventions please pass 0
// returns: 1-ok, 0-failed (no function, frame already exists)

idaman bool ida_export add_frame(func_t *pfn,
                                 asize_t frsize,
                                 ushort frregs,
                                 asize_t argsize);


// Delete a function frame
//      pfn - pointer to function structure
// returns: 1-ok, 0-failed

idaman bool ida_export del_frame(func_t *pfn);


// Set size of function frame
//      pfn - pointer to function structure
//      frsize  - size of function local variables
//      frregs  - size of saved registers
//      argsize - size of function arguments
// returns: 1-ok, 0-failed

idaman bool ida_export set_frame_size(func_t *pfn,
                                      asize_t frsize,
                                      ushort frregs,
                                      asize_t argsize);


// Get full size of a function frame
// This function takes into account size of local variables + size of
// saved registers + size of return address + size of function arguments
//      pfn - pointer to function structure, may be NULL
// returns: size of frame in bytes or zero

idaman asize_t ida_export get_frame_size(func_t *pfn);


// Get size of function return address
//      pfn - pointer to function structure, can't be NULL

idaman int ida_export get_frame_retsize(func_t *pfn);


// Get pointer to function frame
//      pfn - pointer to function structure
// returns: pointer to function frame

idaman struc_t *ida_export get_frame(func_t *pfn);
inline struc_t *get_frame(ea_t ea) { return get_frame(get_func(ea)); }


// Update frame pointer delta
//      pfn - pointer to function structure
//      fpd - new fpd value
// fpd can not be bigger than the local variable area size
// returns: success

idaman bool ida_export update_fpd(func_t *pfn, asize_t fpd);


//--------------------------------------------------------------------------
//      S T A C K   V A R I A B L E S
//--------------------------------------------------------------------------

// Get pointer to stack variable
//      x  - reference to instruction operand
//      v  - immediate value in the operand (usually x.addr)
//      actval - actual value used to fetch stack variable
//               this pointer may point to 'v'
// returns: NULL or ptr to stack variable

idaman member_t *ida_export get_stkvar(const op_t &x, sval_t v, sval_t *actval);


// Automatically add stack variable if doesn't exist
//      x  - reference to instruction operand
//      v  - immediate value in the operand (usually x.addr)
// returns: 1-ok
// Processor modules should use ua_stkvar()

idaman bool ida_export add_stkvar(const op_t &x, sval_t v);


// Build automatic stack variable name
//      buf - pointer to buffer. must be at least MAXNAMELEN
//      pfn - pointer to function (can't be NULL!)
//      v   - value of variable offset
// returns: ptr to buf

idaman char *ida_export build_stkvar_name(char *buf, size_t bufsize, func_t *pfn, sval_t v);


// internal function: create special part of function frame
// this function won't create zero size members
// also it doesn't check the validity of the "name"
// returns: STRUC_ERROR.. codes (see struct.hpp)

int add_frame_spec_member(struc_t *sptr, const char *name, ea_t offset, asize_t nbytes);


// Delete all stack variables in the specified range
//      ea1 - starting linear address
//      ea2 - ending   linear address

void del_stkvars(ea_t ea1, ea_t ea2);


// Calculate offset of stack variable in the frame structure
//      pfn - pointer to function (can't be NULL!)
//      x   - reference to instruction operand
//      v   - value of variable offset in the instruction
// returns: offset of stack variable in the frame structure (0..n)

ea_t calc_frame_offset(func_t *pfn, const op_t &x, sval_t v);


// Calculate offset of stack variable in the frame structure
//      pfn - pointer to function (can't be NULL!)
//      ea  - linear address of the instruction
//      n   - number of operand: (0..UA_MAXOP-1)
//              -1 - error, return BADADDR
// return BADADDR if some error (issue a warning if stack frame is bad)

idaman ea_t ida_export calc_stkvar_struc_offset(func_t *pfn, ea_t ea, int n);


// Find and delete unreferenced stack variable definitions
//      pfn - pointer to the function
// Returns: number of deleted definitions

idaman int ida_export delete_unreferenced_stkvars(func_t *pfn);


// Find and undefine references to dead stack variables
// (i.e. operands displayed in red)
// These operands will be untyped and most likely displayed in hex.
//      pfn - pointer to the function
// Returns: number of reset operands

idaman int ida_export delete_wrong_stkvar_ops(func_t *pfn);


//--------------------------------------------------------------------------
//      R E G I S T E R   V A R I A B L E S
//--------------------------------------------------------------------------
// A register variable allows the user to rename a general processor register
// to a meaningful name.
// IDA doesn't check whether the target assembler supports the register renaming.
// All register definitions will appear at the beginning of the function.

struct regvar_t : public area_t
{
  char *canon;          // canonical register name (case-insensitive)
  char *user;           // user-defined register name
  char *cmt;            // comment to appear near definition
};

// Define a register variable
//      pfn     - function in which the definition will be created
//      ea1,ea2 - range of addresses within the function where the definition
//                will be used
//      canon   - name of a general register
//      user    - user-defined name for the register
//      cmt     - comment for the definition
// returns: error code REGVAR_ERROR_... (see below)

idaman int ida_export add_regvar(func_t *pfn, ea_t ea1, ea_t ea2,
                        const char *canon,
                        const char *user,
                        const char *cmt);
#define REGVAR_ERROR_OK         0     // all ok
#define REGVAR_ERROR_ARG        (-1)  // function arguments are bad
#define REGVAR_ERROR_RANGE      (-2)  // the definition range is bad
#define REGVAR_ERROR_NAME       (-3)  // the provided name(s) can't be accepted


// Find a register variable definition (powerful version)
//      pfn     - function in question
//      ea1,ea2 - range of addresses to search
//      canon   - name of a general register
//      user    - user-defined name for the register
// One of 'canon' and 'user' should be NULL.
// Returns: NULL-not found, otherwise ptr to regvar_t

idaman regvar_t *ida_export find_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon, const char *user);


// Find a register variable definition
//      pfn     - function in question
//      ea      - current address
//      canon   - name of a general register
// Returns: NULL-not found, otherwise ptr to regvar_t

inline regvar_t *find_regvar(func_t *pfn, ea_t ea, const char *canon)
{
  return find_regvar(pfn, ea, ea+1, canon, NULL);
}


// Rename a register variable
//      pfn     - function in question
//      v       - variable to rename
//      user    - new user-defined name for the register
// Returns: REGVAR_ERROR_...

idaman int ida_export rename_regvar(func_t *pfn, regvar_t *v, const char *user);


// Set comment for a register variable
//      pfn     - function in question
//      v       - variable to rename
//      cmt     - new comment
// Returns: REGVAR_ERROR_...

idaman int ida_export set_regvar_cmt(func_t *pfn, regvar_t *v, const char *cmt);


// Delete a register variable definition
//      pfn     - function in question
//      ea1,ea2 - range of addresses within the function where the definition
//                holds
//      canon   - name of a general register
//      user    - user-defined name for the register
// Returns: REGVAR_ERROR_...

idaman int ida_export del_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon);


// These functions are for internal use by the kernel
void read_regvars(func_t *pfn);
bool write_regvars(func_t *pfn);
void del_regvars(ea_t ea);
void free_regvar(regvar_t *v);

//--------------------------------------------------------------------------
//      L O C A L   L A B E L S
//--------------------------------------------------------------------------
// These are LOW LEVEL FUNCTIONS.
// When possible, they should not be used. Use high level functions from <name.hpp>

struct llabel_t
{
  ea_t ea;
  char *name;
};

// Define/rename/delete a local label
//      pfn     - function in which the definition will be created
//      ea      - linear address of the label
//      name    - name of the label. If NULL or empty string, name will be removed
// returns: success
// THIS IS A LOW LEVEL FUNCTION - use set_name() instead of it!

bool set_llabel(func_t *pfn, ea_t ea, const char *name);


// Get address of a local label
//      pfn     - function in question
//      name    - name of the label
// Returns: BADADDR-not found
// THIS IS A LOW LEVEL FUNCTION - use get_name_ea() instead of it!

ea_t get_llabel_ea(func_t *pfn, const char *name);


// Get local label at the specified address
//      pfn     - function in question
//      ea      - linear address of the label
// Returns: NULL or ptr to the name
// THIS IS A LOW LEVEL FUNCTION - use get_name() instead of it!

const char *get_llabel(func_t *pfn, ea_t ea);


// These functions are for internal use by the kernel
void read_llabels(func_t *pfn);
bool write_llabels(func_t *pfn);
void del_llabels(ea_t ea);
void free_llabel(llabel_t *l);

//--------------------------------------------------------------------------
//      S P   R E G I S T E R   C H A N G E   P O I N T S
//--------------------------------------------------------------------------

// Add automatical SP register change point
//      ea    - linear address where SP changes
//      delta - difference between old and new values of SP
// returns: 1-ok, 0-failed

idaman bool ida_export add_auto_stkpnt(ea_t ea, sval_t delta);


// Add user-defined SP register change point
//      ea    - linear address where SP changes
//      delta - difference between old and new values of SP
// returns: 1-ok, 0-failed

idaman bool ida_export add_user_stkpnt(ea_t ea, sval_t delta);


// Delete SP register change point
//      pfn   - pointer to function. may be NULL.
//      ea    - linear address
// returns: 1-ok, 0-failed

idaman bool ida_export del_stkpnt(func_t *pfn, ea_t ea);


// Get difference between [BP-frame size] and SP registers
//      pfn   - pointer to function. may be NULL.
//      ea    - linear address
// returns 0 or the difference

idaman sval_t ida_export get_spd(func_t *pfn, ea_t ea);


// Get modification of SP made at the specified location
//      pfn   - pointer to function. may be NULL.
//      ea    - linear address
// If the specified location doesn't contain a SP change point, return 0
// Otherwise return delta of SP modification

idaman sval_t ida_export get_sp_delta(func_t *pfn, ea_t ea);


// Recalculate SP delta for an instruction that stops execution.
// The next instruction is not reached from the current instruction.
// We need to recalculate SP for the next instruction.
//      cur_ea  - linear address of the current instruction
// This function will create a new automatic SP register change
// point if nesessary. It should be called from the emulator (emu.cpp)
// when auto_state == AU_USED if the current instruction doesn't pass
// the execution flow to the next instruction.
// returns: 1 - new stkpnt is added, 0 - nothing is changed

idaman bool ida_export recalc_spd(ea_t cur_ea);


// Low level functions to work with sp change points. Should not be used
// directly!

stkpnt_t * read_stkpnts(func_t *pfn);
bool write_stkpnts(func_t *pfn);
void del_stkpnts(ea_t ea);
void del_tail_stkpnts(func_t *pfn, ea_t ea1);

#pragma pack(pop)
#endif // _FRAME_HPP
