#include <u.h>
#include <libc.h>
#include <aml.h>

typedef struct Interp Interp;
typedef struct Frame Frame;
typedef struct Heap Heap;

typedef struct Method Method;
typedef struct Region Region;
typedef struct Field Field;

typedef struct Name Name;
typedef struct Ref Ref;
typedef struct Env Env;
typedef struct Op Op;

struct Heap {
	Heap	*link;
	int	size;
	uchar	mark;
	char	tag;
};

#define H2D(h)	(((Heap*)(h))+1)
#define D2H(d)	(((Heap*)(d))-1)
#define TAG(d)	D2H(d)->tag
#define SIZE(d)	D2H(d)->size

static char *spacename[] = {
	"Mem", 
	"Io",
	"Pcicfg",
	"Ebctl",
	"Smbus",
	"Cmos",
	"Pcibar",
	"Ipmi",
};

/* field flags */
enum {
	AnyAcc		= 0x00,
	ByteAcc		= 0x01,
	WordAcc		= 0x02,
	DWordAcc	= 0x03,
	QWordAcc	= 0x04,
	BufferAcc	= 0x05,
	AccMask		= 0x07,

	NoLock		= 0x10,

	Preserve	= 0x00,
	WriteAsOnes	= 0x20,
	WriteAsZeros	= 0x40,
	UpdateMask	= 0x60,
};

struct Method {
	Name	*name;
	int	narg;
	void*	(*eval)(void);
	uchar	*start;
	uchar	*end;
};

struct Region {
	Amlio;
	char	mapped;
};

struct Field {
	void	*reg;	/* Buffer or Region or data Field */
	void	*bank;	/* bank value */
	Field	*index;	/* bank or index Field */
	int	flags;
	int	bitoff;
	int	bitlen;
};

struct Name {
	void	*v;

	Name	*up;
	Name	*next;
	Name	*fork;
	Name	*down;

	char	seg[4];
};

struct Ref {
	void	*ref;
	void	**ptr;
};

struct Env {
	void	*loc[8];
	void	*arg[8];
};

struct Op {
	char	*name;
	char	*sequence;
	void*	(*eval)(void);
};

struct Frame {
	int	tag;
	int	cond;
	char	*phase;
	uchar	*start;
	uchar	*end;
	Op	*op;
	Env	*env;
	Name	*dot;
	void	*ref;
	void	*aux;
	int	narg;
	void	*arg[8];
};

struct Interp {
	uchar	*pc;
	Frame	*fp;
	Frame	*fb;
};

static Interp	interp;
static Frame	stack[32];

#define	PC	interp.pc
#define	FP	interp.fp
#define	FB	interp.fb

#define F0	stack
#define FT	&stack[nelem(stack)]

static Heap *hp;

enum {
	Obad, Onop, Odebug,
	Ostr, Obyte, Oword, Odword, Oqword, Oconst,
	Onamec, Oname, Oscope, Oalias,
	Oreg, Ofld, Oxfld, Obfld, Opkg, Ovpkg, Oenv, Obuf, Omet, 
	Odev, Ocpu, Othz, Oprc,
	Oadd, Osub, Omod, Omul, Odiv, Oshl, Oshr, Oand, Onand, Oor,
	Onor, Oxor, Onot, Olbit, Orbit, Oinc, Odec,
	Oland, Olor, Olnot, Oleq, Olgt, Ollt,
	Oindex, Omatch, Omutex, Oevent,
	Ocfld, Ocfld0, Ocfld1, Ocfld2, Ocfld4, Ocfld8,
	Oif, Oelse, Owhile, Obreak, Oret, Ocall, 
	Ostore, Oderef, Ootype, Osize, Oref, Ocref, Ocat, Ocatr, Omid,
	Oacq, Osignal, Orel, Ostall, Osleep, Oload, Ounload,
	Otodec, Otohex, Otoint, Otostr, Onotify,
};

static Op optab[];
static uchar octab1[];
static uchar octab2[];

static Name*
rootname(Name *dot)
{
	while(dot != dot->up)
		dot = dot->up;
	return dot;
}

static void
gcmark(void *p)
{
	int i;
	Env *e;
	Field *f;
	Heap *h;
	Name *n, *d;

	if(p == nil)
		return;
	h = D2H(p);
	if(h->mark & 1)
		return;
	h->mark |= 1;
	switch(h->tag){
	case 'E':
		e = p;
		for(i=0; i<nelem(e->loc); i++)
			gcmark(e->loc[i]);
		for(i=0; i<nelem(e->arg); i++)
			gcmark(e->arg[i]);
		break;
	case 'R':
	case 'A':
	case 'L':
		gcmark(((Ref*)p)->ref);
		break;
	case 'N':
		n = p;
		gcmark(n->v);
		for(d = n->down; d; d = d->next)
			gcmark(d);
		gcmark(n->fork);
		gcmark(n->up);
		break;
	case 'p':
		for(i=0; i<(SIZE(p)/sizeof(void*)); i++)
			gcmark(((void**)p)[i]);
		break;
	case 'r':
		gcmark(((Region*)p)->name);
		break;
	case 'm':
		gcmark(((Method*)p)->name);
		break;
	case 'f':
	case 'u':
		f = p;
		gcmark(f->reg);
		gcmark(f->bank);
		gcmark(f->index);
		break;
	}
}

static int
gc(void)
{
	int i;
	Heap *h, **hh;
	Frame *f;

	for(h = hp; h; h = h->link)
		h->mark &= ~1;

	for(h = hp; h; h = h->link)
		if(h->mark & 2)
			gcmark(H2D(h));

	for(f = FP; f >= F0; f--){
		for(i=0; i<f->narg; i++)
			gcmark(f->arg[i]);
		gcmark(f->env);
		gcmark(f->dot);
		gcmark(f->ref);
	}

	gcmark(amlroot);

	i = 0;
	hh = &hp;
	while(h = *hh){
		if(h->mark){
			hh = &h->link;
			continue;
		}
		*hh = h->link;
		if(h->tag == 'r'){
			Region *r = (void*)H2D(h);
			if(r->mapped > 0){
				if(amldebug)
					print("\namlunmapio(%N): %-8s %llux - %llux\n", 
						(Name*)r->name, spacename[r->space],
						r->off, r->off + r->len);
				amlunmapio(r);
			}
			r->mapped = 0;
			r->write = nil;
			r->read = nil;
			r->aux = nil;
			r->va = nil;
		}
		memset(h, ~0, sizeof(Heap)+h->size);
		amlfree(h);
		i++;
	}

	return i;
}

static void*
mk(int tag, int size)
{
	Heap *h;
	int a;

	a = sizeof(Heap) + size;
	assert(a >= sizeof(Heap));
	h = amlalloc(a);
	h->size = size;
	h->tag = tag;
	h->link = hp;
	hp = h;
	return h+1;
}

static uvlong*
mki(uvlong i)
{
	uvlong *v;

	v = mk('i', sizeof(uvlong));
	*v = i & amlintmask;
	return v;
}

static char*
mks(char *s)
{
	char *r = mk('s', strlen(s)+1);
	strcpy(r, s);
	return r;
}

static int
pkglen(uchar *p, uchar *e, uchar **np)
{
	ulong n;
	uchar b;

	if(p >= e)
		return -1;
	b = *p++;
	if(b <= 0x3F)
		n = b;
	else {
		n = b & 0xF;
		if(p >= e)
			return -1;
		n += *p++ << 4;
		if(b >= 0x80){
			if(p >= e)
				return -1;
			n += *p++ << 12;
		}
		if(b >= 0xC0){
			if(p >= e)
				return -1;
			n += *p++ << 20;
		}
	}
	if(np)
		*np = p;
	return n;
}

static Name*
forkname(Name *dot)
{
	Name *n;

	n = mk('N', sizeof(Name));
	*n = *dot;
	n->fork = dot;
	n->next = n->down = nil;
	if(dot->v == dot)
		n->v = n;
	if(dot->up == dot)
		n->up = n;
	else {
		if(n->up = forkname(dot->up))
			n->up->down = n;
	}
	return n;
}

