Subversion Repositories planix.SVN

Rev

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