|
|
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; ! 333: short proto; ! 334: ! 335: if (dp->h.bd_dgsts != NISUC) { ! 336: printf("debna %d dg sts %x\n", bn - bna, dp->h.bd_dgsts); ! 337: return; ! 338: } ! 339: len = (dp->h.bd_len+BVPHSIZE)-sizeof(struct bvpdg)-ETHERCKSUM; ! 340: proto = (short)((struct etherpup *)dp->d)->type; ! 341: for (n = 0; n < BNACHAN; n++) ! 342: if (bn->chan[n].proto == proto) ! 343: break; ! 344: if (n >= BNACHAN) ! 345: return; /* unexpected proto */ ! 346: q = bn->chan[n].rq; ! 347: if (q == NULL) ! 348: return; /* snh */ ! 349: if (q->next->flag & QFULL) ! 350: return; /* no room */ ! 351: p = dp->d; ! 352: while (len > 0) { ! 353: if ((bp = allocb(len)) == NULL) ! 354: return; /* wrong, but hard */ ! 355: n = bp->lim - bp->wptr; ! 356: if (len < n) ! 357: n = len; ! 358: bcopy(p, bp->wptr, n); ! 359: bp->wptr += n; ! 360: p += n; ! 361: len -= n; ! 362: if (len <= 0) ! 363: bp->class |= S_DELIM; ! 364: (*q->next->qinfo->putp)(q->next, bp); ! 365: } ! 366: } ! 367: ! 368: /* ! 369: * here is a message ! 370: */ ! 371: ! 372: bnarmsg(bn, dp) ! 373: register struct bnactl *bn; ! 374: register struct bvpmsg *dp; ! 375: { ! 376: register struct bnachan *bc; ! 377: register struct mst *sdp; ! 378: ! 379: switch (dp->bm_opc) { ! 380: case NISTPTDB: /* ptdb set, sent ioctl back */ ! 381: sdp = (struct mst *)dp; ! 382: if (sdp->p.pt_id < 0 || sdp->p.pt_id >= BNACHAN) ! 383: panic("bnastptdb"); ! 384: bc = &bn->chan[sdp->p.pt_id]; ! 385: if (bc->rq == NULL || bc->proto != sdp->p.pt_proto) ! 386: return; /* early close? */ ! 387: if (sdp->h.bm_nists != NISUC) ! 388: printf("debna %d %d stptdb sts %x\n", bn - bna, ! 389: sdp->p.pt_id, sdp->h.bm_nists); ! 390: putctl(bc->rq->next, M_IOCACK); ! 391: return; ! 392: ! 393: default: ! 394: printf("debna %d msg opc %x sts %x\n", dp->bm_opc, dp->bm_nists); ! 395: return; ! 396: } ! 397: } ! 398: ! 399: /* ! 400: * find a channel with a packet, ! 401: * find a buffer, ! 402: * and send ! 403: */ ! 404: bnasend(unit) ! 405: int unit; ! 406: { ! 407: register struct bnactl *bn; ! 408: register struct bnachan *bc; ! 409: register struct dgi *dp; ! 410: register char *p; ! 411: register struct block *bp; ! 412: register int n; ! 413: char *ep; ! 414: ! 415: bn = &bna[unit]; ! 416: n = bn->lastx; ! 417: while (bn->chan[n].dcnt == 0) { ! 418: if (++n >= BNACHAN) ! 419: n = 0; ! 420: if (n == bn->lastx) ! 421: return (0); /* nothing to send */ ! 422: } ! 423: bn->lastx = n; ! 424: bc = &bn->chan[n]; ! 425: if ((dp = bn->xfree) == NULL) ! 426: return (0); /* no buffer */ ! 427: bn->xfree = (struct dgi *)dp->h.q.head; ! 428: bzero((caddr_t)&dp->h, sizeof(dp->h)); ! 429: p = dp->d; ! 430: ep = p + sizeof(dp->d); ! 431: while ((bp = getq(WR(bc->rq))) != NULL) { ! 432: n = bp->wptr - bp->rptr; ! 433: if (p + n > ep) ! 434: n = ep - p; /* quietly truncate */ ! 435: if (n) { ! 436: bcopy(bp->rptr, p, n); ! 437: p += n; ! 438: } ! 439: if (bp->class & S_DELIM) { ! 440: bc->dcnt--; ! 441: freeb(bp); ! 442: break; ! 443: } ! 444: freeb(bp); ! 445: } ! 446: if (bp == NULL) { ! 447: printf("debna %d no delim\n", unit); ! 448: bc->dcnt = 0; ! 449: dp->h.q.head = (quadque *)bn->xfree; ! 450: bn->xfree = dp; ! 451: return (1); /* ok to try another channel */ ! 452: } ! 453: if (p - dp->d < ETHERMINTU) ! 454: p = dp->d + ETHERMINTU; /* quietly extend */ ! 455: dp->h.bd_opc = BVPSNDDGI; ! 456: dp->h.bd_flag = BVPRSP; /* so it will appear on rsp queue */ ! 457: dp->h.bd_len = p - &dp->h.bd_dgsts; ! 458: dp->h.bd_ptdb = unit; /* some valid number */ ! 459: /* probably should zero some other fields */ ! 460: if (insqti((caddr_t)dp, &bn->bvp.d->p.p_cmdq[PCMDQ])) ! 461: bvpcomm(bn->bvp.r, PCOWN|PCCMDQ|(PCMDQ<<PCDS)); ! 462: return (1); ! 463: } ! 464: ! 465: /* ! 466: * free a buffer for the port to fill in ! 467: * -- port fusses if buffer bigger than an Ethernet packet + BVP header ! 468: */ ! 469: bnarbuf(bn, dp) ! 470: register struct bnactl *bn; ! 471: register struct dgi *dp; ! 472: { ! 473: ! 474: dp->h.bd_sts = 0; /* just in case */ ! 475: dp->h.bd_opc = BVPRCVDGI; ! 476: dp->h.bd_len = sizeof(struct bvpdg) + NIRCVBUF - BVPHSIZE; ! 477: if (insqti((caddr_t)dp, &bn->bvp.d->f[PFREQ].f_q)) ! 478: bvpcomm(bn->bvp.r, PCOWN|PCFREQ|(PFREQ<<PCDS)); ! 479: } ! 480: ! 481: bnaxbuf(bn, dp) ! 482: register struct bnactl *bn; ! 483: register struct dgi *dp; ! 484: { ! 485: register int i; ! 486: ! 487: dp->h.q.head = (quadque *)bn->xfree; ! 488: bn->xfree = dp; ! 489: if (bn->needst) { ! 490: bn->needst = 0; ! 491: for (i = 0; i < BNACHAN; i++) ! 492: if (bn->chan[i].needst) ! 493: bnastptdb(bn, &bn->chan[i]); ! 494: } ! 495: } ! 496: ! 497: /* ! 498: * (fairly) general BVP routines ! 499: */ ! 500: bvpinit(bv, ap, roff) ! 501: register struct bvp *bv; ! 502: struct biaddr *ap; ! 503: int roff; ! 504: { ! 505: register struct bvpregs *r; ! 506: register struct biic *rb; ! 507: register long ps; /* most recent status */ ! 508: ! 509: if ((rb = bv->rb) == NULL) { ! 510: if ((rb = (struct biic *)biaddr(ap)) == NULL ! 511: || badaddr(rb, sizeof(long))) { ! 512: printf("bvp absent\n"); ! 513: return (0); ! 514: } ! 515: bv->rb = rb; ! 516: } ! 517: if (bv->r == NULL) { ! 518: r = (struct bvpregs *)((char *)rb + roff); ! 519: if (badaddr(r, sizeof(long))) { ! 520: printf("bvp off %x absent\n", roff); ! 521: return (0); ! 522: } ! 523: bv->r = r; ! 524: } ! 525: if (bv->dbuf == NULL) { ! 526: bv->dbuf = geteblk(); ! 527: if (bv->dbuf->b_bcount < sizeof(struct bvpdata)) { ! 528: printf("bcount too small\n"); ! 529: brelse(bv->dbuf); ! 530: return (0); ! 531: } ! 532: clrbuf(bv->dbuf); ! 533: bv->d = (struct bvpdata *)bv->dbuf->b_un.b_addr; ! 534: } ! 535: r = bv->r; ! 536: ps = r->stat; ! 537: if ((ps & PSSTAT) == SUNDEF) { ! 538: biinit(ap, 0); ! 539: bvpdatinit(bv->d); ! 540: bv->d->p.p_nodmsk = rb->biintr; ! 541: bv->d->p.p_vector = (ap->vec<<2) | 5; /* vec, br */ ! 542: bvpcomm(r, physadr(&bv->d->p)|PCOWN|PCINIT); ! 543: ps = bvpstat(r); ! 544: } ! 545: if ((ps & PSSTAT) == SINIT) { ! 546: bvpcomm(r, PCOWN|PCENAB); ! 547: ps = bvpstat(r); ! 548: } ! 549: if ((ps & PSSTAT) != SENAB) { ! 550: printf("ps %x pe %x pd %x\n", r->stat, r->err, r->data); ! 551: return (0); ! 552: } ! 553: return (1); ! 554: } ! 555: ! 556: bvpdatinit(b) ! 557: register struct bvpdata *b; ! 558: { ! 559: bzero((char *)b, sizeof(*b)); ! 560: b->p.p_nfreeq = NFREEQ; ! 561: b->p.p_fqb = b->f; ! 562: b->p.p_bvplvl = 1; ! 563: b->p.p_pqb = &b->p; ! 564: b->p.p_bdt = b->b; ! 565: b->p.p_bdtlen = NBDT; /* -1? */ ! 566: b->p.p_spt = (struct pte *)physadr(Sysmap); ! 567: b->p.p_sptlen = 0x100000; /* huge */ ! 568: b->p.p_gpt = b->p.p_spt; ! 569: b->p.p_gptlen = b->p.p_sptlen; ! 570: } ! 571: ! 572: /* ! 573: * send a port command ! 574: */ ! 575: #define COMMTIME 1000000 ! 576: ! 577: bvpcomm(r, c) ! 578: register struct bvpregs *r; ! 579: long c; ! 580: { ! 581: register int i; ! 582: ! 583: i = COMMTIME; ! 584: while (r->ctrl & PCOWN) ! 585: if (--i <= 0) { ! 586: printf("bvpcomm timeout c %x\n", c); ! 587: return; /* what else to do? */ ! 588: } ! 589: r->stat &=~ PSOWN; ! 590: r->ctrl = c; ! 591: } ! 592: ! 593: /* ! 594: * wait for status in init ! 595: */ ! 596: ! 597: #define STATTIME 1000000 ! 598: ! 599: int ! 600: bvpstat(r) ! 601: register struct bvpregs *r; ! 602: { ! 603: register long ps; ! 604: register int i; ! 605: ! 606: i = STATTIME; ! 607: while (((ps = r->stat) & PSOWN) == 0) ! 608: if (--i <= 0) { ! 609: printf("bvpstat timeout, ps %x\n", ps); ! 610: return (0); ! 611: } ! 612: return (ps); ! 613: } ! 614: ! 615: /* ! 616: * VAX queue primitives ! 617: * ! 618: * -- to set up empty queue, just zero both longwords ! 619: */ ! 620: ! 621: /* ! 622: * remove entry from head; ! 623: * return entry, or 0 if none ! 624: */ ! 625: caddr_t ! 626: remqhi(q) ! 627: quadque *q; ! 628: { ! 629: asm("0: remqhi *4(ap),r0"); ! 630: asm("bcs 0b"); /* couldn't interlock; try again */ ! 631: asm("bvc 1f"); /* br if removed something */ ! 632: asm("clrl r0"); ! 633: asm("1:"); ! 634: } ! 635: ! 636: /* ! 637: * insert entry to tail ! 638: * return 1 if this was the first entry ! 639: */ ! 640: insqti(e, q) ! 641: caddr_t e; ! 642: quadque *q; ! 643: { ! 644: asm("clrl r0"); ! 645: asm("0: insqti *4(ap),*8(ap)"); ! 646: asm("bcs 0b"); ! 647: asm("bneq 1f"); ! 648: asm("incl r0"); ! 649: asm("1:"); ! 650: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.