#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "sparc.h"

void
ldf(ulong ir)
{
       ulong ea;
       int rd, rs1, rs2;

       getrop23(ir);
       if(ir&IMMBIT) {
               ximm(ea, ir);
               if(trace)
                       itrace("ldf\tf%d,0x%lux(r%d) ea=%lux",rd, ea, rs1, ea+reg.r[rs1]);
               ea += reg.r[rs1];
       }
       else {
               ea = reg.r[rs1] + reg.r[rs2];
               if(trace)
                       itrace("ldf\tf%d,[r%d+r%d] ea=%lux", rd, rs1, rs2, ea);
       }

       reg.di[rd] = getmem_w(ea);
}

void
lddf(ulong ir)
{
       ulong ea;
       int rd, rs1, rs2;

       getrop23(ir);
       if(ir&IMMBIT) {
               ximm(ea, ir);
               if(trace)
                       itrace("lddf\tf%d,0x%lux(r%d) ea=%lux",
                                                       rd, ea, rs1, ea+reg.r[rs1]);
               ea += reg.r[rs1];
       }
       else {
               ea = reg.r[rs1] + reg.r[rs2];
               if(trace)
                       itrace("lddf\tf%d,[r%d+r%d] ea=%lux", rd, rs1, rs2, ea);
       }

       if(ea&7) {
               Bprint(bioout, "mem_address_not_aligned [load addr %.8lux]\n", ea);
               longjmp(errjmp, 0);
       }
       if(rd&1)
               undef(ir);

       reg.di[rd] = getmem_w(ea);
       reg.di[rd+1] = getmem_w(ea+4);
}

void
stf(ulong ir)
{
       ulong ea;
       int rd, rs1, rs2;

       getrop23(ir);
       if(ir&IMMBIT) {
               ximm(ea, ir);
               if(trace)
                       itrace("stf\tf%d,0x%lux(r%d) %lux=%g",
                                       rd, ea, rs1, ea+reg.r[rs1], reg.fl[rd]);
               ea += reg.r[rs1];
       }
       else {
               ea = reg.r[rs1] + reg.r[rs2];
               if(trace)
                       itrace("stf\tf%d,[r%d+r%d] %lux=%lux",
                                       rd, rs1, rs2, ea, reg.r[rd]);
       }

       putmem_w(ea, reg.di[rd]);
}

void
stdf(ulong ir)
{
       ulong ea;
       int rd, rs1, rs2;

       getrop23(ir);
       if(ir&IMMBIT) {
               ximm(ea, ir);
               if(trace)
                       itrace("stdf\tf%d,0x%lux(r%d) %lux=%g",
                                       rd, ea, rs1, ea+reg.r[rs1], reg.fl[rd]);
               ea += reg.r[rs1];
       }
       else {
               ea = reg.r[rs1] + reg.r[rs2];
               if(trace)
                       itrace("stdf\tf%d,[r%d+r%d] %lux=%lux",
                                       rd, rs1, rs2, ea, reg.r[rd]);
       }

       if(ea&7) {
               Bprint(bioout, "mem_address_not_aligned [store addr %.8lux]\n", ea);
               longjmp(errjmp, 0);
       }
       if(rd&1)
               undef(ir);

       putmem_w(ea, reg.di[rd]);
       putmem_w(ea+4, reg.di[rd+1]);
}