static Name*
getseg(Name *dot, void *seg, int new)
{
	Name *n, *l;

	for(n = l = nil; dot; dot = dot->fork){
		for(n = dot->down; n; n = n->next){
			if(memcmp(seg, n->seg, 4) == 0)
				return n;
			l = n;
		}
		if(new){
			n = mk('N', sizeof(Name));
			memmove(n->seg, seg, sizeof(n->seg));
			n->up = dot;
			if(l == nil)
				dot->down = n;
			else
				l->next = n;
			n->v = n;
			break;
		}
	}
	return n;
}

Name*
getname(Name *dot, char *path, int new)
{
	char seg[4];
	int i, s;
	Name *x;

	if(dot == nil)
		return nil;

	s = !new;
	if(*path == '\\'){
		path++;
		dot = rootname(dot);
		s = 0;
	}
	while(*path == '^'){
		path++;
		dot = dot->up;
		s = 0;
	}
	do {
		for(i=0; i<4; i++){
			if(*path == 0 || *path == '.')
				break;
			seg[i] = *path++;
		}
		if(i == 0)
			break;
		while(i < 4)
			seg[i++] = '_';
		if(s && *path == 0){
			for(;;){
				if(x = getseg(dot, seg, 0))
					break;
				if(dot == dot->up)
					break;
				dot = dot->up;
			}
			return x;
		}
		s = 0;
		dot = getseg(dot, seg, new);
	} while(*path++ == '.');

	return dot;
}

static int
fixnames(void *dot, void *arg)
{
	void **r, *v;
	int i;

	if(arg == nil)
		r = &((Name*)dot)->v;
	else
		r = arg;
	v = *r;
	if(v == nil || v == dot)
		return 0;
	if(TAG(v) == 'p'){
		r = (void**)v;
		for(i=0; i<(SIZE(r)/sizeof(void*)); i++)
			fixnames(dot, r+i);
		return 0;
	}
	if(TAG(v) == 'n' && (v = getname(dot, v, 0)) != nil)
		*r = v;
	return 0;
}

static uvlong
getle(uchar *p, int len)
{
	uvlong v;
	int i;

	v = 0ULL;
	for(i=0; i<len; i++)
		v |= ((uvlong)p[i]) << i*8;
	return v;
}

static void
putle(uchar *p, int len, uvlong v)
{
	int i;

	for(i=0; i<len; i++){
		p[i] = v;
		v >>= 8;
	}
}

static uvlong
rwreg(void *reg, int off, int len, uvlong v, int write)
{
	Interp save;
	uchar buf[8], *p;
	Region *r;

	save = interp;	/* save, in case we reenter the interpreter */
	FB = FP+1;	/* allocate new base */

	switch(TAG(reg)){
	case 'b':
		p = reg;
		if((off+len) > SIZE(p))
			break;
		p += off;
		if(write)
			putle(p, len, v);
		else
			v = getle(p, len);
		goto Out;

	case 'r':
		r = reg;
		if((off+len) > r->len)
			break;
		if(r->mapped == 0){
			if(amldebug)
				print("\namlmapio(%N): %-8s %llux - %llux\n", 
					(Name*)r->name, spacename[r->space],
					r->off, r->off + r->len);
			r->mapped = 1;
			if(amlmapio(r) < 0)
				r->mapped = -1;
		}
		if(r->mapped <= 0)
			break;

		if(r->va != nil)
			p = r->va + off;
		else {
			if(len > sizeof(buf))
				break;
			p = buf;
		}

		if(write){
			if(amldebug)
				print("\nrwreg(%N): %-8s [%llux+%x]/%d <- %llux\n", 
					(Name*)r->name, spacename[r->space],
					r->off, off, len, v);
			putle(p, len, v);
			if(r->write != nil){
				if((*r->write)(r, p, len, off) != len)
					break;
			} else if(p == buf)
				break;
		} else {
			if(r->read != nil){
				if((*r->read)(r, p, len, off) != len)
					break;
			} else if(p == buf)
				break;
			v = getle(p, len);
			if(amldebug)
				print("\nrwreg(%N): %-8s [%llux+%x]/%d -> %llux\n", 
					(Name*)r->name, spacename[r->space],
					r->off, off, len, v);
		}
		goto Out;
	}

	v = -1;
Out:
	interp = save;	/* restore */
	return v;
}

static uvlong
ival(void *p)
{
	int n;

	if(p != nil){
		switch(TAG(p)){
		case 'i':
			return *((uvlong*)p);
		case 's':
			if(*((char*)p) == 0)
				break;
			return strtoull((char*)p, 0, 16);
		case 'b':
			n = SIZE(p);
			if(n > 0){
				if(n > 8) n = 8;
				return rwreg(p, 0, n, 0, 0);
			}
		}
	}
	return 0;
}

static void *deref(void *p);
static void *store(void *s, void *d);

static int
fieldalign(int flags)
{
	switch(flags & AccMask){
	default:
	case AnyAcc:
	case ByteAcc:
	case BufferAcc:
		return 1;
	case WordAcc:
		return 2;
	case DWordAcc:
		return 4;
	case QWordAcc:
		return 8;
	}
}

static void *rwfield(Field *f, void *v, int write);

static uvlong
rwfieldunit(Field *f, int off, int len, uvlong v, int write)
{
	if(f->index){
		if(TAG(f->reg) == 'f'){
			/* set index field */
			rwfield(f->index, mki(off), 1);

			/* set or get data field */
			if(write){
				void *b = mk('b', len);
				putle(b, len, v);
				rwfield(f->reg, b, 1);
			}else{
				v = ival(rwfield(f->reg, nil, 0));
			}
			return v;
		}

		/* set bank field */
		rwfield(f->index, f->bank, 1);
	}
	return rwreg(f->reg, off, len, v, write);
}

static void*
rwfield(Field *f, void *v, int write)
{
	int boff, blen, wo, ws, wl, wa, wd, i;
	uvlong w, m;
	uchar *b;

	if(f == nil)
		return nil;
	blen = f->bitlen;
	if(write){
		if(v && TAG(v) == 'b'){
			b = v;
			if(SIZE(b)*8 < blen)
				blen = SIZE(b)*8;
		} else {
			w = ival(v);
			b = mk('b', (blen+7)/8);
			putle(b, SIZE(b), w);
		}
	} else
		b = mk('b', (blen+7)/8);
	/*
	 * don't free b while in rwfieldunit()/rwreg(),
	 * gc can't find this temporary object referenced.
	 */
	amltake(b);
	wa = fieldalign(f->flags);
	wd = wa*8;
	boff = 0;
	while((wl = (blen-boff)) > 0){
		wo = (f->bitoff+boff) / wd;
		ws = (f->bitoff+boff) % wd;
		if(wl > (wd - ws))
			wl = wd - ws;
		if(write){
			w = 0;
			for(i = 0; i < wl; i++, boff++)
				if(b[boff/8] & (1<<(boff%8)))
					w |= 1ULL<<i;
			w <<= ws;
			if(wl != wd){
				m = ((1ULL<<wl)-1) << ws;
				w |= rwfieldunit(f, wo*wa, wa, 0, 0) & ~m;
			}
			rwfieldunit(f, wo*wa, wa, w, 1);
		} else {
			w = rwfieldunit(f, wo*wa, wa, 0, 0) >> ws;
			for(i = 0; i < wl; i++, boff++){
				b[boff/8] |= (w&1)<<(boff%8);
				w >>= 1;
			}
		}
	}
	amldrop(b);
	if(write)
		return nil;
	if(blen > 64)
		return b;
	w = getle(b, SIZE(b));
	return mki(w);
}

