|
|
1.1 ! root 1: /* ! 2: * first cut at NI port in DEBNA ! 3: * slower than it need be, because of all the copying; ! 4: * disallows use of the storage port ! 5: */ ! 6: #include "sys/param.h" ! 7: #include "sys/biaddr.h" ! 8: #include "sys/conf.h" ! 9: #include "sys/user.h" ! 10: #include "sys/buf.h" ! 11: #include "sys/stream.h" ! 12: #include "sys/biic.h" ! 13: #include "sys/ethernet.h" ! 14: #include "sys/enio.h" ! 15: #include "sys/pte.h" ! 16: #include "sys/bvp.h" ! 17: #include "sys/debna.h" ! 18: ! 19: caddr_t remqhi(); ! 20: ! 21: ! 22: #define PCMDQ 0 /* just one command queue */ ! 23: #define PFREQ 0 /* just one free queue for now */ ! 24: ! 25: /* ! 26: * internal ideas about packets ! 27: */ ! 28: ! 29: /* ! 30: * bounds of an Ethernet packet ! 31: * min is probably uninteresting ! 32: * max is constrained by the port, which will fuss and shut down ! 33: * if handed a buffer bigger than ETHERMAXTU+ETHERCKSUM ! 34: */ ! 35: #define ETHERMINTU 60 ! 36: #define ETHERMAXTU 1514 ! 37: #define ETHERCKSUM 4 ! 38: ! 39: #define NIRCVBUF (ETHERMAXTU+ETHERCKSUM) ! 40: ! 41: struct dgi { ! 42: struct bvpdg h; /* header */ ! 43: char d[NIRCVBUF]; ! 44: }; ! 45: ! 46: struct mst { ! 47: struct bvpmsg h; ! 48: struct stptdb p; ! 49: }; ! 50: ! 51: /* ! 52: * config stuff ! 53: */ ! 54: extern struct bnactl bna[]; ! 55: extern struct bnabuf bnabuf[]; ! 56: extern struct biaddr bnaaddr[]; ! 57: extern int bnacnt; ! 58: ! 59: long bnaopen(); ! 60: int bnaclose(), bnaput(); ! 61: ! 62: static struct qinit bnarinit = { noput, NULL, bnaopen, bnaclose, 0, 0 }; ! 63: static struct qinit bnawinit = { bnaput, NULL, bnaopen, bnaclose, 4*ETHERMAXTU, 64 }; ! 64: static struct streamtab bnasinfo = { &bnarinit, &bnawinit }; ! 65: struct cdevsw bnacdev = cstrinit(&bnasinfo); ! 66: ! 67: long ! 68: bnaopen(q, dev) ! 69: register struct queue *q; ! 70: dev_t dev; ! 71: { ! 72: register int unit, chan; ! 73: register struct bnachan *bc; ! 74: ! 75: unit = minor(dev); ! 76: chan = unit % BNACHAN; ! 77: unit /= BNACHAN; ! 78: if (unit >= bnacnt) ! 79: return (0); ! 80: if (bnainit(unit) == 0) ! 81: return (0); ! 82: bc = &bna[unit].chan[chan]; ! 83: if (bc->rq) ! 84: return (0); ! 85: bc->unit = unit; ! 86: bc->proto = 0; ! 87: q->ptr = (caddr_t)bc; ! 88: q->flag |= QDELIM; ! 89: WR(q)->ptr = (caddr_t)bc; ! 90: WR(q)->flag |= QDELIM|QBIGB; ! 91: bc->rq = q; ! 92: return (1); ! 93: } ! 94: ! 95: /* ! 96: * init the bvp port registers and pqb ! 97: * set up our list of free transmit buffers, ! 98: * and the free queue for the port ! 99: * -- only one free queue for now ! 100: */ ! 101: bnainit(unit) ! 102: register int unit; ! 103: { ! 104: register char *cp; ! 105: register struct bnactl *bn; ! 106: register int i; ! 107: ! 108: bn = &bna[unit]; ! 109: if (bvpinit(&bn->bvp, &bnaaddr[unit], NIREGS) == 0) { ! 110: printf("debna %d: can't init\n", unit); ! 111: return (0); ! 112: } ! 113: if (bn->bvp.d->f[PFREQ].f_size == 0) { /* somewhat sleazy init flag */ ! 114: bn->bvp.d->f[PFREQ].f_size = BNABSIZE-BVPHSIZE; ! 115: cp = bnabuf[unit].rbuf; ! 116: cp = (char *)(((int)cp + 7) & ~07); /* quad-align */ ! 117: for (i = 0; i < BNARBUF; i++, cp += BNABSIZE) { ! 118: if (((int)cp&PGOFSET) > (((int)cp+sizeof(struct bvpdg))&PGOFSET)) ! 119: cp = (char *)(((int)cp+PGOFSET)&~PGOFSET); ! 120: bnarbuf(bn, (struct dgi *)cp); ! 121: } ! 122: cp = bnabuf[unit].xbuf; ! 123: cp = (char *)(((int)cp + 7) & ~07); /* quad-align */ ! 124: for (i = 0; i < BNAXBUF; i++, cp += BNABSIZE) { ! 125: if (((int)cp&PGOFSET) > (((int)cp+sizeof(struct bvpdg))&PGOFSET)) ! 126: cp = (char *)(((int)cp+PGOFSET)&~PGOFSET); ! 127: bnaxbuf(bn, (struct dgi *)cp); ! 128: } ! 129: } ! 130: bcopy(bn->bvp.r->addr, bn->myaddr, sizeof(bn->myaddr)); ! 131: return (1); ! 132: } ! 133: ! 134: bnaclose(q) ! 135: register struct queue *q; ! 136: { ! 137: register struct bnachan *bc; ! 138: ! 139: bc = (struct bnachan *)q->ptr; ! 140: /* turn off PTDB? */ ! 141: bc->rq = NULL; ! 142: bc->proto = 0; ! 143: } ! 144: ! 145: /* ! 146: * output put ! 147: */ ! 148: bnaput(q, bp) ! 149: register struct queue *q; ! 150: register struct block *bp; ! 151: { ! 152: register struct bnachan *bc; ! 153: register int s; ! 154: ! 155: bc = (struct bnachan *)q->ptr; ! 156: switch (bp->type) { ! 157: case M_IOCTL: ! 158: bnaioctl(q, bp); ! 159: return; ! 160: ! 161: case M_DATA: ! 162: putq(q, bp); ! 163: if (bp->class & S_DELIM) ! 164: break; ! 165: return; ! 166: ! 167: default: ! 168: freeb(bp); ! 169: return; ! 170: } ! 171: /* ! 172: * at least a whole packet on the queue ! 173: */ ! 174: bc->dcnt++; ! 175: s = spl6(); ! 176: while (bnasend(bc->unit)) ! 177: ; ! 178: splx(s); ! 179: } ! 180: ! 181: bnaioctl(q, bp) ! 182: register struct queue *q; ! 183: register struct block *bp; ! 184: { ! 185: register struct bnachan *bc; ! 186: register struct bnactl *bn; ! 187: ! 188: bc = (struct bnachan *)q->ptr; ! 189: bn = &bna[bc->unit]; ! 190: bp->type = M_IOCACK; ! 191: switch (stiocom(bp)) { ! 192: case ENIOTYPE: /* set proto */ ! 193: bc->proto = *((int *)stiodata(bp)); ! 194: bnastptdb(bn, bc); ! 195: freeb(bp); /* IOCACK when the port finishes */ ! 196: return; ! 197: ! 198: case ENIOADDR: ! 199: bcopy(bn->myaddr, stiodata(bp), sizeof(bn->myaddr)); ! 200: bp->wptr = bp->rptr + sizeof(bn->myaddr) + STIOCHDR; ! 201: break; ! 202: ! 203: default: ! 204: bp->type = M_IOCNAK; ! 205: break; ! 206: } ! 207: qreply(q, bp); ! 208: } ! 209: ! 210: bnastptdb(bn, bc) ! 211: register struct bnactl *bn; ! 212: register struct bnachan *bc; ! 213: { ! 214: register struct mst *dp; ! 215: register int s; ! 216: ! 217: s = spl6(); ! 218: if ((dp = (struct mst *)bn->xfree) == NULL) { ! 219: bc->needst = 1; ! 220: bn->needst = 1; ! 221: splx(s); ! 222: return; ! 223: } ! 224: bn->xfree = (struct dgi *)(dp->h.q.head); ! 225: bc->needst = 0; ! 226: splx(s); ! 227: bzero((caddr_t)dp, sizeof(*dp)); ! 228: dp->h.bd_opc = BVPSNDMSG; ! 229: dp->h.bd_flag = BVPRSP; ! 230: dp->h.bm_len = sizeof(*dp) - ((char *)&dp->h.bm_opc - (char *)dp); ! 231: dp->h.bm_opc = NISTPTDB; ! 232: dp->p.pt_proto = bc->proto; ! 233: dp->p.pt_fqi = PFREQ; ! 234: dp->p.pt_flag = PTABM|PTAAM; ! 235: dp->p.pt_id = bc - bn->chan; ! 236: if (insqti((caddr_t)dp, &bn->bvp.d->p.p_cmdq[PCMDQ])) ! 237: bvpcomm(bn->bvp.r, PCOWN|PCCMDQ|(PCMDQ<<PCDS)); ! 238: } ! 239: ! 240: /* ! 241: * here on an interrupt ! 242: */ ! 243: bna0int(unit) ! 244: int unit; ! 245: { ! 246: register struct bvpdata *b; ! 247: register struct bnactl *bn; ! 248: register struct dgi *dp; ! 249: register struct bnabuf *bf; /* just for safety check */ ! 250: register int i; ! 251: ! 252: if ((unsigned)unit >= bnacnt) ! 253: panic("bna0int"); ! 254: bn = &bna[unit]; ! 255: if (bn->bvp.r == NULL) { ! 256: printf("debna %d stray intr\n", unit); ! 257: return; ! 258: } ! 259: bn->bvp.rsave = *bn->bvp.r; /* copy all, for debugging */ ! 260: if (bn->bvp.rsave.stat & PSOWN) ! 261: bn->bvp.r->stat &=~ PSOWN; /* allow fresh status */ ! 262: if ((bn->bvp.rsave.stat & PSSTAT) != SENAB) { ! 263: printf("debna %d: ps %x pe %x pd %x\n", unit, ! 264: bn->bvp.rsave.stat, bn->bvp.rsave.err, bn->bvp.rsave.data); ! 265: return; ! 266: } ! 267: b = bn->bvp.d; ! 268: bf = &bnabuf[unit]; ! 269: while ((dp = (struct dgi *)remqhi(&b->p.p_rspq)) != NULL) { ! 270: if (dp->h.bd_sts != BVPSUC) ! 271: printf("debna %d opc %x sts %x\n", unit, dp->h.bd_opc, dp->h.bd_sts); ! 272: switch (dp->h.bd_opc) { ! 273: case BVPSNDDGI: /* datagram sent */ ! 274: for (i = ETHERALEN-1; i >= 0; --i) ! 275: if (((struct etherpup *)dp->d)->dhost[i] != 0xff ! 276: && ((struct etherpup *)dp->d)->dhost[i] != bn->myaddr[i]) ! 277: goto notecho; ! 278: /* ! 279: * packet sent to ourselves; ! 280: * port won't echo it back ! 281: */ ! 282: dp->h.bd_len += ETHERCKSUM; /* banrdg will trim */ ! 283: bcopy(bn->myaddr, ((struct etherpup *)dp->d)->shost, sizeof(bn->myaddr)); ! 284: bnardg(bn, dp); ! 285: notecho: ! 286: /* fall through */ ! 287: case BVPSNDMSG: /* message sent */ ! 288: if ((char *)dp < bf->xbuf ! 289: || (char *)dp >= &bf->xbuf[sizeof(bf->xbuf)]) ! 290: panic("bna0int sent"); ! 291: bnaxbuf(bn, dp); ! 292: continue; ! 293: ! 294: case BVPRCVDGI: /* datagram received */ ! 295: if ((char *)dp < bf->rbuf ! 296: || (char *)dp >= &bf->rbuf[sizeof(bf->rbuf)]) ! 297: panic("bna0int rcv dg"); ! 298: bnardg(bn, dp); ! 299: bnarbuf(bn, dp); ! 300: continue; ! 301: ! 302: case BVPRCVMSG: /* message received */ ! 303: if ((char *)dp < bf->rbuf ! 304: || (char *)dp >= &bf->rbuf[sizeof(bf->rbuf)]) ! 305: panic("bna0int rcv msg"); ! 306: bnarmsg(bn, (struct bvpmsg *)dp); ! 307: bnarbuf(bn, dp); ! 308: continue; ! 309: ! 310: default: ! 311: printf("debna %d: %x: opc %x\n", unit, dp, dp->h.bd_opc); ! 312: continue; /* leaving buffer tied up */ ! 313: } ! 314: } ! 315: while (bnasend(unit)) ! 316: ; ! 317: } ! 318: ! 319: /* ! 320: * here is a received datagram; ! 321: * find the right channel, ! 322: * and send it up the queue ! 323: */ ! 324: bnardg(bn, dp) ! 325: register struct bnactl *bn; ! 326: register struct dgi *dp; ! 327: { ! 328: register int n; ! 329: register struct block *bp; ! 330: register char *p; ! 331: register struct queue *q; ! 332: int len, proto; ! 333: ! 334: if (dp->h.bd_dgsts != NISUC) { ! 335: printf("debna %d dg sts %x\n", bn - bna, dp->h.bd_dgsts); ! 336: return; ! 337: } ! 338: len = (dp->h.bd_len+BVPHSIZE)-sizeof(struct bvpdg)-ETHERCKSUM; ! 339: proto = ((struct etherpup *)dp->d)->type; ! 340: for (n = 0; n < BNACHAN; n++) ! 341: if (bn->chan[n].proto == proto) ! 342: break; ! 343: if (n >= BNACHAN) ! 344: return; /* unexpected proto */ ! 345: q = bn->chan[n].rq; ! 346: if (q == NULL) ! 347: return; /* snh */ ! 348: if (q->next->flag & QFULL) ! 349: return; /* no room */ ! 350: p = dp->d; ! 351: while (len > 0) { ! 352: if ((bp = allocb(len)) == NULL) ! 353: return; /* wrong, but hard */ ! 354: n = bp->lim - bp->wptr; ! 355: if (len < n) ! 356: n = len; ! 357: bcopy(p, bp->wptr, n); ! 358: bp->wptr += n; ! 359: p += n; ! 360: len -= n; ! 361: if (len <= 0) ! 362: bp->class |= S_DELIM; ! 363: (*q->next->qinfo->putp)(q->next, bp); ! 364: } ! 365: } ! 366: ! 367: /* ! 368: * here is a message ! 369: */ ! 370: ! 371: bnarmsg(bn, dp) ! 372: register struct bnactl *bn; ! 373: register struct bvpmsg *dp; ! 374: { ! 375: register struct bnachan *bc; ! 376: register struct mst *sdp; ! 377: ! 378: switch (dp->bm_opc) { ! 379: case NISTPTDB: /* ptdb set, sent ioctl back */ ! 380: sdp = (struct mst *)dp; ! 381: if (sdp->p.pt_id < 0 || sdp->p.pt_id >= BNACHAN) ! 382: panic("bnastptdb"); ! 383: bc = &bn->chan[sdp->p.pt_id]; ! 384: if (bc->rq == NULL || bc->proto != sdp->p.pt_proto) ! 385: return; /* early close? */ ! 386: if (sdp->h.bm_nists != NISUC) ! 387: printf("debna %d %d stptdb sts %x\n", bn - bna, ! 388: sdp->p.pt_id, sdp->h.bm_nists); ! 389: putctl(bc->rq->next, M_IOCACK); ! 390: return; ! 391: ! 392: default: ! 393: printf("debna %d msg opc %x sts %x\n", dp->bm_opc, dp->bm_nists); ! 394: return; ! 395: } ! 396: } ! 397: ! 398: /* ! 399: * find a channel with a packet, ! 400: * find a buffer, ! 401: * and send ! 402: */ ! 403: bnasend(unit) ! 404: int unit; ! 405: { ! 406: register struct bnactl *bn; ! 407: register struct bnachan *bc; ! 408: register struct dgi *dp; ! 409: register char *p; ! 410: register struct block *bp; ! 411: register int n; ! 412: char *ep; ! 413: ! 414: bn = &bna[unit]; ! 415: n = bn->lastx; ! 416: while (bn->chan[n].dcnt == 0) { ! 417: if (++n >= BNACHAN) ! 418: n = 0; ! 419: if (n == bn->lastx) ! 420: return (0); /* nothing to send */ ! 421: } ! 422: bn->lastx = n; ! 423: bc = &bn->chan[n]; ! 424: if ((dp = bn->xfree) == NULL) ! 425: return (0); /* no buffer */ ! 426: bn->xfree = (struct dgi *)dp->h.q.head; ! 427: bzero((caddr_t)&dp->h, sizeof(dp->h)); ! 428: p = dp->d; ! 429: ep = p + sizeof(dp->d); ! 430: while ((bp = getq(WR(bc->rq))) != NULL) { ! 431: n = bp->wptr - bp->rptr; ! 432: if (p + n > ep) ! 433: n = ep - p; /* quietly truncate */ ! 434: if (n) { ! 435: bcopy(bp->rptr, p, n); ! 436: p += n; ! 437: } ! 438: if (bp->class & S_DELIM) { ! 439: bc->dcnt--; ! 440: freeb(bp); ! 441: break; ! 442: } ! 443: freeb(bp); ! 444: } ! 445: if (bp == NULL) { ! 446: printf("debna %d no delim\n", unit); ! 447: bc->dcnt = 0; ! 448: dp->h.q.head = (quadque *)bn->xfree; ! 449: bn->xfree = dp; ! 450: return (1); /* ok to try another channel */ ! 451: } ! 452: if (p - dp->d < ETHERMINTU) ! 453: p = dp->d + ETHERMINTU; /* quietly extend */ ! 454: dp->h.bd_opc = BVPSNDDGI; ! 455: dp->h.bd_flag = BVPRSP; /* so it will appear on rsp queue */ ! 456: dp->h.bd_len = p - &dp->h.bd_dgsts; ! 457: dp->h.bd_ptdb = unit; /* some valid number */ ! 458: /* probably should zero some other fields */ ! 459: if (insqti((caddr_t)dp, &bn->bvp.d->p.p_cmdq[PCMDQ])) ! 460: bvpcomm(bn->bvp.r, PCOWN|PCCMDQ|(PCMDQ<<PCDS)); ! 461: return (1); ! 462: } ! 463: ! 464: /* ! 465: * free a buffer for the port to fill in ! 466: * -- port fusses if buffer bigger than an Ethernet packet + BVP header ! 467: */ ! 468: bnarbuf(bn, dp) ! 469: register struct bnactl *bn; ! 470: register struct dgi *dp; ! 471: { ! 472: ! 473: dp->h.bd_sts = 0; /* just in case */ ! 474: dp->h.bd_opc = BVPRCVDGI; ! 475: dp->h.bd_len = sizeof(struct bvpdg) + NIRCVBUF - BVPHSIZE; ! 476: if (insqti((caddr_t)dp, &bn->bvp.d->f[PFREQ].f_q)) ! 477: bvpcomm(bn->bvp.r, PCOWN|PCFREQ|(PFREQ<<PCDS)); ! 478: } ! 479: ! 480: bnaxbuf(bn, dp) ! 481: register struct bnactl *bn; ! 482: register struct dgi *dp; ! 483: { ! 484: register int i; ! 485: ! 486: dp->h.q.head = (quadque *)bn->xfree; ! 487: bn->xfree = dp; ! 488: if (bn->needst) { ! 489: bn->needst = 0; ! 490: for (i = 0; i < BNACHAN; i++) ! 491: if (bn->chan[i].needst) ! 492: bnastptdb(bn, &bn->chan[i]); ! 493: } ! 494: } ! 495: ! 496: /* ! 497: * (fairly) general BVP routines ! 498: */ ! 499: bvpinit(bv, ap, roff) ! 500: register struct bvp *bv; ! 501: struct biaddr *ap; ! 502: int roff; ! 503: { ! 504: register struct bvpregs *r; ! 505: register struct biic *rb; ! 506: register long ps; /* most recent status */ ! 507: ! 508: if ((rb = bv->rb) == NULL) { ! 509: if ((rb = (struct biic *)biaddr(ap)) == NULL ! 510: || badaddr(rb, sizeof(long))) { ! 511: printf("bvp absent\n"); ! 512: return (0); ! 513: } ! 514: bv->rb = rb; ! 515: } ! 516: if (bv->r == NULL) { ! 517: r = (struct bvpregs *)((char *)rb + roff); ! 518: if (badaddr(r, sizeof(long))) { ! 519: printf("bvp off %x absent\n", roff); ! 520: return (0); ! 521: } ! 522: bv->r = r; ! 523: } ! 524: if (bv->dbuf == NULL) { ! 525: bv->dbuf = geteblk(); ! 526: if (bv->dbuf->b_bcount < sizeof(struct bvpdata)) { ! 527: printf("bcount too small\n"); ! 528: brelse(bv->dbuf); ! 529: return (0); ! 530: } ! 531: clrbuf(bv->dbuf); ! 532: bv->d = (struct bvpdata *)bv->dbuf->b_un.b_addr; ! 533: } ! 534: r = bv->r; ! 535: ps = r->stat; ! 536: if ((ps & PSSTAT) == SUNDEF) { ! 537: biinit(ap, 0); ! 538: bvpdatinit(bv->d); ! 539: bv->d->p.p_nodmsk = rb->biintr; ! 540: bv->d->p.p_vector = (ap->vec<<2) | 5; /* vec, br */ ! 541: bvpcomm(r, physadr(&bv->d->p)|PCOWN|PCINIT); ! 542: ps = bvpstat(r); ! 543: } ! 544: if ((ps & PSSTAT) == SINIT) { ! 545: bvpcomm(r, PCOWN|PCENAB); ! 546: ps = bvpstat(r); ! 547: } ! 548: if ((ps & PSSTAT) != SENAB) { ! 549: printf("ps %x pe %x pd %x\n", r->stat, r->err, r->data); ! 550: return (0); ! 551: } ! 552: return (1); ! 553: } ! 554: ! 555: bvpdatinit(b) ! 556: register struct bvpdata *b; ! 557: { ! 558: bzero((char *)b, sizeof(*b)); ! 559: b->p.p_nfreeq = NFREEQ; ! 560: b->p.p_fqb = b->f; ! 561: b->p.p_bvplvl = 1; ! 562: b->p.p_pqb = &b->p; ! 563: b->p.p_bdt = b->b; ! 564: b->p.p_bdtlen = NBDT; /* -1? */ ! 565: b->p.p_spt = (struct pte *)physadr(Sysmap); ! 566: b->p.p_sptlen = 0x100000; /* huge */ ! 567: b->p.p_gpt = b->p.p_spt; ! 568: b->p.p_gptlen = b->p.p_sptlen; ! 569: } ! 570: ! 571: /* ! 572: * send a port command ! 573: */ ! 574: #define COMMTIME 1000000 ! 575: ! 576: bvpcomm(r, c) ! 577: register struct bvpregs *r; ! 578: long c; ! 579: { ! 580: register int i; ! 581: ! 582: i = COMMTIME; ! 583: while (r->ctrl & PCOWN) ! 584: if (--i <= 0) { ! 585: printf("bvpcomm timeout c %x\n", c); ! 586: return; /* what else to do? */ ! 587: } ! 588: r->stat &=~ PSOWN; ! 589: r->ctrl = c; ! 590: } ! 591: ! 592: /* ! 593: * wait for status in init ! 594: */ ! 595: ! 596: #define STATTIME 1000000 ! 597: ! 598: int ! 599: bvpstat(r) ! 600: register struct bvpregs *r; ! 601: { ! 602: register long ps; ! 603: register int i; ! 604: ! 605: i = STATTIME; ! 606: while (((ps = r->stat) & PSOWN) == 0) ! 607: if (--i <= 0) { ! 608: printf("bvpstat timeout, ps %x\n", ps); ! 609: return (0); ! 610: } ! 611: return (ps); ! 612: } ! 613: ! 614: /* ! 615: * VAX queue primitives ! 616: * ! 617: * -- to set up empty queue, just zero both longwords ! 618: */ ! 619: ! 620: /* ! 621: * remove entry from head; ! 622: * return entry, or 0 if none ! 623: */ ! 624: caddr_t ! 625: remqhi(q) ! 626: quadque *q; ! 627: { ! 628: asm("0: remqhi *4(ap),r0"); ! 629: asm("bcs 0b"); /* couldn't interlock; try again */ ! 630: asm("bvc 1f"); /* br if removed something */ ! 631: asm("clrl r0"); ! 632: asm("1:"); ! 633: } ! 634: ! 635: /* ! 636: * insert entry to tail ! 637: * return 1 if this was the first entry ! 638: */ ! 639: insqti(e, q) ! 640: caddr_t e; ! 641: quadque *q; ! 642: { ! 643: asm("clrl r0"); ! 644: asm("0: insqti *4(ap),*8(ap)"); ! 645: asm("bcs 0b"); ! 646: asm("bneq 1f"); ! 647: asm("incl r0"); ! 648: asm("1:"); ! 649: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.