www.pudn.com > open_watcom.rar > alphafmt.c
/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: Alpha AXP instruction formats and encodings.
*
****************************************************************************/
#include "as.h"
#ifndef _STANDALONE_
#include "asinline.h"
#endif
typedef struct reloc_entry *reloc_list;
struct reloc_entry {
reloc_list next;
op_reloc_target target;
owl_reloc_type type;
unsigned loc; // offset of the reloc from &result[0].
bool is_named; // does it have a corr. named symbol
};
typedef struct asm_reloc {
reloc_list first;
reloc_list last;
} asm_reloc;
typedef void (*fmt_func)( ins_table *, instruction *, uint_32 *, asm_reloc * );
typedef struct {
fmt_func func;
op_type ops[ MAX_OPERANDS ];
} alpha_format;
typedef enum {
DOMOV_ORIGINAL,
DOMOV_ABS,
} domov_option;
#define MAX_VARIETIES 3
typedef op_type ot_array[MAX_VARIETIES][3];
#define _FiveBits( x ) ( (x) & 0x001f )
#define _SixBits( x ) ( (x) & 0x003f )
#define _SevenBits( x ) ( (x) & 0x007f )
#define _EightBits( x ) ( (x) & 0x00ff )
#define _ElevenBits( x ) ( (x) & 0x07ff )
#define _FourteenBits( x ) ( (x) & 0x3fff )
#define _SixteenBits( x ) ( (x) & 0xffff )
#define _TwentyOneBits( x ) ( (x) & 0x001fffff )
#define _LIT_value( x ) ( _EightBits( x ) << 1 )
#define _LIT_bit 1
#define _LIT_unshifted( x ) ( _LIT_value( x ) | _LIT_bit )
#define _Opcode( x ) ( _SixBits( x ) << 26 )
#define _Ra( x ) ( _FiveBits( x ) << 21 )
#define _Rb( x ) ( _FiveBits( x ) << 16 )
#define _Rc( x ) ( _FiveBits( x ) << 0 )
#define _Memory_disp( x ) ( _SixteenBits( x ) << 0 )
#define _Mem_Func( x ) ( _SixteenBits( x ) << 0 )
#define _Branch_disp( x ) ( _TwentyOneBits( x ) << 0 )
#define _Op_Func( x ) ( _SevenBits( x ) << 5 )
#define _FP_Op_Func( x ) ( _ElevenBits( x ) << 5 )
#define _LIT( x ) ( _LIT_unshifted( x ) << 12 )
#define _Longword_offset( x ) ( (x) >> 2 )
#define MAX_EXTENDED_INS 20 // Including the default buffer, we have
// 21 dwords for an ins that emits multiple
// instructions. (eg. ldb)
#define RA_REG_IDX 26
#define SP_REG_IDX 30
#define FP_REG_IDX 15
#define ZERO_REG_IDX 31
// Check if ".set noat" is in effect before using this
#define AT_REG_IDX RegIndex( AT_REG )
#define ZERO_REG MakeReg( RC_GPR, ZERO_REG_IDX )
#define OPCODE_BIS 0x11
#define FUNCCODE_BIS 0x0020
#define OPCODE_LDA 0x8
#define OPCODE_LDAH 0x9
#define OP_HAS_RELOC( op ) ((op)->flags & (RELOC | UNNAMED_RELOC))
#define OP_RELOC_NAMED( op ) ((op)->flags & RELOC)
static unsigned numExtendedIns = 0; // >= 1 when we use extendedIns
static uint_32 result[ MAX_EXTENDED_INS + 1 ];
static owl_reloc_type reloc_translate[] = {
OWL_RELOC_ABSOLUTE, // Corresponds to ASM_RELOC_UNSPECIFIED
OWL_RELOC_WORD,
OWL_RELOC_HALF_HI,
OWL_RELOC_HALF_HA,
OWL_RELOC_HALF_LO,
OWL_RELOC_BRANCH_REL, // j^ reloc
OWL_RELOC_JUMP_REL, // jump hint
};
static bool ensureOpAbsolute( ins_operand *op, uint_8 opIdx ) {
//*************************************************************
if( OP_HAS_RELOC( op ) ) {
Error( RELOC_NOT_ALLOWED, opIdx );
return( FALSE );
}
return( TRUE );
}
static void addReloc( asm_reloc *reloc, op_reloc_target target, owl_reloc_type type, unsigned loc, bool is_named ) {
//******************************************************************************************************************
reloc_list new_entry;
new_entry = MemAlloc( sizeof( struct reloc_entry ) );
new_entry->next = NULL;
new_entry->target = target;
new_entry->type = type;
new_entry->loc = loc;
new_entry->target = target;
new_entry->is_named = is_named;
if( reloc->first == NULL ) {
reloc->first = new_entry;
} else {
reloc->last->next = new_entry;
}
reloc->last = new_entry;
}
static owl_reloc_type relocType( asm_reloc_type type, owl_reloc_type default_type ) {
//***********************************************************************************
owl_reloc_type ret;
if( type == ASM_RELOC_UNSPECIFIED ) {
return( default_type );
}
switch( default_type ) {
case OWL_RELOC_HALF_HI:
case OWL_RELOC_HALF_LO:
if( ( ret = reloc_translate[type] ) != OWL_RELOC_HALF_HI &&
ret != OWL_RELOC_HALF_LO ) {
Error( INVALID_RELOC_MODIFIER );
}
break;
case OWL_RELOC_BRANCH_REL: // j^ reloc
if( ( ret = reloc_translate[type] ) != default_type ) {
Error( INVALID_RELOC_MODIFIER );
}
break;
case OWL_RELOC_JUMP_REL: // jump hint
// we accept j^ to be specified for jump hint for now.
if( ( ret = reloc_translate[type] ) != OWL_RELOC_JUMP_REL &&
ret != OWL_RELOC_BRANCH_REL ) {
Error( INVALID_RELOC_MODIFIER );
}
ret = OWL_RELOC_JUMP_REL;
break;
default:
//Error( "internal - unexpected default type" );
assert( FALSE );
ret = OWL_RELOC_ABSOLUTE;
}
return( ret );
}
static void doReloc( asm_reloc *reloc, ins_operand *op, owl_reloc_type rtype, uint_32 *offset ) {
//***********************************************************************************************
if( op == NULL ) return;
if( !( OP_HAS_RELOC( op ) ) ) return;
addReloc( reloc, op->reloc.target, relocType( op->reloc.type, rtype ),
(unsigned)offset - (unsigned)result, ( op->flags & RELOC ) );
}
/*
IT_OPERATE:
QF_V..........+0x40
IT_FP_xxxx:
QF_S..........+0x400
QF_U..........+0x100
QF_V..........+0x100
QF_I..........+0x200
QF_C..........-0x080
QF_M..........-0x040
QF_D..........+0x040
*/
static ins_funccode getFuncCode( ins_table *table, instruction *ins ) {
//*********************************************************************
ins_funccode fc;
fc = table->funccode;
switch( table->template ) {
case IT_OPERATE:
case IT_PSEUDO_NOT:
if( ins->format->flags & QF_V ) {
fc += 0x40;
} else {
assert( ins->format->flags == 0 ); // No other valid flags
}
break;
case IT_FP_OPERATE:
case IT_FP_CONVERT:
case IT_PSEUDO_NEGF:
if( ins->format->flags & QF_S ) fc += 0x400;
if( ins->format->flags & QF_U ) fc += 0x100;
if( ins->format->flags & QF_V ) fc += 0x100;
if( ins->format->flags & QF_I ) fc += 0x200;
if( ins->format->flags & QF_C ) fc -= 0x080;
if( ins->format->flags & QF_M ) fc -= 0x040;
if( ins->format->flags & QF_D ) fc += 0x040;
break;
default:
assert( FALSE ); // Others should have no need to call this.
}
return( fc );
}
static void doOpcodeRaRb( uint_32 *buffer, ins_opcode opcode, uint_8 ra, uint_8 rb, uint_32 remain ) {
//****************************************************************************************************
*buffer = _Opcode( opcode ) | _Ra( ra ) | _Rb( rb ) | remain;
}
static void doOpcodeFcRaRc( uint_32 *buffer, ins_opcode opcode, ins_funccode fc, uint_8 ra, uint_8 rc, uint_32 extra ) {
//**********************************************************************************************************************
// This procedure doesn't fill in all the bits (missing bits 20-12).
// But we can fill it in using extra.
*buffer = _Opcode( opcode ) | _Op_Func( fc ) | _Ra( ra ) | _Rc( rc ) |
extra;
}
static void doFPInst( uint_32 *buffer, ins_opcode opcode, uint_8 ra, uint_8 rb, uint_8 rc, uint_32 remain ) {
//***********************************************************************************************************
*buffer = _Opcode( opcode ) | _Ra( ra ) | _Rb( rb ) |
_FP_Op_Func( remain ) | _Rc( rc );
}
#ifndef _STANDALONE_
static void doAutoVar( asm_reloc *reloc, op_reloc_target targ, uint_32 *buffer, ins_table *table, instruction *ins ) {
//********************************************************************************************************************
ins_operand *op;
op = ins->operands[1];
if( op->reg != ZERO_REG ) {
Error( BAD_BASE_REG_FOR_STACKVAR );
return;
}
addReloc( reloc, targ, OWL_RELOC_FP_OFFSET, (unsigned)buffer - (unsigned)&result[ 0 ], TRUE );
doOpcodeRaRb( buffer, table->opcode, RegIndex( ins->operands[0]->reg ),
FP_REG_IDX, 0 );
}
#endif
static unsigned ldaConst32( uint_32 *buffer, uint_8 d_reg, uint_8 s_reg, ins_operand *op, op_const c, asm_reloc *reloc, bool force_pair ) {
//*****************************************************************************************************************************************
// LDA-LDAH sequence for 32-bit constants
// Given: lda $d_reg, foobar+c($s_reg)
// info for foobar is stored in op
// c should be passed in also
// Returns # of ins generated
int_32 tmp;
int ctr;
unsigned ret = 1;
int_16 low, high[2];
uint_8 base_reg;
owl_reloc_type type;
bool hi_reloc_emitted = FALSE;
if( force_pair ) {
assert( reloc != NULL );
if( op->reloc.type != ASM_RELOC_UNSPECIFIED ) {
Error( INVALID_RELOC_MODIFIER );
return( ret );
}
}
low = ( c & 0xffff );
tmp = c - (int_32)low;
high[0] = ( ( tmp & 0xffff0000 ) >> 16 );
if( c >= 0x7fff8000 ) {
// if c is in range 0x7FFF8000..0x7FFFFFFF, tmp = 0x80000000
// => high[0] = 0x8000 => negative! So we need to split high[0]
// up to high[0] + high[1] and load them up in different LDAH's.
high[1] = 0x4000;
tmp -= 0x40000000;
high[0] = ( ( tmp & 0xffff0000 ) >> 16 );
} else {
high[1] = 0;
}
for( ctr = 0; ctr < 2; ctr++ ) {
if( high[ctr] != 0 ) {
base_reg = ( ctr == 0 ? s_reg : d_reg );
doOpcodeRaRb( buffer, OPCODE_LDAH,
d_reg, base_reg, _Memory_disp( high[ctr] ) );
if( reloc && !hi_reloc_emitted ) {
type = relocType( op->reloc.type, OWL_RELOC_HALF_HI );
if( type == OWL_RELOC_HALF_HI ) {
doReloc( reloc, op, OWL_RELOC_HALF_HI, buffer );
hi_reloc_emitted = TRUE; // only need to generate h^ once
} else {
assert( type == OWL_RELOC_HALF_LO );
// a l^ modifier was specified explicitly
// Do the reloc at the end (lda)
// Doing it in ldah will be wrong.
}
}
++ret;
++buffer;
}
}
if( !hi_reloc_emitted && force_pair ) { // no LDAH has been generated yet
// force_pair => no modifier should've been specified
// We are asked to force one, so here
doOpcodeRaRb( buffer, OPCODE_LDAH, d_reg, s_reg,
_Memory_disp( 0 ) );
doReloc( reloc, op, OWL_RELOC_HALF_HI, buffer );
++buffer;
++ret;
}
base_reg = ( ret == 1 ? s_reg : d_reg );
doOpcodeRaRb( buffer, OPCODE_LDA, d_reg, base_reg,
_Memory_disp( low ) );
if( reloc ) {
doReloc( reloc, op, OWL_RELOC_HALF_LO, buffer );
}
return( ret );
}
#if 0
static unsigned forceLoadAddressComplete( uint_8 d_reg, uint_8 s_reg, ins_operand *op, uint_32 *buffer, asm_reloc *reloc ) {
//***********************************************************************************************************************
/* Generate:
LDAH $d_reg, h^foo($s_reg)
LDA $d_reg, l^foo+c($d_reg)
store them in buffer[0], buffer[1]
*/
doOpcodeRaRb( buffer, OPCODE_LDAH, d_reg, s_reg,
_Memory_disp( 0 ) );
doReloc( reloc, op, OWL_RELOC_HALF_HI, buffer );
buffer++;
doOpcodeRaRb( buffer, OPCODE_LDA, d_reg, d_reg,
_Memory_disp( op->constant ) );
doReloc( reloc, op, OWL_RELOC_HALF_LO, buffer );
return( 2 );
}
#endif
static unsigned load32BitLiteral( uint_32 *buffer, ins_operand *op0, ins_operand *op1, domov_option m_opt ) {
//***********************************************************************************************************
op_const val;
if( m_opt == DOMOV_ABS ) {
val = abs( op0->constant );
} else {
val = op0->constant;
}
return( ldaConst32( buffer, RegIndex( op1->reg ), ZERO_REG_IDX,
op0, val, NULL, FALSE ) );
}
static void doMov( uint_32 *buffer, ins_operand *operands[], domov_option m_opt ) {
//*********************************************************************************
ins_operand *op0, *op1;
uint_32 extra;
uint_32 abs_val;
bool ready = TRUE;
op0 = operands[0];
op1 = operands[1];
if( op0->type == OP_GPR ) {
extra = _Rb( RegIndex( op0->reg ) );
} else if( ( op0->constant & 0xff ) == op0->constant ) { // OP_IMMED implied
extra = _LIT( op0->constant ); // this lit is between 0..255
(void)ensureOpAbsolute( op0, 0 );
} else if( m_opt == DOMOV_ABS &&
( ( ( abs_val = abs( op0->constant ) ) & 0xff ) == abs_val ) ) {
extra = _LIT( abs_val ); // this lit is between 0..255
// ensureOpAbsolute( op0, 0 ); should be done before calling doMov
} else {
ready = FALSE;
}
if( ready ) {
doOpcodeFcRaRc( buffer, OPCODE_BIS, FUNCCODE_BIS,
ZERO_REG_IDX, RegIndex( op1->reg ), extra );
return;
}
// Otherwise it's OP_IMMED with a greater than 8-bit literal.
// We'll then use multiple LDA, LDAH instructions to load the literal.
if( !ensureOpAbsolute( op0, 0 ) ) return;
numExtendedIns += load32BitLiteral( buffer, op0, op1, m_opt ) - 1;
}
static void ITLoadAddress( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**************************************************************************************************
ins_operand *op;
ins_operand *ops[2];
unsigned inc;
op_const val;
uint_8 s_reg;
assert( ins->num_operands == 2 );
assert( table->opcode == OPCODE_LDA );
op = ins->operands[1];
// If op is IMMED foo, it's actually REG_INDIRECT that we want: foo($zero)
if( op->type == OP_IMMED ) {
op->type = OP_REG_INDIRECT;
op->reg = ZERO_REG;
}
assert( op->type == OP_REG_INDIRECT );
val = op->constant;
s_reg = RegIndex( op->reg );
if( !OP_HAS_RELOC( op ) && s_reg == ZERO_REG_IDX ) {
// doMov() can only be called when op->reg is ZERO_REG and no reloc
ops[0] = op;
ops[1] = ins->operands[0];
doMov( buffer, ops, DOMOV_ORIGINAL );
return;
}
if( OP_HAS_RELOC( op ) ) {
#ifndef _STANDALONE_
if( OP_RELOC_NAMED( op ) ) {
char *name;
name = SymName( op->reloc.target.ptr );
if( AsmQueryExternal( name ) == SYM_STACK ) {
doAutoVar( reloc, op->reloc.target, buffer, table, ins );
return;
}
}
#endif
if( op->reloc.type == ASM_RELOC_UNSPECIFIED ) {
// Then we should emit LDA-LDAH pair.
inc = ldaConst32( buffer, RegIndex( ins->operands[0]->reg ),
s_reg, op, op->constant, reloc, TRUE );
numExtendedIns += inc - 1;
return;
}
}
inc = ldaConst32( buffer, RegIndex( ins->operands[0]->reg ), s_reg, op,
op->constant, reloc, FALSE );
numExtendedIns += inc - 1;
/*
doOpcodeRaRb( buffer, table->opcode, RegIndex( ins->operands[0]->reg ),
s_reg, _Memory_disp( val ) );
doReloc( reloc, op, OWL_RELOC_HALF_LO, buffer );*/
}
#if 0
static void ITMemLDAH( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**********************************************************************************************
ins_operand *op;
assert( table->opcode == OPCODE_LDAH );
op = ins->operands[1];
// If op is IMMED foo, it's actually REG_INDIRECT that we want: foo($zero)
if( op->type == OP_IMMED ) {
op->type = OP_REG_INDIRECT;
op->reg = ZERO_REG;
}
assert( op->type == OP_REG_INDIRECT );
doOpcodeRaRb( buffer, table->opcode, RegIndex( ins->operands[0]->reg ),
RegIndex( op->reg ), _Memory_disp( op->constant ) );
doReloc( reloc, op, OWL_RELOC_HALF_HI, buffer );
}
#endif
static void ITMemAll( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*********************************************************************************************
ins_operand *op;
owl_reloc_type type;
ins_opcode opcode;
assert( ins->num_operands == 2 );
op = ins->operands[1];
// If op is IMMED foo, it's actually REG_INDIRECT that we want: foo($zero)
if( op->type == OP_IMMED ) {
op->type = OP_REG_INDIRECT;
op->reg = ZERO_REG;
}
assert( op->type == OP_REG_INDIRECT );
#ifndef _STANDALONE_
if( OP_RELOC_NAMED( op ) ) {
char *name;
name = SymName( op->reloc.target.ptr );
if( AsmQueryExternal( name ) == SYM_STACK ) {
doAutoVar( reloc, op->reloc.target, buffer, table, ins );
return;
}
}
#endif
opcode = table->opcode;
doOpcodeRaRb( buffer, opcode, RegIndex( ins->operands[0]->reg ),
RegIndex( op->reg ), _Memory_disp( op->constant ) );
type = ( opcode == OPCODE_LDAH ) ? OWL_RELOC_HALF_HI : OWL_RELOC_HALF_LO;
doReloc( reloc, op, type, buffer );
}
static void ITMemA( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*******************************************************************************************
assert( ins->num_operands == 1 );
reloc = reloc;
doOpcodeRaRb( buffer, table->opcode, RegIndex( ins->operands[0]->reg ),
31, _Mem_Func( table->funccode ) );
}
static void ITMemB( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*******************************************************************************************
assert( ins->num_operands == 1 );
reloc = reloc;
doOpcodeRaRb( buffer, table->opcode, 31, RegIndex( ins->operands[0]->reg ),
_Mem_Func( table->funccode ) );
}
static void doMemJump( uint_32 *buffer, ins_table *table, uint_8 ra, uint_8 rb, ins_operand *addr_op, uint_32 hint, asm_reloc *reloc ) {
//**************************************************************************************************************************************
// Note that addr_op maybe NULL. If not, addr_op->constant == hint.
assert( addr_op == NULL || addr_op->constant == hint );
assert( addr_op == NULL || addr_op->type == OP_IMMED );
doOpcodeRaRb( buffer, table->opcode, ra, rb,
(table->funccode << 14) | _FourteenBits(hint) );
doReloc( reloc, addr_op, OWL_RELOC_JUMP_REL, buffer );
}
static void opError( instruction *ins, op_type actual, op_type wanted, int i ) {
//******************************************************************************
// Stuff out an error message.
ins = ins;
actual = actual;
wanted = wanted; // it's a set of flags
if( ( wanted & OP_NOTHING ) != OP_NOTHING ) {
Error( OPERAND_INCORRECT, i );
} else {
Error( OPERAND_UNEXPECTED, i );
}
}
static bool opValidate( ot_array *verify, instruction *ins, ins_opcount num_op, unsigned num_var ) {
//**************************************************************************************************
int ctr, var, lasterr;
op_type actual, wanted;
for( var = 0; var < num_var; var++ ) {
lasterr = -1;
for( ctr = 0; ctr < num_op; ctr++ ) {
if( ins->operands[ctr]->type != (*verify)[var][ctr] ) {
lasterr = ctr;
actual = ins->operands[ctr]->type;
wanted = (*verify)[var][ctr];
break;
}
}
if( lasterr == -1 ) { // passed
break;
}
}
if( lasterr != -1 ) {
opError( ins, actual, wanted, lasterr );
return( FALSE );
}
return( TRUE );
}
static bool jmpOperandsValidate( instruction *ins, ins_opcount num_op ) {
//***********************************************************************
// Used by jmp, jsr
static op_type verify1[][3] = { { OP_REG_INDIRECT, OP_NOTHING, OP_NOTHING },
{ OP_IMMED, OP_NOTHING, OP_NOTHING } };
static op_type verify2[][3] = { { OP_GPR, OP_REG_INDIRECT, OP_NOTHING },
{ OP_GPR, OP_IMMED, OP_NOTHING },
{ OP_REG_INDIRECT, OP_IMMED, OP_NOTHING }};
static op_type verify3[][3] = { { OP_GPR, OP_REG_INDIRECT, OP_IMMED } };
ot_array *verify;
ot_array *verify_table[3] = { &verify1, &verify2, &verify3 };
unsigned num_var;
if( num_op == 0 ) return( TRUE );
assert( num_op <= 3 );
verify = verify_table[ num_op - 1 ];
if( num_op == 1 ) {
num_var = sizeof( verify1 ) / sizeof( **verify1 ) / 3;
} else if( num_op == 2 ) {
num_var = sizeof( verify2 ) / sizeof( **verify2 ) / 3;
} else {
num_var = sizeof( verify3 ) / sizeof( **verify3 ) / 3;
}
assert( num_var <= MAX_VARIETIES );
return( opValidate( verify, ins, num_op, num_var ) );
}
static void stdMemJump( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//***********************************************************************************************
ins_operand *op1, *op2;
op1 = ins->operands[1];
if( op1->constant != 0 ) {
Error( IMPROPER_OPERAND, 1 );
return;
}
op2 = ins->operands[2];
doMemJump( buffer, table, RegIndex( ins->operands[0]->reg ),
RegIndex( op1->reg ), op2, op2->constant, reloc );
}
static void ITMemJump( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**********************************************************************************************
ins_operand *op0, *op1;
ins_opcount num_op;
int inc;
uint_8 d_reg_idx; // default d_reg if not specified
num_op = ins->num_operands;
// First check if the operands are of the right types
if( !jmpOperandsValidate( ins, num_op ) ) return;
if( num_op == 3 ) {
stdMemJump( table, ins, buffer, reloc );
return;
}
if( table->funccode == 0x0001 ) { // jsr
// This is according to the MS asaxp documentation.
d_reg_idx = RA_REG_IDX;
} else {
assert( table->funccode == 0x0000 ); // jmp
d_reg_idx = 31; // $zero
}
if( num_op == 2 ) {
if( (op0 = ins->operands[0])->type == OP_GPR ) {
if( (op1 = ins->operands[1])->type == OP_REG_INDIRECT ) {
doMemJump( buffer, table, RegIndex( op0->reg ),
RegIndex( op1->reg ), NULL, 0, reloc );
return;
}
assert( op1->type == OP_IMMED );
if( !_DirIsSet( AT ) ) {
Warning( INS_USES_AT_REG );
}
/* load addr to $at (as s_reg) from op1 */
inc = ldaConst32( buffer, AT_REG_IDX, ZERO_REG_IDX, op1,
op1->constant, reloc, TRUE );
doMemJump( buffer + inc, table, RegIndex( op0->reg ), AT_REG_IDX,
NULL, 0, reloc );
numExtendedIns += inc; // total # of instructions = inc + 1
return;
}
assert( op0->type == OP_REG_INDIRECT );
op1 = ins->operands[1];
assert( op1->type == OP_IMMED );
doMemJump( buffer, table, d_reg_idx, RegIndex( op0->reg ),
op1, op1->constant, reloc );
return;
}
assert( num_op == 1 );
if( (op0 = ins->operands[0])->type == OP_REG_INDIRECT ) {
doMemJump( buffer, table, d_reg_idx, RegIndex( op0->reg ),
NULL, 0, reloc );
return;
}
assert( op0->type == OP_IMMED );
if( !_DirIsSet( AT ) ) {
Warning( INS_USES_AT_REG );
}
/* Gen code to load addr to $at (as s_reg) from op1 */
inc = ldaConst32( buffer, AT_REG_IDX, ZERO_REG_IDX, op0, op0->constant, reloc, TRUE );
doMemJump( buffer + inc, table, d_reg_idx, AT_REG_IDX,
NULL, 0, reloc );
numExtendedIns += inc; // total # of instructions = inc + 1
return;
}
static bool retOperandsValidate( instruction *ins, ins_opcount num_op ) {
//***********************************************************************
// Can be used by ret, jsr_coroutine
static op_type verify1[][3] = { { OP_GPR, OP_NOTHING, OP_NOTHING },
{ OP_IMMED, OP_NOTHING, OP_NOTHING } };
static op_type verify2[][3] = { { OP_GPR, OP_REG_INDIRECT, OP_NOTHING },
{ OP_GPR, OP_IMMED, OP_NOTHING },
{ OP_REG_INDIRECT, OP_IMMED, OP_NOTHING }};
static op_type verify3[][3] = { { OP_GPR, OP_REG_INDIRECT, OP_IMMED } };
ot_array *verify;
ot_array *verify_table[3] = { &verify1, &verify2, &verify3 };
unsigned num_var;
if( num_op == 0 ) return( TRUE );
assert( num_op <= 3 );
verify = verify_table[ num_op - 1 ];
if( num_op == 1 ) {
num_var = sizeof( verify1 ) / sizeof( **verify1 ) / 3;
} else if( num_op == 2 ) {
num_var = sizeof( verify2 ) / sizeof( **verify2 ) / 3;
} else {
num_var = sizeof( verify3 ) / sizeof( **verify3 ) / 3;
}
assert( num_var <= MAX_VARIETIES );
return( opValidate( verify, ins, num_op, num_var ) );
}
static void ITRet( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//******************************************************************************************
// Both ret and jsr coroutine use this
ins_operand *op0, *op1;
ins_opcount num_op;
uint_8 d_reg_idx; // default d_reg if not specified
num_op = ins->num_operands;
// First check if the operands are of the right types
if( !retOperandsValidate( ins, num_op ) ) return;
if( num_op == 3 ) {
stdMemJump( table, ins, buffer, reloc );
return;
}
if( table->funccode == 0x0003 ) { // jsr_coroutine
// This is according to the MS asaxp documentation.
d_reg_idx = RA_REG_IDX;
} else {
assert( table->funccode == 0x0002 ); // ret
d_reg_idx = 31; // $zero
}
if( num_op == 2 ) {
if( (op0 = ins->operands[0])->type == OP_GPR ) {
if( (op1 = ins->operands[1])->type == OP_REG_INDIRECT ) {
doMemJump( buffer, table, RegIndex( op0->reg ),
RegIndex( op1->reg ), NULL, 1, reloc );
return;
}
assert( op1->type == OP_IMMED );
doMemJump( buffer, table, RegIndex( op0->reg ), RA_REG_IDX,
op1, op1->constant, reloc );
return;
}
assert( op0->type == OP_REG_INDIRECT );
op1 = ins->operands[1];
assert( op1->type == OP_IMMED );
doMemJump( buffer, table, d_reg_idx, RegIndex( op0->reg ),
op1, op1->constant, reloc );
return;
}
if( num_op == 1 ) {
if( (op0 = ins->operands[0])->type == OP_GPR ) {
doMemJump( buffer, table, RegIndex( op0->reg ), RA_REG_IDX,
NULL, 1, reloc );
return;
}
assert( op0->type == OP_IMMED );
doMemJump( buffer, table, d_reg_idx, RA_REG_IDX,
op0, op0->constant, reloc );
return;
}
assert( num_op == 0 );
doMemJump( buffer, table, d_reg_idx, RA_REG_IDX, NULL, 1, reloc );
}
static void ITMemNone( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**********************************************************************************************
assert( ins->num_operands == 0 );
reloc = reloc;
doOpcodeRaRb( buffer, table->opcode, 31, 31, _Mem_Func( table->funccode ) );
}
static void ITBranch( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*********************************************************************************************
ins_operand *op;
assert( ins->num_operands == 2 );
op = ins->operands[1];
doOpcodeRaRb( buffer, table->opcode, RegIndex( ins->operands[0]->reg ), 0,
_Branch_disp( _Longword_offset( op->constant ) ) );
doReloc( reloc, op, OWL_RELOC_BRANCH_REL, buffer );
}
static void ITOperate( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**********************************************************************************************
ins_funccode fc;
ins_operand *op;
uint_32 extra;
assert( ins->num_operands == 3 );
reloc = reloc;
op = ins->operands[1];
if( op->type == OP_GPR ) {
extra = _Rb( RegIndex( op->reg ) );
} else { // OP_IMMED
assert( op->type == OP_IMMED );
extra = _LIT( op->constant );
(void)ensureOpAbsolute( op, 1 );
}
fc = getFuncCode( table, ins );
doOpcodeFcRaRc( buffer, table->opcode, fc,
RegIndex( ins->operands[0]->reg ),
RegIndex( ins->operands[2]->reg ), extra );
}
static void ITFPOperate( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
ins_funccode fc;
assert( ins->num_operands == 3 );
reloc = reloc;
fc = getFuncCode( table, ins );
doFPInst( buffer, table->opcode, RegIndex( ins->operands[0]->reg ),
RegIndex( ins->operands[1]->reg ),
RegIndex( ins->operands[2]->reg ), fc );
}
static void ITFPConvert( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
ins_funccode fc;
assert( ins->num_operands == 2 );
reloc = reloc;
fc = getFuncCode( table, ins );
doFPInst( buffer, table->opcode, 31, RegIndex( ins->operands[0]->reg ),
RegIndex( ins->operands[1]->reg ), fc );
}
static void ITBr( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*****************************************************************************************
ins_operand *op0, *op1;
assert( ins->num_operands == 1 || ins->num_operands == 2 );
op0 = ins->operands[0];
if( ins->num_operands == 1 ) {
if( op0->type != OP_IMMED ) {
Error( IMPROPER_OPERAND, 0 );
return;
}
doOpcodeRaRb( buffer, table->opcode, 31, 0,
_Branch_disp( _Longword_offset( op0->constant ) ) );
doReloc( reloc, op0, OWL_RELOC_BRANCH_REL, buffer );
return;
}
// num_operands == 2
op1 = ins->operands[1];
if( op0->type != OP_GPR ) {
Error( IMPROPER_OPERAND, 0 );
return;
}
if( op1->type != OP_IMMED ) {
Error( IMPROPER_OPERAND, 1 );
return;
}
ITBranch( table, ins, buffer, reloc );
}
static void ITMTMFFpcr( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//***********************************************************************************************
assert( ins->num_operands == 1 || ins->num_operands == 3 );
if( ins->num_operands == 1 ) {
uint_8 reg_num;
reg_num = RegIndex( ins->operands[0]->reg );
doFPInst( buffer, table->opcode, reg_num, reg_num, reg_num,
table->funccode );
return;
}
ITFPOperate( table, ins, buffer, reloc );
}
static void ITPseudoClr( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
assert( ins->num_operands == 1 );
reloc = reloc;
doOpcodeFcRaRc( buffer, table->opcode, table->funccode, 31,
RegIndex( ins->operands[0]->reg ), _Rb( 31 ) );
}
static void ITPseudoFclr( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*************************************************************************************************
assert( ins->num_operands == 1 );
reloc = reloc;
doFPInst( buffer, table->opcode, 31, 31,
RegIndex( ins->operands[0]->reg ), table->funccode );
}
static void ITPseudoMov( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
assert( ins->num_operands == 2 );
table = table;
reloc = reloc;
doMov( buffer, ins->operands, DOMOV_ORIGINAL );
}
static void ITPseudoFmov( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*************************************************************************************************
uint_8 reg_idx0;
assert( ins->num_operands == 2 );
reloc = reloc;
reg_idx0 = RegIndex( ins->operands[0]->reg );
doFPInst( buffer, table->opcode, reg_idx0, reg_idx0,
RegIndex( ins->operands[1]->reg ), table->funccode );
}
static void ITPseudoNot( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
ins_funccode fc;
ins_operand *op;
uint_32 extra;
assert( ins->num_operands == 2 );
reloc = reloc;
op = ins->operands[0];
if( op->type == OP_GPR ) {
extra = _Rb( RegIndex( op->reg ) );
} else { // OP_IMMED
assert( op->type == OP_IMMED );
extra = _LIT( op->constant );
(void)ensureOpAbsolute( op, 0 );
}
fc = getFuncCode( table, ins );
doOpcodeFcRaRc( buffer, table->opcode, fc, 31,
RegIndex( ins->operands[1]->reg ), extra );
}
static void ITPseudoNegf( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*************************************************************************************************
ins_funccode fc;
assert( ins->num_operands == 2 );
reloc = reloc;
fc = getFuncCode( table, ins );
doFPInst( buffer, table->opcode, 31, RegIndex( ins->operands[0]->reg ),
RegIndex( ins->operands[1]->reg ), fc );
}
static void ITPseudoFneg( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//*************************************************************************************************
uint_8 reg_idx0;
assert( ins->num_operands == 2 );
reloc = reloc;
reg_idx0 = RegIndex( ins->operands[0]->reg );
doFPInst( buffer, table->opcode, reg_idx0, reg_idx0,
RegIndex( ins->operands[1]->reg ), table->funccode );
}
static void ITPseudoAbs( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//************************************************************************************************
uint_8 s_reg_idx, d_reg_idx, rc_reg_idx;
ins_operand *src_op;
bool same_reg;
assert( ins->num_operands == 2 );
reloc = reloc;
src_op = ins->operands[0];
if( src_op->type == OP_IMMED ) {
// Then just evaluate it and do a mov instead.
// mov abs(src_op->constant), d_reg
if( ensureOpAbsolute( src_op, 0 ) ) {
doMov( buffer, ins->operands, DOMOV_ABS );
}
return;
}
assert( src_op->type == OP_GPR );
/* Emit an instruction sequence like:
1. subq/v $zero, $s_reg, $at // if( $s_reg == $d_reg )
2. cmovlt $s_reg, $at, $s_reg
---or---
1. subq/v $zero, $s_reg, $d_reg // if( $s_reg != $d_reg )
2. cmovgt $s_reg, $s_reg, $d_reg
*/
s_reg_idx = RegIndex( src_op->reg );
d_reg_idx = RegIndex( ins->operands[1]->reg );
if( same_reg = ( s_reg_idx == d_reg_idx ) ) {
// Then $at reg will be needed.
if( !_DirIsSet( AT ) ) {
Warning( INS_USES_AT_REG );
}
rc_reg_idx = AT_REG_IDX;
} else {
rc_reg_idx = d_reg_idx;
}
doOpcodeFcRaRc( buffer, table->opcode, table->funccode, 31,
rc_reg_idx, _Rb( s_reg_idx ) );
// So buffer gets ins #1. Now do ins #2.
++buffer;
#define OPCODE_CMOVGT 0x11
#define OPCODE_CMOVLT 0x11
#define FUNCCODE_CMOVGT 0x0066
#define FUNCCODE_CMOVLT 0x0044
if( same_reg ) {
doOpcodeFcRaRc( buffer, OPCODE_CMOVLT,
FUNCCODE_CMOVLT, s_reg_idx, s_reg_idx,
_Rb( AT_REG_IDX ) );
} else {
doOpcodeFcRaRc( buffer, OPCODE_CMOVGT,
FUNCCODE_CMOVGT, s_reg_idx, d_reg_idx,
_Rb( s_reg_idx ) );
}
++numExtendedIns;
}
static void ITCallPAL( ins_table *table, instruction *ins, uint_32 *buffer, asm_reloc *reloc ) {
//**********************************************************************************************
ins_operand *op;
op_const constant;
uint_32 opcode;
assert( ins->num_operands == 1 );
table = table;
reloc = reloc;
op = ins->operands[0];
(void)ensureOpAbsolute( op, 0 );
constant = op->constant;
opcode = ( constant & 0xfC000000 ) >> 26;
if( opcode != 0x00 &&
opcode != 0x19 &&
opcode != 0x1b &&
opcode != 0x1d &&
opcode != 0x1e &&
opcode != 0x1f ) {
Error( INVALID_PALCODE );
}
*buffer = constant;
}
alpha_format AlphaFormatTable[] = {
#define PICK( a, b, c, d, e ) { b, { c, d, e } },
#include "alphafmt.inc"
#undef PICK
};
bool AlphaValidate( instruction *ins ) {
//**************************************
// Make sure that all operands of the given instruction
// are of the type we are expecting. If not, we print
// out an error message.
int i;
alpha_format *fmt;
ins_operand *op;
fmt = &AlphaFormatTable[ ins->format->table_entry->template ];
for( i = 0; i < ins->num_operands; i++ ) {
op = ins->operands[ i ];
if( ( op->type & fmt->ops[ i ] ) != op->type ) {
opError( ins, op->type, fmt->ops[ i ], i );
return( FALSE );
}
}
if( i < MAX_OPERANDS ) {
if( ( fmt->ops[ i ] & OP_NOTHING ) != OP_NOTHING ) {
Error( NOT_ENOUGH_INSOP );
return( FALSE );
}
} // NOTE: It might not catch all improper operand combinations
// because we're using flags here
return( TRUE );
}
#ifdef _STANDALONE_
static void emitIns( owl_section_handle hdl, char *inscode, int size ) {
//**********************************************************************
ObjEmitData( hdl, inscode, size, TRUE );
}
#else
static void emitIns( char *inscode, int size ) {
//**********************************************
ObjEmitData( inscode, size, TRUE );
}
#endif
#ifdef _STANDALONE_
void AlphaEmit( owl_section_handle hdl, instruction *ins ) {
//**********************************************************
#else
void AlphaEmit( instruction *ins ) {
//**********************************
#endif
// Encode the given instruction (including emitting any
// relocs to the appropriate places), and emit the code
// to the given section.
int ctr;
ins_table *table;
asm_reloc reloc = { NULL, NULL };
reloc_list curr_reloc;
#ifdef _STANDALONE_
uint_8 old_alignment;
if( OWLTellSectionType( hdl ) & OWL_SEC_ATTR_BSS ) {
Error( INVALID_BSS_STATEMENT );
return;
}
old_alignment = CurrAlignment;
if( CurrAlignment < 2 ) { // Instructions should at least be dword aligned
CurrAlignment = 2;
}
#endif
table = ins->format->table_entry;
AlphaFormatTable[ table->template ].func( table, ins, result, &reloc );
for( ctr = 0; ctr <= numExtendedIns; ctr++ ) {
if( ( curr_reloc = reloc.first ) != NULL ) {
assert( curr_reloc->loc >= ctr * sizeof( *result ) );
if( curr_reloc->loc == ctr * sizeof( *result ) ) {
reloc.first = curr_reloc->next;
if( curr_reloc->is_named ) {
#ifdef _STANDALONE_
ObjEmitReloc( hdl, SymName( curr_reloc->target.ptr ),
curr_reloc->type, TRUE, TRUE );
#else
ObjEmitReloc( SymName( curr_reloc->target.ptr ),
curr_reloc->type, TRUE, TRUE );
#endif
} else {
#ifdef _STANDALONE_
ObjEmitReloc( hdl, &curr_reloc->target.label,
curr_reloc->type, TRUE, FALSE );
#else
ObjEmitReloc( &curr_reloc->target.label,
curr_reloc->type, TRUE, FALSE );
#endif
}
#ifndef NDEBUG
switch( curr_reloc->type ) {
case OWL_RELOC_WORD:
_DBGMSG1( "word" ); break;
case OWL_RELOC_HALF_LO:
_DBGMSG1( "l^" ); break;
case OWL_RELOC_HALF_HI:
_DBGMSG1( "h^" ); break;
case OWL_RELOC_BRANCH_REL:
_DBGMSG1( "j^" ); break;
case OWL_RELOC_JUMP_REL:
_DBGMSG1( "jump hint" ); break;
default:
_DBGMSG1( "absolute (shouldn't use)" ); break;
}
_DBGMSG1( " reloc emitted for the instruction.\n" );
#endif
MemFree( curr_reloc );
}
}
#ifdef _STANDALONE_
emitIns( hdl, (char *)&result[ctr], sizeof( uint_32 ) );
#else
emitIns( (char *)&result[ctr], sizeof( uint_32 ) );
#endif
#ifndef NDEBUG
#ifdef _STANDALONE_
if( _IsOption( DUMP_INSTRUCTIONS ) ) {
printf( " [%#010x]\n", result[ctr] );
}
#endif
#endif
}
assert( reloc.first == NULL ); // Should all be emitted already!
reloc.last = NULL;
if( numExtendedIns != 0 ) {
if( !_DirIsSet( MACRO ) ) {
Warning( MACRO_INSTRUCTION );
}
numExtendedIns = 0;
}
#ifdef _STANDALONE_
CurrAlignment = old_alignment;
#endif
}