1 /*
2 * Copyright (C) 2002-2021 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19
20 #include "dosbox.h"
21 #if C_FPU
22
23 #include <math.h>
24 #include <float.h>
25 #include "cross.h"
26 #include "mem.h"
27 #include "fpu.h"
28 #include "cpu.h"
29
30 FPU_rec fpu;
31
FPU_FLDCW(PhysPt addr)32 void FPU_FLDCW(PhysPt addr){
33 Bit16u temp = mem_readw(addr);
34 FPU_SetCW(temp);
35 }
36
FPU_GetTag(void)37 Bit16u FPU_GetTag(void){
38 Bit16u tag=0;
39 for(Bitu i=0;i<8;i++)
40 tag |= ( (fpu.tags[i]&3) <<(2*i));
41 return tag;
42 }
43
44 #if C_FPU_X86
45 #include "fpu_instructions_x86.h"
46 #else
47 #include "fpu_instructions.h"
48 #endif
49
50 /* WATCHIT : ALWAYS UPDATE REGISTERS BEFORE AND AFTER USING THEM
51 STATUS WORD => FPU_SET_TOP(TOP) BEFORE a read
52 TOP=FPU_GET_TOP() after a write;
53 */
54
EATREE(Bitu _rm)55 static void EATREE(Bitu _rm){
56 Bitu group=(_rm >> 3) & 7;
57 switch(group){
58 case 0x00: /* FADD */
59 FPU_FADD_EA(TOP);
60 break;
61 case 0x01: /* FMUL */
62 FPU_FMUL_EA(TOP);
63 break;
64 case 0x02: /* FCOM */
65 FPU_FCOM_EA(TOP);
66 break;
67 case 0x03: /* FCOMP */
68 FPU_FCOM_EA(TOP);
69 FPU_FPOP();
70 break;
71 case 0x04: /* FSUB */
72 FPU_FSUB_EA(TOP);
73 break;
74 case 0x05: /* FSUBR */
75 FPU_FSUBR_EA(TOP);
76 break;
77 case 0x06: /* FDIV */
78 FPU_FDIV_EA(TOP);
79 break;
80 case 0x07: /* FDIVR */
81 FPU_FDIVR_EA(TOP);
82 break;
83 default:
84 break;
85 }
86 }
87
FPU_ESC0_EA(Bitu rm,PhysPt addr)88 void FPU_ESC0_EA(Bitu rm,PhysPt addr) {
89 /* REGULAR TREE WITH 32 BITS REALS */
90 FPU_FLD_F32_EA(addr);
91 EATREE(rm);
92 }
93
FPU_ESC0_Normal(Bitu rm)94 void FPU_ESC0_Normal(Bitu rm) {
95 Bitu group=(rm >> 3) & 7;
96 Bitu sub=(rm & 7);
97 switch (group){
98 case 0x00: /* FADD ST,STi */
99 FPU_FADD(TOP,STV(sub));
100 break;
101 case 0x01: /* FMUL ST,STi */
102 FPU_FMUL(TOP,STV(sub));
103 break;
104 case 0x02: /* FCOM STi */
105 FPU_FCOM(TOP,STV(sub));
106 break;
107 case 0x03: /* FCOMP STi */
108 FPU_FCOM(TOP,STV(sub));
109 FPU_FPOP();
110 break;
111 case 0x04: /* FSUB ST,STi */
112 FPU_FSUB(TOP,STV(sub));
113 break;
114 case 0x05: /* FSUBR ST,STi */
115 FPU_FSUBR(TOP,STV(sub));
116 break;
117 case 0x06: /* FDIV ST,STi */
118 FPU_FDIV(TOP,STV(sub));
119 break;
120 case 0x07: /* FDIVR ST,STi */
121 FPU_FDIVR(TOP,STV(sub));
122 break;
123 default:
124 break;
125 }
126 }
127
FPU_ESC1_EA(Bitu rm,PhysPt addr)128 void FPU_ESC1_EA(Bitu rm,PhysPt addr) {
129 // floats
130 Bitu group=(rm >> 3) & 7;
131 Bitu sub=(rm & 7);
132 switch(group){
133 case 0x00: /* FLD float*/
134 FPU_PREP_PUSH();
135 FPU_FLD_F32(addr,TOP);
136 break;
137 case 0x01: /* UNKNOWN */
138 FPU_LOG_WARN(1,true,group,sub);
139 break;
140 case 0x02: /* FST float*/
141 FPU_FST_F32(addr);
142 break;
143 case 0x03: /* FSTP float*/
144 FPU_FST_F32(addr);
145 FPU_FPOP();
146 break;
147 case 0x04: /* FLDENV */
148 FPU_FLDENV(addr);
149 break;
150 case 0x05: /* FLDCW */
151 FPU_FLDCW(addr);
152 break;
153 case 0x06: /* FSTENV */
154 FPU_FSTENV(addr);
155 break;
156 case 0x07: /* FNSTCW*/
157 mem_writew(addr,fpu.cw);
158 break;
159 default:
160 FPU_LOG_WARN(1,true,group,sub);
161 break;
162 }
163 }
164
FPU_ESC1_Normal(Bitu rm)165 void FPU_ESC1_Normal(Bitu rm) {
166 Bitu group=(rm >> 3) & 7;
167 Bitu sub=(rm & 7);
168 switch (group){
169 case 0x00: /* FLD STi */
170 {
171 Bitu reg_from=STV(sub);
172 FPU_PREP_PUSH();
173 FPU_FST(reg_from, TOP);
174 break;
175 }
176 case 0x01: /* FXCH STi */
177 FPU_FXCH(TOP,STV(sub));
178 break;
179 case 0x02: /* FNOP */
180 FPU_FNOP();
181 break;
182 case 0x03: /* FSTP STi */
183 FPU_FST(TOP,STV(sub));
184 FPU_FPOP();
185 break;
186 case 0x04:
187 switch(sub){
188 case 0x00: /* FCHS */
189 FPU_FCHS();
190 break;
191 case 0x01: /* FABS */
192 FPU_FABS();
193 break;
194 case 0x02: /* UNKNOWN */
195 case 0x03: /* ILLEGAL */
196 FPU_LOG_WARN(1,false,group,sub);
197 break;
198 case 0x04: /* FTST */
199 FPU_FTST();
200 break;
201 case 0x05: /* FXAM */
202 FPU_FXAM();
203 break;
204 case 0x06: /* FTSTP (cyrix)*/
205 case 0x07: /* UNKNOWN */
206 FPU_LOG_WARN(1,false,group,sub);
207 break;
208 }
209 break;
210 case 0x05:
211 switch(sub){
212 case 0x00: /* FLD1 */
213 FPU_FLD1();
214 break;
215 case 0x01: /* FLDL2T */
216 FPU_FLDL2T();
217 break;
218 case 0x02: /* FLDL2E */
219 FPU_FLDL2E();
220 break;
221 case 0x03: /* FLDPI */
222 FPU_FLDPI();
223 break;
224 case 0x04: /* FLDLG2 */
225 FPU_FLDLG2();
226 break;
227 case 0x05: /* FLDLN2 */
228 FPU_FLDLN2();
229 break;
230 case 0x06: /* FLDZ*/
231 FPU_FLDZ();
232 break;
233 case 0x07: /* ILLEGAL */
234 FPU_LOG_WARN(1,false,group,sub);
235 break;
236 }
237 break;
238 case 0x06:
239 switch(sub){
240 case 0x00: /* F2XM1 */
241 FPU_F2XM1();
242 break;
243 case 0x01: /* FYL2X */
244 FPU_FYL2X();
245 break;
246 case 0x02: /* FPTAN */
247 FPU_FPTAN();
248 break;
249 case 0x03: /* FPATAN */
250 FPU_FPATAN();
251 break;
252 case 0x04: /* FXTRACT */
253 FPU_FXTRACT();
254 break;
255 case 0x05: /* FPREM1 */
256 FPU_FPREM1();
257 break;
258 case 0x06: /* FDECSTP */
259 TOP = (TOP - 1) & 7;
260 break;
261 case 0x07: /* FINCSTP */
262 TOP = (TOP + 1) & 7;
263 break;
264 default:
265 FPU_LOG_WARN(1,false,group,sub);
266 break;
267 }
268 break;
269 case 0x07:
270 switch(sub){
271 case 0x00: /* FPREM */
272 FPU_FPREM();
273 break;
274 case 0x01: /* FYL2XP1 */
275 FPU_FYL2XP1();
276 break;
277 case 0x02: /* FSQRT */
278 FPU_FSQRT();
279 break;
280 case 0x03: /* FSINCOS */
281 FPU_FSINCOS();
282 break;
283 case 0x04: /* FRNDINT */
284 FPU_FRNDINT();
285 break;
286 case 0x05: /* FSCALE */
287 FPU_FSCALE();
288 break;
289 case 0x06: /* FSIN */
290 FPU_FSIN();
291 break;
292 case 0x07: /* FCOS */
293 FPU_FCOS();
294 break;
295 default:
296 FPU_LOG_WARN(1,false,group,sub);
297 break;
298 }
299 break;
300 default:
301 FPU_LOG_WARN(1,false,group,sub);
302 break;
303 }
304 }
305
306
FPU_ESC2_EA(Bitu rm,PhysPt addr)307 void FPU_ESC2_EA(Bitu rm,PhysPt addr) {
308 /* 32 bits integer operants */
309 FPU_FLD_I32_EA(addr);
310 EATREE(rm);
311 }
312
FPU_ESC2_Normal(Bitu rm)313 void FPU_ESC2_Normal(Bitu rm) {
314 Bitu group=(rm >> 3) & 7;
315 Bitu sub=(rm & 7);
316 switch(group){
317 case 0x05:
318 switch(sub){
319 case 0x01: /* FUCOMPP */
320 FPU_FUCOM(TOP,STV(1));
321 FPU_FPOP();
322 FPU_FPOP();
323 break;
324 default:
325 FPU_LOG_WARN(2,false,group,sub);
326 break;
327 }
328 break;
329 default:
330 FPU_LOG_WARN(2,false,group,sub);
331 break;
332 }
333 }
334
335
FPU_ESC3_EA(Bitu rm,PhysPt addr)336 void FPU_ESC3_EA(Bitu rm,PhysPt addr) {
337 Bitu group=(rm >> 3) & 7;
338 Bitu sub=(rm & 7);
339 switch(group){
340 case 0x00: /* FILD */
341 FPU_PREP_PUSH();
342 FPU_FLD_I32(addr,TOP);
343 break;
344 case 0x01: /* FISTTP */
345 FPU_LOG_WARN(3,true,group,sub);
346 break;
347 case 0x02: /* FIST */
348 FPU_FST_I32(addr);
349 break;
350 case 0x03: /* FISTP */
351 FPU_FST_I32(addr);
352 FPU_FPOP();
353 break;
354 case 0x05: /* FLD 80 Bits Real */
355 FPU_PREP_PUSH();
356 FPU_FLD_F80(addr);
357 break;
358 case 0x07: /* FSTP 80 Bits Real */
359 FPU_FST_F80(addr);
360 FPU_FPOP();
361 break;
362 default:
363 FPU_LOG_WARN(3,true,group,sub);
364 break;
365 }
366 }
367
FPU_ESC3_Normal(Bitu rm)368 void FPU_ESC3_Normal(Bitu rm) {
369 const auto group = static_cast<unsigned>((rm >> 3) & 7);
370 const auto sub = static_cast<unsigned>(rm & 7);
371 switch (group) {
372 case 0x04:
373 switch (sub) {
374 case 0x00: //FNENI
375 case 0x01: //FNDIS
376 LOG(LOG_FPU,LOG_ERROR)("8087 only fpu code used esc 3: group 4: subfunction: %u", sub);
377 break;
378 case 0x02: //FNCLEX FCLEX
379 FPU_FCLEX();
380 break;
381 case 0x03: //FNINIT FINIT
382 FPU_FINIT();
383 break;
384 case 0x04: //FNSETPM
385 case 0x05: //FRSTPM
386 // LOG(LOG_FPU,LOG_ERROR)("80267 protected mode (un)set. Nothing done");
387 FPU_FNOP();
388 break;
389 default:
390 E_Exit("ESC 3: ILLEGAL OPCODE group %u subfunction %u", group, sub);
391 }
392 break;
393 default:
394 FPU_LOG_WARN(3, false, group, sub);
395 break;
396 }
397 }
398
FPU_ESC4_EA(Bitu rm,PhysPt addr)399 void FPU_ESC4_EA(Bitu rm,PhysPt addr) {
400 /* REGULAR TREE WITH 64 BITS REALS */
401 FPU_FLD_F64_EA(addr);
402 EATREE(rm);
403 }
404
FPU_ESC4_Normal(Bitu rm)405 void FPU_ESC4_Normal(Bitu rm) {
406 /* LOOKS LIKE number 6 without popping */
407 Bitu group=(rm >> 3) & 7;
408 Bitu sub=(rm & 7);
409 switch(group){
410 case 0x00: /* FADD STi,ST*/
411 FPU_FADD(STV(sub),TOP);
412 break;
413 case 0x01: /* FMUL STi,ST*/
414 FPU_FMUL(STV(sub),TOP);
415 break;
416 case 0x02: /* FCOM*/
417 FPU_FCOM(TOP,STV(sub));
418 break;
419 case 0x03: /* FCOMP*/
420 FPU_FCOM(TOP,STV(sub));
421 FPU_FPOP();
422 break;
423 case 0x04: /* FSUBR STi,ST*/
424 FPU_FSUBR(STV(sub),TOP);
425 break;
426 case 0x05: /* FSUB STi,ST*/
427 FPU_FSUB(STV(sub),TOP);
428 break;
429 case 0x06: /* FDIVR STi,ST*/
430 FPU_FDIVR(STV(sub),TOP);
431 break;
432 case 0x07: /* FDIV STi,ST*/
433 FPU_FDIV(STV(sub),TOP);
434 break;
435 default:
436 break;
437 }
438 }
439
FPU_ESC5_EA(Bitu rm,PhysPt addr)440 void FPU_ESC5_EA(Bitu rm,PhysPt addr) {
441 Bitu group=(rm >> 3) & 7;
442 Bitu sub=(rm & 7);
443 switch(group){
444 case 0x00: /* FLD double real*/
445 FPU_PREP_PUSH();
446 FPU_FLD_F64(addr,TOP);
447 break;
448 case 0x01: /* FISTTP longint*/
449 FPU_LOG_WARN(5,true,group,sub);
450 break;
451 case 0x02: /* FST double real*/
452 FPU_FST_F64(addr);
453 break;
454 case 0x03: /* FSTP double real*/
455 FPU_FST_F64(addr);
456 FPU_FPOP();
457 break;
458 case 0x04: /* FRSTOR */
459 FPU_FRSTOR(addr);
460 break;
461 case 0x06: /* FSAVE */
462 FPU_FSAVE(addr);
463 break;
464 case 0x07: /*FNSTSW NG DISAGREES ON THIS*/
465 FPU_SET_TOP(TOP);
466 mem_writew(addr,fpu.sw);
467 //seems to break all dos4gw games :)
468 break;
469 default:
470 FPU_LOG_WARN(5,true,group,sub);
471 break;
472 }
473 }
474
FPU_ESC5_Normal(Bitu rm)475 void FPU_ESC5_Normal(Bitu rm) {
476 Bitu group=(rm >> 3) & 7;
477 Bitu sub=(rm & 7);
478 switch(group){
479 case 0x00: /* FFREE STi */
480 fpu.tags[STV(sub)]=TAG_Empty;
481 break;
482 case 0x01: /* FXCH STi*/
483 FPU_FXCH(TOP,STV(sub));
484 break;
485 case 0x02: /* FST STi */
486 FPU_FST(TOP,STV(sub));
487 break;
488 case 0x03: /* FSTP STi*/
489 FPU_FST(TOP,STV(sub));
490 FPU_FPOP();
491 break;
492 case 0x04: /* FUCOM STi */
493 FPU_FUCOM(TOP,STV(sub));
494 break;
495 case 0x05: /*FUCOMP STi */
496 FPU_FUCOM(TOP,STV(sub));
497 FPU_FPOP();
498 break;
499 default:
500 FPU_LOG_WARN(5,false,group,sub);
501 break;
502 }
503 }
504
FPU_ESC6_EA(Bitu rm,PhysPt addr)505 void FPU_ESC6_EA(Bitu rm,PhysPt addr) {
506 /* 16 bit (word integer) operants */
507 FPU_FLD_I16_EA(addr);
508 EATREE(rm);
509 }
510
FPU_ESC6_Normal(Bitu rm)511 void FPU_ESC6_Normal(Bitu rm) {
512 /* all P variants working only on registers */
513 /* get top before switch and pop afterwards */
514 Bitu group=(rm >> 3) & 7;
515 Bitu sub=(rm & 7);
516 switch(group){
517 case 0x00: /*FADDP STi,ST*/
518 FPU_FADD(STV(sub),TOP);
519 break;
520 case 0x01: /* FMULP STi,ST*/
521 FPU_FMUL(STV(sub),TOP);
522 break;
523 case 0x02: /* FCOMP5*/
524 FPU_FCOM(TOP,STV(sub));
525 break; /* TODO IS THIS ALLRIGHT ????????? */
526 case 0x03: /*FCOMPP*/
527 if(sub != 1) {
528 FPU_LOG_WARN(6,false,group,sub);
529 return;
530 }
531 FPU_FCOM(TOP,STV(1));
532 FPU_FPOP(); /* extra pop at the bottom*/
533 break;
534 case 0x04: /* FSUBRP STi,ST*/
535 FPU_FSUBR(STV(sub),TOP);
536 break;
537 case 0x05: /* FSUBP STi,ST*/
538 FPU_FSUB(STV(sub),TOP);
539 break;
540 case 0x06: /* FDIVRP STi,ST*/
541 FPU_FDIVR(STV(sub),TOP);
542 break;
543 case 0x07: /* FDIVP STi,ST*/
544 FPU_FDIV(STV(sub),TOP);
545 break;
546 default:
547 break;
548 }
549 FPU_FPOP();
550 }
551
552
FPU_ESC7_EA(Bitu rm,PhysPt addr)553 void FPU_ESC7_EA(Bitu rm,PhysPt addr) {
554 Bitu group=(rm >> 3) & 7;
555 Bitu sub=(rm & 7);
556 switch(group){
557 case 0x00: /* FILD Bit16s */
558 FPU_PREP_PUSH();
559 FPU_FLD_I16(addr,TOP);
560 break;
561 case 0x01:
562 FPU_LOG_WARN(7,true,group,sub);
563 break;
564 case 0x02: /* FIST Bit16s */
565 FPU_FST_I16(addr);
566 break;
567 case 0x03: /* FISTP Bit16s */
568 FPU_FST_I16(addr);
569 FPU_FPOP();
570 break;
571 case 0x04: /* FBLD packed BCD */
572 FPU_PREP_PUSH();
573 FPU_FBLD(addr,TOP);
574 break;
575 case 0x05: /* FILD Bit64s */
576 FPU_PREP_PUSH();
577 FPU_FLD_I64(addr,TOP);
578 break;
579 case 0x06: /* FBSTP packed BCD */
580 FPU_FBST(addr);
581 FPU_FPOP();
582 break;
583 case 0x07: /* FISTP Bit64s */
584 FPU_FST_I64(addr);
585 FPU_FPOP();
586 break;
587 default:
588 FPU_LOG_WARN(7,true,group,sub);
589 break;
590 }
591 }
592
FPU_ESC7_Normal(Bitu rm)593 void FPU_ESC7_Normal(Bitu rm) {
594 Bitu group=(rm >> 3) & 7;
595 Bitu sub=(rm & 7);
596 switch (group){
597 case 0x00: /* FFREEP STi*/
598 fpu.tags[STV(sub)]=TAG_Empty;
599 FPU_FPOP();
600 break;
601 case 0x01: /* FXCH STi*/
602 FPU_FXCH(TOP,STV(sub));
603 break;
604 case 0x02: /* FSTP STi*/
605 case 0x03: /* FSTP STi*/
606 FPU_FST(TOP,STV(sub));
607 FPU_FPOP();
608 break;
609 case 0x04:
610 switch(sub){
611 case 0x00: /* FNSTSW AX*/
612 FPU_SET_TOP(TOP);
613 reg_ax = fpu.sw;
614 break;
615 default:
616 FPU_LOG_WARN(7,false,group,sub);
617 break;
618 }
619 break;
620 default:
621 FPU_LOG_WARN(7,false,group,sub);
622 break;
623 }
624 }
625
626
FPU_Init(Section *)627 void FPU_Init(Section*) {
628 FPU_FINIT();
629 }
630
631 #endif
632