Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * this doesn't attempt to implement ARM floating-point properties
3
 * that aren't visible in the Inferno environment.
4
 * all arithmetic is done in double precision.
5
 * the FP trap status isn't updated.
6
 */
7
#include	"u.h"
8
#include	"../port/lib.h"
9
#include	"mem.h"
10
#include	"dat.h"
11
#include	"fns.h"
12
 
13
#include	"ureg.h"
14
 
15
#include	"arm.h"
16
#include	"../port/fpi.h"
17
 
18
#define ARM7500			/* emulate old pre-VFP opcodes */
19
 
20
/* undef this if correct kernel r13 isn't in Ureg;
21
 * check calculation in fpiarm below
22
 */
23
 
24
#define	REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
25
#ifdef ARM7500
26
#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
27
#else
28
#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
29
#endif
30
 
31
typedef struct FP2 FP2;
32
typedef struct FP1 FP1;
33
 
34
struct FP2 {
35
	char*	name;
36
	void	(*f)(Internal, Internal, Internal*);
37
};
38
 
39
struct FP1 {
40
	char*	name;
41
	void	(*f)(Internal*, Internal*);
42
};
43
 
44
enum {
45
	N = 1<<31,
46
	Z = 1<<30,
47
	C = 1<<29,
48
	V = 1<<28,
49
	REGPC = 15,
50
};
51
 
52
enum {
53
	fpemudebug = 0,
54
};
55
 
56
#undef OFR
57
#define	OFR(X)	((ulong)&((Ureg*)0)->X)
58
 
59
static	int	roff[] = {
60
	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
61
	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
62
	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
63
	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
64
};
65
 
66
static Internal fpconst[8] = {		/* indexed by op&7 (ARM 7500 FPA) */
67
	/* s, e, l, h */
68
	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
69
	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
70
	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
71
	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
72
	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
73
	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
74
	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
75
	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
76
};
77
 
78
/*
79
 * arm binary operations
80
 */
81
 
82
static void
83
fadd(Internal m, Internal n, Internal *d)
84
{
85
	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
86
}
87
 
88
static void
89
fsub(Internal m, Internal n, Internal *d)
90
{
91
	m.s ^= 1;
92
	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
93
}
94
 
95
static void
96
fsubr(Internal m, Internal n, Internal *d)
97
{
98
	n.s ^= 1;
99
	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
100
}
101
 
102
static void
103
fmul(Internal m, Internal n, Internal *d)
104
{
105
	fpimul(&m, &n, d);
106
}
107
 
108
static void
109
fdiv(Internal m, Internal n, Internal *d)
110
{
111
	fpidiv(&m, &n, d);
112
}
113
 
114
static void
115
fdivr(Internal m, Internal n, Internal *d)
116
{
117
	fpidiv(&n, &m, d);
118
}
119
 
120
/*
121
 * arm unary operations
122
 */
123
 
124
static void
125
fmov(Internal *m, Internal *d)
126
{
127
	*d = *m;
128
}
129
 
130
static void
131
fmovn(Internal *m, Internal *d)
132
{
133
	*d = *m;
134
	d->s ^= 1;
135
}
136
 
137
static void
138
fabsf(Internal *m, Internal *d)
139
{
140
	*d = *m;
141
	d->s = 0;
142
}
143
 
144
static void
145
frnd(Internal *m, Internal *d)
146
{
147
	short e;
148
 
149
	(m->s? fsub: fadd)(fpconst[6], *m, d);
150
	if(IsWeird(d))
151
		return;
152
	fpiround(d);
153
	e = (d->e - ExpBias) + 1;
154
	if(e <= 0)
155
		SetZero(d);
156
	else if(e > FractBits){
157
		if(e < 2*FractBits)
158
			d->l &= ~((1<<(2*FractBits - e))-1);
159
	}else{
160
		d->l = 0;
161
		if(e < FractBits)
162
			d->h &= ~((1<<(FractBits-e))-1);
163
	}
164
}
165
 
166
/*
167
 * ARM 7500 FPA opcodes
168
 */
169
 
170
static	FP1	optab1[16] = {	/* Fd := OP Fm */
171
[0]	{"MOVF",	fmov},
172
[1]	{"NEGF",	fmovn},
173
[2]	{"ABSF",	fabsf},
174
[3]	{"RNDF",	frnd},
175
[4]	{"SQTF",	/*fsqt*/0},
176
/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
177
/* URD and NRM aren't implemented */
178
};
179
 
180
static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
181
[0]	{"ADDF",	fadd},
182
[1]	{"MULF",	fmul},
183
[2]	{"SUBF",	fsub},
184
[3]	{"RSUBF",	fsubr},
185
[4]	{"DIVF",	fdiv},
186
[5]	{"RDIVF",	fdivr},
187
/* POW, RPW deprecated */
188
[8]	{"REMF",	/*frem*/0},
189
[9]	{"FMF",	fmul},	/* fast multiply */
190
[10]	{"FDV",	fdiv},	/* fast divide */
191
[11]	{"FRD",	fdivr},	/* fast reverse divide */
192
/* POL deprecated */
193
};
194
 
