summaryrefslogtreecommitdiff
path: root/arch/m68k/include/asm/math-emu.h
blob: 5e9249b0014ca0fcd2faa088bfb8f9d5810c003d (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#ifndef _ASM_M68K_SETUP_H
#define _ASM_M68K_SETUP_H

#include <asm/setup.h>
#include <linux/linkage.h>

/* Status Register bits */

/* accrued exception bits */
#define FPSR_AEXC_INEX	3
#define FPSR_AEXC_DZ	4
#define FPSR_AEXC_UNFL	5
#define FPSR_AEXC_OVFL	6
#define FPSR_AEXC_IOP	7

/* exception status bits */
#define FPSR_EXC_INEX1	8
#define FPSR_EXC_INEX2	9
#define FPSR_EXC_DZ	10
#define FPSR_EXC_UNFL	11
#define FPSR_EXC_OVFL	12
#define FPSR_EXC_OPERR	13
#define FPSR_EXC_SNAN	14
#define FPSR_EXC_BSUN	15

/* quotient byte, assumes big-endian, of course */
#define FPSR_QUOTIENT(fpsr) (*((signed char *) &(fpsr) + 1))

/* condition code bits */
#define FPSR_CC_NAN	24
#define FPSR_CC_INF	25
#define FPSR_CC_Z	26
#define FPSR_CC_NEG	27


/* Control register bits */

/* rounding mode */
#define	FPCR_ROUND_RN	0		/* round to nearest/even */
#define FPCR_ROUND_RZ	1		/* round to zero */
#define FPCR_ROUND_RM	2		/* minus infinity */
#define FPCR_ROUND_RP	3		/* plus infinity */

/* rounding precision */
#define FPCR_PRECISION_X	0	/* long double */
#define FPCR_PRECISION_S	1	/* double */
#define FPCR_PRECISION_D	2	/* float */


/* Flags to select the debugging output */
#define PDECODE		0
#define PEXECUTE	1
#define PCONV		2
#define PNORM		3
#define PREGISTER	4
#define PINSTR		5
#define PUNIMPL		6
#define PMOVEM		7

#define PMDECODE	(1<<PDECODE)
#define PMEXECUTE	(1<<PEXECUTE)
#define PMCONV		(1<<PCONV)
#define PMNORM		(1<<PNORM)
#define PMREGISTER	(1<<PREGISTER)
#define PMINSTR		(1<<PINSTR)
#define PMUNIMPL	(1<<PUNIMPL)
#define PMMOVEM		(1<<PMOVEM)

#ifndef __ASSEMBLY__

#include <linux/kernel.h>
#include <linux/sched.h>

union fp_mant64 {
	unsigned long long m64;
	unsigned long m32[2];
};

union fp_mant128 {
	unsigned long long m64[2];
	unsigned long m32[4];
};

/* internal representation of extended fp numbers */
struct fp_ext {
	unsigned char lowmant;
	unsigned char sign;
	unsigned short exp;
	union fp_mant64 mant;
};

/* C representation of FPU registers */
/* NOTE: if you change this, you have to change the assembler offsets
   below and the size in <asm/fpu.h>, too */
struct fp_data {
	struct fp_ext fpreg[8];
	unsigned int fpcr;
	unsigned int fpsr;
	unsigned int fpiar;
	unsigned short prec;
	unsigned short rnd;
	struct fp_ext temp[2];
};

#ifdef FPU_EMU_DEBUG
extern unsigned int fp_debugprint;

#define dprint(bit, fmt, args...) ({			\
	if (fp_debugprint & (1 << (bit)))		\
		printk(fmt, ## args);			\
})
#else
#define dprint(bit, fmt, args...)
#endif

#define uprint(str) ({					\
	static int __count = 3;				\
							\
	if (__count > 0) {				\
		printk("You just hit an unimplemented "	\
		       "fpu instruction (%s)\n", str);	\
		printk("Please report this to ....\n");	\
		__count--;				\
	}						\
})

#define FPDATA		((struct fp_data *)current->thread.fp)

#else	/* __ASSEMBLY__ */

#define FPDATA		%a2

/* offsets from the base register to the floating point data in the task struct */
#define FPD_FPREG	(TASK_THREAD+THREAD_FPREG+0)
#define FPD_FPCR	(TASK_THREAD+THREAD_FPREG+96)
#define FPD_FPSR	(TASK_THREAD+THREAD_FPREG+100)
#define FPD_FPIAR	(TASK_THREAD+THREAD_FPREG+104)
#define FPD_PREC	(TASK_THREAD+THREAD_FPREG+108)
#define FPD_RND		(TASK_THREAD+THREAD_FPREG+110)
#define FPD_TEMPFP1	(TASK_THREAD+THREAD_FPREG+112)
#define FPD_TEMPFP2	(TASK_THREAD+THREAD_FPREG+124)
#define FPD_SIZEOF	(TASK_THREAD+THREAD_FPREG+136)

/* offsets on the stack to access saved registers,
 * these are only used during instruction decoding
 * where we always know how deep we're on the stack.
 */
