/*
 * Linux accessors for sigcontext
 */

#include <stdio.h>
/*
 * Set __USE_GNU so we can get the names of the registers from
 * signal.h. MUST follow <stdio.h>!
 */
#define __USE_GNU
#include <signal.h>

#include "lisp.h"

#include "globals.h"
#include "internals.h"
#include "lispregs.h"
#include "os.h"

#ifdef __i386
unsigned long *
os_sigcontext_reg(ucontext_t *scp, int offset)
{
    switch (offset) {
    case reg_EAX:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_EAX];
    case 2:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_ECX];
    case 4:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_EDX];
    case 6:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_EBX];
    case 8:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_ESP];
    case 10:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_EBP];
    case 12:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_ESI];
    case 14:
	return (unsigned long *) &scp->uc_mcontext.gregs[REG_EDI];
    case 16:
        return (unsigned long*) &scp->uc_mcontext.gregs[REG_EFL];
    }
    return NULL;
}

unsigned long *
os_sigcontext_pc(ucontext_t *scp)
{
    return (unsigned long *) &scp->uc_mcontext.gregs[REG_EIP];
}

unsigned char *
os_sigcontext_fpu_reg(ucontext_t *scp, int offset)
{
    fpregset_t fpregs = scp->uc_mcontext.fpregs;
    unsigned char *reg = NULL;
    
    if (fpregs) {
        if (offset < 8) {
            reg = (unsigned char *) &fpregs->_st[offset];
        }
#ifdef FEATURE_SSE2
        else {
            struct _fpstate *fpstate;
            fpstate = (struct _fpstate*) scp->uc_mcontext.fpregs;
            if (fpstate->magic != 0xffff) {
                reg = (unsigned char *) &fpstate->_xmm[offset - 8];
            }
        }
#endif
    }
    return reg;
}

unsigned int
os_sigcontext_fpu_modes(ucontext_t *scp)
{
    unsigned int modes;
    unsigned short cw, sw;

    if (scp->uc_mcontext.fpregs == NULL) {
	cw = 0;
	sw = 0x3f;
    } else {
	cw = scp->uc_mcontext.fpregs->cw & 0xffff;
	sw = scp->uc_mcontext.fpregs->sw & 0xffff;
    }

    modes = ((cw & 0x3f) << 7) | (sw & 0x3f);

#ifdef FEATURE_SSE2
    /*
     * Add in the SSE2 part, if we're running the sse2 core.
     */
    if (fpu_mode == SSE2) {
        struct _fpstate *fpstate;
	unsigned long mxcsr;

        fpstate = (struct _fpstate*) scp->uc_mcontext.fpregs;
        if (fpstate->magic == 0xffff) {
            mxcsr = 0;
        } else {
            mxcsr = fpstate->mxcsr;
            DPRINTF(0, (stderr, "SSE2 modes = %08lx\n", mxcsr));
        }

	modes |= mxcsr;
    }
#endif

    modes ^= (0x3f << 7);
    return modes;
}
#endif

#ifdef __x86_64
int *
sc_reg(ucontext_t *c, int offset)
{
    switch (offset) {
      case 0:
	  return &c->uc_mcontext.gregs[REG_RAX];
      case 2:
	  return &c->uc_mcontext.gregs[REG_RCX];
      case 4:
	  return &c->uc_mcontext.gregs[REG_RDX];
      case 6:
	  return &c->uc_mcontext.gregs[REG_RBX];
      case 8:
	  return &c->uc_mcontext.gregs[REG_RSP];
      case 10:
	  return &c->uc_mcontext.gregs[REG_RBP];
      case 12:
	  return &c->uc_mcontext.gregs[REG_RSI];
      case 14:
	  return &c->uc_mcontext.gregs[REG_RDI];
      case 16:
	  return &c->uc_mcontext.gregs[REG_R8];
      case 18:
	  return &c->uc_mcontext.gregs[REG_R9];
      case 20:
	  return &c->uc_mcontext.gregs[REG_R10];
      case 22:
	  return &c->uc_mcontext.gregs[REG_R11];
      case 24:
	  return &c->uc_mcontext.gregs[REG_R12];
      case 26:
	  return &c->uc_mcontext.gregs[REG_R13];
      case 28:
	  return &c->uc_mcontext.gregs[REG_R14];
      case 30:
	  return &c->uc_mcontext.gregs[REG_R15];
    }
    return (int *) 0;
}
#endif