195
static ulong
196
fcmp(Internal *n, Internal *m)
197
{
198
	int i;
199
	Internal rm, rn;
200
 
201
	if(IsWeird(m) || IsWeird(n)){
202
		/* BUG: should trap if not masked */
203
		return V|C;
204
	}
205
	rn = *n;
206
	rm = *m;
207
	fpiround(&rn);
208
	fpiround(&rm);
209
	i = fpicmp(&rn, &rm);
210
	if(i > 0)
211
		return C;
212
	else if(i == 0)
213
		return C|Z;
214
	else
215
		return N;
216
}
217
 
218
static void
219
fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
220
{
221
	void *mem;
222
 
223
	mem = (void*)ea;
224
	(*f)(&FR(ufp, d), mem);
225
	if(fpemudebug)
226
		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
227
}
228
 
229
static void
230
fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
231
{
232
	Internal tmp;
233
	void *mem;
234
 
235
	mem = (void*)ea;
236
	tmp = FR(ufp, s);
237
	if(fpemudebug)
238
		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
239
	(*f)(mem, &tmp);
240
}
241
 
242
static int
243
condok(int cc, int c)
244
{
245
	switch(c){
246
	case 0:	/* Z set */
247
		return cc&Z;
248
	case 1:	/* Z clear */
249
		return (cc&Z) == 0;
250
	case 2:	/* C set */
251
		return cc&C;
252
	case 3:	/* C clear */
253
		return (cc&C) == 0;
254
	case 4:	/* N set */
255
		return cc&N;
256
	case 5:	/* N clear */
257
		return (cc&N) == 0;
258
	case 6:	/* V set */
259
		return cc&V;
260
	case 7:	/* V clear */
261
		return (cc&V) == 0;
262
	case 8:	/* C set and Z clear */
263
		return cc&C && (cc&Z) == 0;
264
	case 9:	/* C clear or Z set */
265
		return (cc&C) == 0 || cc&Z;
266
	case 10:	/* N set and V set, or N clear and V clear */
267
		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
268
	case 11:	/* N set and V clear, or N clear and V set */
269
		return (cc&(N|V))==N || (cc&(N|V))==V;
270
	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
271
		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
272
	case 13:	/* Z set, or N set and V clear or N clear and V set */
273
		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
274
	case 14:	/* always */
275
		return 1;
276
	case 15:	/* never (reserved) */
277
		return 0;
278
	}
279
	return 0;	/* not reached */
280
}
281
 
282
static void
283
unimp(ulong pc, ulong op)
284
{
285
	char buf[60];
286
 
287
	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
288
	if(fpemudebug)
289
		print("FPE: %s\n", buf);
290
	error(buf);
291
	/* no return */
292
}
293
 
