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

/*
* Routines needed from db land to support the disassemblers
*/
void
dprint(char *fmt, ...)
{
       char buf[128], *e;

       e = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
       if(xprint == 0)
               Bwrite(bout, buf, e-buf);
       else
               strcat(asmbuf, buf);
}

void
psymoff(ulong ival, int t, char *append)
{
       Symbol s;
       int delta, o;
       char buf[128];

       switch(t) {
       case SEGANY:
               t = CANY;
               break;
       case SEGDATA:
               t = CDATA;
               break;
       case SEGTEXT:
               t = CTEXT;
               break;
       default:
               fatal("psymoff %d", t);
       }
       o = findsym(ival, t, &s);
       delta = ival-s.value;
       if (ival == 0 || o == 0 || delta >= 0x8000)
               sprint(buf, "0x%lux%s", ival, append);
       else {
               if (delta)
                       sprint(buf, "%s+0x%lux%s", s.name, delta, append);
               else
                       sprint(buf, "%s%s", s.name, append);
       }
       if(xprint == 0)
               Bprint(bout, "%s", buf);
       else
               strcat(asmbuf, buf);
}

void
localaddr(Lsym *fn, Lsym *var, Node *r)
{
       Symbol s;
       ulong fp, addr;

       if (lookup(fn->name, 0, &s) == 0)
               error("function not found");

       if ((fp = machdata->findframe(s.value)) == 0)
               error("stack frame not found");

       if (findlocal(&s, var->name, &s) == 0)
               error("bad local variable %s", var->name);

       switch (s.class) {
       case CAUTO:
               addr = fp - s.value;
               break;
       case CPARAM:            /* assume address size is stack width */
               addr = fp + s.value + mach->szaddr;
               break;
       default:
               SET(addr);
               error("bad local symbol");
       }
       indir(cormap, addr, fn->v->fmt, r);
}

static char fpbuf[64];

/*
* These routines assume that if the number is representable
* in IEEE floating point, it will be representable in the native
* double format.  Naive but workable, probably.
*/
char*
ieeedtos(char *fmt, ulong h, ulong l)
{
       double fr;
       int exp;
       char *p = fpbuf;

       if(h & (1L<<31)){
               *p++ = '-';
               h &= ~(1L<<31);
       }
       else
               *p++ = ' ';

       if(l == 0 && h == 0){
               strcpy(p, "0.");
               return fpbuf;
       }
       exp = (h>>20) & ((1L<<11)-1L);
       if(exp == 0){
               sprint(p, "DeN(%.8lux%.8lux)", h, l);
               return fpbuf;
       }
       if(exp == ((1L<<11)-1L)){
               if(l==0 && (h&((1L<<20)-1L)) == 0)
                       sprint(p, "Inf");
               else
                       sprint(p, "NaN(%.8lux%.8lux)", h&((1L<<20)-1L), l);

               return fpbuf;
       }
       exp -= (1L<<10) - 2L;
       fr = (long)l & ((1L<<16)-1L);
       fr /= 1L<<16;
       fr += (long)(l>>16) & ((1L<<16)-1L);
       fr /= 1L<<16;
       fr += (long)(h & (1L<<20)-1L) | (1L<<20);
       fr /= 1L<<21;
       fr = ldexp(fr, exp);
       sprint(p, fmt, fr);
       return fpbuf;
}

char*
ieeeftos(char *fmt, ulong h)
{
       double fr;
       int exp;
       char *p = fpbuf;

       if(h & (1L<<31)){
               *p++ = '-';
               h &= ~(1L<<31);
       }else
               *p++ = ' ';
       if(h == 0){
               strcpy(p, "0.");
               goto ret;
       }
       exp = (h>>23) & ((1L<<8)-1L);
       if(exp == 0){
               sprint(p, "DeN(%.8lux)", h);
               goto ret;
       }
       if(exp == ((1L<<8)-1L)){
               if((h&((1L<<23)-1L)) == 0)
                       sprint(p, "Inf");
               else
                       sprint(p, "NaN(%.8lux)", h&((1L<<23)-1L));
               goto ret;
       }
       exp -= (1L<<7) - 2L;
       fr = (long)(h & ((1L<<23)-1L)) | (1L<<23);
       fr /= 1L<<24;
       fr = ldexp(fr, exp);
       sprint(p, fmt, fr);
   ret:
       return fpbuf;
}
/*
* print a stack traceback
* give locals if argument == 'C'
*/
#define EVEN(x) ((x)&~1)

void
ctrace(int modif)
{
       Symbol s;
       int found;
       long moved, j;
       List **tail, *q, *l;

       USED(modif);

       strc.l = al(TLIST);
       tail = &strc.l;

       j = 0;
       while(strc.pc) {
               if ((moved = pc2sp(strc.pc)) == -1)
                       break;
               found = findsym(strc.pc, CTEXT, &s);
               if (!found)
                       break;

               strc.sp += moved;
               get4(cormap, strc.sp, SEGDATA, (long *)&strc.pc);

               q = al(TLIST);
               *tail = q;
               tail = &q->next;

               l = al(TINT);                   /* Function address */
               q->l = l;
               l->ival = s.value;
               l->fmt = 'X';

               l->next = al(TINT);             /* called from address */
               l = l->next;
               l->ival = strc.pc;
               l->fmt = 'X';

               l->next = al(TLIST);            /* make list of params */
               l = l->next;
               l->l = listparams(&s, strc.sp);

               l->next = al(TLIST);            /* make list of locals */
               l = l->next;
               l->l = listlocals(&s, strc.sp);

               strc.sp += mach->szaddr;        /*assumes address size = stack width*/
               if(++j > 40)
                       break;
       }
       if(j == 0)
               error("no frames found");
}

ulong
findframe(ulong addr)
{
       Symbol s;
       int moved;
       ulong sp, pc, o;

       o = mach->kbase-flen;
       get4(cormap, o+mach->sp, SEGDATA, (long*)&sp);
       sp = EVEN(sp);
       get4(cormap, o+mach->pc, SEGDATA, (long*)&pc);

       for(;;) {
               if ((moved = -pc2sp(pc)) == 1)
                       return sp;
               sp -= moved;
               findsym(pc, CTEXT, &s);
               if (addr == s.value)
                       return sp;
               get4(cormap, sp, SEGDATA, (long *) &pc);
               sp += mach->szaddr;     /*assumes sizeof(addr) = stack width*/
       }
       return 0;
}

long
rget(char *reg)
{
       Lsym *s;
       long x;

       s = look(reg);
       if(s == 0)
               fatal("rget: %s\n", reg);

       get4(cormap, s->v->ival, SEGDATA, &x);
       return x;
}