summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/m68k/m68040/fpsp/x_operr.s
blob: 9fd9c99cf0f963879adf5e3d5034df59a1883d9a (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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
//
//      $Id$
//
//	x_operr.sa 3.5 7/1/91
//
//	fpsp_operr --- FPSP handler for operand error exception
//
//	See 68040 User's Manual pp. 9-44f
//
// Note 1: For trap disabled 040 does the following:
// If the dest is a fp reg, then an extended precision non_signaling
// NAN is stored in the dest reg.  If the dest format is b, w, or l and
// the source op is a NAN, then garbage is stored as the result (actually
// the upper 32 bits of the mantissa are sent to the integer unit). If
// the dest format is integer (b, w, l) and the operr is caused by
// integer overflow, or the source op is inf, then the result stored is
// garbage.
// There are three cases in which operr is incorrectly signaled on the 
// 040.  This occurs for move_out of format b, w, or l for the largest 
// negative integer (-2^7 for b, -2^15 for w, -2^31 for l).
//
//	  On opclass = 011 fmove.(b,w,l) that causes a conversion
//	  overflow -> OPERR, the exponent in wbte (and fpte) is:
//		byte    56 - (62 - exp)
//		word    48 - (62 - exp)
//		long    32 - (62 - exp)
//
//			where exp = (true exp) - 1
//
//  So, wbtemp and fptemp will contain the following on erroneously
//	  signalled operr:
//			fpts = 1
//			fpte = $4000  (15 bit externally)
//		byte	fptm = $ffffffff ffffff80
//		word	fptm = $ffffffff ffff8000
//		long	fptm = $ffffffff 80000000
//
// Note 2: For trap enabled 040 does the following:
// If the inst is move_out, then same as Note 1.
// If the inst is not move_out, the dest is not modified.
// The exceptional operand is not defined for integer overflow 
// during a move_out.
//

//		Copyright (C) Motorola, Inc. 1990
//			All Rights Reserved
//
//	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA 
//	The copyright notice above does not evidence any  
//	actual or intended publication of such source code.

X_OPERR:	//idnt    2,1 | Motorola 040 Floating Point Software Package

	|section	8

#include "fpsp.defs"

	|xref	mem_write
	|xref	real_operr
	|xref	real_inex
	|xref	get_fline
	|xref	fpsp_done
	|xref	reg_dest

	.global	fpsp_operr
fpsp_operr:
//
	link		%a6,#-LOCAL_SIZE
	fsave		-(%a7)
	moveml		%d0-%d1/%a0-%a1,USER_DA(%a6)
	fmovemx	%fp0-%fp3,USER_FP0(%a6)
	fmoveml	%fpcr/%fpsr/%fpiar,USER_FPCR(%a6)

//
// Check if this is an opclass 3 instruction.
//  If so, fall through, else branch to operr_end
//
	btstb	#TFLAG,T_BYTE(%a6)
	beqs	operr_end

//
// If the destination size is B,W,or L, the operr must be 
// handled here.
//
	movel	CMDREG1B(%a6),%d0
	bfextu	%d0{#3:#3},%d0	//0=long, 4=word, 6=byte
	cmpib	#0,%d0		//determine size; check long
	beq	operr_long
	cmpib	#4,%d0		//check word
	beq	operr_word
	cmpib	#6,%d0		//check byte
	beq	operr_byte

//
// The size is not B,W,or L, so the operr is handled by the 
// kernel handler.  Set the operr bits and clean up, leaving
// only the integer exception frame on the stack, and the 
// fpu in the original exceptional state.
//
operr_end:
	bsetb		#operr_bit,FPSR_EXCEPT(%a6)
	bsetb		#aiop_bit,FPSR_AEXCEPT(%a6)

	moveml		USER_DA(%a6),%d0-%d1/%a0-%a1
	fmovemx	USER_FP0(%a6),%fp0-%fp3
	fmoveml	USER_FPCR(%a6),%fpcr/%fpsr/%fpiar
	frestore	(%a7)+
	unlk		%a6
	bral		real_operr

operr_long:
	moveql	#4,%d1		//write size to d1
	moveb	STAG(%a6),%d0	//test stag for nan
	andib	#0xe0,%d0		//clr all but tag
	cmpib	#0x60,%d0		//check for nan
	beq	operr_nan	
	cmpil	#0x80000000,FPTEMP_LO(%a6) //test if ls lword is special
	bnes	chklerr		//if not equal, check for incorrect operr
	bsr	check_upper	//check if exp and ms mant are special
	tstl	%d0
	bnes	chklerr		//if d0 is true, check for incorrect operr
	movel	#0x80000000,%d0	//store special case result
	bsr	operr_store
	bra	not_enabled	//clean and exit
//
//	CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE
//
chklerr:
	movew	FPTEMP_EX(%a6),%d0
	andw	#0x7FFF,%d0	//ignore sign bit
	cmpw	#0x3FFE,%d0	//this is the only possible exponent value
	bnes	chklerr2
fixlong:
	movel	FPTEMP_LO(%a6),%d0
	bsr	operr_store
	bra	not_enabled
chklerr2:
	movew	FPTEMP_EX(%a6),%d0
	andw	#0x7FFF,%d0	//ignore sign bit
	cmpw	#0x4000,%d0
	bcc	store_max	//exponent out of range

	movel	FPTEMP_LO(%a6),%d0
	andl	#0x7FFF0000,%d0	//look for all 1's on bits 30-16
	cmpl	#0x7FFF0000,%d0
	beqs	fixlong

	tstl	FPTEMP_LO(%a6)
	bpls	chklepos
	cmpl	#0xFFFFFFFF,FPTEMP_HI(%a6)
	beqs	fixlong
	bra	store_max
chklepos:
	tstl	FPTEMP_HI(%a6)
	beqs	fixlong
	bra	store_max

operr_word:
	moveql	#2,%d1		//write size to d1
	moveb	STAG(%a6),%d0	//test stag for nan
	andib	#0xe0,%d0		//clr all but tag
	cmpib	#0x60,%d0		//check for nan
	beq	operr_nan	
	cmpil	#0xffff8000,FPTEMP_LO(%a6) //test if ls lword is special
	bnes	chkwerr		//if not equal, check for incorrect operr
	bsr	check_upper	//check if exp and ms mant are special
	tstl	%d0
	bnes	chkwerr		//if d0 is true, check for incorrect operr
	movel	#0x80000000,%d0	//store special case result
	bsr	operr_store
	bra	not_enabled	//clean and exit
//
//	CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE
//
chkwerr:
	movew	FPTEMP_EX(%a6),%d0
	andw	#0x7FFF,%d0	//ignore sign bit
	cmpw	#0x3FFE,%d0	//this is the only possible exponent value
	bnes	store_max
	movel	FPTEMP_LO(%a6),%d0
	swap	%d0
	bsr	operr_store
	bra	not_enabled

operr_byte:
	moveql	#1,%d1		//write size to d1
	moveb	STAG(%a6),%d0	//test stag for nan
	andib	#0xe0,%d0		//clr all but tag
	cmpib	#0x60,%d0		//check for nan
	beqs	operr_nan	
	cmpil	#0xffffff80,FPTEMP_LO(%a6) //test if ls lword is special
	bnes	chkberr		//if not equal, check for incorrect operr
	bsr	check_upper	//check if exp and ms mant are special
	tstl	%d0
	bnes	chkberr		//if d0 is true, check for incorrect operr
	movel	#0x80000000,%d0	//store special case result
	bsr	operr_store
	bra	not_enabled	//clean and exit
//
//	CHECK FOR INCORRECTLY GENERATED OPERR EXCEPTION HERE
//
chkberr:
	movew	FPTEMP_EX(%a6),%d0
	andw	#0x7FFF,%d0	//ignore sign bit
	cmpw	#0x3FFE,%d0	//this is the only possible exponent value
	bnes	store_max
	movel	FPTEMP_LO(%a6),%d0
	asll	#8,%d0
	swap	%d0
	bsr	operr_store
	bra	not_enabled

//
// This operr condition is not of the special case.  Set operr
// and aiop and write the portion of the nan to memory for the
// given size.
//
operr_nan:
	orl	#opaop_mask,USER_FPSR(%a6) //set operr & aiop

	movel	ETEMP_HI(%a6),%d0	//output will be from upper 32 bits
	bsr	operr_store
	bra	end_operr
//
// Store_max loads the max pos or negative for the size, sets
// the operr and aiop bits, and clears inex and ainex, incorrectly
// set by the 040.
//
store_max:
	orl	#opaop_mask,USER_FPSR(%a6) //set operr & aiop
	bclrb	#inex2_bit,FPSR_EXCEPT(%a6)
	bclrb	#ainex_bit,FPSR_AEXCEPT(%a6)
	fmovel	#0,%FPSR
	
	tstw	FPTEMP_EX(%a6)	//check sign
	blts	load_neg
	movel	#0x7fffffff,%d0
	bsr	operr_store
	bra	end_operr
load_neg:
	movel	#0x80000000,%d0
	bsr	operr_store
	bra	end_operr

//
// This routine stores the data in d0, for the given size in d1,
// to memory or data register as required.  A read of the fline
// is required to determine the destination.
//
operr_store:
	movel	%d0,L_SCR1(%a6)	//move write data to L_SCR1
	movel	%d1,-(%a7)	//save register size
	bsrl	get_fline	//fline returned in d0
	movel	(%a7)+,%d1
	bftst	%d0{#26:#3}		//if mode is zero, dest is Dn
	bnes	dest_mem
//
// Destination is Dn.  Get register number from d0. Data is on
// the stack at (a7). D1 has size: 1=byte,2=word,4=long/single
//
	andil	#7,%d0		//isolate register number
	cmpil	#4,%d1
	beqs	op_long		//the most frequent case
	cmpil	#2,%d1
	bnes	op_con
	orl	#8,%d0
	bras	op_con
op_long:
	orl	#0x10,%d0
op_con:
	movel	%d0,%d1		//format size:reg for reg_dest
	bral	reg_dest	//call to reg_dest returns to caller
//				;of operr_store
//
// Destination is memory.  Get <ea> from integer exception frame
// and call mem_write.
//
dest_mem:
	leal	L_SCR1(%a6),%a0	//put ptr to write data in a0
	movel	EXC_EA(%a6),%a1	//put user destination address in a1
	movel	%d1,%d0		//put size in d0
	bsrl	mem_write
	rts
//
// Check the exponent for $c000 and the upper 32 bits of the 
// mantissa for $ffffffff.  If both are true, return d0 clr
// and store the lower n bits of the least lword of FPTEMP
// to d0 for write out.  If not, it is a real operr, and set d0.
//
check_upper:
	cmpil	#0xffffffff,FPTEMP_HI(%a6) //check if first byte is all 1's
	bnes	true_operr	//if not all 1's then was true operr
	cmpiw	#0xc000,FPTEMP_EX(%a6) //check if incorrectly signalled
	beqs	not_true_operr	//branch if not true operr
	cmpiw	#0xbfff,FPTEMP_EX(%a6) //check if incorrectly signalled
	beqs	not_true_operr	//branch if not true operr
true_operr:
	movel	#1,%d0		//signal real operr
	rts
not_true_operr:
	clrl	%d0		//signal no real operr
	rts

//
// End_operr tests for operr enabled.  If not, it cleans up the stack
// and does an rte.  If enabled, it cleans up the stack and branches
// to the kernel operr handler with only the integer exception
// frame on the stack and the fpu in the original exceptional state
// with correct data written to the destination.
//
end_operr:
	btstb		#operr_bit,FPCR_ENABLE(%a6)
	beqs		not_enabled
enabled:
	moveml		USER_DA(%a6),%d0-%d1/%a0-%a1
	fmovemx	USER_FP0(%a6),%fp0-%fp3
	fmoveml	USER_FPCR(%a6),%fpcr/%fpsr/%fpiar
	frestore	(%a7)+
	unlk		%a6
	bral		real_operr

not_enabled:
//
// It is possible to have either inex2 or inex1 exceptions with the
// operr.  If the inex enable bit is set in the FPCR, and either
// inex2 or inex1 occurred, we must clean up and branch to the
// real inex handler.
//
ck_inex:
	moveb	FPCR_ENABLE(%a6),%d0
	andb	FPSR_EXCEPT(%a6),%d0
	andib	#0x3,%d0
	beq	operr_exit
//
// Inexact enabled and reported, and we must take an inexact exception.
//
take_inex:
	moveb		#INEX_VEC,EXC_VEC+1(%a6)
	movel		USER_FPSR(%a6),FPSR_SHADOW(%a6)
	orl		#sx_mask,E_BYTE(%a6)
	moveml		USER_DA(%a6),%d0-%d1/%a0-%a1
	fmovemx	USER_FP0(%a6),%fp0-%fp3
	fmoveml	USER_FPCR(%a6),%fpcr/%fpsr/%fpiar
	frestore	(%a7)+
	unlk		%a6
	bral		real_inex
//
// Since operr is only an E1 exception, there is no need to frestore
// any state back to the fpu.
//
operr_exit:
	moveml		USER_DA(%a6),%d0-%d1/%a0-%a1
	fmovemx	USER_FP0(%a6),%fp0-%fp3
	fmoveml	USER_FPCR(%a6),%fpcr/%fpsr/%fpiar
	unlk		%a6
	bral		fpsp_done

	|end