#define FPS_DO		(PT_OFF_D0)
#define FPS_D1		(PT_OFF_D1)
#define FPS_D2		(PT_OFF_D2)
#define FPS_A0		(PT_OFF_A0)
#define FPS_A1		(PT_OFF_A1)
#define FPS_A2		(PT_OFF_A2)
#define FPS_SR		(PT_OFF_SR)
#define FPS_PC		(PT_OFF_PC)
#define FPS_EA		(PT_OFF_PC+6)
#define FPS_PC2		(PT_OFF_PC+10)

.macro	fp_get_fp_reg
	lea	(FPD_FPREG,FPDATA,%d0.w*4),%a0
	lea	(%a0,%d0.w*8),%a0
.endm

/* Macros used to get/put the current program counter.
 * 020/030 use a different stack frame then 040/060, for the
 * 040/060 the return pc points already to the next location,
 * so this only needs to be modified for jump instructions.
 */
.macro	fp_get_pc dest
	move.l	(FPS_PC+4,%sp),\dest
.endm

.macro	fp_put_pc src,jump=0
	move.l	\src,(FPS_PC+4,%sp)
.endm

.macro	fp_get_instr_data	f,s,dest,label
	getuser	\f,%sp@(FPS_PC+4)@(0),\dest,\label,%sp@(FPS_PC+4)
	addq.l	#\s,%sp@(FPS_PC+4)
.endm

.macro	fp_get_instr_word	dest,label,addr
	fp_get_instr_data	w,2,\dest,\label,\addr
.endm

.macro	fp_get_instr_long	dest,label,addr
	fp_get_instr_data	l,4,\dest,\label,\addr
.endm

/* These macros are used to read from/write to user space
 * on error we jump to the fixup section, load the fault
 * address into %a0 and jump to the exit.
 * (derived from <asm/uaccess.h>)
 */
.macro	getuser	size,src,dest,label,addr
|	printf	,"[\size<%08x]",1,\addr
.Lu1\@:	moves\size	\src,\dest

	.section .fixup,"ax"
	.even
.Lu2\@:	move.l	\addr,%a0
	jra	\label
	.previous

	.section __ex_table,"a"
	.align	4
	.long	.Lu1\@,.Lu2\@
	.previous
.endm

.macro	putuser	size,src,dest,label,addr
|	printf	,"[\size>%08x]",1,\addr
.Lu1\@:	moves\size	\src,\dest
.Lu2\@:

	.section .fixup,"ax"
	.even
.Lu3\@:	move.l	\addr,%a0
	jra	\label
	.previous

	.section __ex_table,"a"
	.align	4
	.long	.Lu1\@,.Lu3\@
	.long	.Lu2\@,.Lu3\@
	.previous
.endm

/* work around binutils idiocy */
old_gas=-1
.irp    gas_ident.x .x
old_gas=old_gas+1
.endr
.if !old_gas
.irp	m b,w,l
.macro	getuser.\m src,dest,label,addr
	getuser .\m,\src,\dest,\label,\addr
.endm
.macro	putuser.\m src,dest,label,addr
	putuser .\m,\src,\dest,\label,\addr
.endm
.endr
.endif

.macro	movestack	nr,arg1,arg2,arg3,arg4,arg5
	.if	\nr
	movestack	(\nr-1),\arg2,\arg3,\arg4,\arg5
	move.l	\arg1,-(%sp)
	.endif
.endm

.macro	printf	bit=-1,string,nr=0,arg1,arg2,arg3,arg4,arg5
#ifdef FPU_EMU_DEBUG
	.data
.Lpdata\@:
	.string	"\string"
	.previous

	movem.l	%d0/%d1/%a0/%a1,-(%sp)
	.if	\bit+1
#if 0
	moveq	#\bit,%d0
	andw	#7,%d0
	btst	%d0,fp_debugprint+((31-\bit)/8)
#else
	btst	#\bit,fp_debugprint+((31-\bit)/8)
#endif
	jeq	.Lpskip\@
	.endif
	movestack	\nr,\arg1,\arg2,\arg3,\arg4,\arg5
	pea	.Lpdata\@
	jsr	printk
	lea	((\nr+1)*4,%sp),%sp
.Lpskip\@:
	movem.l	(%sp)+,%d0/%d1/%a0/%a1
#endif
.endm

.macro	printx	bit,fp
#ifdef FPU_EMU_DEBUG
	movem.l	%d0/%a0,-(%sp)
	lea	\fp,%a0
#if 0
	moveq	#'+',%d0
	tst.w	(%a0)
	jeq	.Lx1\@
	moveq	#'-',%d0
.Lx1\@:	printf	\bit," %c",1,%d0
	move.l	(4,%a0),%d0
	bclr	#31,%d0
	jne	.Lx2\@
	printf	\bit,"0."
	jra	.Lx3\@
.Lx2\@:	printf	\bit,"1."
.Lx3\@:	printf	\bit,"%08x%08x",2,%d0,%a0@(8)
	move.w	(2,%a0),%d0
	ext.l	%d0
	printf	\bit,"E%04x",1,%d0
#else
	printf	\bit," %08x%08x%08x",3,%a0@,%a0@(4),%a0@(8)
#endif
	movem.l	(%sp)+,%d0/%a0
#endif
.endm

.macro	debug	instr,args
#ifdef FPU_EMU_DEBUG
	\instr	\args
#endif
.endm


#endif	/* __ASSEMBLY__ */

#endif	/* _ASM_M68K_SETUP_H */