static void*
deref(void *p)
{
	if(p) switch(TAG(p)){
	case 'N':
		return ((Name*)p)->v;
	case 'R': case 'A': case 'L':
		return *((Ref*)p)->ptr;
	case 'f': case 'u':
		return rwfield(p, nil, 0);
	}
	return p;
}

static char*
todecstr(uchar *buf, int len, int sep)
{
	char *r, *d;
	int i, v;

	r = d = mk('s', len*4 + 1);
	if(len == 0){
		*d = 0;
		return r;
	}
	if(sep == 0)
		sep = ' ';
	for(i=0; i<len; i++){
		v = buf[i];
		if((*d = '0' + ((v/100) % 10)) != '0')
			d++;
		if((*d = '0' + ((v/10) % 10)) != '0')
			d++;
		*d++ = '0' + (v % 10);
		*d++ = sep;
	}
	d[-1] = 0;
	return r;
}

static char hex[] = "0123456789ABCDEF";

static char*
tohexstr(uchar *buf, int len, int sep)
{
	char *r, *d;
	int i;

	r = d = mk('s', len*3 + 1);
	if(len == 0){
		*d = 0;
		return r;
	}
	if(sep == 0)
		sep = ' ';
	for(i=0; i<len; i++){
		*d++ = hex[buf[i] >> 4];
		*d++ = hex[buf[i] & 0xF];
		*d++ = sep;
	}
	d[-1] = 0;
	return r;
}

static void*
copy(int tag, void *s)
{
	uvlong v;
	void *d;
	int n;

	if(tag == 0){
		if(s == nil)
			return nil;
		tag = TAG(s);
	}
	if(s == nil || TAG(s) == 'i'){
		n = 4;
		v = ival(s);
		if(v > 0xFFFFFFFFULL)
			n <<= 1;
		switch(tag){
		case 'b':
			d = mk(tag, n);
			rwreg(d, 0, n, v, 1);
			return d;
		case 's':
			n <<= 1;
			d = mk(tag, n+1);
			((char*)d)[n] = 0;
			while(n > 0){
				((char*)d)[--n] = hex[v & 0xF];
				v >>= 4;
			}
			return d;
		case 'i':
			if(v == 0ULL)
				return nil;
			return mki(v);
		}
	} else {
		n = SIZE(s);
		switch(tag){
		case 's':
			if(TAG(s) == 'b')
				return tohexstr(s, n, ' ');
			/* no break */
		case 'b':
			if(TAG(s) == 's'){
				n = strlen(s);
				/* zero length string is converted to zero length buffer */
				if(n > 0) n++;
			}
			d = mk(tag, n);
			memmove(d, s, n);
			return d;
		}
	}
	return s;
}

static void*
store(void *s, void *d)
{
	void *p, **pp;

	if(d == nil)
		return nil;
	switch(TAG(d)){
	default:
		return nil;
	case 'A':
		s = deref(s);
		/* no break */
	case 'R': case 'L':
		pp = ((Ref*)d)->ptr;
		while((p = *pp) != nil){
			switch(TAG(p)){
			case 'R': case 'A': case 'L':
				pp = ((Ref*)p)->ptr;
				break;
			case 'N':
				pp = &((Name*)p)->v;
				break;
			}
			if(*pp == p)
				break;
		}
		break;
	case 'N':
		pp = &((Name*)d)->v;
		break;
	}
	p = *pp;
	if(p != nil && TAG(p) != 'N'){
		switch(TAG(p)){
		case 'f':
		case 'u':
			rwfield(p, s, 1);
			return d;
		}
		if(TAG(d) != 'A' && TAG(d) != 'L'){
			*pp = copy(TAG(p), s);
			return d;
		}
	}
	*pp = copy(0, s);
	return d;
}

static int
Nfmt(Fmt *f)
{
	char buf[5];
	int i;
	Name *n;

	n = va_arg(f->args, Name*);
	if(n == nil)
		return fmtprint(f, "?NIL");
	if(n == n->up)
		return fmtprint(f, "\\");
	strncpy(buf, n->seg, 4);
	buf[4] = 0;
	for(i=3; i>0; i--){
		if(buf[i] != '_')
			break;
		buf[i] = 0;
	}
	if(n->up == n->up->up)
		return fmtprint(f, "\\%s", buf);
	return fmtprint(f, "%N.%s", n->up, buf);
}

static int
Vfmt(Fmt *f)
{
	void *p;
	int i, n, c;
	Env *e;
	Field *l;
	Name *nm;
	Method *m;
	Region *g;
	Ref *r;

	p = va_arg(f->args, void*);
	if(p == nil)
		return fmtprint(f, "nil");
	c = TAG(p);
	switch(c){
	case 'N':
		nm = p;
		if(nm->v != nm)
			return fmtprint(f, "%N=%V", nm, nm->v);
		return fmtprint(f, "%N=*", nm);
	case 'A':
	case 'L':
		r = p;
		e = r->ref;
		if(c == 'A')
			return fmtprint(f, "Arg%zd=%V", r->ptr - e->arg, *r->ptr);
		if(c == 'L')
			return fmtprint(f, "Local%zd=%V", r->ptr - e->loc, *r->ptr);
	case 'n':
		return fmtprint(f, "%s", (char*)p);
	case 's':
		return fmtprint(f, "\"%s\"", (char*)p);
	case 'i':
		return fmtprint(f, "%#llux", *((uvlong*)p));
	case 'p':
		n = SIZE(p)/sizeof(void*);
		fmtprint(f, "Package(%d){", n);
		for(i=0; i<n; i++){
			if(i > 0)
				fmtprint(f, ", ");
			fmtprint(f, "%V", ((void**)p)[i]);
		}
		fmtprint(f, "}");
		return 0;
	case 'b':
		n = SIZE(p);
		fmtprint(f, "Buffer(%d){", n);
		for(i=0; i<n; i++){
			if(i > 0)
				fmtprint(f, ", ");
			fmtprint(f, "%.2uX", ((uchar*)p)[i]);
		}
		fmtprint(f, "}");
		return 0;
	case 'r':
		g = p;
		return fmtprint(f, "Region(%s, %#llux, %#llux)",
			spacename[g->space & 7], g->off, g->len);
	case 'm':
		m = p;
		fmtprint(f, "%N(", m->name);
		for(i=0; i < m->narg; i++){
			if(i > 0)
				fmtprint(f, ", ");
			fmtprint(f, "Arg%d", i);
		}
		fmtprint(f, ")");
		return 0;
	case 'u':
		fmtprint(f, "Buffer");
		/* no break */
	case 'f':
		l = p;
		if(l->index){
			if(TAG(l->reg) == 'f')
				return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
					l->flags, l->bitoff, l->bitlen, l->reg, l->index);
			else
				return fmtprint(f, "BankField(%x, %x, %x, %V=%V) @ %V",
					l->flags, l->bitoff, l->bitlen, l->index, l->bank, l->reg);
		}
		return fmtprint(f, "Field(%x, %x, %x) @ %V",
			l->flags, l->bitoff, l->bitlen, l->reg);
	default:
		return fmtprint(f, "%c:%p", c, p);
	}
}

static void
dumpregs(void)
{
	Frame *f;
	Env *e;
	int i;

	print("\n*** dumpregs: PC=%p FP=%p\n", PC, FP);
	e = nil;
	for(f = FP; f >= FB; f--){
		print("%.8p.%.2zx: %-8s %N\t", f->start, f-FB, f->phase, f->dot);
		if(f->op)
			print("%s", f->op->name);
		print("(");
		for(i=0; i<f->narg; i++){
			if(i > 0)
				print(", ");
			print("%V", f->arg[i]);
		}
		print(")\n");
		if(e == f->env)
			continue;
		if(e = f->env){
			for(i=0; i<nelem(e->arg); i++)
				print("Arg%d=%V ", i, e->arg[i]);
			print("\n");
			for(i=0; i<nelem(e->loc); i++)
				print("Local%d=%V ", i, e->loc[i]);
			print("\n");
		}
	}
}

