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(¤t->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(x: &st(i), y: st_new_ptr); |
117 | tag = FPU_gettagi(stnr: 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(r: sti_ptr, tag: 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(r: st0_ptr, tag: st0_tag, stnr: i); |
158 | } |
159 | FPU_stack_underflow(); |
160 | return; |
161 | } |
162 | clear_C1(); |
163 | |
164 | reg_copy(x: st0_ptr, y: &t); |
165 | reg_copy(x: sti_ptr, y: st0_ptr); |
166 | reg_copy(x: &t, y: 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(x: sti_ptr, y: 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(r: &st(0), tag: FPU_gettag0(), FPU_rm); |
260 | } |
261 | |
262 | void fstp_i(void) |
263 | { |
264 | /* fstp st(i) */ |
265 | FPU_copy_to_regi(r: &st(0), tag: FPU_gettag0(), FPU_rm); |
266 | FPU_pop(); |
267 | } |
268 | |