void
fcmp(ulong ir)
{
       int fc, rd, rs1, rs2;

       getrop23(ir);
       USED(rd);
       SET(fc);
       switch((ir>>5)&0x1FF) {
       default:
               undef(ir);
       case 0x51:              /* fcmps */
               if(trace)
                       itrace("fcmps\tf%d,f%d", rs1, rs2);
               if(isNaN(reg.fl[rs1]) || isNaN(reg.fl[rs2])) {
                       fc = 3;
                       break;
               }
               if(reg.fl[rs1] == reg.fl[rs2]) {
                       fc = 0;
                       break;
               }
               if(reg.fl[rs1] < reg.fl[rs2]) {
                       fc = 1;
                       break;
               }
               if(reg.fl[rs1] > reg.fl[rs2]) {
                       fc = 2;
                       break;
               }
               print("ki: fcmp error\n");
               break;
       case 0x52:
               if(trace)
                       itrace("fcmpd\tf%d,f%d", rs1, rs2);
               rs1 >>= 1;
               rs2 >>= 1;
               if(isNaN(reg.fd[rs1]) || isNaN(reg.fd[rs2])) {
                       fc = 3;
                       break;
               }
               if(reg.fd[rs1] == reg.fd[rs2]) {
                       fc = 0;
                       break;
               }
               if(reg.fd[rs1] < reg.fd[rs2]) {
                       fc = 1;
                       break;
               }
               if(reg.fd[rs1] > reg.fd[rs2]) {
                       fc = 2;
                       break;
               }
               print("ki: fcmp error\n");
               break;
       case 0x55:              /* fcmpes */
               if(trace)
                       itrace("fcmpes\tf%d,f%d", rs1, rs2);
               rs1 >>= 1;
               rs2 >>= 2;
               if(isNaN(reg.fl[rs1]) || isNaN(reg.fl[rs2])) {
                       Bprint(bioout, "invalid_fp_register\n");
                       longjmp(errjmp, 0);
               }
               if(reg.fl[rs1] == reg.fl[rs2]) {
                       fc = 0;
                       break;
               }
               if(reg.fl[rs1] < reg.fl[rs2]) {
                       fc = 1;
                       break;
               }
               if(reg.fl[rs1] > reg.fl[rs2]) {
                       fc = 2;
                       break;
               }
               print("ki: fcmp error\n");
               break;
       case 0x56:
               if(trace)
                       itrace("fcmped\tf%d,f%d", rs1, rs2);
               if(isNaN(reg.fd[rs1]) || isNaN(reg.fd[rs2])) {
                       Bprint(bioout, "invalid_fp_register\n");
                       longjmp(errjmp, 0);
               }
               if(reg.fd[rs1] == reg.fd[rs2]) {
                       fc = 0;
                       break;
               }
               if(reg.fd[rs1] < reg.fd[rs2]) {
                       fc = 1;
                       break;
               }
               if(reg.fd[rs1] > reg.fd[rs2]) {
                       fc = 2;
                       break;
               }
               print("ki: fcmp error\n");
               break;

       }
       reg.fpsr = (reg.fpsr&~(0x3<<10)) | (fc<<10);
}

void
fbcc(ulong ir)
{
       char *op;
       ulong npc;
       int takeit, fc, ba, anul;

       fc = (reg.fpsr>>10)&3;
       ba = 0;
       SET(op, takeit);
       switch((ir>>25)&0x0F) {
       case 0:
               op = "fbn";
               takeit = 0;
               break;
       case 1:
               op = "fbne";
               takeit = fc == FP_L || fc == FP_G || fc == FP_U;
               break;
       case 2:
               op = "fblg";
               takeit = fc == FP_L || fc == FP_G;
               break;
       case 3:
               op = "fbul";
               takeit = fc == FP_L || fc == FP_U;
               break;
       case 4:
               op = "fbl";
               takeit = fc == FP_L;
               break;
       case 5:
               op = "fbug";
               takeit = fc == FP_U || fc == FP_G;
               break;
       case 6:
               op = "fbg";
               takeit = fc == FP_G;
               break;
       case 7:
               op = "fbu";
               takeit = fc == FP_U;
               break;
       case 8:
               op = "fba";
               ba = 1;
               takeit = 1;
               break;
       case 9:
               op = "fbe";
               takeit = fc == FP_E;
               break;
       case 10:
               op = "fbue";
               takeit = fc == FP_E || fc == FP_U;
               break;
       case 11:
               op = "fbge";
               takeit = fc == FP_E || fc == FP_G;
               break;
       case 12:
               op = "fbuge";
               takeit = fc == FP_E || fc == FP_G || fc == FP_U;
               break;
       case 13:
               op = "fble";
               takeit = fc == FP_E || fc == FP_L;
               break;
       case 14:
               op = "fbule";
               takeit = fc == FP_E || fc == FP_L || fc == FP_U;
               break;
       case 15:
               op = "fbo";
               takeit = fc == FP_E || fc == FP_L || fc == FP_G;
               break;
       }

       npc = ir & 0x3FFFFF;
       if(npc & (1<<21))
               npc |= ~((1<<22)-1);
       npc = (npc<<2) + reg.pc;

       anul = ir&ANUL;
       if(trace) {
               if(anul)
                       itrace("%s,a\t%lux", op, npc);
               else
                       itrace("%s\t%lux", op, npc);
       }

       if(takeit == 0) {
               reg.pc += 4;
               if(anul == 0) {
                       reg.ir = ifetch(reg.pc);
                       delay(reg.pc+4);
               }
               else
                       anulled++;
               return;
       }

       ci->taken++;
       if(ba && anul) {
               reg.pc = npc-4;
               anulled++;
               return;
       }
       reg.ir = ifetch(reg.pc+4);
       delay(npc);
       reg.pc = npc-4;
}