static int
xec(uchar *pc, uchar *end, Name *dot, Env *env, void **pret)
{
	static int loop;
	int i, c;
	void *r;

	PC = pc;
	if(FB < F0 || FB >= FT)
		goto Out;
	FP = FB;

	FP->tag = 0;
	FP->cond = 0;
	FP->narg = 0;
	FP->phase = "}";
	FP->start = PC;
	FP->end = end;
	FP->aux = end;
	FP->ref = nil;
	FP->dot = dot;
	FP->env = env;
	FP->op = nil;

	for(;;){
		if((++loop & 127) == 0)
			gc();
		if(amldebug)
			print("\n%.8p.%.2zx %-8s\t%N\t", PC, FP - FB, FP->phase, FP->dot);
		r = nil;
		c = *FP->phase++;
		switch(c){
		default:
			if(PC >= FP->end){
			Overrun:
				print("aml: PC overrun frame end");
				goto Out;
			}
			FP++;
			if(FP >= FT){
				print("aml: frame stack overflow");
				goto Out;
			}
			*FP = FP[-1];
			FP->aux = nil;
			FP->ref = nil;
			FP->tag = c;
			FP->start = PC;
			c = *PC++;
			if(amldebug) print("%.2X", c);
			if(c == '['){
				if(PC >= FP->end)
					goto Overrun;
				c = *PC++;
				if(amldebug) print("%.2X", c);
				c = octab2[c];
			}else
				c = octab1[c];
			FP->op = &optab[c];
			FP->narg = 0;
			FP->phase = FP->op->sequence;
			if(amldebug) print("\t%s %s", FP->op->name, FP->phase);
			continue;
		case '{':
			end = PC;
			c = pkglen(PC, FP->end, &PC);
			end += c;
			if(c < 0 || end > FP->end)
				goto Overrun;
			FP->end = end;
			continue;
		case ',':
			FP->start = PC;
			continue;
		case 's':
			if(end = memchr(PC, 0, FP->end - PC))
				end++;
			else
				end = FP->end;
			c = end - PC;
			r = mk('s', c+1);
			memmove(r, PC, c);
			((uchar*)r)[c] = 0;
			PC = end;
			break;
		case '1':
		case '2':
		case '4':
		case '8':
			end = PC+(c-'0');
			if(end > FP->end)
				goto Overrun;
			else {
				r = mki(*PC++);
				for(i = 8; PC < end; i += 8)
					*((uvlong*)r) |= ((uvlong)*PC++) << i;
			}
			break;
		case '}':
		case 0:
			if(FP->op){
				if(amldebug){
					print("*%p,%V\t%s(", FP->aux, FP->ref, FP->op->name);
					for(i = 0; i < FP->narg; i++){
						if(i > 0)
							print(", ");
						print("%V", FP->arg[i]);
					}
					print(")");
				}
				for(i = FP->narg; i < nelem(FP->arg); i++)
					FP->arg[i] = nil;
				r = FP->op->eval();
				if(amldebug)
					print(" -> %V", r);
			}

			c = FP->phase[-1];
			if(c == '}' && PC < FP->end){
				FP->narg = 0;
				FP->phase = "*}";
				continue;
			}

			if(r) switch(FP->tag){
			case '@':
				break;
			case 'n':
			case 'N':
				if(TAG(r) != 'N')
					r = nil;
				break;
			default:
				if((r = deref(r)) == nil)
					break;
				switch(TAG(r)){
				case 'f': case 'u':
					r = rwfield(r, nil, 0);
					break;
				case 'm': {
					Method *m = r;
					FP->ref = m;
					FP->narg = 0;
					FP->phase = "********}" + (8 - m->narg);
					FP->op = &optab[Ocall];
					continue;
					}
				}
			}
			FP--;
			break;
		}
		if(FP < FB){
			if(pret){
				if(amldebug) print(" -> %V\n", r);
				*pret = r;
			}
			return 0;
		}
		FP->arg[FP->narg++] = r;
	}
Out:
	if(amldebug)
		dumpregs();
	return -1;
}

static void*
evalnamec(void)
{
	static char name[1024];
	char *d;
	void *r;
	int c;

	c = 1;
	d = name;
	PC = FP->start;
	if(*PC == '\\')
		*d++ = *PC++;
	while(*PC == '^'){
		if(d >= &name[sizeof(name)-1]){
		Toolong:
			*d = 0;
			print("aml: name too long: %s\n", name);
			PC = FP->end;
			return nil;
		}
		*d++ = *PC++;
	}
	if(*PC == '.'){
		PC++;
		c = 2;
	} else if(*PC == '/'){
		PC++;
		c = *PC++;
	} else if(*PC == 0){
		PC++;
		c = 0;
	}
	while(c > 0){
		if(d >= &name[sizeof(name)-5])
			goto Toolong;
		*d++ = *PC++;
		*d++ = *PC++;
		*d++ = *PC++;
		*d++ = *PC++;
		while((d > &name[0]) && (d[-1] == '_' || d[-1] == 0))
			d--;
		if(--c > 0)
			*d++ = '.';
	}
	*d = 0;
	if((r = getname(FP->dot, name, FP->tag == 'N')) == nil){
		r = mks(name);
		D2H(r)->tag = 'n';	/* unresolved name */
	}
	return r;
}

static void*
evaliarg0(void)
{
	return FP->arg[0];
}

static void*
evalconst(void)
{
	switch(FP->start[0]){
	case 0x01:
		return mki(1);
	case 0xFF:
		return mki(~0ULL);
	}
	return nil;
}

static void*
evalbuf(void)
{
	int n, m;
	uchar *p;

	n = ival(FP->arg[0]);
	p = mk('b', n);
	m = FP->end - PC;
	if(m > n)
		m = n;
	memmove(p, PC, m);
	PC = FP->end;
	return p;
}

static void*
evalpkg(void)
{
	void **p, **x;
	int n;

	if((p = FP->ref) != nil){
		x = FP->aux;
		if(x >= p && x < (p+(SIZE(p)/sizeof(void*)))){
			*x++ = FP->arg[0];
			FP->aux = x;
		}
	}else {
		n = ival(FP->arg[0]);
		if(n < 0) n = 0;
		p = mk('p', n*sizeof(void*));
		FP->aux = p;
		FP->ref = p;
	}
	return p;
}

static void*
evalname(void)
{
	Name *n;

	if(n = FP->arg[0])
		n->v = FP->arg[1];
	else
		PC = FP->end;
	return nil;
}

static void*
evalscope(void)
{
	Name *n;

	if(n = FP->arg[0])
		FP->dot = n;
	else
		PC = FP->end;
	FP->op = nil;
	return nil;
}

static void*
evalalias(void)
{
	Name *n;

	if(n = FP->arg[1])
		n->v = deref(FP->arg[0]);
	return nil;
}

static void*
evalmet(void)
{
	Name *n;
	Method *m;

	if((n = FP->arg[0]) != nil){
		m = mk('m', sizeof(Method));
		m->narg = ival(FP->arg[1]) & 7;
		m->start = PC;
		m->end = FP->end;
		m->name = n;
		n->v = m;
	}
	PC = FP->end;
	return nil;
}

static void*
evalreg(void)
{
	Name *n;
	Region *r;

	if((n = FP->arg[0]) != nil){
		r = mk('r', sizeof(Region));
		r->space = ival(FP->arg[1]);
		r->off = ival(FP->arg[2]);
		r->len = ival(FP->arg[3]);
		r->name = n;
		r->va = nil;
		n->v = r;
	}
	return nil;
}

