#include #include #include #include #include "arm.h" static int dummy; static char* shtype[4] = { "<<", ">>", "->", "@>", }; static char* cond[16] = { ".EQ", ".NE", ".HS", ".LO", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".NO", }; void Idp0(ulong); void Idp1(ulong); void Idp2(ulong); void Idp3(ulong); void Imul(ulong); void Imula(ulong); void Imull(ulong); void Iswap(ulong); void Imem1(ulong); void Imem2(ulong); void Ilsm(ulong inst); void Ib(ulong); void Ibl(ulong); void Ssyscall(ulong); Inst itab[] = { { Idp0, "AND", Iarith }, /* 00 - r,r,r */ { Idp0, "EOR", Iarith }, /* 01 */ { Idp0, "SUB", Iarith }, /* 02 */ { Idp0, "RSB", Iarith }, /* 03 */ { Idp0, "ADD", Iarith }, /* 04 */ { Idp0, "ADC", Iarith }, /* 05 */ { Idp0, "SBC", Iarith }, /* 06 */ { Idp0, "RSC", Iarith }, /* 07 */ { Idp0, "TST", Iarith }, /* 08 */ { Idp0, "TEQ", Iarith }, /* 09 */ { Idp0, "CMP", Iarith }, /* 10 */ { Idp0, "CMN", Iarith }, /* 11 */ { Idp0, "ORR", Iarith }, /* 12 */ { Idp0, "MOV", Iarith }, /* 13 */ { Idp0, "BIC", Iarith }, /* 14 */ { Idp0, "MVN", Iarith }, /* 15 */ { Idp1, "AND", Iarith }, /* 16 */ { Idp1, "EOR", Iarith }, /* 17 */ { Idp1, "SUB", Iarith }, /* 18 */ { Idp1, "RSB", Iarith }, /* 19 */ { Idp1, "ADD", Iarith }, /* 20 */ { Idp1, "ADC", Iarith }, /* 21 */ { Idp1, "SBC", Iarith }, /* 22 */ { Idp1, "RSC", Iarith }, /* 23 */ { Idp1, "TST", Iarith }, /* 24 */ { Idp1, "TEQ", Iarith }, /* 25 */ { Idp1, "CMP", Iarith }, /* 26 */ { Idp1, "CMN", Iarith }, /* 27 */ { Idp1, "ORR", Iarith }, /* 28 */ { Idp1, "MOV", Iarith }, /* 29 */ { Idp1, "BIC", Iarith }, /* 30 */ { Idp1, "MVN", Iarith }, /* 31 */ { Idp2, "AND", Iarith }, /* 32 */ { Idp2, "EOR", Iarith }, /* 33 */ { Idp2, "SUB", Iarith }, /* 34 */ { Idp2, "RSB", Iarith }, /* 35 */ { Idp2, "ADD", Iarith }, /* 36 */ { Idp2, "ADC", Iarith }, /* 37 */ { Idp2, "SBC", Iarith }, /* 38 */ { Idp2, "RSC", Iarith }, /* 39 */ { Idp2, "TST", Iarith }, /* 40 */ { Idp2, "TEQ", Iarith }, /* 41 */ { Idp2, "CMP", Iarith }, /* 42 */ { Idp2, "CMN", Iarith }, /* 43 */ { Idp2, "ORR", Iarith }, /* 44 */ { Idp2, "MOV", Iarith }, /* 45 */ { Idp2, "BIC", Iarith }, /* 46 */ { Idp2, "MVN", Iarith }, /* 47 */ { Idp3, "AND", Iarith }, /* 48 - i,r,r */ { Idp3, "EOR", Iarith }, /* 49 */ { Idp3, "SUB", Iarith }, /* 50 */ { Idp3, "RSB", Iarith }, /* 51 */ { Idp3, "ADD", Iarith }, /* 52 */ { Idp3, "ADC", Iarith }, /* 53 */ { Idp3, "SBC", Iarith }, /* 54 */ { Idp3, "RSC", Iarith }, /* 55 */ { Idp3, "TST", Iarith }, /* 56 */ { Idp3, "TEQ", Iarith }, /* 57 */ { Idp3, "CMP", Iarith }, /* 58 */ { Idp3, "CMN", Iarith }, /* 59 */ { Idp3, "ORR", Iarith }, /* 60 */ { Idp3, "MOV", Iarith }, /* 61 */ { Idp3, "BIC", Iarith }, /* 62 */ { Idp3, "MVN", Iarith }, /* 63 */ { Imul, "MUL", Iarith }, /* 64 */ { Imula, "MULA", Iarith }, /* 65 */ { Iswap, "SWPW", Imem }, /* 66 */ { Iswap, "SWPBU", Imem }, /* 67 */ { Imem2, "MOV", Imem }, /* 68 load/store h/sb */ { Imem2, "MOV", Imem }, /* 69 */ { Imem2, "MOV", Imem }, /* 70 */ { Imem2, "MOV", Imem }, /* 71 */ { Imem1, "MOVW", Imem }, /* 72 load/store w/ub i,r */ { Imem1, "MOVB", Imem }, /* 73 */ { Imem1, "MOVW", Imem }, /* 74 */ { Imem1, "MOVB", Imem }, /* 75 */ { Imem1, "MOVW", Imem }, /* 76 load/store r,r */ { Imem1, "MOVB", Imem }, /* 77 */ { Imem1, "MOVW", Imem }, /* 78 */ { Imem1, "MOVB", Imem }, /* 79 */ { Ilsm, "LDM", Imem }, /* 80 block move r,r */ { Ilsm, "STM", Imem }, /* 81 */ { Ib, "B", Ibranch }, /* 82 branch */ { Ibl, "BL", Ibranch }, /* 83 */ { Ssyscall, "SWI", Isyscall }, /* 84 co processor */ { undef, "undef" }, /* 85 */ { undef, "undef" }, /* 86 */ { undef, "undef" }, /* 87 */ { Imull, "MULLU", Iarith }, /* 88 */ { Imull, "MULALU", Iarith }, /* 89 */ { Imull, "MULL", Iarith }, /* 90 */ { Imull, "MULAL", Iarith }, /* 91 */ { undef, "undef" }, /* 92 */ { 0 } }; int runcmp(void) { switch(reg.cond) { case 0x0: /* eq */ return (reg.cc1 == reg.cc2); case 0x1: /* ne */ return (reg.cc1 != reg.cc2); case 0x2: /* hs */ return ((ulong)reg.cc1 >= (ulong)reg.cc2); case 0x3: /* lo */ return ((ulong)reg.cc1 < (ulong)reg.cc2); case 0x4: /* mi */ return (reg.cc1 - reg.cc2 < 0); case 0x5: /* pl */ return (reg.cc1 - reg.cc2 >= 0); case 0x8: /* hi */ return ((ulong)reg.cc1 > (ulong)reg.cc2); case 0x9: /* ls */ return ((ulong)reg.cc1 <= (ulong)reg.cc2); case 0xa: /* ge */ return (reg.cc1 >= reg.cc2); case 0xb: /* lt */ return (reg.cc1 < reg.cc2); case 0xc: /* gt */ return (reg.cc1 > reg.cc2); case 0xd: /* le */ return (reg.cc1 <= reg.cc2); case 0xe: /* al */ return 1; case 0xf: /* nv */ return 0; default: Bprint(bioout, "unimplemented condition prefix %x (%ld %ld)\n", reg.cond, reg.cc1, reg.cc2); undef(reg.ir); return 0; } } int runteq(void) { long res = reg.cc1 ^ reg.cc2; switch(reg.cond) { case 0x0: /* eq */ return res == 0; case 0x1: /* ne */ return res != 0; case 0x4: /* mi */ return (res & SIGNBIT) != 0; case 0x5: /* pl */ return (res & SIGNBIT) == 0; case 0xe: /* al */ return 1; case 0xf: /* nv */ return 0; default: Bprint(bioout, "unimplemented condition prefix %x (%ld %ld)\n", reg.cond, reg.cc1, reg.cc2); undef(reg.ir); return 0; } } int runtst(void) { long res = reg.cc1 & reg.cc2; switch(reg.cond) { case 0x0: /* eq */ return res == 0; case 0x1: /* ne */ return res != 0; case 0x4: /* mi */ return (res & SIGNBIT) != 0; case 0x5: /* pl */ return (res & SIGNBIT) == 0; case 0xe: /* al */ return 1; case 0xf: /* nv */ return 0; default: Bprint(bioout, "unimplemented condition prefix %x (%ld %ld)\n", reg.cond, reg.cc1, reg.cc2); undef(reg.ir); return 0; } } void run(void) { int execute; do { if(trace) Bflush(bioout); reg.ar = reg.r[REGPC]; reg.ir = ifetch(reg.ar); reg.class = armclass(reg.ir); reg.ip = &itab[reg.class]; reg.cond = (reg.ir>>28) & 0xf; switch(reg.compare_op) { case CCcmp: execute = runcmp(); break; case CCteq: execute = runteq(); break; case CCtst: execute = runtst(); break; default: Bprint(bioout, "unimplemented compare operation %x\n", reg.compare_op); return; } if(execute) { reg.ip->count++; (*reg.ip->func)(reg.ir); } else { if(trace) itrace("%s%s IGNORED", reg.ip->name, cond[reg.cond]); } reg.r[REGPC] += 4; if(bplist) brkchk(reg.r[REGPC], Instruction); } while(--count); } void undef(ulong inst) { Bprint(bioout, "undefined instruction trap pc #%lux inst %.8lux class %d\n", reg.r[REGPC], inst, reg.class); longjmp(errjmp, 0); } long shift(long v, int st, int sc, int isreg) { if(sc == 0) { switch(st) { case 0: /* logical left */ reg.cout = reg.cbit; break; case 1: /* logical right */ reg.cout = (v >> 31) & 1; break; case 2: /* arith right */ reg.cout = reg.cbit; break; case 3: /* rotate right */ if(isreg) { reg.cout = reg.cbit; } else { reg.cout = v & 1; v = ((ulong)v >> 1) | (reg.cbit << 31); } } } else { switch(st) { case 0: /* logical left */ reg.cout = (v >> (32 - sc)) & 1; v = v << sc; break; case 1: /* logical right */ reg.cout = (v >> (sc - 1)) & 1; v = (ulong)v >> sc; break; case 2: /* arith right */ if(sc >= 32) { reg.cout = (v >> 31) & 1; if(reg.cout) v = 0xFFFFFFFF; else v = 0; } else { reg.cout = (v >> (sc - 1)) & 1; v = (long)v >> sc; } break; case 3: /* rotate right */ reg.cout = (v >> (sc - 1)) & 1; v = (v << (32-sc)) | ((ulong)v >> sc); break; } } return v; } void dpex(long inst, long o1, long o2, int rd) { int cbit; cbit = 0; switch((inst>>21) & 0xf) { case 0: /* and */ reg.r[rd] = o1 & o2; cbit = 1; break; case 1: /* eor */ reg.r[rd] = o1 ^ o2; cbit = 1; break; case 2: /* sub */ reg.r[rd] = o1 - o2; case 10: /* cmp */ if(inst & Sbit) { reg.cc1 = o1; reg.cc2 = o2; reg.compare_op = CCcmp; } return; case 3: /* rsb */ reg.r[rd] = o2 - o1; if(inst & Sbit) { reg.cc1 = o2; reg.cc2 = o1; reg.compare_op = CCcmp; } return; case 4: /* add */ if(calltree && rd == REGPC && o2 == 0) { Symbol s; findsym(o1 + o2, CTEXT, &s); Bprint(bioout, "%8lux return to %lux %s r0=%lux\n", reg.r[REGPC], o1 + o2, s.name, reg.r[REGRET]); } reg.r[rd] = o1 + o2; if(inst & Sbit) { if(((uvlong)(ulong)o1 + (uvlong)(ulong)o2) & (1LL << 32)) reg.cbit = 1; else reg.cbit = 0; reg.cc1 = o2; reg.cc2 = -o1; reg.compare_op = CCcmp; } return; case 5: /* adc */ case 6: /* sbc */ case 7: /* rsc */ undef(inst); case 8: /* tst */ if(inst & Sbit) { reg.cc1 = o1; reg.cc2 = o2; reg.compare_op = CCtst; } return; case 9: /* teq */ if(inst & Sbit) { reg.cc1 = o1; reg.cc2 = o2; reg.compare_op = CCteq; } return; case 11: /* cmn */ if(inst & Sbit) { reg.cc1 = o1; reg.cc2 = -o2; reg.compare_op = CCcmp; } return; case 12: /* orr */ reg.r[rd] = o1 | o2; cbit = 1; break; case 13: /* mov */ reg.r[rd] = o2; cbit = 1; break; case 14: /* bic */ reg.r[rd] = o1 & ~o2; cbit = 1; break; case 15: /* mvn */ reg.r[rd] = ~o2; cbit = 1; break; } if(inst & Sbit) { if(cbit) reg.cbit = reg.cout; reg.cc1 = reg.r[rd]; reg.cc2 = 0; reg.compare_op = CCcmp; } } /* * data processing instruction R,R,R */ void Idp0(ulong inst) { int rn, rd, rm; long o1, o2; rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; rm = inst & 0xf; o1 = reg.r[rn]; if(rn == REGPC) o1 += 8; o2 = reg.r[rm]; if(rm == REGPC) o2 += 8; dpex(inst, o1, o2, rd); if(trace) itrace("%s%s\tR%d,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], rm, rn, rd, reg.r[rd]); if(rd == REGPC) reg.r[rd] -= 4; } /* * data processing instruction (R<>#),R,R */ void Idp1(ulong inst) { int rn, rd, rm, st, sc; long o1, o2; rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; rm = inst & 0xf; st = (inst>>5) & 0x3; sc = (inst>>7) & 0x1f; o1 = reg.r[rn]; if(rn == REGPC) o1 += 8; o2 = reg.r[rm]; if(rm == REGPC) o2 += 8; o2 = shift(o2, st, sc, 0); dpex(inst, o1, o2, rd); if(trace) itrace("%s%s\tR%d%s%d,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], rm, shtype[st], sc, rn, rd, reg.r[rd]); if(rd == REGPC) reg.r[rd] -= 4; } /* * data processing instruction (R<>R),R,R */ void Idp2(ulong inst) { int rn, rd, rm, rs, st; long o1, o2, o3; rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; rm = inst & 0xf; st = (inst>>5) & 0x3; rs = (inst>>8) & 0xf; o1 = reg.r[rn]; if(rn == REGPC) o1 += 8; o2 = reg.r[rm]; if(rm == REGPC) o2 += 8; o3 = reg.r[rs]; if(rs == REGPC) o3 += 8; o2 = shift(o2, st, o3, 1); dpex(inst, o1, o2, rd); if(trace) itrace("%s%s\tR%d%sR%d=%d,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], rm, shtype[st], rs, o3, rn, rd, reg.r[rd]); if(rd == REGPC) reg.r[rd] -= 4; } /* * data processing instruction #<>#,R,R */ void Idp3(ulong inst) { int rn, rd, sc; long o1, o2; rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; o1 = reg.r[rn]; if(rn == REGPC) o1 += 8; o2 = inst & 0xff; sc = (inst>>7) & 0x1e; o2 = (o2 >> sc) | (o2 << (32 - sc)); dpex(inst, o1, o2, rd); if(trace) itrace("%s%s\t#%x,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], o2, rn, rd, reg.r[rd]); if(rd == REGPC) reg.r[rd] -= 4; } void Imul(ulong inst) { int rs, rd, rm; rd = (inst>>16) & 0xf; rs = (inst>>8) & 0xf; rm = inst & 0xf; if(rd == REGPC || rs == REGPC || rm == REGPC || rd == rm) undef(inst); reg.r[rd] = reg.r[rm]*reg.r[rs]; if(trace) itrace("%s%s\tR%d,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], rs, rm, rd, reg.r[rd]); } void Imull(ulong inst) { vlong v; int rs, rd, rm, rn; rd = (inst>>16) & 0xf; rn = (inst>>12) & 0xf; rs = (inst>>8) & 0xf; rm = inst & 0xf; if(rd == REGPC || rn == REGPC || rs == REGPC || rm == REGPC || rd == rm || rn == rm || rd == rn) undef(inst); if(inst & (1<<22)){ v = (vlong)reg.r[rm] * (vlong)reg.r[rs]; if(inst & (1 << 21)) v += reg.r[rn]; }else{ v = (uvlong)(ulong)reg.r[rm] * (uvlong)(ulong)reg.r[rs]; if(inst & (1 << 21)) v += (ulong)reg.r[rn]; } reg.r[rd] = v >> 32; reg.r[rn] = v; if(trace) itrace("%s%s\tR%d,R%d,(R%d,R%d) =#%llx", reg.ip->name, cond[reg.cond], rs, rm, rn, rd, v); } void Imula(ulong inst) { int rs, rd, rm, rn; rd = (inst>>16) & 0xf; rn = (inst>>12) & 0xf; rs = (inst>>8) & 0xf; rm = inst & 0xf; if(rd == REGPC || rn == REGPC || rs == REGPC || rm == REGPC || rd == rm) undef(inst); reg.r[rd] = reg.r[rm]*reg.r[rs] + reg.r[rn]; if(trace) itrace("%s%s\tR%d,R%d,R%d,R%d =#%x", reg.ip->name, cond[reg.cond], rs, rm, rn, rd, reg.r[rd]); } void Iswap(ulong inst) { int rn, rd, rm; ulong address, value, bbit; bbit = inst & (1<<22); rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; rm = (inst>>0) & 0xf; address = reg.r[rn]; if(bbit) { value = getmem_b(address); putmem_b(address, reg.r[rm]); } else { value = getmem_w(address); putmem_w(address, reg.r[rm]); } reg.r[rd] = value; if(trace) { char *bw, *dotc; bw = ""; if(bbit) bw = "B"; dotc = cond[reg.cond]; itrace("SWP%s%s\t#%x(R%d),R%d #%lux=#%x", bw, dotc, rn, rd, address, value); } } /* * load/store word/byte */ void Imem1(ulong inst) { int rn, rd, off, rm, sc, st; ulong address, value, pbit, ubit, bbit, wbit, lbit, bit25; bit25 = inst & (1<<25); pbit = inst & (1<<24); ubit = inst & (1<<23); bbit = inst & (1<<22); wbit = inst & (1<<21); lbit = inst & (1<<20); rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; SET(st); SET(sc); SET(rm); if(bit25) { rm = inst & 0xf; st = (inst>>5) & 0x3; sc = (inst>>7) & 0x1f; off = reg.r[rm]; if(rm == REGPC) off += 8; off = shift(off, st, sc, 0); } else { off = inst & 0xfff; } if(!ubit) off = -off; if(rn == REGPC) off += 8; address = reg.r[rn]; if(pbit) address += off; if(lbit) { if(bbit) value = getmem_b(address); else value = getmem_w(address); if(rd == REGPC) value -= 4; reg.r[rd] = value; } else { value = reg.r[rd]; if(rd == REGPC) value -= 4; if(bbit) putmem_b(address, value); else putmem_w(address, value); } if(!(pbit && !wbit)) reg.r[rn] += off; if(trace) { char *bw, *dotp, *dotc; bw = "W"; if(bbit) bw = "BU"; dotp = ""; if(!pbit) dotp = ".P"; dotc = cond[reg.cond]; if(lbit) { if(!bit25) itrace("MOV%s%s%s\t#%x(R%d),R%d #%lux=#%x", bw, dotp, dotc, off, rn, rd, address, value); else itrace("MOV%s%s%s\t(R%d%s%d)(R%d),R%d #%lux=#%x", bw, dotp, dotc, rm, shtype[st], sc, rn, rd, address, value); } else { if(!bit25) itrace("MOV%s%s%s\tR%d,#%x(R%d) #%lux=#%x", bw, dotp, dotc, rd, off, rn, address, value); else itrace("MOV%s%s%s\tR%d,(R%d%s%d)(R%d) #%lux=#%x", bw, dotp, dotc, rd, rm, shtype[st], sc, rn, address, value); } } } /* * load/store unsigned byte/half word */ void Imem2(ulong inst) { int rn, rd, off, rm; ulong address, value, pbit, ubit, hbit, sbit, wbit, lbit, bit22; pbit = inst & (1<<24); ubit = inst & (1<<23); bit22 = inst & (1<<22); wbit = inst & (1<<21); lbit = inst & (1<<20); sbit = inst & (1<<6); hbit = inst & (1<<5); rn = (inst>>16) & 0xf; rd = (inst>>12) & 0xf; SET(rm); if(bit22) { off = ((inst>>4) & 0xf0) | (inst & 0xf); } else { rm = inst & 0xf; off = reg.r[rm]; if(rm == REGPC) off += 8; } if(!ubit) off = -off; if(rn == REGPC) off += 8; address = reg.r[rn]; if(pbit) address += off; if(lbit) { if(hbit) { value = getmem_h(address); if(sbit && (value & 0x8000)) value |= 0xffff0000; } else { value = getmem_b(address); if(value & 0x80) value |= 0xffffff00; } if(rd == REGPC) value -= 4; reg.r[rd] = value; } else { value = reg.r[rd]; if(rd == REGPC) value -= 4; if(hbit) { putmem_h(address, value); } else { putmem_b(address, value); } } if(!(pbit && !wbit)) reg.r[rn] += off; if(trace) { char *hb, *dotp, *dotc; hb = "B"; if(hbit) hb = "H"; dotp = ""; if(!pbit) dotp = ".P"; dotc = cond[reg.cond]; if(lbit) { if(bit22) itrace("MOV%s%s%s\t#%x(R%d),R%d #%lux=#%x", hb, dotp, dotc, off, rn, rd, address, value); else itrace("MOV%s%s%s\t(R%d)(R%d),R%d #%lux=#%x", hb, dotp, dotc, rm, rn, rd, address, value); } else { if(bit22) itrace("MOV%s%s%s\tR%d,#%x(R%d) #%lux=#%x", hb, dotp, dotc, rd, off, rn, address, value); else itrace("MOV%s%s%s\tR%d,(R%d)(R%d) #%lux=#%x", hb, dotp, dotc, rd, rm, rn, address, value); } } } void Ilsm(ulong inst) { char pbit, ubit, sbit, wbit, lbit; int i, rn, reglist; ulong address, predelta, postdelta; pbit = (inst>>24) & 0x1; ubit = (inst>>23) & 0x1; sbit = (inst>>22) & 0x1; wbit = (inst>>21) & 0x1; lbit = (inst>>20) & 0x1; rn = (inst>>16) & 0xf; reglist = inst & 0xffff; if(reglist & 0x8000) undef(reg.ir); if(sbit) undef(reg.ir); address = reg.r[rn]; if(pbit) { predelta = 4; postdelta = 0; } else { predelta = 0; postdelta = 4; } if(ubit) { for (i = 0; i < 16; ++i) { if(!(reglist & (1 << i))) continue; address += predelta; if(lbit) reg.r[i] = getmem_w(address); else putmem_w(address, reg.r[i]); address += postdelta; } } else { for (i = 15; 0 <= i; --i) { if(!(reglist & (1 << i))) continue; address -= predelta; if(lbit) reg.r[i] = getmem_w(address); else putmem_w(address, reg.r[i]); address -= postdelta; } } if(wbit) { reg.r[rn] = address; } if(trace) { itrace("%s.%c%c\tR%d=%lux%s, <%lux>", (lbit ? "LDM" : "STM"), (ubit ? 'I' : 'D'), (pbit ? 'B' : 'A'), rn, reg.r[rn], (wbit ? "!" : ""), reglist); } } void Ib(ulong inst) { long v; v = inst & 0xffffff; v = reg.r[REGPC] + 8 + ((v << 8) >> 6); if(trace) itrace("B%s\t#%lux", cond[reg.cond], v); reg.r[REGPC] = v - 4; } void Ibl(ulong inst) { long v; Symbol s; v = inst & 0xffffff; v = reg.r[REGPC] + 8 + ((v << 8) >> 6); if(trace) itrace("BL%s\t#%lux", cond[reg.cond], v); if(calltree) { findsym(v, CTEXT, &s); Bprint(bioout, "%8lux %s(", reg.r[REGPC], s.name); printparams(&s, reg.r[13]); Bprint(bioout, "from "); printsource(reg.r[REGPC]); Bputc(bioout, '\n'); } reg.r[REGLINK] = reg.r[REGPC] + 4; reg.r[REGPC] = v - 4; }