linux/arch/x86/math-emu/fpu_aux.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*---------------------------------------------------------------------------+
   3 |  fpu_aux.c                                                                |
   4 |                                                                           |
   5 | Code to implement some of the FPU auxiliary instructions.                 |
   6 |                                                                           |
   7 | Copyright (C) 1992,1993,1994,1997                                         |
   8 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   9 |                  E-mail   billm@suburbia.net                              |
  10 |                                                                           |
  11 |                                                                           |
  12 +---------------------------------------------------------------------------*/
  13
  14#include "fpu_system.h"
  15#include "exception.h"
  16#include "fpu_emu.h"
  17#include "status_w.h"
  18#include "control_w.h"
  19
  20static void fnop(void)
  21{
  22}
  23
  24static void fclex(void)
  25{
  26        partial_status &=
  27            ~(SW_Backward | SW_Summary | SW_Stack_Fault | SW_Precision |
  28              SW_Underflow | SW_Overflow | SW_Zero_Div | SW_Denorm_Op |
  29              SW_Invalid);
  30        no_ip_update = 1;
  31}
  32
  33/* Needs to be externally visible */
  34void fpstate_init_soft(struct swregs_state *soft)
  35{
  36        struct address *oaddr, *iaddr;
  37        memset(soft, 0, sizeof(*soft));
  38        soft->cwd = 0x037f;
  39        soft->swd = 0;
  40        soft->ftop = 0; /* We don't keep top in the status word internally. */
  41        soft->twd = 0xffff;
  42        /* The behaviour is different from that detailed in
  43           Section 15.1.6 of the Intel manual */
  44        oaddr = (struct address *)&soft->foo;
  45        oaddr->offset = 0;
  46        oaddr->selector = 0;
  47        iaddr = (struct address *)&soft->fip;
  48        iaddr->offset = 0;
  49        iaddr->selector = 0;
  50        iaddr->opcode = 0;
  51        soft->no_update = 1;
  52}
  53
  54void finit(void)
  55{
  56        fpstate_init_soft(&current->thread.fpu.state.soft);
  57}
  58
  59/*
  60 * These are nops on the i387..
  61 */
  62#define feni fnop
  63#define fdisi fnop
  64#define fsetpm fnop
  65
  66static FUNC const finit_table[] = {
  67        feni, fdisi, fclex, finit,
  68        fsetpm, FPU_illegal, FPU_illegal, FPU_illegal
  69};
  70
  71void finit_(void)
  72{
  73        (finit_table[FPU_rm]) ();
  74}
  75
  76static void fstsw_ax(void)
  77{
  78        *(short *)&FPU_EAX = status_word();
  79        no_ip_update = 1;
  80}
  81
  82static FUNC const fstsw_table[] = {
  83        fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
  84        FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
  85};
  86
  87void fstsw_(void)
  88{
  89        (fstsw_table[FPU_rm]) ();
  90}
  91
  92static FUNC const fp_nop_table[] = {
  93        fnop, FPU_illegal, FPU_illegal, FPU_illegal,
  94        FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
  95};
  96
  97void fp_nop(void)
  98{
  99        (fp_nop_table[FPU_rm]) ();
 100}
 101
 102void fld_i_(void)
 103{
 104        FPU_REG *st_new_ptr;
 105        int i;
 106        u_char tag;
 107
 108        if (STACK_OVERFLOW) {
 109                FPU_stack_overflow();
 110                return;
 111        }
 112
 113        /* fld st(i) */
 114        i = FPU_rm;
 115        if (NOT_EMPTY(i)) {
 116                reg_copy(&st(i), st_new_ptr);
 117                tag = FPU_gettagi(i);
 118                push();
 119                FPU_settag0(tag);
 120        } else {
 121                if (control_word & CW_Invalid) {
 122                        /* The masked response */
 123                        FPU_stack_underflow();
 124                } else
 125                        EXCEPTION(EX_StackUnder);
 126        }
 127
 128}
 129
 130void fxch_i(void)
 131{
 132        /* fxch st(i) */
 133        FPU_REG t;
 134        int i = FPU_rm;
 135        FPU_REG *st0_ptr = &st(0), *sti_ptr = &st(i);
 136        long tag_word = fpu_tag_word;
 137        int regnr = top & 7, regnri = ((regnr + i) & 7);
 138        u_char st0_tag = (tag_word >> (regnr * 2)) & 3;
 139        u_char sti_tag = (tag_word >> (regnri * 2)) & 3;
 140
 141        if (st0_tag == TAG_Empty) {
 142                if (sti_tag == TAG_Empty) {
 143                        FPU_stack_underflow();
 144                        FPU_stack_underflow_i(i);
 145                        return;
 146                }
 147                if (control_word & CW_Invalid) {
 148                        /* Masked response */
 149                        FPU_copy_to_reg0(sti_ptr, sti_tag);
 150                }
 151                FPU_stack_underflow_i(i);
 152                return;
 153        }
 154        if (sti_tag == TAG_Empty) {
 155                if (control_word & CW_Invalid) {
 156                        /* Masked response */
 157                        FPU_copy_to_regi(st0_ptr, st0_tag, i);
 158                }
 159                FPU_stack_underflow();
 160                return;
 161        }
 162        clear_C1();
 163
 164        reg_copy(st0_ptr, &t);
 165        reg_copy(sti_ptr, st0_ptr);
 166        reg_copy(&t, sti_ptr);
 167
 168        tag_word &= ~(3 << (regnr * 2)) & ~(3 << (regnri * 2));
 169        tag_word |= (sti_tag << (regnr * 2)) | (st0_tag << (regnri * 2));
 170        fpu_tag_word = tag_word;
 171}
 172
 173static void fcmovCC(void)
 174{
 175        /* fcmovCC st(i) */
 176        int i = FPU_rm;
 177        FPU_REG *st0_ptr = &st(0);
 178        FPU_REG *sti_ptr = &st(i);
 179        long tag_word = fpu_tag_word;
 180        int regnr = top & 7;
 181        int regnri = (top + i) & 7;
 182        u_char sti_tag = (tag_word >> (regnri * 2)) & 3;
 183
 184        if (sti_tag == TAG_Empty) {
 185                FPU_stack_underflow();
 186                clear_C1();
 187                return;
 188        }
 189        reg_copy(sti_ptr, st0_ptr);
 190        tag_word &= ~(3 << (regnr * 2));
 191        tag_word |= (sti_tag << (regnr * 2));
 192        fpu_tag_word = tag_word;
 193}
 194
 195void fcmovb(void)
 196{
 197        if (FPU_EFLAGS & X86_EFLAGS_CF)
 198                fcmovCC();
 199}
 200
 201void fcmove(void)
 202{
 203        if (FPU_EFLAGS & X86_EFLAGS_ZF)
 204                fcmovCC();
 205}
 206
 207void fcmovbe(void)
 208{
 209        if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))
 210                fcmovCC();
 211}
 212
 213void fcmovu(void)
 214{
 215        if (FPU_EFLAGS & X86_EFLAGS_PF)
 216                fcmovCC();
 217}
 218
 219void fcmovnb(void)
 220{
 221        if (!(FPU_EFLAGS & X86_EFLAGS_CF))
 222                fcmovCC();
 223}
 224
 225void fcmovne(void)
 226{
 227        if (!(FPU_EFLAGS & X86_EFLAGS_ZF))
 228                fcmovCC();
 229}
 230
 231void fcmovnbe(void)
 232{
 233        if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)))
 234                fcmovCC();
 235}
 236
 237void fcmovnu(void)
 238{
 239        if (!(FPU_EFLAGS & X86_EFLAGS_PF))
 240                fcmovCC();
 241}
 242
 243void ffree_(void)
 244{
 245        /* ffree st(i) */
 246        FPU_settagi(FPU_rm, TAG_Empty);
 247}
 248
 249void ffreep(void)
 250{
 251        /* ffree st(i) + pop - unofficial code */
 252        FPU_settagi(FPU_rm, TAG_Empty);
 253        FPU_pop();
 254}
 255
 256void fst_i_(void)
 257{
 258        /* fst st(i) */
 259        FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
 260}
 261
 262void fstp_i(void)
 263{
 264        /* fstp st(i) */
 265        FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
 266        FPU_pop();
 267}
 268