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  
fnop(void)20  static void fnop(void)
21  {
22  }
23  
fclex(void)24  static 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 */
fpstate_init_soft(struct swregs_state * soft)34  void 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  
finit(void)54  void finit(void)
55  {
56  	fpstate_init_soft(&current->thread.fpu.fpstate->regs.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  
66  static FUNC const finit_table[] = {
67  	feni, fdisi, fclex, finit,
68  	fsetpm, FPU_illegal, FPU_illegal, FPU_illegal
69  };
70  
finit_(void)71  void finit_(void)
72  {
73  	(finit_table[FPU_rm]) ();
74  }
75  
fstsw_ax(void)76  static void fstsw_ax(void)
77  {
78  	*(short *)&FPU_EAX = status_word();
79  	no_ip_update = 1;
80  }
81  
82  static FUNC const fstsw_table[] = {
83  	fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
84  	FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
85  };
86  
fstsw_(void)87  void fstsw_(void)
88  {
89  	(fstsw_table[FPU_rm]) ();
90  }
91  
92  static FUNC const fp_nop_table[] = {
93  	fnop, FPU_illegal, FPU_illegal, FPU_illegal,
94  	FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
95  };
96  
fp_nop(void)97  void fp_nop(void)
98  {
99  	(fp_nop_table[FPU_rm]) ();
100  }
101  
fld_i_(void)102  void 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  
fxch_i(void)130  void 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  
fcmovCC(void)173  static 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  
fcmovb(void)195  void fcmovb(void)
196  {
197  	if (FPU_EFLAGS & X86_EFLAGS_CF)
198  		fcmovCC();
199  }
200  
fcmove(void)201  void fcmove(void)
202  {
203  	if (FPU_EFLAGS & X86_EFLAGS_ZF)
204  		fcmovCC();
205  }
206  
fcmovbe(void)207  void fcmovbe(void)
208  {
209  	if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))
210  		fcmovCC();
211  }
212  
fcmovu(void)213  void fcmovu(void)
214  {
215  	if (FPU_EFLAGS & X86_EFLAGS_PF)
216  		fcmovCC();
217  }
218  
fcmovnb(void)219  void fcmovnb(void)
220  {
221  	if (!(FPU_EFLAGS & X86_EFLAGS_CF))
222  		fcmovCC();
223  }
224  
fcmovne(void)225  void fcmovne(void)
226  {
227  	if (!(FPU_EFLAGS & X86_EFLAGS_ZF))
228  		fcmovCC();
229  }
230  
fcmovnbe(void)231  void fcmovnbe(void)
232  {
233  	if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)))
234  		fcmovCC();
235  }
236  
fcmovnu(void)237  void fcmovnu(void)
238  {
239  	if (!(FPU_EFLAGS & X86_EFLAGS_PF))
240  		fcmovCC();
241  }
242  
ffree_(void)243  void ffree_(void)
244  {
245  	/* ffree st(i) */
246  	FPU_settagi(FPU_rm, TAG_Empty);
247  }
248  
ffreep(void)249  void ffreep(void)
250  {
251  	/* ffree st(i) + pop - unofficial code */
252  	FPU_settagi(FPU_rm, TAG_Empty);
253  	FPU_pop();
254  }
255  
fst_i_(void)256  void fst_i_(void)
257  {
258  	/* fst st(i) */
259  	FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
260  }
261  
fstp_i(void)262  void fstp_i(void)
263  {
264  	/* fstp st(i) */
265  	FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
266  	FPU_pop();
267  }
268