ref: 81c9ede53f6f9ebd67e7134e664c98e08b9fcca6
dir: /sys/src/cmd/qi/branch.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <mach.h> #define Extern extern #include "power.h" void mcrf(ulong); void bclr(ulong); void crop(ulong); void bcctr(ulong); void call(ulong); void ret(ulong); void isync(ulong); Inst op19[] = { [0] {mcrf, "mcrf", Ibranch}, [16] {bclr, "bclr", Ibranch}, [33] {crop, "crnor", Ibranch}, [15] {0, "rfi", Ibranch}, [129] {crop, "crandc", Ibranch}, [150] {isync, "isync", Ibranch}, [193] {crop, "crxor", Ibranch}, [225] {crop, "crnand", Ibranch}, [257] {crop, "crand", Ibranch}, [289] {crop, "creqv", Ibranch}, [417] {crop, "crorc", Ibranch}, [449] {crop, "cror", Ibranch}, [528] {bcctr, "bcctr", Ibranch}, {0, 0, 0} }; Inset ops19 = {op19, nelem(op19)-1}; static char * boname(int bo) { static char buf[8]; switch(bo>>1){ case 0: return "dnzf"; case 1: return "dzf"; case 2: return "f"; case 4: return "dnzt"; case 5: return "dzt"; case 6: return "t"; case 8: return "dnz"; case 9: return "dz"; case 10: return "a"; default: sprint(buf, "%d?", bo); return buf; } } static char * cname(int bo, int bi) { int f; char *p; static char buf[20]; static char *f0[] = {"lt", "gt", "eq", "so/un"}; if(bo == 0x14){ /* branch always */ sprint(buf,"%d", bi); return buf; } for(f = 0; bi >= 4; bi -= 4) f++; p = buf; p += sprint(buf, "%d[", bi); if(f) p += sprint(buf, "cr%d+", f); strcpy(p, f0[bi&3]); strcat(p, "]"); return buf; } static int condok(ulong ir, int ctr) { int bo, bi, xx; getbobi(ir); if(xx) undef(ir); if((bo & 0x4) == 0) { if(!ctr) undef(ir); reg.ctr--; } if(bo & 0x4 || (reg.ctr!=0)^((bo>>1)&1)) { if(bo & 0x10 || (((reg.cr & bits[bi])!=0)==((bo>>3)&1))) return 1; } return 0; } static void dobranch(ulong ir, ulong *r, int ctr) { int bo, bi, xx; ulong nia; getbobi(ir); USED(xx); if(condok(ir, ctr)) { ci->taken++; nia = *r & ~3; if(bo & 4) /* assume counting branches aren't returns */ ret(nia); } else nia = reg.pc + 4; if(trace) itrace("%s%s\t%s,%s,#%.8lux", ci->name, ir&1? "l": "", boname(bo), cname(bo, bi), nia); if(ir & 1) { call(nia); reg.lr = reg.pc + 4; } reg.pc = nia-4; /* branch delays? */ } void bcctr(ulong ir) { dobranch(ir, ®.ctr, 1); } void bclr(ulong ir) { dobranch(ir, ®.lr, 0); } void bcx(ulong ir) { int bo, bi, xx; ulong ea; long imm; static char *opc[] = {"bc", "bcl", "bca", "bcla"}; getbobi(ir); USED(xx); imm = ir & 0xFFFC; if(ir & 0x08000) imm |= 0xFFFF0000; if((ir & 2) == 0) { /* not absolute address */ ea = reg.pc + imm; if(trace) itrace("%s\t%s,%s,.%s%ld\tea = #%.8lux", opc[ir&3], boname(bo), cname(bo, bi), imm<0?"":"+", imm, ea); } else { ea = imm; if(trace) itrace("%s\t%s,%s,#%.8lux", opc[ir&3], boname(bo), cname(bo, bi), ea); } if(condok(ir&0xFFFF0000, 1)) ci->taken++; else ea = reg.pc + 4; if(ir & 1) { call(ea); reg.lr = reg.pc+4; } reg.pc = ea-4; /* branch delay? */ } void crop(ulong ir) { int rd, ra, rb, d; getarrr(ir); if(trace) itrace("%s\tcrb%d,crb%d,crb%d", ci->name, rd, ra, rb); ra = (reg.cr & bits[ra]) != 0; rb = (reg.cr & bits[rb]) != 0; d = 0; switch(getxo(ir)) { case 257: d = ra & rb; break; case 129: d = ra & !rb; break; case 289: d = ra == rb; break; case 225: d = !(ra & rb); break; case 33: d = !(ra | rb); break; case 449: d = ra | rb; break; case 417: d = ra | !rb; break; case 193: d = ra ^ rb; break; default: undef(ir); break; } if(d) reg.cr |= bits[rd]; } void mcrf(ulong ir) { int rd, ra, rb; getarrr(ir); if(ir & 1 || rd & 3 || ra & 3 || rb) undef(ir); ra >>= 2; rd >>= 2; reg.cr = (reg.cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, reg.cr)); if(trace) itrace("mcrf\tcrf%d,crf%d", rd, ra); } void call(ulong npc) { Symbol s; if(calltree) { findsym(npc, CTEXT, &s); Bprint(bioout, "%8lux %s(", reg.pc, s.name); printparams(&s, reg.r[1]); Bprint(bioout, "from "); printsource(reg.pc); Bputc(bioout, '\n'); } } void ret(ulong npc) { Symbol s; if(calltree) { findsym(npc, CTEXT, &s); Bprint(bioout, "%8lux return to #%lux %s r3=#%lux (%ld)\n", reg.pc, npc, s.name, reg.r[3], reg.r[3]); } } void bx(ulong ir) { ulong ea; long imm; static char *opc[] = {"b", "bl", "ba", "bla"}; imm = ir & 0x03FFFFFC; if(ir & 0x02000000) imm |= 0xFC000000; if((ir & 2) == 0) { /* not absolute address */ ea = reg.pc + imm; if(trace) itrace("%s\t.%s%ld\tea = #%.8lux", opc[ir&3], imm<0?"":"+", imm, ea); } else { ea = imm; if(trace) itrace("%s\t#%.8lux", opc[ir&3], ea); } ci->taken++; if(ir & 1) { call(ea); reg.lr = reg.pc+4; } reg.pc = ea-4; /* branch delay? */ } void isync(ulong ir) { USED(ir); if(trace) itrace("isync"); }