shithub: drawcpu

ref: 6ca7a7fcb03e0ef651368e5a782b29c392e16149
dir: /kern/vfp.c/

View raw version
#include <u.h>
#include <lib.h>
#include "dat.h"
#include "fns.h"
#include "proc.h"

void
resetvfp(void)
{
	int i;

	up->FPSR = 0x00000000;
	for(i = 0; i < Nfpregs; i++)
		up->F[i] = 0;
}

void
vfpregtransfer(u32int instr)
{
	u32int *Rt;
	double *Fn;
	Rt = up->R + ((instr>>12)&0xF);
	Fn = up->F + ((instr>>16)&0xF);
	switch((instr>>20)&0xF){
	case 0:
		*((int*)Fn) = *Rt; break;
	case 1:
		*Rt = *((int*)Fn); break;
	case 14:
		up->FPSR = *Rt; break;
	case 15:
		if(Rt == (up->R + 15))
			up->CPSR = up->FPSR;
		else
			*Rt = up->FPSR;
		break;
	default:
		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
	}
}

void
vfprmtransfer(u32int instr)
{
	int n, d, off, sz;
	void* ea;
	Segment *seg;
	n = (instr>>16) & 0xF;
	d = (instr>>12) & 0xF;
	off = (instr & 0xFF) << 2;
	sz = instr & (1<<8);
	if((instr & (1<<23)) == 0)
		off = -off;
	ea = vaddr(evenaddr(up->R[n] + off, 3), sz ? 8 : 4, &seg);
	switch((instr>>20)&0x3){
	case 0:
		if(sz)
			*(double*)ea = up->F[d];
		else
			*(float*)ea = up->F[d];
		break;
	case 1:
		if(sz)
			up->F[d] = *(double*)ea;
		else
			up->F[d] = *(float*)ea;
		break;
	case 2:
        if(sz)
            *(double*)ea = up->F[d];
        else
            *(float*)ea = up->F[d];
		up->R[n] += off;
        break;
	default:
		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
	}
	segunlock(seg);
}

void
vfparithop(int opc, u32int instr)
{
	int o;
	double *Fd, *Fn, *Fm;

	Fd = up->F + ((instr>>12)&0xF);
	Fn = up->F + ((instr>>16)&0xF);
	Fm = up->F + (instr&0xF);
	o = ((opc&0x3)<<1) | (opc&0x8) | ((instr>>6)&0x1);

	switch(o){
	case 4:
		*Fd = *Fn * *Fm; break;
	case 6:
		*Fd = *Fn + *Fm; break;
	case 7:
		*Fd = *Fn - *Fm; break;
	case 8:
		*Fd = *Fn / *Fm; break;
	default:
		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
	}
}

void
vfpotherop(u32int instr)
{
	int o2, o3;
	double *Fd, *Fm, F0;

	Fd = up->F + ((instr>>12)&0xF);
	Fm = up->F + (instr&0xF);
	F0 = 0.0;
	o2 = (instr>>16) & 0xF;
	o3 = (instr>>6) & 0x3;

	if((o3&1) == 0)
		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
	switch(o2){
	case 0x5:
		Fm = &F0;
	case 0x4:
		if(*Fd < *Fm)
			up->FPSR = (up->FPSR & ~FLAGS) | flN;
		else if(*Fd >= *Fm) {
			up->FPSR = (up->FPSR & ~FLAGS) | flC;
			if(*Fd == *Fm)
				up->FPSR |= flZ;
		} else
			up->FPSR = (up->FPSR & ~FLAGS) | flV | flC;
		break;
	case 0x8:
		*Fd = *((int*)Fm); break;
	case 0xD:
		*((int*)Fd) = (int)*Fm; break;
	default:
		switch((o2<<1)|(o3>>1)){
		case 0:
		case 15:
			*Fd = *Fm; break;
		case 1:
			*Fd = fabs(*Fm); break;
		case 2:
			*Fd = -*Fm; break;
		case 3:
			*Fd = sqrt(*Fm); break;
		default:
			sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, up->R[15] - 4);
		}
	}
}

void
vfpoperation(u32int instr)
{
	int o1;
	o1 = (instr>>20) & 0xF;
	if(o1 == 0xB)
		vfpotherop(instr);
	else
		vfparithop(o1, instr);
}