static void*
evalcfield(void)
{
	void *r;
	Field *f;
	Name *n;
	int c;

	r = FP->arg[0];
	if(r == nil || (TAG(r) != 'b' && TAG(r) != 'r'))
		return nil;
	c = FP->op - optab;
	if(c == Ocfld)
		n = FP->arg[3];
	else
		n = FP->arg[2];
	if(n == nil || TAG(n) != 'N')
		return nil;
	if(TAG(r) == 'b')
		f = mk('u', sizeof(Field));
	else
		f = mk('f', sizeof(Field));
	switch(c){
	case Ocfld:
		f->bitoff = ival(FP->arg[1]);
		f->bitlen = ival(FP->arg[2]);
		break;
	case Ocfld0:
		f->bitoff = ival(FP->arg[1]);
		f->bitlen = 1;
		break;
	case Ocfld1:
	case Ocfld2:
	case Ocfld4:
	case Ocfld8:
		f->bitoff = 8*ival(FP->arg[1]);
		f->bitlen = 8*(c - Ocfld0);
		break;
	}
	f->reg = r;
	n->v = f;
	return nil;
}

static void*
evalfield(void)
{
	int flags, bitoff, n;
	Field *f, *index;
	void *reg, *bank;
	Name *d;
	uchar *p;

	bank = nil;
	index = nil;
	bitoff = 0;
	switch(FP->op - optab){
	default:
		goto Out;
	case Ofld:
		if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
			goto Out;
		flags = ival(FP->arg[1]);
		break;
	case Oxfld:
		if((index = deref(FP->arg[0])) == nil || TAG(index) != 'f')
			goto Out;
		if((reg = deref(FP->arg[1])) == nil || TAG(reg) != 'f')	/* data field */
			goto Out;
		flags = ival(FP->arg[2]);
		break;
	case Obfld:
		if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
			goto Out;
		if((index = deref(FP->arg[1])) == nil || TAG(index) != 'f')
			goto Out;
		bank = FP->arg[2];
		flags = ival(FP->arg[3]);
		break;
	}
	p = PC;
	if(p >= FP->end)
		return nil;
	while(p < FP->end){
		if(*p == 0x00){
			p++;
			if((n = pkglen(p, FP->end, &p)) < 0)
				break;
			bitoff += n;
			continue;
		}
		if(*p == 0x01){
			p++;
			flags = *p;
			p += 2;
			continue;
		}
		if(p+4 >= FP->end)
			break;
		if((d = getseg(FP->dot, p, 1)) == nil)
			break;
		if((n = pkglen(p+4, FP->end, &p)) < 0)
			break;
		f = mk('f', sizeof(Field));
		f->flags = flags;
		f->bitlen = n;
		f->bitoff = bitoff;
		f->reg = reg;
		f->bank = bank;
		f->index = index;
		bitoff += n;
		d->v = f;
	}
Out:
	PC = FP->end;
	return nil;
}

static void*
evalnop(void)
{
	return nil;
}

static void*
evalbad(void)
{
	int i;

	print("aml: bad opcode %p: ", PC);
	for(i=0; i < 8 && (FP->start+i) < FP->end; i++){
		if(i > 0)
			print(" ");
		print("%.2X", FP->start[i]);
	}
	if((FP->start+i) < FP->end)
		print("...");
	print("\n");
	PC = FP->end;
	return nil;
}

static void*
evalcond(void)
{
	switch(FP->op - optab){
	case Oif:
		if(FP <= FB)
			break;
		FP[-1].cond = ival(FP->arg[0]) != 0;
		if(!FP[-1].cond)
			PC = FP->end;
		break;
	case Oelse:
		if(FP <= FB)
			break;
		if(FP[-1].cond)
			PC = FP->end;
		break;
	case Owhile:
		if(FP->aux){
			if(PC >= FP->end){
				PC = FP->start;
				FP->aux = nil;
			}
			return nil;
		}
		FP->aux = FP->end;
		if(ival(FP->arg[0]) == 0){
			PC = FP->end;
			break;
		}
		return nil;
	}
	FP->op = nil;
	return nil;
}

static vlong
cmp1(void *a, void *b)
{
	vlong c;
	int tag;

	if(a == nil || TAG(a) == 'i'){
		c = ival(a) - ival(b);
	} else {
		tag = TAG(a);
		if(b == nil || TAG(b) != tag)
			b = copy(tag, b);
		if(b == nil || TAG(b) != tag)
			return -1;	/* botch */
		switch(tag){
		default:
			return -1;	/* botch */
		case 's':
			c = strcmp((char*)a, (char*)b);
			break;
		case 'b':
			c = SIZE(a) - SIZE(b);
			if(c == 0)
				c = memcmp(a, b, SIZE(a));
			break;
		}
	}
	return c;
}

static void*
evalcmp(void)
{
	vlong c = cmp1(FP->arg[0], FP->arg[1]);
	switch(FP->op - optab){
	case Oleq:
		if(c == 0) return mki(1);
		break;
	case Olgt:
		if(c > 0) return mki(1);
		break;
	case Ollt:
		if(c < 0) return mki(1);
		break;
	}
	return nil;
}

static void*
evalcall(void)
{
	Method *m;
	Env *e;
	int i;

	if(FP->aux){
		if(PC >= FP->end){
			PC = FP->aux;
			FP->end = PC;
		}
		return nil;
	}
	m = FP->ref;
	e = mk('E', sizeof(Env));
	for(i=0; i<FP->narg; i++)
		e->arg[i] = deref(FP->arg[i]);
	FP->env = e;
	FP->narg = 0;
	FP->dot = m->name;
	if(m->eval != nil){
		FP->op = nil;
		FP->end = PC;
		return (*m->eval)();
	}
	FP->dot = forkname(FP->dot);
	FP->aux = PC;
	FP->start = m->start;
	FP->end = m->end;
	PC = FP->start;
	return nil;
}

static void*
evalret(void)
{
	void *r = FP->arg[0];
	int brk = (FP->op - optab) != Oret;
	while(--FP >= FB){
		switch(FP->op - optab){
		case Owhile:
			if(!brk)
				continue;
			PC = FP->end;
			return nil;
		case Ocall:
			PC = FP->aux;
			return r;
		}
	}
	FP = FB;
	PC = FB->end;
	return r;
}

static void*
evalenv(void)
{
	Ref *r;
	Env *e;
	int c;

	if((e = FP->env) == nil)
		return nil;
	c = FP->start[0];
	if(c >= 0x60 && c <= 0x67){
		r = mk('L', sizeof(Ref));
		r->ptr = &e->loc[c - 0x60];
	} else if(c >= 0x68 && c <= 0x6E){
		r = mk('A', sizeof(Ref));
		r->ptr = &e->arg[c - 0x68];
	} else
		return nil;
	r->ref = e;
	return r;
}

static void*
evalstore(void)
{
	return store(FP->arg[0], FP->arg[1]);
}

static void*
evalcat(void)
{
	void *r, *a, *b;
	int tag, n, m;

	a = FP->arg[0];
	b = FP->arg[1];
	if(a == nil || TAG(a) == 'i')
		a = copy('b', a);	/* Concat(Int, ???) -> Buf */
	tag = TAG(a);
	if(b == nil || TAG(b) != tag)
		b = copy(tag, b);
	if(TAG(b) != tag)
		return nil;	/* botch */
	switch(tag){
	default:
		return nil;	/* botch */
	case 'b':
		n = SIZE(a);
		m = SIZE(b);
		r = mk('b', n + m);
		memmove(r, a, n);
		memmove((uchar*)r + n, b, m);
		break;
	case 's':
		n = strlen((char*)a);
		m = strlen((char*)b);
		r = mk('s', n + m + 1);
		memmove(r, a, n);
		memmove((char*)r + n, b, m);
		((char*)r)[n+m] = 0;
		break;
	}
	store(r, FP->arg[2]);
	return r;
}

static void*
evalcatres(void)
{
	void *r, *a, *b;
	int n, m;
	uchar c[2];

	a = FP->arg[0];
	b = FP->arg[1];
	if(a == nil)
		a = copy('b', a);
	if(b == nil)
		b = copy('b', b);
	if(TAG(a) != 'b' || TAG(b) != 'b')
		return nil;

	n = SIZE(a);
	m = SIZE(b);
	r = mk('b', n + m + 2);
	memmove(r, a, n);
	memmove((uchar*)r + n, b, m);

	c[0] = 0x78;
	c[1] = 0;
	memmove((uchar*)r + n + m, c, 2);

	store(r, FP->arg[2]);
	return r;
}

