summaryrefslogtreecommitdiff
path: root/arch/x86/math-emu/fpu_etc.c
blob: 1b118fd93140931e515898e51bd316a50dab97cf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-License-Identifier: GPL-2.0
/*---------------------------------------------------------------------------+
 |  fpu_etc.c                                                                |
 |                                                                           |
 | Implement a few FPU instructions.                                         |
 |                                                                           |
 | Copyright (C) 1992,1993,1994,1997                                         |
 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
 |                       Australia.  E-mail   billm@suburbia.net             |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/

#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "reg_constant.h"

static void fchs(FPU_REG *st0_ptr, u_char st0tag)
{
	if (st0tag ^ TAG_Empty) {
		signbyte(st0_ptr) ^= SIGN_NEG;
		clear_C1();
	} else
		FPU_stack_underflow();
}

static void fabs(FPU_REG *st0_ptr, u_char st0tag)
{
	if (st0tag ^ TAG_Empty) {
		setpositive(st0_ptr);
		clear_C1();
	} else
		FPU_stack_underflow();
}

static void ftst_(FPU_REG *st0_ptr, u_char st0tag)
{
	switch (st0tag) {
	case TAG_Zero:
		setcc(SW_C3);
		break;
	case TAG_Valid:
		if (getsign(st0_ptr) == SIGN_POS)
			setcc(0);
		else
			setcc(SW_C0);
		break;
	case TAG_Special:
		switch (FPU_Special(st0_ptr)) {
		case TW_Denormal:
			if (getsign(st0_ptr) == SIGN_POS)
				setcc(0);
			else
				setcc(SW_C0);
			if (denormal_operand() < 0) {
#ifdef PECULIAR_486
				/* This is weird! */
				if (getsign(st0_ptr) == SIGN_POS)
					setcc(SW_C3);
#endif /* PECULIAR_486 */
				return;
			}
			break;
		case TW_NaN:
			setcc(SW_C0 | SW_C2 | SW_C3);	/* Operand is not comparable */
			EXCEPTION(EX_Invalid);
			break;
		case TW_Infinity:
			if (getsign(st0_ptr) == SIGN_POS)
				setcc(0);
			else
				setcc(SW_C0);
			break;
		default:
			setcc(SW_C0 | SW_C2 | SW_C3);	/* Operand is not comparable */
			EXCEPTION(EX_INTERNAL | 0x14);
			break;
		}
		break;
	case TAG_Empty:
		setcc(SW_C0 | SW_C2 | SW_C3);
		EXCEPTION(EX_StackUnder);
		break;
	}
}

static void fxam(FPU_REG *st0_ptr, u_char st0tag)
{
	int c = 0;
	switch (st0tag) {
	case TAG_Empty:
		c = SW_C3 | SW_C0;
		break;
	case TAG_Zero:
		c = SW_C3;
		break;
	case TAG_Valid:
		c = SW_C2;
		break;
	case TAG_Special:
		switch (FPU_Special(st0_ptr)) {
		case TW_Denormal:
			c = SW_C2 | SW_C3;	/* Denormal */
			break;
		case TW_NaN:
			/* We also use NaN for unsupported types. */
			if ((st0_ptr->sigh & 0x80000000)
			    && (exponent(st0_ptr) == EXP_OVER))
				c = SW_C0;
			break;
		case TW_Infinity:
			c = SW_C2 | SW_C0;
			break;
		}
	}
	if (getsign(st0_ptr) == SIGN_NEG)
		c |= SW_C1;
	setcc(c);
}

static FUNC_ST0 const fp_etc_table[] = {
	fchs, fabs, (FUNC_ST0) FPU_illegal, (FUNC_ST0) FPU_illegal,
	ftst_, fxam, (FUNC_ST0) FPU_illegal, (FUNC_ST0) FPU_illegal
};

void FPU_etc(void)
{
	(fp_etc_table[FPU_rm]) (&st(0), FPU_gettag0());
}