xref: /qemu/linux-user/arm/nwfpe/fpa11_cpdt.c (revision 1be5a765c08cee3a9587c8a8d3fc2ea247b13f9c)
100406dffSbellard /*
200406dffSbellard     NetWinder Floating Point Emulator
300406dffSbellard     (c) Rebel.com, 1998-1999
400406dffSbellard     (c) Philip Blundell, 1998
500406dffSbellard 
600406dffSbellard     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
700406dffSbellard 
800406dffSbellard     This program is free software; you can redistribute it and/or modify
900406dffSbellard     it under the terms of the GNU General Public License as published by
1000406dffSbellard     the Free Software Foundation; either version 2 of the License, or
1100406dffSbellard     (at your option) any later version.
1200406dffSbellard 
1300406dffSbellard     This program is distributed in the hope that it will be useful,
1400406dffSbellard     but WITHOUT ANY WARRANTY; without even the implied warranty of
1500406dffSbellard     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1600406dffSbellard     GNU General Public License for more details.
1700406dffSbellard 
1800406dffSbellard     You should have received a copy of the GNU General Public License
1970539e18SBlue Swirl     along with this program; if not, see <http://www.gnu.org/licenses/>.
2000406dffSbellard */
2100406dffSbellard 
22d39594e9SPeter Maydell #include "qemu/osdep.h"
2300406dffSbellard #include "fpa11.h"
246b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
2500406dffSbellard #include "fpopcode.h"
2600406dffSbellard //#include "fpmodule.h"
2700406dffSbellard //#include "fpmodule.inl"
2800406dffSbellard 
2900406dffSbellard //#include <asm/uaccess.h>
3000406dffSbellard 
3100406dffSbellard static inline
loadSingle(const unsigned int Fn,target_ulong addr)3265a650c2SPaul Brook void loadSingle(const unsigned int Fn, target_ulong addr)
3300406dffSbellard {
3400406dffSbellard    FPA11 *fpa11 = GET_FPA11();
3500406dffSbellard    fpa11->fType[Fn] = typeSingle;
362f619698Sbellard    /* FIXME - handle failure of get_user() */
37005e1a0aSPeter Maydell    get_user_u32(float32_val(fpa11->fpreg[Fn].fSingle), addr);
3800406dffSbellard }
3900406dffSbellard 
4000406dffSbellard static inline
loadDouble(const unsigned int Fn,target_ulong addr)4165a650c2SPaul Brook void loadDouble(const unsigned int Fn, target_ulong addr)
4200406dffSbellard {
4300406dffSbellard    FPA11 *fpa11 = GET_FPA11();
4400406dffSbellard    unsigned int *p;
4500406dffSbellard    p = (unsigned int*)&fpa11->fpreg[Fn].fDouble;
4600406dffSbellard    fpa11->fType[Fn] = typeDouble;
47*e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
482f619698Sbellard    /* FIXME - handle failure of get_user() */
492f619698Sbellard    get_user_u32(p[0], addr); /* sign & exponent */
502f619698Sbellard    get_user_u32(p[1], addr + 4);
51a8d3431aSbellard #else
522f619698Sbellard    /* FIXME - handle failure of get_user() */
532f619698Sbellard    get_user_u32(p[0], addr + 4);
542f619698Sbellard    get_user_u32(p[1], addr); /* sign & exponent */
55a8d3431aSbellard #endif
5600406dffSbellard }
5700406dffSbellard 
5800406dffSbellard static inline
loadExtended(const unsigned int Fn,target_ulong addr)5965a650c2SPaul Brook void loadExtended(const unsigned int Fn, target_ulong addr)
6000406dffSbellard {
6100406dffSbellard    FPA11 *fpa11 = GET_FPA11();
6200406dffSbellard    unsigned int *p;
6300406dffSbellard    p = (unsigned int*)&fpa11->fpreg[Fn].fExtended;
6400406dffSbellard    fpa11->fType[Fn] = typeExtended;
652f619698Sbellard    /* FIXME - handle failure of get_user() */
662f619698Sbellard    get_user_u32(p[0], addr);  /* sign & exponent */
672f619698Sbellard    get_user_u32(p[1], addr + 8);  /* ls bits */
682f619698Sbellard    get_user_u32(p[2], addr + 4);  /* ms bits */
6900406dffSbellard }
7000406dffSbellard 
7100406dffSbellard static inline
loadMultiple(const unsigned int Fn,target_ulong addr)7265a650c2SPaul Brook void loadMultiple(const unsigned int Fn, target_ulong addr)
7300406dffSbellard {
7400406dffSbellard    FPA11 *fpa11 = GET_FPA11();
7500406dffSbellard    register unsigned int *p;
7600406dffSbellard    unsigned long x;
7700406dffSbellard 
7800406dffSbellard    p = (unsigned int*)&(fpa11->fpreg[Fn]);
792f619698Sbellard    /* FIXME - handle failure of get_user() */
802f619698Sbellard    get_user_u32(x, addr);
8100406dffSbellard    fpa11->fType[Fn] = (x >> 14) & 0x00000003;
8200406dffSbellard 
8300406dffSbellard    switch (fpa11->fType[Fn])
8400406dffSbellard    {
8500406dffSbellard       case typeSingle:
8600406dffSbellard       case typeDouble:
8700406dffSbellard       {
882f619698Sbellard          /* FIXME - handle failure of get_user() */
892f619698Sbellard          get_user_u32(p[0], addr + 8);  /* Single */
902f619698Sbellard          get_user_u32(p[1], addr + 4);  /* double msw */
9100406dffSbellard          p[2] = 0;        /* empty */
9200406dffSbellard       }
9300406dffSbellard       break;
9400406dffSbellard 
9500406dffSbellard       case typeExtended:
9600406dffSbellard       {
972f619698Sbellard          /* FIXME - handle failure of get_user() */
982f619698Sbellard          get_user_u32(p[1], addr + 8);
992f619698Sbellard          get_user_u32(p[2], addr + 4);  /* msw */
10000406dffSbellard          p[0] = (x & 0x80003fff);
10100406dffSbellard       }
10200406dffSbellard       break;
10300406dffSbellard    }
10400406dffSbellard }
10500406dffSbellard 
10600406dffSbellard static inline
storeSingle(const unsigned int Fn,target_ulong addr)10765a650c2SPaul Brook void storeSingle(const unsigned int Fn, target_ulong addr)
10800406dffSbellard {
10900406dffSbellard    FPA11 *fpa11 = GET_FPA11();
11000406dffSbellard    float32 val;
11100406dffSbellard    register unsigned int *p = (unsigned int*)&val;
11200406dffSbellard 
11300406dffSbellard    switch (fpa11->fType[Fn])
11400406dffSbellard    {
11500406dffSbellard       case typeDouble:
11620495218Sbellard          val = float64_to_float32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
11700406dffSbellard       break;
11800406dffSbellard 
11900406dffSbellard       case typeExtended:
12020495218Sbellard          val = floatx80_to_float32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
12100406dffSbellard       break;
12200406dffSbellard 
12300406dffSbellard       default: val = fpa11->fpreg[Fn].fSingle;
12400406dffSbellard    }
12500406dffSbellard 
1262f619698Sbellard    /* FIXME - handle put_user() failures */
1272f619698Sbellard    put_user_u32(p[0], addr);
12800406dffSbellard }
12900406dffSbellard 
13000406dffSbellard static inline
storeDouble(const unsigned int Fn,target_ulong addr)13165a650c2SPaul Brook void storeDouble(const unsigned int Fn, target_ulong addr)
13200406dffSbellard {
13300406dffSbellard    FPA11 *fpa11 = GET_FPA11();
13400406dffSbellard    float64 val;
13500406dffSbellard    register unsigned int *p = (unsigned int*)&val;
13600406dffSbellard 
13700406dffSbellard    switch (fpa11->fType[Fn])
13800406dffSbellard    {
13900406dffSbellard       case typeSingle:
14020495218Sbellard          val = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
14100406dffSbellard       break;
14200406dffSbellard 
14300406dffSbellard       case typeExtended:
14420495218Sbellard          val = floatx80_to_float64(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
14500406dffSbellard       break;
14600406dffSbellard 
14700406dffSbellard       default: val = fpa11->fpreg[Fn].fDouble;
14800406dffSbellard    }
1492f619698Sbellard    /* FIXME - handle put_user() failures */
150*e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
1512f619698Sbellard    put_user_u32(p[0], addr);	/* msw */
1522f619698Sbellard    put_user_u32(p[1], addr + 4);	/* lsw */
153a8d3431aSbellard #else
1542f619698Sbellard    put_user_u32(p[1], addr);	/* msw */
1552f619698Sbellard    put_user_u32(p[0], addr + 4);	/* lsw */
156a8d3431aSbellard #endif
15700406dffSbellard }
15800406dffSbellard 
15900406dffSbellard static inline
storeExtended(const unsigned int Fn,target_ulong addr)16065a650c2SPaul Brook void storeExtended(const unsigned int Fn, target_ulong addr)
16100406dffSbellard {
16200406dffSbellard    FPA11 *fpa11 = GET_FPA11();
16300406dffSbellard    floatx80 val;
16400406dffSbellard    register unsigned int *p = (unsigned int*)&val;
16500406dffSbellard 
16600406dffSbellard    switch (fpa11->fType[Fn])
16700406dffSbellard    {
16800406dffSbellard       case typeSingle:
16920495218Sbellard          val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
17000406dffSbellard       break;
17100406dffSbellard 
17200406dffSbellard       case typeDouble:
17320495218Sbellard          val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
17400406dffSbellard       break;
17500406dffSbellard 
17600406dffSbellard       default: val = fpa11->fpreg[Fn].fExtended;
17700406dffSbellard    }
17800406dffSbellard 
1792f619698Sbellard    /* FIXME - handle put_user() failures */
1802f619698Sbellard    put_user_u32(p[0], addr); /* sign & exp */
1812f619698Sbellard    put_user_u32(p[1], addr + 8);
1822f619698Sbellard    put_user_u32(p[2], addr + 4); /* msw */
18300406dffSbellard }
18400406dffSbellard 
18500406dffSbellard static inline
storeMultiple(const unsigned int Fn,target_ulong addr)18665a650c2SPaul Brook void storeMultiple(const unsigned int Fn, target_ulong addr)
18700406dffSbellard {
18800406dffSbellard    FPA11 *fpa11 = GET_FPA11();
18900406dffSbellard    register unsigned int nType, *p;
19000406dffSbellard 
19100406dffSbellard    p = (unsigned int*)&(fpa11->fpreg[Fn]);
19200406dffSbellard    nType = fpa11->fType[Fn];
19300406dffSbellard 
19400406dffSbellard    switch (nType)
19500406dffSbellard    {
19600406dffSbellard       case typeSingle:
19700406dffSbellard       case typeDouble:
19800406dffSbellard       {
1992f619698Sbellard          put_user_u32(p[0], addr + 8); /* single */
2002f619698Sbellard 	 put_user_u32(p[1], addr + 4); /* double msw */
2012f619698Sbellard 	 put_user_u32(nType << 14, addr);
20200406dffSbellard       }
20300406dffSbellard       break;
20400406dffSbellard 
20500406dffSbellard       case typeExtended:
20600406dffSbellard       {
2072f619698Sbellard          put_user_u32(p[2], addr + 4); /* msw */
2082f619698Sbellard 	 put_user_u32(p[1], addr + 8);
2092f619698Sbellard 	 put_user_u32((p[0] & 0x80003fff) | (nType << 14), addr);
21000406dffSbellard       }
21100406dffSbellard       break;
21200406dffSbellard    }
21300406dffSbellard }
21400406dffSbellard 
PerformLDF(const unsigned int opcode)21565a650c2SPaul Brook static unsigned int PerformLDF(const unsigned int opcode)
21600406dffSbellard {
21765a650c2SPaul Brook     target_ulong pBase, pAddress, pFinal;
21865a650c2SPaul Brook     unsigned int nRc = 1,
21900406dffSbellard      write_back = WRITE_BACK(opcode);
22000406dffSbellard 
22100406dffSbellard    //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
22200406dffSbellard 
22365a650c2SPaul Brook    pBase = readRegister(getRn(opcode));
2247cb4db8fSPeter Maydell    if (ARM_REG_PC == getRn(opcode))
22500406dffSbellard    {
22665a650c2SPaul Brook      pBase += 8;
22700406dffSbellard      write_back = 0;
22800406dffSbellard    }
22900406dffSbellard 
23000406dffSbellard    pFinal = pBase;
23100406dffSbellard    if (BIT_UP_SET(opcode))
23265a650c2SPaul Brook      pFinal += getOffset(opcode) * 4;
23300406dffSbellard    else
23465a650c2SPaul Brook      pFinal -= getOffset(opcode) * 4;
23500406dffSbellard 
23600406dffSbellard    if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
23700406dffSbellard 
23800406dffSbellard    switch (opcode & MASK_TRANSFER_LENGTH)
23900406dffSbellard    {
24000406dffSbellard       case TRANSFER_SINGLE  : loadSingle(getFd(opcode),pAddress);   break;
24100406dffSbellard       case TRANSFER_DOUBLE  : loadDouble(getFd(opcode),pAddress);   break;
24200406dffSbellard       case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
24300406dffSbellard       default: nRc = 0;
24400406dffSbellard    }
24500406dffSbellard 
24600406dffSbellard    if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
24700406dffSbellard    return nRc;
24800406dffSbellard }
24900406dffSbellard 
PerformSTF(const unsigned int opcode)25065a650c2SPaul Brook static unsigned int PerformSTF(const unsigned int opcode)
25100406dffSbellard {
25265a650c2SPaul Brook    target_ulong pBase, pAddress, pFinal;
25365a650c2SPaul Brook    unsigned int nRc = 1,
25400406dffSbellard      write_back = WRITE_BACK(opcode);
25500406dffSbellard 
25600406dffSbellard    //printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
25700406dffSbellard    SetRoundingMode(ROUND_TO_NEAREST);
25800406dffSbellard 
25965a650c2SPaul Brook    pBase = readRegister(getRn(opcode));
2607cb4db8fSPeter Maydell    if (ARM_REG_PC == getRn(opcode))
26100406dffSbellard    {
26265a650c2SPaul Brook      pBase += 8;
26300406dffSbellard      write_back = 0;
26400406dffSbellard    }
26500406dffSbellard 
26600406dffSbellard    pFinal = pBase;
26700406dffSbellard    if (BIT_UP_SET(opcode))
26865a650c2SPaul Brook      pFinal += getOffset(opcode) * 4;
26900406dffSbellard    else
27065a650c2SPaul Brook      pFinal -= getOffset(opcode) * 4;
27100406dffSbellard 
27200406dffSbellard    if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
27300406dffSbellard 
27400406dffSbellard    switch (opcode & MASK_TRANSFER_LENGTH)
27500406dffSbellard    {
27600406dffSbellard       case TRANSFER_SINGLE  : storeSingle(getFd(opcode),pAddress);   break;
27700406dffSbellard       case TRANSFER_DOUBLE  : storeDouble(getFd(opcode),pAddress);   break;
27800406dffSbellard       case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
27900406dffSbellard       default: nRc = 0;
28000406dffSbellard    }
28100406dffSbellard 
28200406dffSbellard    if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
28300406dffSbellard    return nRc;
28400406dffSbellard }
28500406dffSbellard 
PerformLFM(const unsigned int opcode)28665a650c2SPaul Brook static unsigned int PerformLFM(const unsigned int opcode)
28700406dffSbellard {
28865a650c2SPaul Brook    unsigned int i, Fd,
28900406dffSbellard      write_back = WRITE_BACK(opcode);
29065a650c2SPaul Brook    target_ulong pBase, pAddress, pFinal;
29100406dffSbellard 
29265a650c2SPaul Brook    pBase = readRegister(getRn(opcode));
2937cb4db8fSPeter Maydell    if (ARM_REG_PC == getRn(opcode))
29400406dffSbellard    {
29565a650c2SPaul Brook      pBase += 8;
29600406dffSbellard      write_back = 0;
29700406dffSbellard    }
29800406dffSbellard 
29900406dffSbellard    pFinal = pBase;
30000406dffSbellard    if (BIT_UP_SET(opcode))
30165a650c2SPaul Brook      pFinal += getOffset(opcode) * 4;
30200406dffSbellard    else
30365a650c2SPaul Brook      pFinal -= getOffset(opcode) * 4;
30400406dffSbellard 
30500406dffSbellard    if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
30600406dffSbellard 
30700406dffSbellard    Fd = getFd(opcode);
30800406dffSbellard    for (i=getRegisterCount(opcode);i>0;i--)
30900406dffSbellard    {
31000406dffSbellard      loadMultiple(Fd,pAddress);
31165a650c2SPaul Brook      pAddress += 12; Fd++;
31200406dffSbellard      if (Fd == 8) Fd = 0;
31300406dffSbellard    }
31400406dffSbellard 
31500406dffSbellard    if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
31600406dffSbellard    return 1;
31700406dffSbellard }
31800406dffSbellard 
PerformSFM(const unsigned int opcode)31965a650c2SPaul Brook static unsigned int PerformSFM(const unsigned int opcode)
32000406dffSbellard {
32165a650c2SPaul Brook    unsigned int i, Fd,
32200406dffSbellard      write_back = WRITE_BACK(opcode);
32365a650c2SPaul Brook    target_ulong pBase, pAddress, pFinal;
32400406dffSbellard 
32565a650c2SPaul Brook    pBase = readRegister(getRn(opcode));
3267cb4db8fSPeter Maydell    if (ARM_REG_PC == getRn(opcode))
32700406dffSbellard    {
32865a650c2SPaul Brook      pBase += 8;
32900406dffSbellard      write_back = 0;
33000406dffSbellard    }
33100406dffSbellard 
33200406dffSbellard    pFinal = pBase;
33300406dffSbellard    if (BIT_UP_SET(opcode))
33465a650c2SPaul Brook      pFinal += getOffset(opcode) * 4;
33500406dffSbellard    else
33665a650c2SPaul Brook      pFinal -= getOffset(opcode) * 4;
33700406dffSbellard 
33800406dffSbellard    if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
33900406dffSbellard 
34000406dffSbellard    Fd = getFd(opcode);
34100406dffSbellard    for (i=getRegisterCount(opcode);i>0;i--)
34200406dffSbellard    {
34300406dffSbellard      storeMultiple(Fd,pAddress);
34465a650c2SPaul Brook      pAddress += 12; Fd++;
34500406dffSbellard      if (Fd == 8) Fd = 0;
34600406dffSbellard    }
34700406dffSbellard 
34800406dffSbellard    if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
34900406dffSbellard    return 1;
35000406dffSbellard }
35100406dffSbellard 
35200406dffSbellard #if 1
EmulateCPDT(const unsigned int opcode)35300406dffSbellard unsigned int EmulateCPDT(const unsigned int opcode)
35400406dffSbellard {
35500406dffSbellard   unsigned int nRc = 0;
35600406dffSbellard 
35700406dffSbellard   //printk("EmulateCPDT(0x%08x)\n",opcode);
35800406dffSbellard 
35900406dffSbellard   if (LDF_OP(opcode))
36000406dffSbellard   {
36100406dffSbellard     nRc = PerformLDF(opcode);
36200406dffSbellard   }
36300406dffSbellard   else if (LFM_OP(opcode))
36400406dffSbellard   {
36500406dffSbellard     nRc = PerformLFM(opcode);
36600406dffSbellard   }
36700406dffSbellard   else if (STF_OP(opcode))
36800406dffSbellard   {
36900406dffSbellard     nRc = PerformSTF(opcode);
37000406dffSbellard   }
37100406dffSbellard   else if (SFM_OP(opcode))
37200406dffSbellard   {
37300406dffSbellard     nRc = PerformSFM(opcode);
37400406dffSbellard   }
37500406dffSbellard   else
37600406dffSbellard   {
37700406dffSbellard     nRc = 0;
37800406dffSbellard   }
37900406dffSbellard 
38000406dffSbellard   return nRc;
38100406dffSbellard }
38200406dffSbellard #endif
383