static void*
evalmid(void)
{
	void *r, *a;
	int n, m;

	a = FP->arg[1];
	n = ival(FP->arg[1]);
	m = ival(FP->arg[2]);
	if(a == nil)
		a = copy('b', a);

	switch(TAG(a)) {
	default:
		return nil; /* botch */
	case 's':
	case 'b':
		if(n > SIZE(a))
			n = SIZE(a);
		if((n + m) > SIZE(a))
			m = SIZE(a) - n;

		r = mk(TAG(a), m); memmove(r, (uchar*)a + n, m);
		break;
	}

	store(r, FP->arg[3]);
	return r;
}

static void*
evalindex(void)
{
	Field *f;
	void *p;
	Ref *r;
	uvlong x;

	x = ival(FP->arg[1]);
	if(p = deref(FP->arg[0])) switch(TAG(p)){
	case 's':
		if(x >= strlen((char*)p))
			break;
		/* no break */
	case 'b':
		if(x >= SIZE(p))
			break;
		f = mk('u', sizeof(Field));
		f->reg = p;
		f->bitlen = 8;
		f->bitoff = 8*x;
		store(f, FP->arg[2]);
		return f;
	case 'p':
		if(x >= (SIZE(p)/sizeof(void*)))
			break;
		if(TAG(FP->arg[0]) == 'A' || TAG(FP->arg[0]) == 'L')
			r = mk(TAG(FP->arg[0]), sizeof(Ref));
		else
			r = mk('R', sizeof(Ref));
		r->ref = p;
		r->ptr = ((void**)p) + x;
		store(r, FP->arg[2]);
		return r;
	}
	return nil;
}

static int
match1(int op, void *a, void *b)
{
	vlong c = cmp1(a, b);
	switch(op){
	case 0:	return 1;
	case 1:	return c == 0;
	case 2: return c <= 0;
	case 3: return c < 0;
	case 4: return c >= 0;
	case 5: return c > 0;
	}
	return 0;
}

static void*
evalmatch(void)
{
	void **p = FP->arg[0];
	if(p != nil && TAG(p) == 'p'){
		uvlong i, n = SIZE(p)/sizeof(void*);
		int o1 = ival(FP->arg[1]), o2 = ival(FP->arg[3]);
		for(i=ival(FP->arg[5]); i<n; i++){
			void *a = deref(p[i]);
			if(match1(o1, a, FP->arg[2]) && match1(o2, a, FP->arg[4]))
				return mki(i);
		}
	}
	return mki(~0ULL);
}

static void*
evalcondref(void)
{
	void *s;
	if((s = FP->arg[0]) == nil)
		return nil;
	store(s, FP->arg[1]);
	return mki(1);
}

static void*
evalotype(void)
{
	void *r;
	int t;

	t = 0;	/* Uninitialized */
	r = FP->arg[0];
	while(r != nil){
		switch(TAG(r)){
		case 'R': case 'A': case 'L':	/* Ref */
			r = *((Ref*)r)->ptr;
			continue;
		case 'N':			/* Name */
			r = ((Name*)r)->v;
			continue;
		case 'i': t = 1; break;		/* Integer */
		case 's': t = 2; break;		/* String */
		case 'b': t = 3; break;		/* Buffer */
		case 'p': t = 4; break;		/* Package */
		case 'f': t = 5; break;		/* FieldUnit */
		case 'm': t = 8; break;		/* Method */
		case 'r': t = 10; break;	/* OperationRegion */
		case 'u': t = 14; break;	/* BufferField */
		}
		break;
	}
	return mki(t);	
}

static void*
evalsize(void)
{
	return mki(amllen(FP->arg[0]));
}

static void*
evalderef(void)
{
	void *p;

	if(p = FP->arg[0]){
		if(TAG(p) == 's' || TAG(p) == 'n')
			p = getname(FP->dot, (char*)p, 0);
		p = deref(p);
	}
	return p;
}

static void*
evalarith(void)
{
	uvlong v, d;
	void *r;
	int i;

	r = nil;
	switch(FP->op - optab){
	case Oadd:
		r = mki(ival(FP->arg[0]) + ival(FP->arg[1]));
		break;
	case Osub:
		r = mki(ival(FP->arg[0]) - ival(FP->arg[1]));
		break;
	case Omul:
		r = mki(ival(FP->arg[0]) * ival(FP->arg[1]));
		break;
	case Omod:
	case Odiv:
		v = ival(FP->arg[0]);
		d = ival(FP->arg[1]);
		if(d == 0){
			print("aml: division by zero: PC=%#p\n", PC);
			return nil;
		}
		r = mki(v % d);
		store(r, FP->arg[2]);
		if((FP->op - optab) != Odiv)
			return r;
		r = mki(v / d);
		store(r, FP->arg[3]); 
		return r;
	case Oshl:
		r = mki(ival(FP->arg[0]) << ival(FP->arg[1]));
		break;
	case Oshr:
		r = mki(ival(FP->arg[0]) >> ival(FP->arg[1]));
		break;
	case Oand:
		r = mki(ival(FP->arg[0]) & ival(FP->arg[1]));
		break;
	case Onand:
		r = mki(~(ival(FP->arg[0]) & ival(FP->arg[1])));
		break;
	case Oor:
		r = mki(ival(FP->arg[0]) | ival(FP->arg[1]));
		break;
	case Onor:
		r = mki(~(ival(FP->arg[0]) | ival(FP->arg[1])));
		break;
	case Oxor:
		r = mki(ival(FP->arg[0]) ^ ival(FP->arg[1]));
		break;
	case Onot:
		r = mki(~ival(FP->arg[0]));
		store(r, FP->arg[1]);
		return r;
	case Olbit:
		v = ival(FP->arg[0]);
		if(v == 0)
			break;
		for(i=0; (v & 1) == 0; i++)
			v >>= 1;
		r = mki(i+1);
		break;
	case Orbit:
		v = ival(FP->arg[0]);
		if(v == 0)
			break;
		for(i=0; v != 0; i++)
			v >>= 1;
		r = mki(i);
		break;
	case Oland:
		return mki(ival(FP->arg[0]) && ival(FP->arg[1]));
	case Olor:
		return mki(ival(FP->arg[0]) || ival(FP->arg[1]));
	case Olnot:
		return mki(ival(FP->arg[0]) == 0);

	case Oinc:
		r = mki(ival(deref(FP->arg[0]))+1);
		store(r, FP->arg[0]);
		return r;
	case Odec:
		r = mki(ival(deref(FP->arg[0]))-1);
		store(r, FP->arg[0]);
		return r;
	}

	store(r, FP->arg[2]);
	return r;
}

static void*
evalload(void)
{
	enum { LenOffset = 4, HdrLen = 36 };
	uvlong *tid;
	Region *r;
	int l;

	tid = nil;
	if(FP->aux){
		if(PC >= FP->end){
			amlenum(amlroot, nil, fixnames, nil);
			FP->aux = nil;
			FP->end = PC;
			tid = mki(1);	/* fake */
		}
	} else {
		store(nil, FP->arg[1]);
		if(FP->arg[0] == nil)
			return nil;

		l = rwreg(FP->arg[0], LenOffset, 4, 0, 0);
		if(l <= HdrLen)
			return nil;

		FP->aux = PC;	/* save */
		FP->ref = FP->arg[0];
		switch(TAG(FP->ref)){
		case 'b':
			if(SIZE(FP->ref) < l)
				return nil;
			PC = (uchar*)FP->ref + HdrLen;
			break;
		case 'r':
			r = FP->ref;
			if(r->len < l || r->va == nil || r->mapped <= 0)
				return nil;
			PC = (uchar*)r->va + HdrLen;
			break;
		default:
			return nil;
		}
		FP->end = PC + (l - HdrLen);
		FP->dot = amlroot;
		FP->env = nil;

		tid = mki(1); /* fake */
		store(tid, FP->arg[1]);
	}
	return tid;
}

