dosbox-wii/src/fpu/fpu_instructions.h

395 lines
9.2 KiB
C
Raw Normal View History

2009-05-02 23:12:18 +02:00
/*
2009-05-02 23:53:27 +02:00
* Copyright (C) 2002-2004 The DOSBox Team
2009-05-02 23:12:18 +02:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-05-03 00:02:15 +02:00
* GNU General Public License for more details.
2009-05-02 23:12:18 +02:00
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
2009-05-03 00:02:15 +02:00
/* $Id: fpu_instructions.h,v 1.21 2004/09/09 18:36:50 qbix79 Exp $ */
2009-05-02 23:43:00 +02:00
static void FPU_FINIT(void) {
FPU_SetCW(0x37F);
fpu.sw=0;
TOP=FPU_GET_TOP();
fpu.tags[0]=TAG_Empty;
fpu.tags[1]=TAG_Empty;
fpu.tags[2]=TAG_Empty;
fpu.tags[3]=TAG_Empty;
fpu.tags[4]=TAG_Empty;
fpu.tags[5]=TAG_Empty;
fpu.tags[6]=TAG_Empty;
fpu.tags[7]=TAG_Empty;
fpu.tags[8]=TAG_Valid; // is only used by us
}
static void FPU_FCLEX(void){
fpu.sw&=0x7f00; //should clear exceptions
};
static void FPU_FNOP(void){
return;
}
static void FPU_PUSH(double in){
TOP = (TOP - 1) &7;
//actually check if empty
fpu.tags[TOP]=TAG_Valid;
fpu.regs[TOP].d=in;
// LOG(LOG_FPU,LOG_ERROR)("Pushed at %d %g to the stack",newtop,in);
return;
}
static void FPU_PUSH_ZERO(void){
FPU_PUSH(0.0);
return; //maybe oneday needed
}
static void FPU_FPOP(void){
fpu.tags[TOP]=TAG_Empty;
//maybe set zero in it as well
TOP = ((TOP+1)&7);
// LOG(LOG_FPU,LOG_ERROR)("popped from %d %g off the stack",top,fpu.regs[top].d);
return;
}
static void FPU_FADD(Bitu op1, Bitu op2){
fpu.regs[op1].d+=fpu.regs[op2].d;
//flags and such :)
return;
}
static void FPU_FSIN(void){
fpu.regs[TOP].d = sin(fpu.regs[TOP].d);
FPU_SET_C2(0);
//flags and such :)
return;
}
static void FPU_FSINCOS(void){
Real64 temp = fpu.regs[TOP].d;
fpu.regs[TOP].d = sin(temp);
FPU_PUSH(cos(temp));
FPU_SET_C2(0);
//flags and such :)
return;
}
static void FPU_FCOS(void){
fpu.regs[TOP].d = cos(fpu.regs[TOP].d);
FPU_SET_C2(0);
//flags and such :)
return;
}
static void FPU_FSQRT(void){
fpu.regs[TOP].d = sqrt(fpu.regs[TOP].d);
//flags and such :)
return;
}
static void FPU_FPATAN(void){
2009-05-02 23:53:27 +02:00
fpu.regs[ST(1)].d = atan2(fpu.regs[ST(1)].d,fpu.regs[TOP].d);
2009-05-02 23:43:00 +02:00
FPU_FPOP();
FPU_SET_C2(0);
//flags and such :)
return;
}
static void FPU_FPTAN(void){
fpu.regs[TOP].d = tan(fpu.regs[TOP].d);
FPU_PUSH(1.0);
FPU_SET_C2(0);
//flags and such :)
return;
}
static void FPU_FDIV(Bitu st, Bitu other){
fpu.regs[st].d= fpu.regs[st].d/fpu.regs[other].d;
//flags and such :)
return;
}
static void FPU_FDIVR(Bitu st, Bitu other){
fpu.regs[st].d= fpu.regs[other].d/fpu.regs[st].d;
// flags and such :)
return;
};
static void FPU_FMUL(Bitu st, Bitu other){
fpu.regs[st].d*=fpu.regs[other].d;
//flags and such :)
return;
}
static void FPU_FSUB(Bitu st, Bitu other){
fpu.regs[st].d = fpu.regs[st].d - fpu.regs[other].d;
//flags and such :)
return;
}
static void FPU_FSUBR(Bitu st, Bitu other){
fpu.regs[st].d= fpu.regs[other].d - fpu.regs[st].d;
//flags and such :)
return;
2009-05-02 23:12:18 +02:00
}
2009-05-02 23:43:00 +02:00
static void FPU_FXCH(Bitu st, Bitu other){
FPU_Tag tag = fpu.tags[other];
FPU_Reg reg = fpu.regs[other];
fpu.tags[other] = fpu.tags[st];
fpu.regs[other] = fpu.regs[st];
fpu.tags[st] = tag;
fpu.regs[st] = reg;
}
static void FPU_FST(Bitu st, Bitu other){
fpu.tags[other] = fpu.tags[st];
fpu.regs[other] = fpu.regs[st];
}
static void FPU_FCOM(Bitu st, Bitu other){
if((fpu.tags[st] != TAG_Valid) || (fpu.tags[other] != TAG_Valid)){
FPU_SET_C3(1);FPU_SET_C2(1);FPU_SET_C0(1);return;
}
if(fpu.regs[st].d == fpu.regs[other].d){
FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(0);return;
}
if(fpu.regs[st].d < fpu.regs[other].d){
FPU_SET_C3(0);FPU_SET_C2(0);FPU_SET_C0(1);return;
}
// st > other
FPU_SET_C3(0);FPU_SET_C2(0);FPU_SET_C0(0);return;
}
static void FPU_FUCOM(Bitu st, Bitu other){
//does atm the same as fcom
FPU_FCOM(st,other);
}
static double FROUND(double in){
switch(fpu.round){
case ROUND_Nearest:
if (in-floor(in)>0.5) return (floor(in)+1);
else if (in-floor(in)<0.5) return (floor(in));
else return (((static_cast<Bit64s>(floor(in)))&1)!=0)?(floor(in)+1):(floor(in));
break;
case ROUND_Down:
return (floor(in));
break;
case ROUND_Up:
return (ceil(in));
break;
case ROUND_Chop:
return in; //the cast afterwards will do it right maybe cast here
break;
default:
return in;
break;
}
}
static void FPU_FPREM(void){
Real64 valtop = fpu.regs[TOP].d;
Real64 valdiv = fpu.regs[ST(1)].d;
2009-05-02 23:53:27 +02:00
Bit64s ressaved = static_cast<Bit64s>( (valtop/valdiv) );
// Some backups
// Real64 res=valtop - ressaved*valdiv;
// res= fmod(valtop,valdiv);
fpu.regs[TOP].d = valtop - ressaved*valdiv;
2009-05-02 23:43:00 +02:00
FPU_SET_C0(static_cast<Bitu>(ressaved&4));
FPU_SET_C3(static_cast<Bitu>(ressaved&2));
FPU_SET_C1(static_cast<Bitu>(ressaved&1));
FPU_SET_C2(0);
}
static void FPU_FXAM(void){
if(fpu.tags[TOP] == TAG_Empty)
{
FPU_SET_C3(1);FPU_SET_C0(1);
return;
}
if(fpu.regs[TOP].ll & LONGTYPE(0x8000000000000000)) //sign
{
FPU_SET_C1(1);
}
else
{
FPU_SET_C1(0);
}
if(fpu.regs[TOP].d == 0.0) //zero or normalized number.
{
FPU_SET_C3(1);FPU_SET_C2(0);FPU_SET_C0(0);
}
else{
FPU_SET_C3(0);FPU_SET_C2(1);FPU_SET_C0(0);
}
}
static void FPU_FBST(PhysPt addr)
{
FPU_Reg val = fpu.regs[TOP];
bool sign = false;
if(val.d<0.0){ //sign
sign=true;
val.d=-val.d;
}
//numbers from back to front
Real64 temp=val.d;
Bitu p;
for(Bitu i=0;i<9;i++){
val.d=temp;
temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
p = static_cast<Bitu>(val.d - 10.0*temp);
val.d=temp;
temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
p |= (static_cast<Bitu>(val.d - 10.0*temp)<<4);
mem_writeb(addr+i,p);
}
val.d=temp;
temp = static_cast<Real64>(static_cast<Bit64s>(floor(val.d/10.0)));
p = static_cast<Bitu>(val.d - 10.0*temp);
if(sign)
p|=0x80;
mem_writeb(addr+9,p);
}
2009-05-03 00:02:15 +02:00
static Real64 FPU_FBLD(PhysPt addr)
{
Bit64u val = 0;
Bitu in = 0;
Bit64u base = 1;
for(Bitu i = 0;i < 9;i++){
in = mem_readb(addr + i);
val += ( (in&0xf) * base); //in&0xf shouldn't be higher then 9
base *= 10;
val += ((( in>>4)&0xf) * base);
base *= 10;
}
//last number, only now convert to float in order to get
//the best signification
Real64 temp = static_cast<Real64>(val);
in = mem_readb(addr + 9);
temp += ( (in&0xf) * base );
if(in&0x80) temp *= -1.0;
return temp;
}
2009-05-02 23:43:00 +02:00
#define BIAS80 16383
#define BIAS64 1023
2009-05-03 00:02:15 +02:00
static Real64 FPU_FLD80(PhysPt addr)
2009-05-02 23:43:00 +02:00
{
struct{
Bit16s begin;
FPU_Reg eind;
} test;
test.eind.l.lower=mem_readd(addr);
test.eind.l.upper =mem_readd(addr+4);
test.begin=mem_readw(addr+8);
Bit64s exp64= (((test.begin & 0x7fff) - BIAS80));
Bit64s blah= ((exp64 >0)?exp64:-exp64)&0x3ff;
Bit64s exp64final= ((exp64 >0)?blah:-blah) +BIAS64;
2009-05-02 23:12:18 +02:00
2009-05-02 23:43:00 +02:00
Bit64s mant64= (test.eind.ll >> 11) & LONGTYPE(0xfffffffffffff);
Bit64s sign = (test.begin &0x8000)?1:0;
FPU_Reg result;
result.ll= (sign <<63)|(exp64final << 52)| mant64;
2009-05-03 00:02:15 +02:00
return result.d;
2009-05-02 23:43:00 +02:00
//mant64= test.mant80/2***64 * 2 **53
}
2009-05-03 00:02:15 +02:00
static void FPU_ST80(PhysPt addr,Bitu reg)
2009-05-02 23:43:00 +02:00
{
struct{
Bit16s begin;
FPU_Reg eind;
} test;
2009-05-03 00:02:15 +02:00
Bit64s sign80= (fpu.regs[reg].ll&LONGTYPE(0x8000000000000000))?1:0;
Bit64s exp80 = fpu.regs[reg].ll&LONGTYPE(0x7ff0000000000000);
2009-05-02 23:43:00 +02:00
Bit64s exp80final= (exp80>>52) - BIAS64 + BIAS80;
2009-05-03 00:02:15 +02:00
Bit64s mant80 = fpu.regs[reg].ll&LONGTYPE(0x000fffffffffffff);
2009-05-02 23:43:00 +02:00
Bit64s mant80final= (mant80 << 11) | LONGTYPE(0x8000000000000000);
test.begin= (static_cast<Bit16s>(sign80)<<15)| static_cast<Bit16s>(exp80final);
test.eind.ll=mant80final;
mem_writed(addr,test.eind.l.lower);
mem_writed(addr+4,test.eind.l.upper);
mem_writew(addr+8,test.begin);
}
2009-05-02 23:12:18 +02:00
2009-05-02 23:43:00 +02:00
static void FPU_F2XM1(void){
fpu.regs[TOP].d=pow(2.0,fpu.regs[TOP].d) -1;
return;
}
2009-05-02 23:53:27 +02:00
static void FPU_FYL2X(void){
2009-05-02 23:43:00 +02:00
fpu.regs[ST(1)].d*=log(fpu.regs[TOP].d)/log(static_cast<Real64>(2.0));
FPU_FPOP();
return;
}
static void FPU_FSCALE(void){
2009-05-02 23:53:27 +02:00
fpu.regs[TOP].d *= pow(2.0,static_cast<Real64>(static_cast<Bit64s>(fpu.regs[ST(1)].d)));
2009-05-02 23:43:00 +02:00
return; //2^x where x is chopped.
}
2009-05-02 23:12:18 +02:00
2009-05-03 00:02:15 +02:00
static void FPU_FSTENV(PhysPt addr){
if(!cpu.code.big) {
mem_writew(addr+0,static_cast<Bit16u>(fpu.cw));
mem_writew(addr+2,static_cast<Bit16u>(fpu.sw));
mem_writew(addr+4,static_cast<Bit16u>(FPU_GetTag()));
} else {
mem_writed(addr+0,static_cast<Bit32u>(fpu.cw));
mem_writed(addr+4,static_cast<Bit32u>(fpu.sw));
mem_writed(addr+8,static_cast<Bit32u>(FPU_GetTag()));
}
}
static void FPU_FLDENV(PhysPt addr){
Bit16u tag;
Bit32u tagbig;
Bitu cw;
if(!cpu.code.big) {
cw = mem_readw(addr+0);
fpu.sw = mem_readw(addr+2);
tag = mem_readw(addr+4);
} else {
cw = mem_readd(addr+0);
fpu.sw = mem_readd(addr+4);
tagbig = mem_readd(addr+8);
tag = static_cast<Bit16u>(tagbig);
}
FPU_SetTag(tag);
FPU_SetCW(cw);
}
static void FPU_FSAVE(PhysPt addr){
FPU_FSTENV(addr);
Bitu start=(cpu.code.big?28:14);
for(Bitu i=0;i<8;i++){
FPU_ST80(addr+start,i);
start+=10;
}
}
static void FPU_FSTOR(PhysPt addr){
FPU_FLDENV(addr);
Bitu start=(cpu.code.big?28:14);
for(Bitu i=0;i<8;i++){
fpu.regs[i].d=FPU_FLD80(addr+start);
start+=10;
}
}