blob: eecee1901d853b4278196cf020862ca02b619ebd [file] [log] [blame]
/* $IdPath$
* YASM architecture interface header file
*
* Copyright (C) 2002 Peter Johnson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER 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 AUTHOR OR OTHER 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.
*/
#ifndef YASM_ARCH_H
#define YASM_ARCH_H
typedef enum yasm_arch_check_id_retval {
YASM_ARCH_CHECK_ID_NONE = 0, /* just a normal identifier */
YASM_ARCH_CHECK_ID_INSN, /* an instruction */
YASM_ARCH_CHECK_ID_PREFIX, /* an instruction prefix */
YASM_ARCH_CHECK_ID_REG, /* a register */
YASM_ARCH_CHECK_ID_SEGREG, /* a segment register (for memory overrides) */
YASM_ARCH_CHECK_ID_TARGETMOD /* an target modifier (for jumps) */
} yasm_arch_check_id_retval;
typedef struct yasm_insn_operand yasm_insn_operand;
typedef struct yasm_insn_operandhead yasm_insn_operandhead;
#ifdef YASM_INTERNAL
/*@reldef@*/ STAILQ_HEAD(yasm_insn_operandhead, yasm_insn_operand);
#endif
/* Different assemblers order instruction operands differently. Also, some
* differ on how exactly various registers are specified. There's no great
* solution to this, as the parsers aren't supposed to have knowledge of the
* architectural internals, and the architecture is supposed to be parser-
* independent. To make things work, as a rather hackish solution, we give the
* architecture a little knowledge about the general "flavor" of the parser,
* and let the architecture decide what to do with it. Most architectures will
* probably not even use this, but it's required for some (x86 in particular)
* for correct behavior on all parsers.
*/
typedef enum yasm_arch_syntax_flavor {
YASM_ARCH_SYNTAX_FLAVOR_NASM = 1, /* like NASM */
YASM_ARCH_SYNTAX_FLAVOR_GAS /* like GAS */
} yasm_arch_syntax_flavor;
struct yasm_arch {
/* one-line description of the architecture */
const char *name;
/* keyword used to select architecture */
const char *keyword;
void (*initialize) (void);
void (*cleanup) (void);
struct {
/* All "data" below starts the parse initialized to 0. Thus, it is
* okay for a funtion to use/check previously stored data to see if
* it's been called before on the same piece of data.
*/
/* Switches available instructions/registers/etc. based on a
* user-specified CPU identifier. Should modify behavior ONLY of
* parse functions! The bytecode and output functions should be able
* to handle any CPU.
*/
void (*switch_cpu) (const char *cpuid, unsigned long lindex);
/* Checks an generic identifier to see if it matches architecture
* specific names for instructions, registers, etc (see the
* arch_check_id_retval enum above for the various types this function
* can detect & return. Unrecognized identifiers should be returned
* as NONE so they can be treated as normal symbols. Any additional
* data beyond just the type (almost always necessary) should be
* returned into the space provided by the data parameter.
* Note: even though this is passed a data[4], only data[0] should be
* used for TARGETMOD, REG, and SEGREG return values.
*/
yasm_arch_check_id_retval (*check_identifier)
(unsigned long data[4], const char *id, unsigned long lindex);
/* Architecture-specific directive support. Returns 1 if directive was
* not recognized. Returns 0 if directive was recognized, even if it
* wasn't valid. Should modify behavior ONLY of parse functions, much
* like switch_cpu() above.
*/
int (*directive) (const char *name, yasm_valparamhead *valparams,
/*@null@*/ yasm_valparamhead *objext_valparams,
yasm_sectionhead *headp, unsigned long lindex);
/* Creates an instruction. Creates a bytecode by matching the
* instruction data and the parameters given with a valid instruction.
* If no match is found (the instruction is invalid), returns NULL.
* All zero data indicates an empty instruction should be created.
*/
/*@null@*/ yasm_bytecode * (*new_insn)
(const unsigned long data[4], int num_operands,
/*@null@*/ yasm_insn_operandhead *operands,
yasm_section *cur_section, /*@null@*/ yasm_bytecode *prev_bc,
unsigned long lindex);
/* Handle an instruction prefix by modifying bc as necessary. */
void (*handle_prefix) (yasm_bytecode *bc, const unsigned long data[4],
unsigned long lindex);
/* Handle an segment register instruction prefix by modifying bc as
* necessary.
*/
void (*handle_seg_prefix) (yasm_bytecode *bc, unsigned long segreg,
unsigned long lindex);
/* Handle memory expression segment overrides by modifying ea as
* necessary.
*/
void (*handle_seg_override) (yasm_effaddr *ea, unsigned long segreg,
unsigned long lindex);
/* Convert an expression into an effective address. */
yasm_effaddr * (*ea_new_expr) (/*@keep@*/ yasm_expr *e);
} parse;
struct {
/* Maximum used bytecode type value+1. Should be set to
* BYTECODE_TYPE_BASE if no additional bytecode types are defined by
* the architecture.
*/
const int type_max;
void (*bc_delete) (yasm_bytecode *bc);
void (*bc_print) (FILE *f, int indent_level, const yasm_bytecode *bc);
/* See bytecode.h comments on bc_resolve() */
yasm_bc_resolve_flags (*bc_resolve)
(yasm_bytecode *bc, int save, const yasm_section *sect,
yasm_calc_bc_dist_func calc_bc_dist);
/* See bytecode.h comments on bc_tobytes() */
int (*bc_tobytes) (yasm_bytecode *bc, unsigned char **bufp,
const yasm_section *sect, void *d,
yasm_output_expr_func output_expr);
} bc;
/* Functions to output floats and integers, architecture-specific because
* of endianness. Returns nonzero on error, otherwise updates bufp by
* valsize (bytes saved to bufp). For intnums, rel indicates a relative
* displacement, and bc is the containing bytecode to compute it from.
*/
int (*floatnum_tobytes) (const yasm_floatnum *flt, unsigned char **bufp,
unsigned long valsize, const yasm_expr *e);
int (*intnum_tobytes) (const yasm_intnum *intn, unsigned char **bufp,
unsigned long valsize, const yasm_expr *e,
const yasm_bytecode *bc, int rel);
/* Gets the equivalent register size in bytes. Returns 0 if there is no
* suitable equivalent size.
*/
unsigned int (*get_reg_size) (unsigned long reg);
void (*reg_print) (FILE *f, unsigned long reg);
void (*segreg_print) (FILE *f, unsigned long segreg);
/* Deletes the arch-specific data in ea. May be NULL if no special
* deletion is required (e.g. there's no dynamically allocated pointers
* in the ea data).
*/
void (*ea_data_delete) (yasm_effaddr *ea);
void (*ea_data_print) (FILE *f, int indent_level, const yasm_effaddr *ea);
};
#ifdef YASM_INTERNAL
struct yasm_insn_operand {
/*@reldef@*/ STAILQ_ENTRY(yasm_insn_operand) link;
enum {
YASM_INSN__OPERAND_REG = 1, /* a register */
YASM_INSN__OPERAND_SEGREG, /* a segment register */
YASM_INSN__OPERAND_MEMORY,/* an effective address (memory reference) */
YASM_INSN__OPERAND_IMM /* an immediate or jump target */
} type;
union {
unsigned long reg; /* arch data for reg/segreg */
yasm_effaddr *ea; /* effective address for memory references */
yasm_expr *val; /* value of immediate or jump target */
} data;
unsigned long targetmod; /* arch target modifier, 0 if none */
/* Specified size of the operand, in bytes. 0 if not user-specified. */
unsigned int size;
};
#endif
void yasm_arch_common_initialize(yasm_arch *a);
/* insn_operand constructors. operand_new_imm() will look for cases of a
* single register and create an INSN_OPERAND_REG variant of insn_operand.
*/
yasm_insn_operand *yasm_operand_new_reg(unsigned long reg);
yasm_insn_operand *yasm_operand_new_segreg(unsigned long segreg);
yasm_insn_operand *yasm_operand_new_mem(/*@only@*/ yasm_effaddr *ea);
yasm_insn_operand *yasm_operand_new_imm(/*@only@*/ yasm_expr *val);
void yasm_operand_print(FILE *f, int indent_level,
const yasm_insn_operand *op);
void yasm_ops_initialize(yasm_insn_operandhead *headp);
yasm_insn_operand *yasm_ops_first(yasm_insn_operandhead *headp);
yasm_insn_operand *yasm_ops_next(yasm_insn_operand *cur);
#ifdef YASM_INTERNAL
#define yasm_ops_initialize(headp) STAILQ_INIT(headp)
#define yasm_ops_first(headp) STAILQ_FIRST(headp)
#define yasm_ops_next(cur) STAILQ_NEXT(cur, link)
#endif
/* Deletes operands linked list. Deletes content of each operand if content i
* nonzero.
*/
void yasm_ops_delete(yasm_insn_operandhead *headp, int content);
/* Adds op to the list of operands headp.
* NOTE: Does not make a copy of op; so don't pass this function
* static or local variables, and discard the op pointer after calling
* this function. If op was actually appended (it wasn't NULL), then
* returns op, otherwise returns NULL.
*/
/*@null@*/ yasm_insn_operand *yasm_ops_append
(yasm_insn_operandhead *headp,
/*@returned@*/ /*@null@*/ yasm_insn_operand *op);
void yasm_ops_print(FILE *f, int indent_level,
const yasm_insn_operandhead *headp);
#endif