static void*
evalstall(void)
{
	amldelay(ival(FP->arg[0]));
	return nil;
}

static void*
evalsleep(void)
{
	amldelay(ival(FP->arg[0])*1000);
	return nil;
}

static void*
evalconv(void)
{
	void *r, *a;

	r = nil;
	a = FP->arg[0];
	switch(FP->op - optab){
	case Otodec:
		if(a == nil)
			break;
		if(TAG(a) == 's'){
			r = a;
			break;
		}
		if(TAG(a) == 'b'){
			r = todecstr(a, SIZE(a), ',');
			break;
		}
		break;
	case Otohex:
		if(a == nil)
			break;
		if(TAG(a) == 's'){
			r = a;
			break;
		}
		if(TAG(a) == 'b'){
			r = tohexstr(a, SIZE(a), ',');
			break;
		}
		break;
	case Otoint:
		if(a != nil && TAG(a) == 's')
			r = mki(strtoull((char*)a, 0, 0));
		else
			r = mki(ival(a));
		break;
	}
	store(r, FP->arg[1]);
	return r;
}

static void*
evaltostr(void)
{
	char *buf, *r, *e;
	int len;

	buf = FP->arg[0];
	if(buf == nil || (TAG(buf) != 'b' && TAG(buf) != 's'))
		buf = copy('b', buf);
	len = ival(FP->arg[1]);
	if(~len == 0 || len > SIZE(buf))
		len = SIZE(buf);
	if(len > 0 && (e = memchr(buf, 0, len)) != nil)
		len = e - buf;
	r = mk('s', len+1);
	r[len] = '\0';
	memmove(r, buf, len);
	store(r, FP->arg[2]);
	return r;
}

static Op optab[] = {
	[Obad]		"",			"",		evalbad,
	[Onop]		"Noop",			"",		evalnop,
	[Odebug]	"Debug",		"",		evalnop,

	[Ostr]		".str",			"s",		evaliarg0,
	[Obyte]		".byte",		"1",		evaliarg0,
	[Oword]		".word",		"2",		evaliarg0,
	[Odword]	".dword",		"4",		evaliarg0,
	[Oqword]	".qword",		"8",		evaliarg0,
	[Oconst]	".const",		"",		evalconst,
	[Onamec]	".name",		"",		evalnamec,
	[Oenv]		".env",			"",		evalenv,

	[Oname]		"Name",			"N*",		evalname,
	[Oscope]	"Scope",		"{n}",		evalscope,
	[Oalias]	"Alias",		"nN",		evalalias,

	[Odev]		"Device",		"{N}",		evalscope,
	[Ocpu]		"Processor",		"{N141}",	evalscope,
	[Othz]		"ThermalZone",		"{N}",		evalscope,
	[Oprc]		"PowerResource",	"{N12}",	evalscope,

	[Oreg]		"OperationRegion",	"N1ii",		evalreg,
	[Ofld]		"Field",		"{n1",		evalfield,
	[Oxfld]		"IndexField",		"{nn1",		evalfield,
	[Obfld]		"BankField",		"{nni1",	evalfield,

	[Ocfld]		"CreateField",		"*iiN",		evalcfield,
	[Ocfld0]	"CreateBitField",	"*iN",		evalcfield,
	[Ocfld1]	"CreateByteField",	"*iN",		evalcfield,
	[Ocfld2]	"CreateWordField",	"*iN",		evalcfield,
	[Ocfld4]	"CreateDWordField",	"*iN",		evalcfield,
	[Ocfld8]	"CreateQWordField",	"*iN",		evalcfield,

	[Opkg]		"Package",		"{1}",		evalpkg,
	[Ovpkg]		"VarPackage",		"{i}",		evalpkg,
	[Obuf]		"Buffer",		"{i",		evalbuf,
	[Omet]		"Method",		"{N1",		evalmet,

	[Oadd]		"Add",			"ii@",		evalarith,
	[Osub]		"Subtract",		"ii@",		evalarith,
	[Omod]		"Mod",			"ii@",		evalarith,
	[Omul]		"Multiply",		"ii@",		evalarith,
	[Odiv]		"Divide",		"ii@@",		evalarith,
	[Oshl]		"ShiftLef",		"ii@",		evalarith,
	[Oshr]		"ShiftRight",		"ii@",		evalarith,
	[Oand]		"And",			"ii@",		evalarith,
	[Onand]		"Nand",			"ii@",		evalarith,
	[Oor]		"Or",			"ii@",		evalarith,
	[Onor]		"Nor",			"ii@",		evalarith,
	[Oxor]		"Xor",			"ii@",		evalarith,
	[Onot]		"Not",			"i@",		evalarith,

	[Olbit]		"FindSetLeftBit",	"i@",		evalarith,
	[Orbit]		"FindSetRightBit",	"i@",		evalarith,

	[Oinc]		"Increment",		"@",		evalarith,
	[Odec]		"Decrement",		"@",		evalarith,

	[Oland]		"LAnd",			"ii",		evalarith,
	[Olor]		"LOr",			"ii",		evalarith,
	[Olnot]		"LNot",			"i",		evalarith,

	[Oleq]		"LEqual",		"**",		evalcmp,
	[Olgt]		"LGreater",		"**",		evalcmp,
	[Ollt]		"LLess",		"**",		evalcmp,

	[Omutex]	"Mutex",		"N1",		evalnop,
	[Oevent]	"Event",		"N",		evalnop,

	[Oif]		"If",			"{i}",		evalcond,
	[Oelse]		"Else",			"{}",		evalcond,
	[Owhile]	"While",		"{,i}",		evalcond,
	[Obreak]	"Break",		"",		evalret,
	[Oret]		"Return",		"*",		evalret,
	[Ocall]		"Call",			"",		evalcall,

	[Ostore]	"Store",		"*@",		evalstore,
	[Oindex]	"Index",		"@i@",		evalindex,
	[Omatch]	"Match",		"*1*1*i",	evalmatch,
	[Ootype]	"ObjectType",		"@",		evalotype,
	[Osize]		"SizeOf",		"*",		evalsize,
	[Oref]		"RefOf",		"@",		evaliarg0,
	[Ocref]		"CondRefOf",		"@@",		evalcondref,
	[Oderef]	"DerefOf",		"@",		evalderef,
	[Ocat]		"Concatenate",		"**@",		evalcat,
	[Ocatr]		"ConcatenateRes",	"***",		evalcatres,
	[Omid]		"Mid",				"*ii@",		evalmid,

	[Oacq]		"Acquire",		"@2",		evalnop,
	[Osignal]	"Signal",		"@",		evalnop,
	[Orel]		"Release",		"@",		evalnop,
	[Ostall]	"Stall",		"i",		evalstall,
	[Osleep]	"Sleep",		"i",		evalsleep,
	[Oload] 	"Load", 		"*@}", 		evalload,
	[Ounload]	"Unload",		"@",		evalnop,

	[Otodec]	"ToDecimalString",	"*@",		evalconv,
	[Otohex]	"ToHexString",		"*@",		evalconv,
	[Otoint]	"ToInteger",		"*@",		evalconv,
	[Otostr]	"ToString",		"*i@",		evaltostr,

	[Onotify]	"Notify",		"@i",		evalnop,
};

