/****************************************************************************
*
*                            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:  WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
*               DESCRIBE IT HERE!
*
****************************************************************************/


#include "as.h"

static dir_operand *dirOpAlloc( void ) {
//**************************************

    return( MemAlloc( sizeof( dir_operand ) ) );
}

static void dirOpFree( dir_operand *dirop ) {
//*******************************************

    MemFree( dirop );
}

static bool dirOpGetNumber( dir_operand *dirop, expr_tree *expr ) {
//*****************************************************************
// Fills out the content and type fields of the operand.
// Can be used to fill out the operand or just to evaluate the expression.

    bool    status = TRUE;

    assert( expr != NULL );
    assert( dirop != NULL );
    expr = ETBurn( expr );
    switch( expr->type ) {
    case ET_CONSTANT:
        NUMBER_INTEGER( dirop ) = CONST_VALUE( expr );
        dirop->type = DIROP_INTEGER;
        break;
    case ET_FP_CONSTANT:
        NUMBER_FLOAT( dirop ) = FP_CONST_VALUE( expr );
        dirop->type = DIROP_FLOATING;
        break;
    default:
        Error( IMPROPERLY_FORMED_DIROP );
        status = FALSE;
    }
    ETFree( expr );
    return( status );
}

extern dir_operand *DirOpLine( const char *string ) {
//***************************************************

    dir_operand *dirop;

    dirop = dirOpAlloc();
    dirop->next = NULL;
    dirop->type = DIROP_LINE;
    STRING_CONTENT( dirop ) = AsStrdup( string );
    return( dirop );
}

extern dir_operand *DirOpString( const char *string ) {
//*****************************************************

    dir_operand *dirop;

    dirop = dirOpAlloc();
    dirop->next = NULL;
    dirop->type = DIROP_STRING;
    STRING_CONTENT( dirop ) = AsStrdup( string );
    return( dirop );
}

extern dir_operand *DirOpNumber( expr_tree *expr ) {
//**************************************************

    dir_operand *dirop;

    assert( expr != NULL );
    dirop = dirOpAlloc();
    dirop->next = NULL;
    if( !dirOpGetNumber( dirop, expr ) ) {
        dirop->type = DIROP_ERROR;
    }
    return( dirop );
}

static dir_operand *doDirOpSymbol( asm_reloc_type rtype, void *target, expr_tree *expr, int sign, bool is_named ) {
//*****************************************************************************************************************
// Does both named and unnamed symbols

    dir_operand *dirop, *tmp;

    dirop = dirOpAlloc();
    dirop->next = NULL;
    if( is_named ) {
        dirop->type = DIROP_SYMBOL;
        SYMBOL_HANDLE( dirop ) = target;
    } else {
        dirop->type = DIROP_NUMLABEL_REF;
        SYMBOL_LABEL_REF( dirop ) = *(int_32 *)target;
    }
    SYMBOL_RELOC_TYPE( dirop ) = rtype;
    if( expr ) {
        tmp = dirOpAlloc();
        if( !dirOpGetNumber( tmp, expr ) || tmp->type == DIROP_FLOATING ) {
            dirop->type = DIROP_ERROR;
        } else {
            SYMBOL_OFFSET( dirop ) = sign * NUMBER_INTEGER( tmp );
        }
        dirOpFree( tmp );
    } else {
        SYMBOL_OFFSET( dirop ) = 0;
    }
    return( dirop );
}

extern dir_operand *DirOpIdentifier( asm_reloc_type rtype, sym_handle symbol, expr_tree *expr, int sign ) {
//*********************************************************************************************************

    return( doDirOpSymbol( rtype, symbol, expr, sign, TRUE ) );
}

extern dir_operand *DirOpNumLabelRef( asm_reloc_type rtype, int_32 label_ref, expr_tree *expr, int sign ) {
//*********************************************************************************************************

    return( doDirOpSymbol( rtype, &label_ref, expr, sign, FALSE ) );
}

extern dir_operand *DirOpRepeat( expr_tree *number, expr_tree *repeat ) {
//***********************************************************************

    dir_operand *dirop, *tmp;

    assert( number != NULL && repeat != NULL );
    dirop = dirOpAlloc();
    dirop->next = NULL;
    tmp = dirOpAlloc();     // use it to transfer type and content data
    if( dirOpGetNumber( tmp, number ) ) {
        REPEAT_NUMBER( dirop ) = tmp->content.number;
        switch( tmp->type ) {
        case DIROP_INTEGER:
            dirop->type = DIROP_REP_INT;
            break;
        case DIROP_FLOATING:
            dirop->type = DIROP_REP_FLT;
            break;
        default:
            assert( FALSE );
        }
        if( dirOpGetNumber( tmp, repeat ) ) {
            if( tmp->type != DIROP_INTEGER ) {
                // Error: invalid repeat constant
                dirop->type = DIROP_ERROR;
            } else if( NUMBER_INTEGER( tmp ) < 0 ) {
                dirop->type = DIROP_ERROR;
            } else {
                REPEAT_COUNT( dirop ) = NUMBER_INTEGER( tmp );
            }
        } else {
            dirop->type = DIROP_ERROR;
        }
    } else {
        dirop->type = DIROP_ERROR;
    }
    dirOpFree( tmp );
    return( dirop );
}

extern void DirOpDestroy( dir_operand *dirop ) {
//**********************************************

    if( !dirop ) return;
    if( dirop->type == DIROP_LINE || dirop->type == DIROP_STRING ) {
        MemFree( STRING_CONTENT( dirop ) );
    }
    dirOpFree( dirop );
}