294
static void
295
fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
296
{
297
	int rn, rd, tag, o;
298
	long off;
299
	ulong ea;
300
	Internal tmp, *fm, *fn;
301
 
302
	/* note: would update fault status here if we noted numeric exceptions */
303
 
304
	/*
305
	 * LDF, STF; 10.1.1
306
	 */
307
	if(((op>>25)&7) == 6){
308
		if(op & (1<<22))
309
			unimp(pc, op);	/* packed or extended */
310
		rn = (op>>16)&0xF;
311
		off = (op&0xFF)<<2;
312
		if((op & (1<<23)) == 0)
313
			off = -off;
314
		ea = REG(ur, rn);
315
		if(rn == REGPC)
316
			ea += 8;
317
		if(op & (1<<24))
318
			ea += off;
319
		rd = (op>>12)&7;
320
		if(op & (1<<20)){
321
			if(op & (1<<15))
322
				fld(fpid2i, rd, ea, 8, ufp);
323
			else
324
				fld(fpis2i, rd, ea, 4, ufp);
325
		}else{
326
			if(op & (1<<15))
327
				fst(fpii2d, ea, rd, 8, ufp);
328
			else
329
				fst(fpii2s, ea, rd, 4, ufp);
330
		}
331
		if((op & (1<<24)) == 0)
332
			ea += off;
333
		if(op & (1<<21))
334
			REG(ur, rn) = ea;
335
		return;
336
	}
337
 
338
	/*
339
	 * CPRT/transfer, 10.3
340
	 */
341
	if(op & (1<<4)){
342
		rd = (op>>12) & 0xF;
343
 
344
		/*
345
		 * compare, 10.3.1
346
		 */
347
		if(rd == 15 && op & (1<<20)){
348
			rn = (op>>16)&7;
349
			fn = &FR(ufp, rn);
350
			if(op & (1<<3)){
351
				fm = &fpconst[op&7];
352
				if(fpemudebug)
353
					tag = 'C';
354
			}else{
355
				fm = &FR(ufp, op&7);
356
				if(fpemudebug)
357
					tag = 'F';
358
			}
359
			switch((op>>21)&7){
360
			default:
361
				unimp(pc, op);
362
			case 4:	/* CMF: Fn :: Fm */
363
			case 6:	/* CMFE: Fn :: Fm (with exception) */
364
				ur->psr &= ~(N|C|Z|V);
365
				ur->psr |= fcmp(fn, fm);
366
				break;
367
			case 5:	/* CNF: Fn :: -Fm */
368
			case 7:	/* CNFE: Fn :: -Fm (with exception) */
369
				tmp = *fm;
370
				tmp.s ^= 1;
371
				ur->psr &= ~(N|C|Z|V);
372
				ur->psr |= fcmp(fn, &tmp);
373
				break;
374
			}
375
			if(fpemudebug)
376
				print("CMPF	%c%d,F%ld =%#lux\n",
377
					tag, rn, op&7, ur->psr>>28);
378
			return;
379
		}
380
 
381
		/*
382
		 * other transfer, 10.3
383
		 */
384
		switch((op>>20)&0xF){
385
		default:
386
			unimp(pc, op);
387
		case 0:	/* FLT */
388
			rn = (op>>16) & 7;
389
			fpiw2i(&FR(ufp, rn), &REG(ur, rd));
390
			if(fpemudebug)
391
				print("MOVW[FD]	R%d, F%d\n", rd, rn);
392
			break;
393
		case 1:	/* FIX */
394
			if(op & (1<<3))
395
				unimp(pc, op);
396
			rn = op & 7;
397
			tmp = FR(ufp, rn);
398
			fpii2w(&REG(ur, rd), &tmp);
399
			if(fpemudebug)
400
				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
401
			break;
402
		case 2:	/* FPSR := Rd */
403
			ufp->status = REG(ur, rd);
404
			if(fpemudebug)
405
				print("MOVW	R%d, FPSR\n", rd);
406
			break;
407
		case 3:	/* Rd := FPSR */
408
			REG(ur, rd) = ufp->status;
409
			if(fpemudebug)
410
				print("MOVW	FPSR, R%d\n", rd);
411
			break;
412
		case 4:	/* FPCR := Rd */
413
			ufp->control = REG(ur, rd);
414
			if(fpemudebug)
415
				print("MOVW	R%d, FPCR\n", rd);
416
			break;
417
		case 5:	/* Rd := FPCR */
418
			REG(ur, rd) = ufp->control;
419
			if(fpemudebug)
420
				print("MOVW	FPCR, R%d\n", rd);
421
			break;
422
		}
423
		return;
424
	}
425
 
426
	/*
427
	 * arithmetic
428
	 */
429
 
430
	if(op & (1<<3)){	/* constant */
431
		fm = &fpconst[op&7];
432
		if(fpemudebug)
433
			tag = 'C';
434
	}else{
435
		fm = &FR(ufp, op&7);
436
		if(fpemudebug)
437
			tag = 'F';
438
	}
439
	rd = (op>>12)&7;
440
	o = (op>>20)&0xF;
441
	if(op & (1<<15)){	/* monadic */
442
		FP1 *fp;
443
		fp = &optab1[o];
444
		if(fp->f == nil)
445
			unimp(pc, op);
446
		if(fpemudebug)
447
			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
448
		(*fp->f)(fm, &FR(ufp, rd));
449
	} else {
450
		FP2 *fp;
451
		fp = &optab2[o];
452
		if(fp->f == nil)
453
			unimp(pc, op);
454
		rn = (op>>16)&7;
455
		if(fpemudebug)
456
			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
457
		(*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
458
	}
459
}
460
 
461
/*
462
 * returns the number of FP instructions emulated
463
 */
464
int
465
fpiarm(Ureg *ur)
466
{
467
	ulong op, o, cp;
468
	FPsave *ufp;
469
	int n;
470
 
471
	if(up == nil)
472
		panic("fpiarm not in a process");
473
	ufp = &up->fpsave;
474
	/*
475
	 * because all the emulated fp state is in the proc structure,
476
	 * it need not be saved/restored
477
	 */
478
	switch(up->fpstate){
479
	case FPactive:
480
	case FPinactive:
481
		error("illegal instruction: emulated fpu opcode in VFP mode");
482
	case FPinit:
483
		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
484
		up->fpstate = FPemu;
485
		ufp->control = 0;
486
		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
487
		for(n = 0; n < 8; n++)
488
			FR(ufp, n) = fpconst[0];
489
	}
490
	for(n=0; ;n++){
491
		validaddr(ur->pc, 4, 0);
492
		op = *(ulong*)(ur->pc);
493
		if(fpemudebug)
494
			print("%#lux: %#8.8lux ", ur->pc, op);
495
		o = (op>>24) & 0xF;
496
		cp = (op>>8) & 0xF;
497
		if(!ISFPAOP(cp, o))
498
			break;
499
		if(condok(ur->psr, op>>28))
500
			fpemu(ur->pc, op, ur, ufp);
501
		ur->pc += 4;		/* pretend cpu executed the instr */
502
	}
503
	if(fpemudebug)
504
		print("\n");
505
	return n;
506
}