static uchar octab1[] = {
/* 00 */	Oconst,	Oconst,	Obad,	Obad,	Obad,	Obad,	Oalias,	Obad,
/* 08 */	Oname,	Obad,	Obyte,	Oword,	Odword,	Ostr,	Oqword,	Obad,
/* 10 */	Oscope,	Obuf,	Opkg,	Ovpkg,	Omet,	Obad,	Obad,	Obad,
/* 18 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 20 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 28 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Onamec,	Onamec,
/* 30 */	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,
/* 38 */	Onamec,	Onamec,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 40 */	Obad,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,
/* 48 */	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,
/* 50 */	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,	Onamec,
/* 58 */	Onamec,	Onamec,	Onamec,	Obad,	Onamec,	Obad,	Onamec,	Onamec,
/* 60 */	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,
/* 68 */	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Oenv,	Obad,
/* 70 */	Ostore,	Oref,	Oadd,	Ocat,	Osub,	Oinc,	Odec,	Omul,
/* 78 */	Odiv,	Oshl,	Oshr,	Oand,	Onand,	Oor,	Onor,	Oxor,
/* 80 */	Onot,	Olbit,	Orbit,	Oderef,	Ocatr,	Omod,	Onotify,Osize,
/* 88 */	Oindex,	Omatch,	Ocfld4,	Ocfld2,	Ocfld1,	Ocfld0,	Ootype,	Ocfld8,
/* 90 */	Oland,	Olor,	Olnot,	Oleq,	Olgt,	Ollt,	Obad,	Otodec,
/* 98 */	Otohex,	Otoint,	Obad,	Obad,	Otostr,	Obad,	Omid,	Obad,
/* A0 */	Oif,	Oelse,	Owhile,	Onop,	Oret,	Obreak,	Obad,	Obad,
/* A8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* B0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* B8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* C0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* C8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* D0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* D8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* E0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* E8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* F0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* F8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Oconst,
};

static uchar octab2[] = {
/* 00 */	Obad,	Omutex,	Oevent,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 08 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 10 */	Obad,	Obad,	Ocref,	Ocfld,	Obad,	Obad,	Obad,	Obad,
/* 18 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 20 */	Oload,	Ostall,	Osleep,	Oacq,	Osignal,Obad,	Obad,	Orel,
/* 28 */	Obad,	Obad,	Ounload,Obad,	Obad,	Obad,	Obad,	Obad,
/* 30 */	Obad,	Odebug,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 38 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 40 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 48 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 50 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 58 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 60 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 68 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 70 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 78 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 80 */	Oreg,	Ofld,	Odev,	Ocpu,	Oprc,	Othz,	Oxfld,	Obfld,
/* 88 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 90 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* 98 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* A0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* A8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* B0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* B8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* C0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* C8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* D0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* D8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* E0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* E8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* F0 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
/* F8 */	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,	Obad,
};

int
amltag(void *p)
{
	return p ? TAG(p) : 0;
}

void*
amlval(void *p)
{
	return deref(p);
}

uvlong
amlint(void *p)
{
	return ival(p);
}

int
amllen(void *p)
{
	while(p){
		switch(TAG(p)){
		case 'R':
			p = *((Ref*)p)->ptr;
			continue;
		case 'n':
		case 's':
			return strlen((char*)p);
		case 'p':
			return SIZE(p)/sizeof(void*);
		default:
			return SIZE(p);
		}
	}
	return 0;
}

void*
amlnew(char tag, int len)
{
	switch(tag){
	case 'i':
		return mki(0);
	case 'n':
	case 's':
		return mk(tag, len + 1);
	case 'p':
		return mk(tag, len * sizeof(void*));
	default:
		return mk(tag, len);
	}
}

static void*
evalosi(void)
{
	static char *w[] = { 
		"Windows 2001",
		"Windows 2001 SP1",
		"Windows 2001 SP2",
		"Windows 2006",
	};
	char *s;
	int i;

	s = FP->env->arg[0];
	if(s == nil || TAG(s) != 's')
		return nil;
	for(i = 0; i < nelem(w); i++)
		if(strcmp(s, w[i]) == 0)
			return mki(0xFFFFFFFF);
	return nil;
}

void
amlinit(void)
{
	Name *n;

	FB = F0;
	FP = F0-1;

	fmtinstall('V', Vfmt);
	fmtinstall('N', Nfmt);

	if(!amlintmask)
		amlintmask = ~0ULL;

	n = mk('N', sizeof(Name));
	n->up = n;

	amlroot = n;

	getname(amlroot, "_GPE", 1);
	getname(amlroot, "_PR", 1);
	getname(amlroot, "_SB", 1);
	getname(amlroot, "_TZ", 1);
	getname(amlroot, "_SI", 1);
	getname(amlroot, "_GL", 1);

	if(n = getname(amlroot, "_REV", 1))
		n->v = mki(2);
	if(n = getname(amlroot, "_OS", 1))
		n->v = mks("Microsoft Windows");
	if(n = getname(amlroot, "_OSI", 1)){
		Method *m;

		m = mk('m', sizeof(Method));
		m->narg = 1;
		m->eval = evalosi;
		m->name = n;
		n->v = m;
	}
}

void
amlexit(void)
{
	amlroot = nil;
	FB = F0;
	FP = F0-1;
	gc();
}

int
amlload(uchar *data, int len)
{
	int ret;

	ret = xec(data, data+len, amlroot, nil, nil);
	amlenum(amlroot, nil, fixnames, nil);
	return ret;
}

void*
amlwalk(void *dot, char *name)
{
	return getname(dot, name, 0);
}

void
amlenum(void *dot, char *seg, int (*proc)(void *, void *), void *arg)
{
	Name *n, *d;
	int rec;

	d = dot;
	if(d == nil || TAG(d) != 'N')
		return;
	do {
		rec = 1;
		if(seg == nil || memcmp(seg, d->seg, sizeof(d->seg)) == 0)
			rec = (*proc)(d, arg) == 0;
		for(n = d->down; n && rec; n = n->next)
			amlenum(n, seg, proc, arg);
		d = d->fork;
	} while(d);
}

int
amleval(void *dot, char *fmt, ...)
{
	va_list a;
	Method *m;
	void **r;
	Env *e;
	int i;

	va_start(a, fmt);
	e = mk('E', sizeof(Env));
	for(i=0;*fmt;fmt++){
		switch(*fmt){
		default:
			return -1;
		case 's':
			e->arg[i++] = mks(va_arg(a, char*));
			break;
		case 'i':
			e->arg[i++] = mki(va_arg(a, int));
			break;
		case 'I':
			e->arg[i++] = mki(va_arg(a, uvlong));
			break;
		case 'b':
		case 'p':
		case '*':
			e->arg[i++] = va_arg(a, void*);
			break;
		}
	}
	r = va_arg(a, void**);
	va_end(a);
	if(dot = deref(dot))
	if(TAG(dot) == 'm'){
		m = dot;
		if(i != m->narg)
			return -1;
		if(m->eval == nil)
			return xec(m->start, m->end, forkname(m->name), e, r);
		if(FB < F0 || FB >= FT)
			return -1;
		FP = FB;
		FP->op = nil;
		FP->env = e;
		FP->narg = 0;
		FP->dot = m->name;
		FP->ref = FP->aux = nil;
		dot = (*m->eval)();
	}
	if(r != nil)
		*r = dot;
	return 0;
}

void
amltake(void *p)
{
	if(p != nil)
		D2H(p)->mark |= 2;
}

void
amldrop(void *p)
{
	if(p != nil)
		D2H(p)->mark &= ~2;
}

char*
amleisaid(void *v)
{
	int i;
	ulong l, m, n;
	static char s[8];

	if(amltag(v) == 's')
		return v;

	if(amltag(v) == 'i') {
		m = 0;
		l = amlint(v);
		for(i = 24; i >= 0; i -= 8) {
			m |= (l & 0xff) << i;
			l >>= 8;
		}

		s[7] = 0;
		for(i = 6; i >= 3; i--) {
			n = m & 0xf;
			s[i] = "0123456789ABCDEF"[n]; m >>= 4;
		}

		for(i = 2; i >= 0; i--) {
			n = m & 0x1f;
			s[i] = '@' + n; m >>= 5;
		}

		return s;
	}

	return nil;
}
