/****************************************************************************
*
*                            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:  FP fixups for 16-bit code. These fixups allow the linker
* or program loader to replace FP instructions by calls to an FP emulation
* library.
*
****************************************************************************/

#include "globals.h"
#include "parser.h"
#include "extern.h"
#include "fixup.h"
#include "mangle.h"
#include "myassert.h"
#include "segment.h"
#include "omf.h"
#include "omfspec.h"

enum fp_patches {
    FPP_WAIT,
    FPP_NORMAL,
    FPP_ES, /* last 6 entries match order of ASSUME_ES, ... */
    FPP_CS,
    FPP_SS,
    FPP_DS,
    FPP_FS,
    FPP_GS
};

/* FP 16-bit fixup names.
 * Known by MS VC, Open Watcom, Borland and Digital Mars:
 *  FIWRQQ, FIDRQQ, FIERQQ, FICRQQ, FISRQQ, FIARQQ, FIFRQQ, FIGRQQ,
 *                          FJCRQQ, FJSRQQ, FJARQQ, FJFRQQ, FJGRQQ
 */

static const uint_16 patchmask = 0xF8FF;

static const char patchchr2[] = {
    'W', 'D', 'E', 'C', 'S', 'A', 'F', 'G'
};

void AddFloatingPointEmulationFixup( struct code_info *CodeInfo )
/***************************************************************/
{
    int i;
    enum fp_patches patch;
    struct asym *sym[2];
    struct fixup *fixup;
    int_32 data;
    char name[8] = "F__RQQ";

    DebugMsg(("AddFloatingPointEmulationFixup enter, token=%u, regoverride=%d\n", CodeInfo->token, CodeInfo->prefix.RegOverride ));

    if( CodeInfo->token == T_FWAIT ) {
        patch = FPP_WAIT;
    } else if ( CodeInfo->prefix.RegOverride == EMPTY ) {
        patch = FPP_NORMAL;
    } else {
        patch = CodeInfo->prefix.RegOverride + 2;
    }

    /* emit 1-2 externals for the patch if not done already */
    for ( i = 0; i < 2; i++ ) {
        sym[i] = NULL;
        if ( patchmask & ( 1 << ( i*8+patch ) ) ) {
            name[1] = 'I' + i;
            name[2] = patchchr2[patch];
            sym[i] = SymSearch( name );
            if( sym[i] == NULL || sym[i]->state == SYM_UNDEFINED ) {
                sym[i] = MakeExtern( name, MT_FAR, NULL, sym[i], USE16 );
                sym[i]->langtype = LANG_NONE;
            }
        }
    }

    /* no need for fixups if no object file is written */
    if ( write_to_file == FALSE )
        return;

    /* make sure the next 3 bytes in code stream aren't separated.
     * The first fixup covers bytes $+0 and $+1, the (possible) second
     * fixup covers bytes $+1 and $+2.
     */
    if( Options.output_format == OFORMAT_OMF &&
       ( CurrSeg->e.seginfo->current_loc - CurrSeg->e.seginfo->start_loc + 3 ) > MAX_LEDATA_THRESHOLD )
        omf_FlushCurrSeg();

    for ( i = 0; i < 2 ; i++ ) {
        if ( sym[i] ) {
            fixup = FixupCreate( sym[i], FIX_OFF16, OPTJ_NONE );
            fixup->frame_type = FRAME_TARG;
            /* assume locofs has been set inside FixupCreate() */
            //fixup->locofs = CurrSeg->e.seginfo->current_loc + i;
            fixup->locofs += i;
            data = 0;
            store_fixup( fixup, CurrSeg, &data );
        }
    }
    return;
}
