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