text: // Inferno utils/8l/span.c
  // http://code.google.com/p/inferno-os/source/browse/utils/8l/span.c
  //
  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
  //	Portions Copyright © 1997-1999 Vita Nuova Limited
  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
  //	Portions Copyright © 2004,2006 Bruce Ellis
  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
  //	Portions Copyright © 2009 The Go Authors.  All rights reserved.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  
  // Instruction layout.
  
  #include	"l.h"
  #include	"../ld/lib.h"
  
  static int32	vaddr(Adr*, Reloc*);
  
  void
  span1(Sym *s)
  {
  	Prog *p, *q;
  	int32 c, v, loop;
  	uchar *bp;
  	int n, m, i;
  
  	cursym = s;
  
  	for(p = s->text; p != P; p = p->link) {
  		p->back = 2;	// use short branches first time through
  		if((q = p->pcond) != P && (q->back & 2))
  			p->back |= 1;	// backward jump
  
  		if(p->as == AADJSP) {
  			p->to.type = D_SP;
  			v = -p->from.offset;
  			p->from.offset = v;
  			p->as = AADDL;
  			if(v < 0) {
  				p->as = ASUBL;
  				v = -v;
  				p->from.offset = v;
  			}
  			if(v == 0)
  				p->as = ANOP;
  		}
  	}
  	
  	n = 0;
  	do {
  		loop = 0;
  		memset(s->r, 0, s->nr*sizeof s->r[0]);
  		s->nr = 0;
  		s->np = 0;
  		c = 0;
  		for(p = s->text; p != P; p = p->link) {
  			p->pc = c;
  
  			// process forward jumps to p
  			for(q = p->comefrom; q != P; q = q->forwd) {
  				v = p->pc - (q->pc + q->mark);
  				if(q->back & 2)	{	// short
  					if(v > 127) {
  						loop++;
  						q->back ^= 2;
  					}
  					s->p[q->pc+1] = v;
  				} else {
  					bp = s->p + q->pc + q->mark - 4;
  					*bp++ = v;
  					*bp++ = v>>8;
  					*bp++ = v>>16;
  					*bp++ = v>>24;
  				}	
  			}
  			p->comefrom = P;
  
  			asmins(p);
  			p->pc = c;
  			m = andptr-and;
  			symgrow(s, p->pc+m);
  			memmove(s->p+p->pc, and, m);
  			p->mark = m;
  			c += m;
  		}
  		if(++n > 20) {
  			diag("span must be looping");
  			errorexit();
  		}
  	} while(loop);
  	s->size = c;
  
  	if(debug['a'] > 1) {
  		print("span1 %s %d (%d tries)\n %.6ux", s->name, s->size, n, 0);
  		for(i=0; i<s->np; i++) {
  			print(" %.2ux", s->p[i]);
  			if(i%16 == 15)
  				print("\n  %.6ux", i+1);
  		}
  		if(i%16)
  			print("\n");
  	
  		for(i=0; i<s->nr; i++) {
  			Reloc *r;
  			
  			r = &s->r[i];
  			print(" rel %#.4ux/%d %s%+d\n", r->off, r->siz, r->sym->name, r->add);
  		}
  	}
  }
  
  void
  span(void)
  {
  	Prog *p, *q;
  	int32 v;
  	int n;
  
  	if(debug['v'])
  		Bprint(&bso, "%5.2f span\n", cputime());
  
  	// NOTE(rsc): If we get rid of the globals we should
  	// be able to parallelize these iterations.
  	for(cursym = textp; cursym != nil; cursym = cursym->next) {
  		if(cursym->text == nil || cursym->text->link == nil)
  			continue;
  
  		// TODO: move into span1
  		for(p = cursym->text; p != P; p = p->link) {
  			n = 0;
  			if(p->to.type == D_BRANCH)
  				if(p->pcond == P)
  					p->pcond = p;
  			if((q = p->pcond) != P)
  				if(q->back != 2)
  					n = 1;
  			p->back = n;
  			if(p->as == AADJSP) {
  				p->to.type = D_SP;
  				v = -p->from.offset;
  				p->from.offset = v;
  				p->as = AADDL;
  				if(v < 0) {
  					p->as = ASUBL;
  					v = -v;
  					p->from.offset = v;
  				}
  				if(v == 0)
  					p->as = ANOP;
  			}
  		}
  		span1(cursym);
  	}
  }
  
  void
  xdefine(char *p, int t, int32 v)
  {
  	Sym *s;
  
  	s = lookup(p, 0);
  	s->type = t;
  	s->value = v;
  	s->reachable = 1;
  	s->special = 1;
  }
  
  void
  instinit(void)
  {
  	int i;
  
  	for(i=1; optab[i].as; i++)
  		if(i != optab[i].as) {
  			diag("phase error in optab: %d", i);
  			errorexit();
  		}
  	maxop = i;
  
  	for(i=0; i<Ymax; i++)
  		ycover[i*Ymax + i] = 1;
  
  	ycover[Yi0*Ymax + Yi8] = 1;
  	ycover[Yi1*Ymax + Yi8] = 1;
  
  	ycover[Yi0*Ymax + Yi32] = 1;
  	ycover[Yi1*Ymax + Yi32] = 1;
  	ycover[Yi8*Ymax + Yi32] = 1;
  
  	ycover[Yal*Ymax + Yrb] = 1;
  	ycover[Ycl*Ymax + Yrb] = 1;
  	ycover[Yax*Ymax + Yrb] = 1;
  	ycover[Ycx*Ymax + Yrb] = 1;
  	ycover[Yrx*Ymax + Yrb] = 1;
  
  	ycover[Yax*Ymax + Yrx] = 1;
  	ycover[Ycx*Ymax + Yrx] = 1;
  
  	ycover[Yax*Ymax + Yrl] = 1;
  	ycover[Ycx*Ymax + Yrl] = 1;
  	ycover[Yrx*Ymax + Yrl] = 1;
  
  	ycover[Yf0*Ymax + Yrf] = 1;
  
  	ycover[Yal*Ymax + Ymb] = 1;
  	ycover[Ycl*Ymax + Ymb] = 1;
  	ycover[Yax*Ymax + Ymb] = 1;
  	ycover[Ycx*Ymax + Ymb] = 1;
  	ycover[Yrx*Ymax + Ymb] = 1;
  	ycover[Yrb*Ymax + Ymb] = 1;
  	ycover[Ym*Ymax + Ymb] = 1;
  
  	ycover[Yax*Ymax + Yml] = 1;
  	ycover[Ycx*Ymax + Yml] = 1;
  	ycover[Yrx*Ymax + Yml] = 1;
  	ycover[Yrl*Ymax + Yml] = 1;
  	ycover[Ym*Ymax + Yml] = 1;
  
  	for(i=0; i<D_NONE; i++) {
  		reg[i] = -1;
  		if(i >= D_AL && i <= D_BH)
  			reg[i] = (i-D_AL) & 7;
  		if(i >= D_AX && i <= D_DI)
  			reg[i] = (i-D_AX) & 7;
  		if(i >= D_F0 && i <= D_F0+7)
  			reg[i] = (i-D_F0) & 7;
  	}
  }
  
  int
  prefixof(Adr *a)
  {
  	switch(a->type) {
  	case D_INDIR+D_CS:
  		return 0x2e;
  	case D_INDIR+D_DS:
  		return 0x3e;
  	case D_INDIR+D_ES:
  		return 0x26;
  	case D_INDIR+D_FS:
  		return 0x64;
  	case D_INDIR+D_GS:
  		return 0x65;
  	}
  	return 0;
  }
  
  int
  oclass(Adr *a)
  {
  	int32 v;
  
  	if((a->type >= D_INDIR && a->type < 2*D_INDIR) || a->index != D_NONE) {
  		if(a->index != D_NONE && a->scale == 0) {
  			if(a->type == D_ADDR) {
  				switch(a->index) {
  				case D_EXTERN:
  				case D_STATIC:
  					return Yi32;
  				case D_AUTO:
  				case D_PARAM:
  					return Yiauto;
  				}
  				return Yxxx;
  			}
  			return Ycol;
  		}
  		return Ym;
  	}
  	switch(a->type)
  	{
  	case D_AL:
  		return Yal;
  
  	case D_AX:
  		return Yax;
  
  	case D_CL:
  	case D_DL:
  	case D_BL:
  	case D_AH:
  	case D_CH:
  	case D_DH:
  	case D_BH:
  		return Yrb;
  
  	case D_CX:
  		return Ycx;
  
  	case D_DX:
  	case D_BX:
  		return Yrx;
  
  	case D_SP:
  	case D_BP:
  	case D_SI:
  	case D_DI:
  		return Yrl;
  
  	case D_F0+0:
  		return	Yf0;
  
  	case D_F0+1:
  	case D_F0+2:
  	case D_F0+3:
  	case D_F0+4:
  	case D_F0+5:
  	case D_F0+6:
  	case D_F0+7:
  		return	Yrf;
  
  	case D_NONE:
  		return Ynone;
  
  	case D_CS:	return	Ycs;
  	case D_SS:	return	Yss;
  	case D_DS:	return	Yds;
  	case D_ES:	return	Yes;
  	case D_FS:	return	Yfs;
  	case D_GS:	return	Ygs;
  
  	case D_GDTR:	return	Ygdtr;
  	case D_IDTR:	return	Yidtr;
  	case D_LDTR:	return	Yldtr;
  	case D_MSW:	return	Ymsw;
  	case D_TASK:	return	Ytask;
  
  	case D_CR+0:	return	Ycr0;
  	case D_CR+1:	return	Ycr1;
  	case D_CR+2:	return	Ycr2;
  	case D_CR+3:	return	Ycr3;
  	case D_CR+4:	return	Ycr4;
  	case D_CR+5:	return	Ycr5;
  	case D_CR+6:	return	Ycr6;
  	case D_CR+7:	return	Ycr7;
  
  	case D_DR+0:	return	Ydr0;
  	case D_DR+1:	return	Ydr1;
  	case D_DR+2:	return	Ydr2;
  	case D_DR+3:	return	Ydr3;
  	case D_DR+4:	return	Ydr4;
  	case D_DR+5:	return	Ydr5;
  	case D_DR+6:	return	Ydr6;
  	case D_DR+7:	return	Ydr7;
  
  	case D_TR+0:	return	Ytr0;
  	case D_TR+1:	return	Ytr1;
  	case D_TR+2:	return	Ytr2;
  	case D_TR+3:	return	Ytr3;
  	case D_TR+4:	return	Ytr4;
  	case D_TR+5:	return	Ytr5;
  	case D_TR+6:	return	Ytr6;
  	case D_TR+7:	return	Ytr7;
  
  	case D_EXTERN:
  	case D_STATIC:
  	case D_AUTO:
  	case D_PARAM:
  		return Ym;
  
  	case D_CONST:
  	case D_CONST2:
  	case D_ADDR:
  		if(a->sym == S) {
  			v = a->offset;
  			if(v == 0)
  				return Yi0;
  			if(v == 1)
  				return Yi1;
  			if(v >= -128 && v <= 127)
  				return Yi8;
  		}
  		return Yi32;
  
  	case D_BRANCH:
  		return Ybr;
  	}
  	return Yxxx;
  }
  
  void
  asmidx(int scale, int index, int base)
  {
  	int i;
  
  	switch(index) {
  	default:
  		goto bad;
  
  	case D_NONE:
  		i = 4 << 3;
  		goto bas;
  
  	case D_AX:
  	case D_CX:
  	case D_DX:
  	case D_BX:
  	case D_BP:
  	case D_SI:
  	case D_DI:
  		i = reg[index] << 3;
  		break;
  	}
  	switch(scale) {
  	default:
  		goto bad;
  	case 1:
  		break;
  	case 2:
  		i |= (1<<6);
  		break;
  	case 4:
  		i |= (2<<6);
  		break;
  	case 8:
  		i |= (3<<6);
  		break;
  	}
  bas:
  	switch(base) {
  	default:
  		goto bad;
  	case D_NONE:	/* must be mod=00 */
  		i |= 5;
  		break;
  	case D_AX:
  	case D_CX:
  	case D_DX:
  	case D_BX:
  	case D_SP:
  	case D_BP:
  	case D_SI:
  	case D_DI:
  		i |= reg[base];
  		break;
  	}
  	*andptr++ = i;
  	return;
  bad:
  	diag("asmidx: bad address %d,%d,%d", scale, index, base);
  	*andptr++ = 0;
  	return;
  }
  
  static void
  put4(int32 v)
  {
  	andptr[0] = v;
  	andptr[1] = v>>8;
  	andptr[2] = v>>16;
  	andptr[3] = v>>24;
  	andptr += 4;
  }
  
  static void
  relput4(Prog *p, Adr *a)
  {
  	vlong v;
  	Reloc rel, *r;
  	
  	v = vaddr(a, &rel);
  	if(rel.siz != 0) {
  		if(rel.siz != 4)
  			diag("bad reloc");
  		r = addrel(cursym);
  		*r = rel;
  		r->off = p->pc + andptr - and;
  	}
  	put4(v);
  }
  
  int32
  symaddr(Sym *s)
  {
  	if(!s->reachable)
  		diag("unreachable symbol in symaddr - %s", s->name);
  	return s->value;
  }
  
  static int32
  vaddr(Adr *a, Reloc *r)
  {
  	int t;
  	int32 v;
  	Sym *s;
  	
  	if(r != nil)
  		memset(r, 0, sizeof *r);
  
  	t = a->type;
  	v = a->offset;
  	if(t == D_ADDR)
  		t = a->index;
  	switch(t) {
  	case D_STATIC:
  	case D_EXTERN:
  		s = a->sym;
  		if(s != nil) {
  			if(!s->reachable)
  				sysfatal("unreachable symbol in vaddr - %s", s->name);
  			if(r == nil) {
  				diag("need reloc for %D", a);
  				errorexit();
  			}
  			r->type = D_ADDR;
  			r->siz = 4;
  			r->off = -1;
  			r->sym = s;
  			r->add = v;
  			v = 0;
  		}
  	}
  	return v;
  }
  
  void
  asmand(Adr *a, int r)
  {
  	int32 v;
  	int t, scale;
  	Reloc rel;
  
  	v = a->offset;
  	t = a->type;
  	rel.siz = 0;
  	if(a->index != D_NONE) {
  		if(t < D_INDIR || t >= 2*D_INDIR) {
  			switch(t) {
  			default:
  				goto bad;
  			case D_STATIC:
  			case D_EXTERN:
  				t = D_NONE;
  				v = vaddr(a, &rel);
  				break;
  			case D_AUTO:
  			case D_PARAM:
  				t = D_SP;
  				break;
  			}
  		} else
  			t -= D_INDIR;
  
  		if(t == D_NONE) {
  			*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
  			asmidx(a->scale, a->index, t);
  			goto putrelv;
  		}
  		if(v == 0 && rel.siz == 0 && t != D_BP) {
  			*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
  			asmidx(a->scale, a->index, t);
  			return;
  		}
  		if(v >= -128 && v < 128 && rel.siz == 0) {
  			*andptr++ = (1 << 6) | (4 << 0) | (r << 3);
  			asmidx(a->scale, a->index, t);
  			*andptr++ = v;
  			return;
  		}
  		*andptr++ = (2 << 6) | (4 << 0) | (r << 3);
  		asmidx(a->scale, a->index, t);
  		goto putrelv;
  	}
  	if(t >= D_AL && t <= D_F0+7) {
  		if(v)
  			goto bad;
  		*andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3);
  		return;
  	}
  	
  	scale = a->scale;
  	if(t < D_INDIR || t >= 2*D_INDIR) {
  		switch(a->type) {
  		default:
  			goto bad;
  		case D_STATIC:
  		case D_EXTERN:
  			t = D_NONE;
  			v = vaddr(a, &rel);
  			break;
  		case D_AUTO:
  		case D_PARAM:
  			t = D_SP;
  			break;
  		}
  		scale = 1;
  	} else
  		t -= D_INDIR;
  
  	if(t == D_NONE || (D_CS <= t && t <= D_GS)) {
  		*andptr++ = (0 << 6) | (5 << 0) | (r << 3);
  		goto putrelv;
  	}
  	if(t == D_SP) {
  		if(v == 0 && rel.siz == 0) {
  			*andptr++ = (0 << 6) | (4 << 0) | (r << 3);
  			asmidx(scale, D_NONE, t);
  			return;
  		}
  		if(v >= -128 && v < 128 && rel.siz == 0) {
  			*andptr++ = (1 << 6) | (4 << 0) | (r << 3);
  			asmidx(scale, D_NONE, t);
  			*andptr++ = v;
  			return;
  		}
  		*andptr++ = (2 << 6) | (4 << 0) | (r << 3);
  		asmidx(scale, D_NONE, t);
  		goto putrelv;
  	}
  	if(t >= D_AX && t <= D_DI) {
  		if(v == 0 && rel.siz == 0 && t != D_BP) {
  			*andptr++ = (0 << 6) | (reg[t] << 0) | (r << 3);
  			return;
  		}
  		if(v >= -128 && v < 128 && rel.siz == 0) {
  			andptr[0] = (1 << 6) | (reg[t] << 0) | (r << 3);
  			andptr[1] = v;
  			andptr += 2;
  			return;
  		}
  		*andptr++ = (2 << 6) | (reg[t] << 0) | (r << 3);
  		goto putrelv;
  	}
  	goto bad;
  
  putrelv:
  	if(rel.siz != 0) {
  		Reloc *r;
  		
  		if(rel.siz != 4) {
  			diag("bad rel");
  			goto bad;
  		}
  		r = addrel(cursym);
  		*r = rel;
  		r->off = curp->pc + andptr - and;
  	}
  	put4(v);
  	return;
  
  bad:
  	diag("asmand: bad address %D", a);
  	return;
  }
  
  #define	E	0xff
  uchar	ymovtab[] =
  {
  /* push */
  	APUSHL,	Ycs,	Ynone,	0,	0x0e,E,0,0,
  	APUSHL,	Yss,	Ynone,	0,	0x16,E,0,0,
  	APUSHL,	Yds,	Ynone,	0,	0x1e,E,0,0,
  	APUSHL,	Yes,	Ynone,	0,	0x06,E,0,0,
  	APUSHL,	Yfs,	Ynone,	0,	0x0f,0xa0,E,0,
  	APUSHL,	Ygs,	Ynone,	0,	0x0f,0xa8,E,0,
  
  	APUSHW,	Ycs,	Ynone,	0,	Pe,0x0e,E,0,
  	APUSHW,	Yss,	Ynone,	0,	Pe,0x16,E,0,
  	APUSHW,	Yds,	Ynone,	0,	Pe,0x1e,E,0,
  	APUSHW,	Yes,	Ynone,	0,	Pe,0x06,E,0,
  	APUSHW,	Yfs,	Ynone,	0,	Pe,0x0f,0xa0,E,
  	APUSHW,	Ygs,	Ynone,	0,	Pe,0x0f,0xa8,E,
  
  /* pop */
  	APOPL,	Ynone,	Yds,	0,	0x1f,E,0,0,
  	APOPL,	Ynone,	Yes,	0,	0x07,E,0,0,
  	APOPL,	Ynone,	Yss,	0,	0x17,E,0,0,
  	APOPL,	Ynone,	Yfs,	0,	0x0f,0xa1,E,0,
  	APOPL,	Ynone,	Ygs,	0,	0x0f,0xa9,E,0,
  
  	APOPW,	Ynone,	Yds,	0,	Pe,0x1f,E,0,
  	APOPW,	Ynone,	Yes,	0,	Pe,0x07,E,0,
  	APOPW,	Ynone,	Yss,	0,	Pe,0x17,E,0,
  	APOPW,	Ynone,	Yfs,	0,	Pe,0x0f,0xa1,E,
  	APOPW,	Ynone,	Ygs,	0,	Pe,0x0f,0xa9,E,
  
  /* mov seg */
  	AMOVW,	Yes,	Yml,	1,	0x8c,0,0,0,
  	AMOVW,	Ycs,	Yml,	1,	0x8c,1,0,0,
  	AMOVW,	Yss,	Yml,	1,	0x8c,2,0,0,
  	AMOVW,	Yds,	Yml,	1,	0x8c,3,0,0,
  	AMOVW,	Yfs,	Yml,	1,	0x8c,4,0,0,
  	AMOVW,	Ygs,	Yml,	1,	0x8c,5,0,0,
  
  	AMOVW,	Yml,	Yes,	2,	0x8e,0,0,0,
  	AMOVW,	Yml,	Ycs,	2,	0x8e,1,0,0,
  	AMOVW,	Yml,	Yss,	2,	0x8e,2,0,0,
  	AMOVW,	Yml,	Yds,	2,	0x8e,3,0,0,
  	AMOVW,	Yml,	Yfs,	2,	0x8e,4,0,0,
  	AMOVW,	Yml,	Ygs,	2,	0x8e,5,0,0,
  
  /* mov cr */
  	AMOVL,	Ycr0,	Yml,	3,	0x0f,0x20,0,0,
  	AMOVL,	Ycr2,	Yml,	3,	0x0f,0x20,2,0,
  	AMOVL,	Ycr3,	Yml,	3,	0x0f,0x20,3,0,
  	AMOVL,	Ycr4,	Yml,	3,	0x0f,0x20,4,0,
  
  	AMOVL,	Yml,	Ycr0,	4,	0x0f,0x22,0,0,
  	AMOVL,	Yml,	Ycr2,	4,	0x0f,0x22,2,0,
  	AMOVL,	Yml,	Ycr3,	4,	0x0f,0x22,3,0,
  	AMOVL,	Yml,	Ycr4,	4,	0x0f,0x22,4,0,
  
  /* mov dr */
  	AMOVL,	Ydr0,	Yml,	3,	0x0f,0x21,0,0,
  	AMOVL,	Ydr6,	Yml,	3,	0x0f,0x21,6,0,
  	AMOVL,	Ydr7,	Yml,	3,	0x0f,0x21,7,0,
  
  	AMOVL,	Yml,	Ydr0,	4,	0x0f,0x23,0,0,
  	AMOVL,	Yml,	Ydr6,	4,	0x0f,0x23,6,0,
  	AMOVL,	Yml,	Ydr7,	4,	0x0f,0x23,7,0,
  
  /* mov tr */
  	AMOVL,	Ytr6,	Yml,	3,	0x0f,0x24,6,0,
  	AMOVL,	Ytr7,	Yml,	3,	0x0f,0x24,7,0,
  
  	AMOVL,	Yml,	Ytr6,	4,	0x0f,0x26,6,E,
  	AMOVL,	Yml,	Ytr7,	4,	0x0f,0x26,7,E,
  
  /* lgdt, sgdt, lidt, sidt */
  	AMOVL,	Ym,	Ygdtr,	4,	0x0f,0x01,2,0,
  	AMOVL,	Ygdtr,	Ym,	3,	0x0f,0x01,0,0,
  	AMOVL,	Ym,	Yidtr,	4,	0x0f,0x01,3,0,
  	AMOVL,	Yidtr,	Ym,	3,	0x0f,0x01,1,0,
  
  /* lldt, sldt */
  	AMOVW,	Yml,	Yldtr,	4,	0x0f,0x00,2,0,
  	AMOVW,	Yldtr,	Yml,	3,	0x0f,0x00,0,0,
  
  /* lmsw, smsw */
  	AMOVW,	Yml,	Ymsw,	4,	0x0f,0x01,6,0,
  	AMOVW,	Ymsw,	Yml,	3,	0x0f,0x01,4,0,
  
  /* ltr, str */
  	AMOVW,	Yml,	Ytask,	4,	0x0f,0x00,3,0,
  	AMOVW,	Ytask,	Yml,	3,	0x0f,0x00,1,0,
  
  /* load full pointer */
  	AMOVL,	Yml,	Ycol,	5,	0,0,0,0,
  	AMOVW,	Yml,	Ycol,	5,	Pe,0,0,0,
  
  /* double shift */
  	ASHLL,	Ycol,	Yml,	6,	0xa4,0xa5,0,0,
  	ASHRL,	Ycol,	Yml,	6,	0xac,0xad,0,0,
  
  /* extra imul */
  	AIMULW,	Yml,	Yrl,	7,	Pq,0xaf,0,0,
  	AIMULL,	Yml,	Yrl,	7,	Pm,0xaf,0,0,
  	0
  };
  
  int
  isax(Adr *a)
  {
  
  	switch(a->type) {
  	case D_AX:
  	case D_AL:
  	case D_AH:
  	case D_INDIR+D_AX:
  		return 1;
  	}
  	if(a->index == D_AX)
  		return 1;
  	return 0;
  }
  
  void
  subreg(Prog *p, int from, int to)
  {
  
  	if(debug['Q'])
  		print("\n%P	s/%R/%R/\n", p, from, to);
  
  	if(p->from.type == from) {
  		p->from.type = to;
  		p->ft = 0;
  	}
  	if(p->to.type == from) {
  		p->to.type = to;
  		p->tt = 0;
  	}
  
  	if(p->from.index == from) {
  		p->from.index = to;
  		p->ft = 0;
  	}
  	if(p->to.index == from) {
  		p->to.index = to;
  		p->tt = 0;
  	}
  
  	from += D_INDIR;
  	if(p->from.type == from) {
  		p->from.type = to+D_INDIR;
  		p->ft = 0;
  	}
  	if(p->to.type == from) {
  		p->to.type = to+D_INDIR;
  		p->tt = 0;
  	}
  
  	if(debug['Q'])
  		print("%P\n", p);
  }
  
  void
  doasm(Prog *p)
  {
  	Optab *o;
  	Prog *q, pp;
  	uchar *t;
  	int z, op, ft, tt;
  	int32 v, pre;
  	Reloc rel, *r;
  	Adr *a;
  	
  	curp = p;	// TODO
  
  	pre = prefixof(&p->from);
  	if(pre)
  		*andptr++ = pre;
  	pre = prefixof(&p->to);
  	if(pre)
  		*andptr++ = pre;
  
  	if(p->ft == 0)
  		p->ft = oclass(&p->from);
  	if(p->tt == 0)
  		p->tt = oclass(&p->to);
  
  	ft = p->ft * Ymax;
  	tt = p->tt * Ymax;
  	o = &optab[p->as];
  	t = o->ytab;
  	if(t == 0) {
  		diag("asmins: noproto %P", p);
  		return;
  	}
  	for(z=0; *t; z+=t[3],t+=4)
  		if(ycover[ft+t[0]])
  		if(ycover[tt+t[1]])
  			goto found;
  	goto domov;
  
  found:
  	switch(o->prefix) {
  	case Pq:	/* 16 bit escape and opcode escape */
  		*andptr++ = Pe;
  		*andptr++ = Pm;
  		break;
  
  	case Pm:	/* opcode escape */
  		*andptr++ = Pm;
  		break;
  
  	case Pe:	/* 16 bit escape */
  		*andptr++ = Pe;
  		break;
  
  	case Pb:	/* botch */
  		break;
  	}
  
  	op = o->op[z];
  	switch(t[2]) {
  	default:
  		diag("asmins: unknown z %d %P", t[2], p);
  		return;
  
  	case Zpseudo:
  		break;
  
  	case Zlit:
  		for(; op = o->op[z]; z++)
  			*andptr++ = op;
  		break;
  
  	case Zm_r:
  		*andptr++ = op;
  		asmand(&p->from, reg[p->to.type]);
  		break;
  
  	case Zaut_r:
  		*andptr++ = 0x8d;	/* leal */
  		if(p->from.type != D_ADDR)
  			diag("asmins: Zaut sb type ADDR");
  		p->from.type = p->from.index;
  		p->from.index = D_NONE;
  		p->ft = 0;
  		asmand(&p->from, reg[p->to.type]);
  		p->from.index = p->from.type;
  		p->from.type = D_ADDR;
  		p->ft = 0;
  		break;
  
  	case Zm_o:
  		*andptr++ = op;
  		asmand(&p->from, o->op[z+1]);
  		break;
  
  	case Zr_m:
  		*andptr++ = op;
  		asmand(&p->to, reg[p->from.type]);
  		break;
  
  	case Zo_m:
  		*andptr++ = op;
  		asmand(&p->to, o->op[z+1]);
  		break;
  
  	case Zm_ibo:
  		*andptr++ = op;
  		asmand(&p->from, o->op[z+1]);
  		*andptr++ = vaddr(&p->to, nil);
  		break;
  
  	case Zibo_m:
  		*andptr++ = op;
  		asmand(&p->to, o->op[z+1]);
  		*andptr++ = vaddr(&p->from, nil);
  		break;
  
  	case Z_ib:
  	case Zib_:
  		if(t[2] == Zib_)
  			a = &p->from;
  		else
  			a = &p->to;
  		v = vaddr(a, nil);
  		*andptr++ = op;
  		*andptr++ = v;
  		break;
  
  	case Zib_rp:
  		*andptr++ = op + reg[p->to.type];
  		*andptr++ = vaddr(&p->from, nil);
  		break;
  
  	case Zil_rp:
  		*andptr++ = op + reg[p->to.type];
  		if(o->prefix == Pe) {
  			v = vaddr(&p->from, nil);
  			*andptr++ = v;
  			*andptr++ = v>>8;
  		}
  		else
  			relput4(p, &p->from);
  		break;
  
  	case Zib_rr:
  		*andptr++ = op;
  		asmand(&p->to, reg[p->to.type]);
  		*andptr++ = vaddr(&p->from, nil);
  		break;
  
  	case Z_il:
  	case Zil_:
  		if(t[2] == Zil_)
  			a = &p->from;
  		else
  			a = &p->to;
  		*andptr++ = op;
  		if(o->prefix == Pe) {
  			v = vaddr(a, nil);
  			*andptr++ = v;
  			*andptr++ = v>>8;
  		}
  		else
  			relput4(p, a);
  		break;
  
  	case Zm_ilo:
  	case Zilo_m:
  		*andptr++ = op;
  		if(t[2] == Zilo_m) {
  			a = &p->from;
  			asmand(&p->to, o->op[z+1]);
  		} else {
  			a = &p->to;
  			asmand(&p->from, o->op[z+1]);
  		}
  		if(o->prefix == Pe) {
  			v = vaddr(a, nil);
  			*andptr++ = v;
  			*andptr++ = v>>8;
  		}
  		else
  			relput4(p, a);
  		break;
  
  	case Zil_rr:
  		*andptr++ = op;
  		asmand(&p->to, reg[p->to.type]);
  		if(o->prefix == Pe) {
  			v = vaddr(&p->from, nil);
  			*andptr++ = v;
  			*andptr++ = v>>8;
  		}
  		else
  			relput4(p, &p->from);
  		break;
  
  	case Z_rp:
  		*andptr++ = op + reg[p->to.type];
  		break;
  
  	case Zrp_:
  		*andptr++ = op + reg[p->from.type];
  		break;
  
  	case Zclr:
  		*andptr++ = op;
  		asmand(&p->to, reg[p->to.type]);
  		break;
  	
  	case Zcall:
  		q = p->pcond;
  		if(q == nil) {
  			diag("call without target");
  			errorexit();
  		}
  		if(q->as != ATEXT) {
  			// Could handle this case by making D_PCREL
  			// record the Prog* instead of the Sym*, but let's
  			// wait until the need arises.
  			diag("call of non-TEXT %P", q);
  			errorexit();
  		}
  		*andptr++ = op;
  		r = addrel(cursym);
  		r->off = p->pc + andptr - and;
  		r->type = D_PCREL;
  		r->siz = 4;
  		r->sym = q->from.sym;
  		put4(0);
  		break;
  
  	case Zbr:
  	case Zjmp:
  		q = p->pcond;
  		if(q == nil) {
  			diag("jmp/branch without target");
  			errorexit();
  		}
  		if(q->as == ATEXT) {
  			// jump out of function
  			if(t[2] == Zbr) {
  				diag("branch to ATEXT");
  				errorexit();
  			}
  			*andptr++ = o->op[z+1];
  			r = addrel(cursym);
  			r->off = p->pc + andptr - and;
  			r->sym = q->from.sym;
  			r->type = D_PCREL;
  			r->siz = 4;
  			put4(0);
  			break;
  		}
  		
  		// Assumes q is in this function.
  		// TODO: Check in input, preserve in brchain.
  		
  		// Fill in backward jump now.
  		if(p->back & 1) {
  			v = q->pc - (p->pc + 2);
  			if(v >= -128) {
  				*andptr++ = op;
  				*andptr++ = v;
  			} else {
  				v -= 5-2;
  				if(t[2] == Zbr) {
  					*andptr++ = 0x0f;
  					v--;
  				}
  				*andptr++ = o->op[z+1];
  				*andptr++ = v;
  				*andptr++ = v>>8;
  				*andptr++ = v>>16;
  				*andptr++ = v>>24;
  			}
  			break;
  		}
  
  		// Annotate target; will fill in later.
  		p->forwd = q->comefrom;
  		q->comefrom = p;
  		if(p->back & 2)	{ // short
  			*andptr++ = op;
  			*andptr++ = 0;
  		} else {
  			if(t[2] == Zbr)
  				*andptr++ = 0x0f;
  			*andptr++ = o->op[z+1];
  			*andptr++ = 0;
  			*andptr++ = 0;
  			*andptr++ = 0;
  			*andptr++ = 0;
  		}
  		break;
  
  	case Zcallcon:
  	case Zjmpcon:
  		if(t[2] == Zcallcon)
  			*andptr++ = op;
  		else
  			*andptr++ = o->op[z+1];
  		r = addrel(cursym);
  		r->off = p->pc + andptr - and;
  		r->type = D_PCREL;
  		r->siz = 4;
  		r->add = p->to.offset;
  		put4(0);
  		break;
  
  	case Zloop:
  		q = p->pcond;
  		if(q == nil) {
  			diag("loop without target");
  			errorexit();
  		}
  		v = q->pc - p->pc - 2;
  		if(v < -128 && v > 127)
  			diag("loop too far: %P", p);
  		*andptr++ = op;
  		*andptr++ = v;
  		break;
  
  	case Zbyte:
  		v = vaddr(&p->from, &rel);
  		if(rel.siz != 0) {
  			rel.siz = op;
  			r = addrel(cursym);
  			*r = rel;
  			r->off = p->pc + andptr - and;
  		}
  		*andptr++ = v;
  		if(op > 1) {
  			*andptr++ = v>>8;
  			if(op > 2) {
  				*andptr++ = v>>16;
  				*andptr++ = v>>24;
  			}
  		}
  		break;
  
  	case Zmov:
  		goto domov;
  	}
  	return;
  
  domov:
  	for(t=ymovtab; *t; t+=8)
  		if(p->as == t[0])
  		if(ycover[ft+t[1]])
  		if(ycover[tt+t[2]])
  			goto mfound;
  bad:
  	/*
  	 * here, the assembly has failed.
  	 * if its a byte instruction that has
  	 * unaddressable registers, try to
  	 * exchange registers and reissue the
  	 * instruction with the operands renamed.
  	 */
  	pp = *p;
  	z = p->from.type;
  	if(z >= D_BP && z <= D_DI) {
  		if(isax(&p->to)) {
  			*andptr++ = 0x87;			/* xchg lhs,bx */
  			asmand(&p->from, reg[D_BX]);
  			subreg(&pp, z, D_BX);
  			doasm(&pp);
  			*andptr++ = 0x87;			/* xchg lhs,bx */
  			asmand(&p->from, reg[D_BX]);
  		} else {
  			*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
  			subreg(&pp, z, D_AX);
  			doasm(&pp);
  			*andptr++ = 0x90 + reg[z];		/* xchg lsh,ax */
  		}
  		return;
  	}
  	z = p->to.type;
  	if(z >= D_BP && z <= D_DI) {
  		if(isax(&p->from)) {
  			*andptr++ = 0x87;			/* xchg rhs,bx */
  			asmand(&p->to, reg[D_BX]);
  			subreg(&pp, z, D_BX);
  			doasm(&pp);
  			*andptr++ = 0x87;			/* xchg rhs,bx */
  			asmand(&p->to, reg[D_BX]);
  		} else {
  			*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
  			subreg(&pp, z, D_AX);
  			doasm(&pp);
  			*andptr++ = 0x90 + reg[z];		/* xchg rsh,ax */
  		}
  		return;
  	}
  	diag("doasm: notfound t2=%ux from=%ux to=%ux %P", t[2], p->from.type, p->to.type, p);
  	return;
  
  mfound:
  	switch(t[3]) {
  	default:
  		diag("asmins: unknown mov %d %P", t[3], p);
  		break;
  
  	case 0:	/* lit */
  		for(z=4; t[z]!=E; z++)
  			*andptr++ = t[z];
  		break;
  
  	case 1:	/* r,m */
  		*andptr++ = t[4];
  		asmand(&p->to, t[5]);
  		break;
  
  	case 2:	/* m,r */
  		*andptr++ = t[4];
  		asmand(&p->from, t[5]);
  		break;
  
  	case 3:	/* r,m - 2op */
  		*andptr++ = t[4];
  		*andptr++ = t[5];
  		asmand(&p->to, t[6]);
  		break;
  
  	case 4:	/* m,r - 2op */
  		*andptr++ = t[4];
  		*andptr++ = t[5];
  		asmand(&p->from, t[6]);
  		break;
  
  	case 5:	/* load full pointer, trash heap */
  		if(t[4])
  			*andptr++ = t[4];
  		switch(p->to.index) {
  		default:
  			goto bad;
  		case D_DS:
  			*andptr++ = 0xc5;
  			break;
  		case D_SS:
  			*andptr++ = 0x0f;
  			*andptr++ = 0xb2;
  			break;
  		case D_ES:
  			*andptr++ = 0xc4;
  			break;
  		case D_FS:
  			*andptr++ = 0x0f;
  			*andptr++ = 0xb4;
  			break;
  		case D_GS:
  			*andptr++ = 0x0f;
  			*andptr++ = 0xb5;
  			break;
  		}
  		asmand(&p->from, reg[p->to.type]);
  		break;
  
  	case 6:	/* double shift */
  		z = p->from.type;
  		switch(z) {
  		default:
  			goto bad;
  		case D_CONST:
  			*andptr++ = 0x0f;
  			*andptr++ = t[4];
  			asmand(&p->to, reg[p->from.index]);
  			*andptr++ = p->from.offset;
  			break;
  		case D_CL:
  		case D_CX:
  			*andptr++ = 0x0f;
  			*andptr++ = t[5];
  			asmand(&p->to, reg[p->from.index]);
  			break;
  		}
  		break;
  
  	case 7: /* imul rm,r */
  		if(t[4] == Pq) {
  			*andptr++ = Pe;
  			*andptr++ = Pm;
  		} else
  			*andptr++ = t[4];
  		*andptr++ = t[5];
  		asmand(&p->from, reg[p->to.type]);
  		break;
  	}
  }
  
  void
  asmins(Prog *p)
  {
  	andptr = and;
  	doasm(p);
  	if(andptr > and+sizeof and) {
  		print("and[] is too short - %d byte instruction\n", andptr - and);
  		errorexit();
  	}
  }
  