xref: /linux/arch/x86/math-emu/fpu_aux.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
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 
20 static void fnop(void)
21 {
22 }
23 
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 */
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 
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 
71 void finit_(void)
72 {
73 	(finit_table[FPU_rm]) ();
74 }
75 
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 
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 
97 void fp_nop(void)
98 {
99 	(fp_nop_table[FPU_rm]) ();
100 }
101 
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 
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 
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 
195 void fcmovb(void)
196 {
197 	if (FPU_EFLAGS & X86_EFLAGS_CF)
198 		fcmovCC();
199 }
200 
201 void fcmove(void)
202 {
203 	if (FPU_EFLAGS & X86_EFLAGS_ZF)
204 		fcmovCC();
205 }
206 
207 void fcmovbe(void)
208 {
209 	if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))
210 		fcmovCC();
211 }
212 
213 void fcmovu(void)
214 {
215 	if (FPU_EFLAGS & X86_EFLAGS_PF)
216 		fcmovCC();
217 }
218 
219 void fcmovnb(void)
220 {
221 	if (!(FPU_EFLAGS & X86_EFLAGS_CF))
222 		fcmovCC();
223 }
224 
225 void fcmovne(void)
226 {
227 	if (!(FPU_EFLAGS & X86_EFLAGS_ZF))
228 		fcmovCC();
229 }
230 
231 void fcmovnbe(void)
232 {
233 	if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)))
234 		fcmovCC();
235 }
236 
237 void fcmovnu(void)
238 {
239 	if (!(FPU_EFLAGS & X86_EFLAGS_PF))
240 		fcmovCC();
241 }
242 
243 void ffree_(void)
244 {
245 	/* ffree st(i) */
246 	FPU_settagi(FPU_rm, TAG_Empty);
247 }
248 
249 void ffreep(void)
250 {
251 	/* ffree st(i) + pop - unofficial code */
252 	FPU_settagi(FPU_rm, TAG_Empty);
253 	FPU_pop();
254 }
255 
256 void fst_i_(void)
257 {
258 	/* fst st(i) */
259 	FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm);
260 }
261 
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