void
farith(ulong ir)
{
       char *op;
       long v;
       int rd, rs1, rs2, fmt;

       fmt = 0;
       getrop23(ir);
       switch((ir>>5)&0x1FF) {
       default:
               undef(ir);
       case 0x41:
               reg.fl[rd] = reg.fl[rs1] + reg.fl[rs2];
               op = "fadds";
               break;
       case 0x42:
               reg.fd[rd>>1] = reg.fd[rs1>>1] + reg.fd[rs2>>1];
               op = "faddd";
               break;
       case 0x45:
               reg.fl[rd] = reg.fl[rs1] - reg.fl[rs2];
               op = "fsubs";
               break;
       case 0x46:
               reg.fd[rd>>1] = reg.fd[rs1>>1] - reg.fd[rs2>>1];
               op = "fsubd";
               break;
       case 0x4d:
               if(reg.fl[rs2] == 0.0) {
                       Bprint(bioout, "fp_exception DZ\n");
                       longjmp(errjmp, 0);
               }
               reg.fl[rd] = reg.fl[rs1] / reg.fl[rs2];
               op = "fdivs";
               break;
       case 0x4e:
               if(reg.fd[rs2>>1] == 0.0) {
                       Bprint(bioout, "fp_exception DZ\n");
                       longjmp(errjmp, 0);
               }
               reg.fd[rd>>1] = reg.fd[rs1>>1] / reg.fd[rs2>>1];
               op = "fdivd";
               break;
       case 0x49:
               reg.fl[rd] = reg.fl[rs1] * reg.fl[rs2];
               op = "fmuls";
               break;
       case 0x4a:
               reg.fd[rd>>1] = reg.fd[rs1>>1] * reg.fd[rs2>>1];
               op = "fmuld";
               break;
       case 0xc4:
               reg.fl[rd] = (long)reg.di[rs2];
               fmt = 1;
               op = "fitos";
               break;
       case 0xc8:
               reg.fd[rd>>1] = (long)reg.di[rs2];
               fmt = 1;
               op = "fitod";
               break;
       case 0xd1:
               v = reg.fl[rs2];
               reg.di[rd] = v;
               fmt = 1;
               op = "fstoi";
               break;
       case 0xd2:
               v = reg.fd[rs2>>1];
               reg.di[rd] = v;
               fmt = 1;
               op = "fdtoi";
               break;
       case 0x01:
               reg.di[rd] = reg.di[rs2];
               fmt = 1;
               op = "fmovs";
               break;
       case 0x05:
               reg.fl[rd] = -reg.fl[rs2];
               fmt = 1;
               op = "fnegs";
               break;
       case 0x09:
               reg.fl[rd] = fabs(reg.fl[rs2]);
               fmt = 1;
               op = "fabss";
               break;
       case 0xc9:
               reg.fd[rd>>1] = reg.fl[rs2];
               fmt = 1;
               op = "fstod";
               break;
       case 0xc6:
               reg.fl[rd] = reg.fd[rs2>>1];
               fmt = 1;
               op = "fdtos";
               break;
       }

       if(trace) {
               switch(fmt) {
               case 0:
                       itrace("%s\tf%d,f%d,f%d", op, rs1, rs2, rd);
                       break;
               case 1:
                       itrace("%s\tf%d,f%d", op, rs2, rd);
                       break;
               }
       }
}