diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/5l/asm.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/5l/asm.c')
-rwxr-xr-x | sys/src/cmd/5l/asm.c | 1652 |
1 files changed, 1652 insertions, 0 deletions
diff --git a/sys/src/cmd/5l/asm.c b/sys/src/cmd/5l/asm.c new file mode 100755 index 000000000..32f2991cf --- /dev/null +++ b/sys/src/cmd/5l/asm.c @@ -0,0 +1,1652 @@ +#include "l.h" + +long OFFSET; + +static Prog *PP; + +long +entryvalue(void) +{ + char *a; + Sym *s; + + a = INITENTRY; + if(*a >= '0' && *a <= '9') + return atolwhex(a); + s = lookup(a, 0); + if(s->type == 0) + return INITTEXT; + switch(s->type) { + case STEXT: + case SLEAF: + break; + case SDATA: + if(dlm) + return s->value+INITDAT; + default: + diag("entry not text: %s", s->name); + } + return s->value; +} + +void +asmb(void) +{ + Prog *p; + long t, etext; + Optab *o; + + if(debug['v']) + Bprint(&bso, "%5.2f asm\n", cputime()); + Bflush(&bso); + OFFSET = HEADR; + seek(cout, OFFSET, 0); + pc = INITTEXT; + for(p = firstp; p != P; p = p->link) { + if(p->as == ATEXT) { + curtext = p; + autosize = p->to.offset + 4; + } + if(p->pc != pc) { + diag("phase error %lux sb %lux", + p->pc, pc); + if(!debug['a']) + prasm(curp); + pc = p->pc; + } + curp = p; + o = oplook(p); /* could probably avoid this call */ + asmout(p, o); + pc += o->size; + } + + if(debug['a']) + Bprint(&bso, "\n"); + Bflush(&bso); + cflush(); + + /* output strings in text segment */ + etext = INITTEXT + textsize; + for(t = pc; t < etext; t += sizeof(buf)-100) { + if(etext-t > sizeof(buf)-100) + datblk(t, sizeof(buf)-100, 1); + else + datblk(t, etext-t, 1); + } + + curtext = P; + switch(HEADTYPE) { + case 0: + case 1: + case 2: + case 5: + OFFSET = HEADR+textsize; + seek(cout, OFFSET, 0); + break; + case 3: + OFFSET = rnd(HEADR+textsize, 4096); + seek(cout, OFFSET, 0); + break; + } + if(dlm){ + char buf[8]; + + write(cout, buf, INITDAT-textsize); + textsize = INITDAT; + } + for(t = 0; t < datsize; t += sizeof(buf)-100) { + if(datsize-t > sizeof(buf)-100) + datblk(t, sizeof(buf)-100, 0); + else + datblk(t, datsize-t, 0); + } + + symsize = 0; + lcsize = 0; + if(!debug['s']) { + if(debug['v']) + Bprint(&bso, "%5.2f sym\n", cputime()); + Bflush(&bso); + switch(HEADTYPE) { + case 0: + case 1: + case 4: + case 5: + debug['s'] = 1; + break; + case 2: + OFFSET = HEADR+textsize+datsize; + seek(cout, OFFSET, 0); + break; + case 3: + OFFSET += rnd(datsize, 4096); + seek(cout, OFFSET, 0); + break; + } + if(!debug['s']) + asmsym(); + if(debug['v']) + Bprint(&bso, "%5.2f pc\n", cputime()); + Bflush(&bso); + if(!debug['s']) + asmlc(); + if(dlm) + asmdyn(); + cflush(); + } + else if(dlm){ + seek(cout, HEADR+textsize+datsize, 0); + asmdyn(); + cflush(); + } + + if(debug['v']) + Bprint(&bso, "%5.2f header\n", cputime()); + Bflush(&bso); + OFFSET = 0; + seek(cout, OFFSET, 0); + switch(HEADTYPE) { + case 0: /* no header */ + break; + case 1: /* aif for risc os */ + lputl(0xe1a00000); /* NOP - decompress code */ + lputl(0xe1a00000); /* NOP - relocation code */ + lputl(0xeb000000 + 12); /* BL - zero init code */ + lputl(0xeb000000 + + (entryvalue() + - INITTEXT + + HEADR + - 12 + - 8) / 4); /* BL - entry code */ + + lputl(0xef000011); /* SWI - exit code */ + lputl(textsize+HEADR); /* text size */ + lputl(datsize); /* data size */ + lputl(0); /* sym size */ + + lputl(bsssize); /* bss size */ + lputl(0); /* sym type */ + lputl(INITTEXT-HEADR); /* text addr */ + lputl(0); /* workspace - ignored */ + + lputl(32); /* addr mode / data addr flag */ + lputl(0); /* data addr */ + for(t=0; t<2; t++) + lputl(0); /* reserved */ + + for(t=0; t<15; t++) + lputl(0xe1a00000); /* NOP - zero init code */ + lputl(0xe1a0f00e); /* B (R14) - zero init return */ + break; + case 2: /* plan 9 */ + if(dlm) + lput(0x80000000|0x647); /* magic */ + else + lput(0x647); /* magic */ + lput(textsize); /* sizes */ + lput(datsize); + lput(bsssize); + lput(symsize); /* nsyms */ + lput(entryvalue()); /* va of entry */ + lput(0L); + lput(lcsize); + break; + case 3: /* boot for NetBSD */ + lput((143<<16)|0413); /* magic */ + lputl(rnd(HEADR+textsize, 4096)); + lputl(rnd(datsize, 4096)); + lputl(bsssize); + lputl(symsize); /* nsyms */ + lputl(entryvalue()); /* va of entry */ + lputl(0L); + lputl(0L); + break; + case 4: /* boot for IXP1200 */ + break; + case 5: /* boot for ipaq */ + lputl(0xe3300000); /* nop */ + lputl(0xe3300000); /* nop */ + lputl(0xe3300000); /* nop */ + lputl(0xe3300000); /* nop */ + break; + } + cflush(); +} + +void +strnput(char *s, int n) +{ + for(; *s; s++){ + cput(*s); + n--; + } + for(; n > 0; n--) + cput(0); +} + +void +cput(int c) +{ + cbp[0] = c; + cbp++; + cbc--; + if(cbc <= 0) + cflush(); +} + +void +wput(long l) +{ + + cbp[0] = l>>8; + cbp[1] = l; + cbp += 2; + cbc -= 2; + if(cbc <= 0) + cflush(); +} + +void +lput(long l) +{ + + cbp[0] = l>>24; + cbp[1] = l>>16; + cbp[2] = l>>8; + cbp[3] = l; + cbp += 4; + cbc -= 4; + if(cbc <= 0) + cflush(); +} + +void +lputl(long l) +{ + + cbp[3] = l>>24; + cbp[2] = l>>16; + cbp[1] = l>>8; + cbp[0] = l; + cbp += 4; + cbc -= 4; + if(cbc <= 0) + cflush(); +} + +void +cflush(void) +{ + int n; + + n = sizeof(buf.cbuf) - cbc; + if(n) + write(cout, buf.cbuf, n); + cbp = buf.cbuf; + cbc = sizeof(buf.cbuf); +} + +void +nopstat(char *f, Count *c) +{ + if(c->outof) + Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f, + c->outof - c->count, c->outof, + (double)(c->outof - c->count)/c->outof); +} + +void +asmsym(void) +{ + Prog *p; + Auto *a; + Sym *s; + int h; + + s = lookup("etext", 0); + if(s->type == STEXT) + putsymb(s->name, 'T', s->value, s->version); + + for(h=0; h<NHASH; h++) + for(s=hash[h]; s!=S; s=s->link) + switch(s->type) { + case SCONST: + putsymb(s->name, 'D', s->value, s->version); + continue; + + case SDATA: + putsymb(s->name, 'D', s->value+INITDAT, s->version); + continue; + + case SBSS: + putsymb(s->name, 'B', s->value+INITDAT, s->version); + continue; + + case SSTRING: + putsymb(s->name, 'T', s->value, s->version); + continue; + + case SFILE: + putsymb(s->name, 'f', s->value, s->version); + continue; + } + + for(p=textp; p!=P; p=p->cond) { + s = p->from.sym; + if(s->type != STEXT && s->type != SLEAF) + continue; + + /* filenames first */ + for(a=p->to.autom; a; a=a->link) + if(a->type == D_FILE) + putsymb(a->asym->name, 'z', a->aoffset, 0); + else + if(a->type == D_FILE1) + putsymb(a->asym->name, 'Z', a->aoffset, 0); + + if(s->type == STEXT) + putsymb(s->name, 'T', s->value, s->version); + else + putsymb(s->name, 'L', s->value, s->version); + + /* frame, auto and param after */ + putsymb(".frame", 'm', p->to.offset+4, 0); + for(a=p->to.autom; a; a=a->link) + if(a->type == D_AUTO) + putsymb(a->asym->name, 'a', -a->aoffset, 0); + else + if(a->type == D_PARAM) + putsymb(a->asym->name, 'p', a->aoffset, 0); + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %lud\n", symsize); + Bflush(&bso); +} + +void +putsymb(char *s, int t, long v, int ver) +{ + int i, f; + + if(t == 'f') + s++; + lput(v); + if(ver) + t += 'a' - 'A'; + cput(t+0x80); /* 0x80 is variable length */ + + if(t == 'Z' || t == 'z') { + cput(s[0]); + for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) { + cput(s[i]); + cput(s[i+1]); + } + cput(0); + cput(0); + i++; + } + else { + for(i=0; s[i]; i++) + cput(s[i]); + cput(0); + } + symsize += 4 + 1 + i + 1; + + if(debug['n']) { + if(t == 'z' || t == 'Z') { + Bprint(&bso, "%c %.8lux ", t, v); + for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) { + f = ((s[i]&0xff) << 8) | (s[i+1]&0xff); + Bprint(&bso, "/%x", f); + } + Bprint(&bso, "\n"); + return; + } + if(ver) + Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver); + else + Bprint(&bso, "%c %.8lux %s\n", t, v, s); + } +} + +#define MINLC 4 +void +asmlc(void) +{ + long oldpc, oldlc; + Prog *p; + long v, s; + + oldpc = INITTEXT; + oldlc = 0; + for(p = firstp; p != P; p = p->link) { + if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) { + if(p->as == ATEXT) + curtext = p; + if(debug['L']) + Bprint(&bso, "%6lux %P\n", + p->pc, p); + continue; + } + if(debug['L']) + Bprint(&bso, "\t\t%6ld", lcsize); + v = (p->pc - oldpc) / MINLC; + while(v) { + s = 127; + if(v < 127) + s = v; + cput(s+128); /* 129-255 +pc */ + if(debug['L']) + Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128); + v -= s; + lcsize++; + } + s = p->line - oldlc; + oldlc = p->line; + oldpc = p->pc + MINLC; + if(s > 64 || s < -64) { + cput(0); /* 0 vv +lc */ + cput(s>>24); + cput(s>>16); + cput(s>>8); + cput(s); + if(debug['L']) { + if(s > 0) + Bprint(&bso, " lc+%ld(%d,%ld)\n", + s, 0, s); + else + Bprint(&bso, " lc%ld(%d,%ld)\n", + s, 0, s); + Bprint(&bso, "%6lux %P\n", + p->pc, p); + } + lcsize += 5; + continue; + } + if(s > 0) { + cput(0+s); /* 1-64 +lc */ + if(debug['L']) { + Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s); + Bprint(&bso, "%6lux %P\n", + p->pc, p); + } + } else { + cput(64-s); /* 65-128 -lc */ + if(debug['L']) { + Bprint(&bso, " lc%ld(%ld)\n", s, 64-s); + Bprint(&bso, "%6lux %P\n", + p->pc, p); + } + } + lcsize++; + } + while(lcsize & 1) { + s = 129; + cput(s); + lcsize++; + } + if(debug['v'] || debug['L']) + Bprint(&bso, "lcsize = %ld\n", lcsize); + Bflush(&bso); +} + +void +datblk(long s, long n, int str) +{ + Sym *v; + Prog *p; + char *cast; + long a, l, fl, j, d; + int i, c; + + memset(buf.dbuf, 0, n+100); + for(p = datap; p != P; p = p->link) { + if(str != (p->from.sym->type == SSTRING)) + continue; + curp = p; + a = p->from.sym->value + p->from.offset; + l = a - s; + c = p->reg; + i = 0; + if(l < 0) { + if(l+c <= 0) + continue; + while(l < 0) { + l++; + i++; + } + } + if(l >= n) + continue; + if(p->as != AINIT && p->as != ADYNT) { + for(j=l+(c-i)-1; j>=l; j--) + if(buf.dbuf[j]) { + print("%P\n", p); + diag("multiple initialization"); + break; + } + } + switch(p->to.type) { + default: + diag("unknown mode in initialization%P", p); + break; + + case D_FCONST: + switch(c) { + default: + case 4: + fl = ieeedtof(p->to.ieee); + cast = (char*)&fl; + for(; i<c; i++) { + buf.dbuf[l] = cast[fnuxi4[i]]; + l++; + } + break; + case 8: + cast = (char*)p->to.ieee; + for(; i<c; i++) { + buf.dbuf[l] = cast[fnuxi8[i]]; + l++; + } + break; + } + break; + + case D_SCONST: + for(; i<c; i++) { + buf.dbuf[l] = p->to.sval[i]; + l++; + } + break; + + case D_CONST: + d = p->to.offset; + v = p->to.sym; + if(v) { + switch(v->type) { + case SUNDEF: + ckoff(v, d); + case STEXT: + case SLEAF: + case SSTRING: + d += p->to.sym->value; + break; + case SDATA: + case SBSS: + d += p->to.sym->value + INITDAT; + } + if(dlm) + dynreloc(v, a+INITDAT, 1); + } + cast = (char*)&d; + switch(c) { + default: + diag("bad nuxi %d %d%P", c, i, curp); + break; + case 1: + for(; i<c; i++) { + buf.dbuf[l] = cast[inuxi1[i]]; + l++; + } + break; + case 2: + for(; i<c; i++) { + buf.dbuf[l] = cast[inuxi2[i]]; + l++; + } + break; + case 4: + for(; i<c; i++) { + buf.dbuf[l] = cast[inuxi4[i]]; + l++; + } + break; + } + break; + } + } + write(cout, buf.dbuf, n); +} + +void +asmout(Prog *p, Optab *o) +{ + long o1, o2, o3, o4, o5, o6, v; + int r, rf, rt, rt2; + Sym *s; + +PP = p; + o1 = 0; + o2 = 0; + o3 = 0; + o4 = 0; + o5 = 0; + o6 = 0; + switch(o->type) { + default: + diag("unknown asm %d", o->type); + prasm(p); + break; + + case 0: /* pseudo ops */ + break; + + case 1: /* op R,[R],R */ + o1 = oprrr(p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + r = p->reg; + if(p->to.type == D_NONE) + rt = 0; + if(p->as == AMOVW || p->as == AMVN) + r = 0; + else if(r == NREG) + r = rt; + o1 |= rf | (r<<16) | (rt<<12); + break; + + case 2: /* movbu $I,[R],R */ + aclass(&p->from); + o1 = oprrr(p->as, p->scond); + o1 |= immrot(instoffset); + rt = p->to.reg; + r = p->reg; + if(p->to.type == D_NONE) + rt = 0; + if(p->as == AMOVW || p->as == AMVN) + r = 0; + else if(r == NREG) + r = rt; + o1 |= (r<<16) | (rt<<12); + break; + + case 3: /* add R<<[IR],[R],R */ + mov: + aclass(&p->from); + o1 = oprrr(p->as, p->scond); + o1 |= p->from.offset; + rt = p->to.reg; + r = p->reg; + if(p->to.type == D_NONE) + rt = 0; + if(p->as == AMOVW || p->as == AMVN) + r = 0; + else if(r == NREG) + r = rt; + o1 |= (r<<16) | (rt<<12); + break; + + case 4: /* add $I,[R],R */ + aclass(&p->from); + o1 = oprrr(AADD, p->scond); + o1 |= immrot(instoffset); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 |= r << 16; + o1 |= p->to.reg << 12; + break; + + case 5: /* bra s */ + v = -8; + if(p->cond == UP) { + s = p->to.sym; + if(s->type != SUNDEF) + diag("bad branch sym type"); + v = (ulong)s->value >> (Roffset-2); + dynreloc(s, p->pc, 0); + } + else if(p->cond != P) + v = (p->cond->pc - pc) - 8; + o1 = opbra(p->as, p->scond); + o1 |= (v >> 2) & 0xffffff; + break; + + case 6: /* b ,O(R) -> add $O,R,PC */ + aclass(&p->to); + o1 = oprrr(AADD, p->scond); + o1 |= immrot(instoffset); + o1 |= p->to.reg << 16; + o1 |= REGPC << 12; + break; + + case 7: /* bl ,O(R) -> mov PC,link; add $O,R,PC */ + aclass(&p->to); + o1 = oprrr(AADD, p->scond); + o1 |= immrot(0); + o1 |= REGPC << 16; + o1 |= REGLINK << 12; + + o2 = oprrr(AADD, p->scond); + o2 |= immrot(instoffset); + o2 |= p->to.reg << 16; + o2 |= REGPC << 12; + break; + + case 8: /* sll $c,[R],R -> mov (R<<$c),R */ + aclass(&p->from); + o1 = oprrr(p->as, p->scond); + r = p->reg; + if(r == NREG) + r = p->to.reg; + o1 |= r; + o1 |= (instoffset&31) << 7; + o1 |= p->to.reg << 12; + break; + + case 9: /* sll R,[R],R -> mov (R<<R),R */ + o1 = oprrr(p->as, p->scond); + r = p->reg; + if(r == NREG) + r = p->to.reg; + o1 |= r; + o1 |= (p->from.reg << 8) | (1<<4); + o1 |= p->to.reg << 12; + break; + + case 10: /* swi [$con] */ + o1 = oprrr(p->as, p->scond); + if(p->to.type != D_NONE) { + aclass(&p->to); + o1 |= instoffset & 0xffffff; + } + break; + + case 11: /* word */ + switch(aclass(&p->to)) { + case C_LCON: + if(!dlm) + break; + if(p->to.name != D_EXTERN && p->to.name != D_STATIC) + break; + case C_ADDR: + if(p->to.sym->type == SUNDEF) + ckoff(p->to.sym, p->to.offset); + dynreloc(p->to.sym, p->pc, 1); + } + o1 = instoffset; + break; + + case 12: /* movw $lcon, reg */ + o1 = omvl(p, &p->from, p->to.reg); + break; + + case 13: /* op $lcon, [R], R */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + o2 = oprrr(p->as, p->scond); + o2 |= REGTMP; + r = p->reg; + if(p->as == AMOVW || p->as == AMVN) + r = 0; + else if(r == NREG) + r = p->to.reg; + o2 |= r << 16; + if(p->to.type != D_NONE) + o2 |= p->to.reg << 12; + break; + + case 14: /* movb/movbu/movh/movhu R,R */ + o1 = oprrr(ASLL, p->scond); + + if(p->as == AMOVBU || p->as == AMOVHU) + o2 = oprrr(ASRL, p->scond); + else + o2 = oprrr(ASRA, p->scond); + + r = p->to.reg; + o1 |= (p->from.reg)|(r<<12); + o2 |= (r)|(r<<12); + if(p->as == AMOVB || p->as == AMOVBU) { + o1 |= (24<<7); + o2 |= (24<<7); + } else { + o1 |= (16<<7); + o2 |= (16<<7); + } + break; + + case 15: /* mul r,[r,]r */ + o1 = oprrr(p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + r = p->reg; + if(r == NREG) + r = rt; + if(rt == r) { + r = rf; + rf = rt; + } + if(0) + if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) { + diag("bad registers in MUL"); + prasm(p); + } + o1 |= (rf<<8) | r | (rt<<16); + break; + + + case 16: /* div r,[r,]r */ + o1 = 0xf << 28; + o2 = 0; + break; + + case 17: + o1 = oprrr(p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + rt2 = p->to.offset; + r = p->reg; + o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12); + break; + + case 20: /* mov/movb/movbu R,O(R) */ + aclass(&p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = osr(p->as, p->from.reg, instoffset, r, p->scond); + break; + + case 21: /* mov/movbu O(R),R -> lr */ + aclass(&p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = olr(instoffset, r, p->to.reg, p->scond); + if(p->as != AMOVW) + o1 |= 1<<22; + break; + + case 22: /* movb/movh/movhu O(R),R -> lr,shl,shr */ + aclass(&p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = olr(instoffset, r, p->to.reg, p->scond); + + o2 = oprrr(ASLL, p->scond); + o3 = oprrr(ASRA, p->scond); + r = p->to.reg; + if(p->as == AMOVB) { + o2 |= (24<<7)|(r)|(r<<12); + o3 |= (24<<7)|(r)|(r<<12); + } else { + o2 |= (16<<7)|(r)|(r<<12); + if(p->as == AMOVHU) + o3 = oprrr(ASRL, p->scond); + o3 |= (16<<7)|(r)|(r<<12); + } + break; + + case 23: /* movh/movhu R,O(R) -> sb,sb */ + aclass(&p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = osr(AMOVH, p->from.reg, instoffset, r, p->scond); + + o2 = oprrr(ASRL, p->scond); + o2 |= (8<<7)|(p->from.reg)|(REGTMP<<12); + + o3 = osr(AMOVH, REGTMP, instoffset+1, r, p->scond); + break; + + case 30: /* mov/movb/movbu R,L(R) */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = osrr(p->from.reg, REGTMP,r, p->scond); + if(p->as != AMOVW) + o2 |= 1<<22; + break; + + case 31: /* mov/movbu L(R),R -> lr[b] */ + case 32: /* movh/movb L(R),R -> lr[b] */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = olrr(REGTMP,r, p->to.reg, p->scond); + if(p->as == AMOVBU || p->as == AMOVB) + o2 |= 1<<22; + if(o->type == 31) + break; + + o3 = oprrr(ASLL, p->scond); + + if(p->as == AMOVBU || p->as == AMOVHU) + o4 = oprrr(ASRL, p->scond); + else + o4 = oprrr(ASRA, p->scond); + + r = p->to.reg; + o3 |= (r)|(r<<12); + o4 |= (r)|(r<<12); + if(p->as == AMOVB || p->as == AMOVBU) { + o3 |= (24<<7); + o4 |= (24<<7); + } else { + o3 |= (16<<7); + o4 |= (16<<7); + } + break; + + case 33: /* movh/movhu R,L(R) -> sb, sb */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = osrr(p->from.reg, REGTMP, r, p->scond); + o2 |= (1<<22) ; + + o3 = oprrr(ASRL, p->scond); + o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12); + o3 |= (1<<6); /* ROR 8 */ + + o4 = oprrr(AADD, p->scond); + o4 |= (REGTMP << 12) | (REGTMP << 16); + o4 |= immrot(1); + + o5 = osrr(p->from.reg, REGTMP,r,p->scond); + o5 |= (1<<22); + + o6 = oprrr(ASRL, p->scond); + o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12); + o6 |= (1<<6); /* ROL 8 */ + + break; + + case 34: /* mov $lacon,R */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + + o2 = oprrr(AADD, p->scond); + o2 |= REGTMP; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 |= r << 16; + if(p->to.type != D_NONE) + o2 |= p->to.reg << 12; + break; + + case 35: /* mov PSR,R */ + o1 = (2<<23) | (0xf<<16) | (0<<0); + o1 |= (p->scond & C_SCOND) << 28; + o1 |= (p->from.reg & 1) << 22; + o1 |= p->to.reg << 12; + break; + + case 36: /* mov R,PSR */ + o1 = (2<<23) | (0x29f<<12) | (0<<4); + if(p->scond & C_FBIT) + o1 ^= 0x010 << 12; + o1 |= (p->scond & C_SCOND) << 28; + o1 |= (p->to.reg & 1) << 22; + o1 |= p->from.reg << 0; + break; + + case 37: /* mov $con,PSR */ + aclass(&p->from); + o1 = (2<<23) | (0x29f<<12) | (0<<4); + if(p->scond & C_FBIT) + o1 ^= 0x010 << 12; + o1 |= (p->scond & C_SCOND) << 28; + o1 |= immrot(instoffset); + o1 |= (p->to.reg & 1) << 22; + o1 |= p->from.reg << 0; + break; + + case 38: /* movm $con,oreg -> stm */ + o1 = (0x4 << 25); + o1 |= p->from.offset & 0xffff; + o1 |= p->to.reg << 16; + aclass(&p->to); + goto movm; + + case 39: /* movm oreg,$con -> ldm */ + o1 = (0x4 << 25) | (1 << 20); + o1 |= p->to.offset & 0xffff; + o1 |= p->from.reg << 16; + aclass(&p->from); + movm: + if(instoffset != 0) + diag("offset must be zero in MOVM"); + o1 |= (p->scond & C_SCOND) << 28; + if(p->scond & C_PBIT) + o1 |= 1 << 24; + if(p->scond & C_UBIT) + o1 |= 1 << 23; + if(p->scond & C_SBIT) + o1 |= 1 << 22; + if(p->scond & C_WBIT) + o1 |= 1 << 21; + break; + + case 40: /* swp oreg,reg,reg */ + aclass(&p->from); + if(instoffset != 0) + diag("offset must be zero in SWP"); + o1 = (0x2<<23) | (0x9<<4); + if(p->as != ASWPW) + o1 |= 1 << 22; + o1 |= p->from.reg << 16; + o1 |= p->reg << 0; + o1 |= p->to.reg << 12; + o1 |= (p->scond & C_SCOND) << 28; + break; + + case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ + o1 = 0xe8fd8000; + break; + + case 50: /* floating point store */ + v = regoff(&p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = ofsr(p->as, p->from.reg, v, r, p->scond, p); + break; + + case 51: /* floating point load */ + v = regoff(&p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = ofsr(p->as, p->to.reg, v, r, p->scond, p) | (1<<20); + break; + + case 52: /* floating point store, long offset UGLY */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; + o3 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); + break; + + case 53: /* floating point load, long offset UGLY */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; + o3 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + break; + + case 54: /* floating point arith */ + o1 = oprrr(p->as, p->scond); + if(p->from.type == D_FCONST) { + rf = chipfloat(p->from.ieee); + if(rf < 0){ + diag("invalid floating-point immediate\n%P", p); + rf = 0; + } + rf |= (1<<3); + } else + rf = p->from.reg; + rt = p->to.reg; + r = p->reg; + if(p->to.type == D_NONE) + rt = 0; /* CMP[FD] */ + else if(o1 & (1<<15)) + r = 0; /* monadic */ + else if(r == NREG) + r = rt; + o1 |= rf | (r<<16) | (rt<<12); + break; + + case 55: /* floating point fix and float */ + o1 = oprrr(p->as, p->scond); + rf = p->from.reg; + rt = p->to.reg; + if(p->to.type == D_NONE){ + rt = 0; + diag("to.type==D_NONE (asm/fp)"); + } + if(p->from.type == D_REG) + o1 |= (rf<<12) | (rt<<16); + else + o1 |= rf | (rt<<12); + break; + + /* old arm 7500 fp using coproc 1 (1<<8) */ + case 56: /* move to FP[CS]R */ + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12); + break; + + case 57: /* move from FP[CS]R */ + o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); + o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20); + break; + case 58: /* movbu R,R */ + o1 = oprrr(AAND, p->scond); + o1 |= immrot(0xff); + rt = p->to.reg; + r = p->from.reg; + if(p->to.type == D_NONE) + rt = 0; + if(r == NREG) + r = rt; + o1 |= (r<<16) | (rt<<12); + break; + + case 59: /* movw/bu R<<I(R),R -> ldr indexed */ + if(p->from.reg == NREG) { + if(p->as != AMOVW) + diag("byte MOV from shifter operand"); + goto mov; + } + if(p->from.offset&(1<<4)) + diag("bad shift in LDR"); + o1 = olrr(p->from.offset, p->from.reg, p->to.reg, p->scond); + if(p->as == AMOVBU) + o1 |= 1<<22; + break; + + case 60: /* movb R(R),R -> ldrsb indexed */ + if(p->from.reg == NREG) { + diag("byte MOV from shifter operand"); + goto mov; + } + if(p->from.offset&(~0xf)) + diag("bad shift in LDRSB"); + o1 = olhrr(p->from.offset, p->from.reg, p->to.reg, p->scond); + o1 ^= (1<<5)|(1<<6); + break; + + case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */ + if(p->to.reg == NREG) + diag("MOV to shifter operand"); + o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond); + if(p->as == AMOVB || p->as == AMOVBU) + o1 |= 1<<22; + break; + + case 62: /* case R -> movw R<<2(PC),PC */ + o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); + o1 |= 2<<7; + break; + + case 63: /* bcase */ + if(p->cond != P) { + o1 = p->cond->pc; + if(dlm) + dynreloc(S, p->pc, 1); + } + break; + + /* reloc ops */ + case 64: /* mov/movb/movbu R,addr */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + break; + + case 65: /* mov/movbu addr,R */ + case 66: /* movh/movhu/movb addr,R */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + o2 = olr(0, REGTMP, p->to.reg, p->scond); + if(p->as == AMOVBU || p->as == AMOVB) + o2 |= 1<<22; + if(o->type == 65) + break; + + o3 = oprrr(ASLL, p->scond); + + if(p->as == AMOVBU || p->as == AMOVHU) + o4 = oprrr(ASRL, p->scond); + else + o4 = oprrr(ASRA, p->scond); + + r = p->to.reg; + o3 |= (r)|(r<<12); + o4 |= (r)|(r<<12); + if(p->as == AMOVB || p->as == AMOVBU) { + o3 |= (24<<7); + o4 |= (24<<7); + } else { + o3 |= (16<<7); + o4 |= (16<<7); + } + break; + + case 67: /* movh/movhu R,addr -> sb, sb */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + + o3 = oprrr(ASRL, p->scond); + o3 |= (8<<7)|(p->from.reg)|(p->from.reg<<12); + o3 |= (1<<6); /* ROR 8 */ + + o4 = oprrr(AADD, p->scond); + o4 |= (REGTMP << 12) | (REGTMP << 16); + o4 |= immrot(1); + + o5 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + + o6 = oprrr(ASRL, p->scond); + o6 |= (24<<7)|(p->from.reg)|(p->from.reg<<12); + o6 |= (1<<6); /* ROL 8 */ + break; + + case 68: /* floating point store -> ADDR */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); + break; + + case 69: /* floating point load <- ADDR */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + break; + + /* ArmV4 ops: */ + case 70: /* movh/movhu R,O(R) -> strh */ + aclass(&p->to); + r = p->to.reg; + if(r == NREG) + r = o->param; + o1 = oshr(p->from.reg, instoffset, r, p->scond); + break; + case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ + aclass(&p->from); + r = p->from.reg; + if(r == NREG) + r = o->param; + o1 = olhr(instoffset, r, p->to.reg, p->scond); + if(p->as == AMOVB) + o1 ^= (1<<5)|(1<<6); + else if(p->as == AMOVH) + o1 ^= (1<<6); + break; + case 72: /* movh/movhu R,L(R) -> strh */ + o1 = omvl(p, &p->to, REGTMP); + if(!o1) + break; + r = p->to.reg; + if(r == NREG) + r = o->param; + o2 = oshrr(p->from.reg, REGTMP,r, p->scond); + break; + case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ + o1 = omvl(p, &p->from, REGTMP); + if(!o1) + break; + r = p->from.reg; + if(r == NREG) + r = o->param; + o2 = olhrr(REGTMP, r, p->to.reg, p->scond); + if(p->as == AMOVB) + o2 ^= (1<<5)|(1<<6); + else if(p->as == AMOVH) + o2 ^= (1<<6); + break; + } + + if(debug['a'] > 1) + Bprint(&bso, "%2d ", o->type); + + v = p->pc; + switch(o->size) { + default: + if(debug['a']) + Bprint(&bso, " %.8lux:\t\t%P\n", v, p); + break; + case 4: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p); + lputl(o1); + break; + case 8: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p); + lputl(o1); + lputl(o2); + break; + case 12: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p); + lputl(o1); + lputl(o2); + lputl(o3); + break; + case 16: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n", + v, o1, o2, o3, o4, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + break; + case 20: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + v, o1, o2, o3, o4, o5, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + lputl(o5); + break; + case 24: + if(debug['a']) + Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n", + v, o1, o2, o3, o4, o5, o6, p); + lputl(o1); + lputl(o2); + lputl(o3); + lputl(o4); + lputl(o5); + lputl(o6); + break; + } +} + +long +oprrr(int a, int sc) +{ + long o; + + o = (sc & C_SCOND) << 28; + if(sc & C_SBIT) + o |= 1 << 20; + if(sc & (C_PBIT|C_WBIT)) + diag(".P/.W on dp instruction"); + switch(a) { + case AMULU: + case AMUL: return o | (0x0<<21) | (0x9<<4); + case AMULA: return o | (0x1<<21) | (0x9<<4); + case AMULLU: return o | (0x4<<21) | (0x9<<4); + case AMULL: return o | (0x6<<21) | (0x9<<4); + case AMULALU: return o | (0x5<<21) | (0x9<<4); + case AMULAL: return o | (0x7<<21) | (0x9<<4); + case AAND: return o | (0x0<<21); + case AEOR: return o | (0x1<<21); + case ASUB: return o | (0x2<<21); + case ARSB: return o | (0x3<<21); + case AADD: return o | (0x4<<21); + case AADC: return o | (0x5<<21); + case ASBC: return o | (0x6<<21); + case ARSC: return o | (0x7<<21); + case ATST: return o | (0x8<<21) | (1<<20); + case ATEQ: return o | (0x9<<21) | (1<<20); + case ACMP: return o | (0xa<<21) | (1<<20); + case ACMN: return o | (0xb<<21) | (1<<20); + case AORR: return o | (0xc<<21); + case AMOVW: return o | (0xd<<21); + case ABIC: return o | (0xe<<21); + case AMVN: return o | (0xf<<21); + case ASLL: return o | (0xd<<21) | (0<<5); + case ASRL: return o | (0xd<<21) | (1<<5); + case ASRA: return o | (0xd<<21) | (2<<5); + case ASWI: return o | (0xf<<24); + + /* old arm 7500 fp using coproc 1 (1<<8) */ + case AADDD: return o | (0xe<<24) | (0x0<<20) | (1<<8) | (1<<7); + case AADDF: return o | (0xe<<24) | (0x0<<20) | (1<<8); + case AMULD: return o | (0xe<<24) | (0x1<<20) | (1<<8) | (1<<7); + case AMULF: return o | (0xe<<24) | (0x1<<20) | (1<<8); + case ASUBD: return o | (0xe<<24) | (0x2<<20) | (1<<8) | (1<<7); + case ASUBF: return o | (0xe<<24) | (0x2<<20) | (1<<8); + case ADIVD: return o | (0xe<<24) | (0x4<<20) | (1<<8) | (1<<7); + case ADIVF: return o | (0xe<<24) | (0x4<<20) | (1<<8); + case ACMPD: + case ACMPF: return o | (0xe<<24) | (0x9<<20) | (0xF<<12) | (1<<8) | (1<<4); /* arguably, ACMPF should expand to RNDF, CMPD */ + + case AMOVF: + case AMOVDF: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8); + case AMOVD: + case AMOVFD: return o | (0xe<<24) | (0x0<<20) | (1<<15) | (1<<8) | (1<<7); + + case AMOVWF: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4); + case AMOVWD: return o | (0xe<<24) | (0<<20) | (1<<8) | (1<<4) | (1<<7); + case AMOVFW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4); + case AMOVDW: return o | (0xe<<24) | (1<<20) | (1<<8) | (1<<4) | (1<<7); + } + diag("bad rrr %d", a); + prasm(curp); + return 0; +} + +long +opbra(int a, int sc) +{ + + if(sc & (C_SBIT|C_PBIT|C_WBIT)) + diag(".S/.P/.W on bra instruction"); + sc &= C_SCOND; + if(a == ABL) + return (sc<<28)|(0x5<<25)|(0x1<<24); + if(sc != 0xe) + diag(".COND on bcond instruction"); + switch(a) { + case ABEQ: return (0x0<<28)|(0x5<<25); + case ABNE: return (0x1<<28)|(0x5<<25); + case ABCS: return (0x2<<28)|(0x5<<25); + case ABHS: return (0x2<<28)|(0x5<<25); + case ABCC: return (0x3<<28)|(0x5<<25); + case ABLO: return (0x3<<28)|(0x5<<25); + case ABMI: return (0x4<<28)|(0x5<<25); + case ABPL: return (0x5<<28)|(0x5<<25); + case ABVS: return (0x6<<28)|(0x5<<25); + case ABVC: return (0x7<<28)|(0x5<<25); + case ABHI: return (0x8<<28)|(0x5<<25); + case ABLS: return (0x9<<28)|(0x5<<25); + case ABGE: return (0xa<<28)|(0x5<<25); + case ABLT: return (0xb<<28)|(0x5<<25); + case ABGT: return (0xc<<28)|(0x5<<25); + case ABLE: return (0xd<<28)|(0x5<<25); + case AB: return (0xe<<28)|(0x5<<25); + } + diag("bad bra %A", a); + prasm(curp); + return 0; +} + +long +olr(long v, int b, int r, int sc) +{ + long o; + + if(sc & C_SBIT) + diag(".S on LDR/STR instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(!(sc & C_UBIT)) + o |= 1 << 23; + if(sc & C_WBIT) + o |= 1 << 21; + o |= (0x1<<26) | (1<<20); + if(v < 0) { + v = -v; + o ^= 1 << 23; + } + if(v >= (1<<12)) + diag("literal span too large: %ld (R%d)\n%P", v, b, PP); + o |= v; + o |= b << 16; + o |= r << 12; + return o; +} + +long +olhr(long v, int b, int r, int sc) +{ + long o; + + if(sc & C_SBIT) + diag(".S on LDRH/STRH instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(sc & C_WBIT) + o |= 1 << 21; + o |= (1<<23) | (1<<20)|(0xb<<4); + if(v < 0) { + v = -v; + o ^= 1 << 23; + } + if(v >= (1<<8)) + diag("literal span too large: %ld (R%d)\n%P", v, b, PP); + o |= (v&0xf)|((v>>4)<<8)|(1<<22); + o |= b << 16; + o |= r << 12; + return o; +} + +long +osr(int a, int r, long v, int b, int sc) +{ + long o; + + o = olr(v, b, r, sc) ^ (1<<20); + if(a != AMOVW) + o |= 1<<22; + return o; +} + +long +oshr(int r, long v, int b, int sc) +{ + long o; + + o = olhr(v, b, r, sc) ^ (1<<20); + return o; +} + + +long +osrr(int r, int i, int b, int sc) +{ + + return olr(i, b, r, sc) ^ ((1<<25) | (1<<20)); +} + +long +oshrr(int r, int i, int b, int sc) +{ + return olhr(i, b, r, sc) ^ ((1<<22) | (1<<20)); +} + +long +olrr(int i, int b, int r, int sc) +{ + + return olr(i, b, r, sc) ^ (1<<25); +} + +long +olhrr(int i, int b, int r, int sc) +{ + return olhr(i, b, r, sc) ^ (1<<22); +} + +long +ofsr(int a, int r, long v, int b, int sc, Prog *p) +{ + long o; + + if(sc & C_SBIT) + diag(".S on FLDR/FSTR instruction"); + o = (sc & C_SCOND) << 28; + if(!(sc & C_PBIT)) + o |= 1 << 24; + if(sc & C_WBIT) + o |= 1 << 21; + o |= (6<<25) | (1<<24) | (1<<23); + if(v < 0) { + v = -v; + o ^= 1 << 23; + } + if(v & 3) + diag("odd offset for floating point op: %ld\n%P", v, p); + else if(v >= (1<<10)) + diag("literal span too large: %ld\n%P", v, p); + o |= (v>>2) & 0xFF; + o |= b << 16; + o |= r << 12; + o |= 1 << 8; + + switch(a) { + default: + diag("bad fst %A", a); + case AMOVD: + o |= 1<<15; + case AMOVF: + break; + } + return o; +} + +long +omvl(Prog *p, Adr *a, int dr) +{ + long v, o1; + if(!p->cond) { + aclass(a); + v = immrot(~instoffset); + if(v == 0) { + diag("missing literal"); + prasm(p); + return 0; + } + o1 = oprrr(AMVN, p->scond&C_SCOND); + o1 |= v; + o1 |= dr << 12; + } else { + v = p->cond->pc - p->pc - 8; + o1 = olr(v, REGPC, dr, p->scond&C_SCOND); + } + return o1; +} + +static Ieee chipfloats[] = { + {0x00000000, 0x00000000}, /* 0 */ + {0x00000000, 0x3ff00000}, /* 1 */ + {0x00000000, 0x40000000}, /* 2 */ + {0x00000000, 0x40080000}, /* 3 */ + {0x00000000, 0x40100000}, /* 4 */ + {0x00000000, 0x40140000}, /* 5 */ + {0x00000000, 0x3fe00000}, /* .5 */ + {0x00000000, 0x40240000}, /* 10 */ +}; + +int +chipfloat(Ieee *e) +{ + Ieee *p; + int n; + + for(n = sizeof(chipfloats)/sizeof(chipfloats[0]); --n >= 0;){ + p = &chipfloats[n]; + if(p->l == e->l && p->h == e->h) + return n; + } + return -1; +} |