#include <u.h>
#include <libc.h>
#include <mach.h>
#include "macho.h"
#include "uregpower.h"

enum
{
	ThreadState = 1,
	FloatState,
	ExceptionState,
	VectorState,
	ThreadState64,
	ExceptionState64,
	ThreadStateNone,
};

typedef struct Lreg Lreg;
typedef struct Lflt Lflt;
typedef struct Lexc Lexc;

struct Lreg
{
	u32int srr0;
	u32int srr1;
	u32int r0;
	u32int r1;
	u32int r2;
	u32int r3;
	u32int r4;
	u32int r5;
	u32int r6;
	u32int r7;
	u32int r8;
	u32int r9;
	u32int r10;
	u32int r11;
	u32int r12;
	u32int r13;
	u32int r14;
	u32int r15;
	u32int r16;
	u32int r17;
	u32int r18;
	u32int r19;
	u32int r20;
	u32int r21;
	u32int r22;
	u32int r23;
	u32int r24;
	u32int r25;
	u32int r26;
	u32int r27;
	u32int r28;
	u32int r29;
	u32int r30;
	u32int r31;

	u32int cr;
	u32int xer;
	u32int lr;
	u32int ctr;
	u32int mq;

	u32int vrsave;
};

struct Lflt
{
	u32int fpregs[32*2];		/* 32 doubles */
	u32int fpscr[2];

};

struct Lexc
{
	u32int dar;
	u32int dsisr;
	u32int exception;
	u32int pad0;
	u32int pad1[4];
};

static void
lreg2ureg(Lreg *l, Ureg *u)
{
	u->pc = l->srr0;
	u->srr1 = l->srr1;
	u->lr = l->lr;
	u->cr = l->cr;
	u->xer = l->xer;
	u->ctr = l->ctr;
	u->vrsave = l->vrsave;
	memmove(&u->r0, &l->r0, 32*4);
}

static void
lexc2ureg(Lexc *l, Ureg *u)
{
	u->cause = l->exception;
	u->dar = l->dar;
	u->dsisr = l->dsisr;
}

static uchar*
load(int fd, ulong off, int size)
{
	uchar *a;

	a = malloc(size);
	if(a == nil)
		return nil;
	if(seek(fd, off, 0) < 0 || readn(fd, a, size) != size){
		free(a);
		return nil;
	}
	return a;
}

int
coreregsmachopower(Macho *m, uchar **up)
{
	int i, havereg, haveexc;
	uchar *a, *p, *nextp;
	Ureg *u;
	ulong flavor, count;
	MachoCmd *c;

	*up = nil;
	for(i=0; i<m->ncmd; i++)
		if(m->cmd[i].type == MachoCmdThread)
			break;
	if(i == m->ncmd){
		werrstr("no registers found");
		return -1;
	}

	c = &m->cmd[i];
	a = load(m->fd, c->off, c->size);
	if(a == nil)
		return -1;

	if((u = mallocz(sizeof(Ureg), 1)) == nil){
		free(a);
		return -1;
	}

	havereg = haveexc = 0;
	for(p=a+8; p<a+c->size; p=nextp){
		flavor = m->e4(p);
		count = m->e4(p+4);
		nextp = p+8+count*4;
		if(flavor == ThreadState && count*4 == sizeof(Lreg)){
			havereg = 1;
			lreg2ureg((Lreg*)(p+8), u);
		}
		if(flavor == ExceptionState && count*4 == sizeof(Lexc)){
			haveexc = 1;
			lexc2ureg((Lexc*)(p+8), u);
		}
	}
	free(a);
	if(!havereg){
		werrstr("no registers found");
		free(u);
		return -1;
	}
	if(!haveexc)
		fprint(2, "warning: no exception state in core file registers\n");
	*up = (uchar*)u;
	return sizeof(*u);
}