! "W N0 V=    @ #D  D ҃TP  B ы e@W 0 ,"& 7   0ߋp@E A Ze      |@7x@ eE " 0 ɋ -hɋ e-NJDɋ Z?./370.h JͲ./buf.h5 P/./conf.h ߵ./dsk.h ߷;./file.h ߹./filsys.h  W#./ino.h; Z?./inode.hu \./manifest.h/ `./param.h Wd./proc.hG g./reg.hkc./systm.h m./text.h q ./tty.hD [,r./user.h+ wpae/trap.c 6pae/data.c_ cpae/dummy.c FBpae/ipl.c gpae/alloc.c ,!Mpae/iget.c +-pae/nami.c &6pae/fio.c >bpae/pipe.cw #Gpae/rdwri.c! neNXpae/sys2.c tU]pae/sysent.ce]pae/sys3.c 0bpae/subr.c} ipae/sys1.c| ,qpae/prf.c >+~0pae/ipl2.s #^pae/sys4.c (pugs/clock.c Fopugs/sig.c c;bin/preproc" pugs/slp.c pugs/page.c `+pugs/pagetab.mac ?pugs/user.mac{ !15jpugs/main.cV a)pugs/mem.s\ Xpugs/sureg.s  pugs/txt.c Kjskud/conf.cV D=Yjskud/cons.c} jskud/baddr.c /jskud/charq.a Umjskud/dskio.sx tjskud/bio.c$ *jskud/dsk.cI  Ljskud/mkfs2.c  jskud/tty.c' "rjskud/memio.ck 6?jskud/charq.c T8integ); printf("psw = %x%x\n",ps,pc); panic("trap"); } u->u_arsp = &rsp; u->u_orega = (&rsp) - DROPSIZE; /* point at saved regs, so exec can clear them */ if((ps&PGMTRAP) == 0) { /* svc */ ps =& ~CC; callp = &sysent[(SVCCODE)->integ & 077]; ar1 = r1; for(i = 0; icount; i++) { u->u_arg[i] = fuword(ar1); ar1 =+ 4; } u->u_dirp = u->u_arg[0]; u->u_error = 0; trap1(callp->call); if(u->u_intflg) u->u_error = EINTR; if(u->u_error) { if(u->u_error >= 100) { i = SIGSYS; goto signal; } ps =| CC; /* set overflow cc */ r0 = u->u_error; /* remember that the 360 doesnt extend the sign */ } } else { /* program check */ i = PGMCODE->integ & 037; if(i == 16 || i == 17) { grow(rsp); goto out; } i =+ SIGPGM-1; signal: psignal(u->u_procp,i); } out: if(issig()) psig(); setpri(u->u_procp); } trap1(f) int (*f)(); { u->u_intflg = 1; savu(u->u_qsav); (*f)(); u->u_intflg = 0; } nosys() { u->u_error = 100; } nullsys() { } { i = SIGSYS; goto signal; } ps =| CC; /* set ove# /* all the data for the system */ #define extern /* we don't want it to be external this time */ #include "../manifest.h" #include "../param.h" #include "../370.h" #include "../proc.h" #include "../inode.h" #include "../systm.h" #include "../text.h" #include "../buf.h" #include "../file.h" #include "../filsys.h" char buffers[NBUF][BLKSIZE]; 1; savu(u->u_qsav); (*f)(); u->u_intflg = 0; } nosys() { u->u_error = 100; } nullsys() { } { i = SIGSYS; goto signal; } ps =| CC; /* set ovecinit() { } he data for the system */ #define extern /* we don't want it to be external this time */ #include "../manifest.h" #include "../param.h" #include "../370.h" #include "../proc.h" #include "../inode.h" #include "../systm.h" #include "../text.h" #include "../buf.h" #include "../file.h" #include "../filsys.h" char buffers[NBUF][BLKSIZE]; 1; savu(u->u_qsav); (*f)(); u->u_intflg = 0; } nosys() { u->u_error = 100; } nullsys() { } { i = SIGSYS; goto signal; } ps =| CC; /* set ove /* ipl program */ struct { int magic; int tsize; int dsize; int fill[5]; } header; char magic[4] {0107, 0360, 0, 040}; main(argc, argv) char **argv; { int fd, area, size; if(argc != 2) { printf("arg count\n"); cexit(1); } fd = copen(argv[1], 'r'); uread(fd, &header, 32); if(header.magic != magic[0].magic) { printf("not an a.out file\n"); cexit(2); } size = header.tsize + header.dsize; area = calloc(size + 16, 1); uread(fd, area, size); loadit(area, size); /* should not return */ } #include "../link/uio.c" { int magic; int tsize; int dsize; int fill[5]; } header; char magic[4] {0107, 0360, 0, 040}; main(argc, argv) char **argv; { int fd, area, size; if(argc != 2) { printf("arg count\n"); cexit(1); } fd = copen(argv[1], 'r'); uread(fd, &header, 32); if(header.magic != magic[0].magic) { printf("not an a.out file\n"); cexit(2); } size = header.tsize + header.dsize; area = calloc(size + 16, 1); uread(fd, area, size); loadit(area, size); /* should not return */ # /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../filsys.h" #include "../conf.h" #include "../buf.h" #include "../inode.h" #include "../user.h" /* * iinit is called once (from main) * very early in initialization. * It reads the root's super block * and initializes the current date * from the last modified date. * * panic: iinit -- cannot read the super * block. Usually because of an IO error. */ iinit() { register *cp, *bp; (*bdevsw[rootdev.d_major].d_open)(rootdev, 1); bp = bread(rootdev, 1); cp = getblk(NODEV); if(u->u_error) panic("iinit"); bcopy(bp->b_addr, cp->b_addr, BLKSIZE); brelse(bp); mount[0].m_bufp = cp; mount[0].m_dev = rootdev; cp = cp->b_addr; cp->s_flock = 0; cp->s_ilock = 0; cp->s_ronly = 0; time = cp->s_time; } /* * alloc will obtain the next available * free disk block from the free list of * the specified device. * The super block has up to SFREESIZ remembered * free blocks; the last of these is read to * obtain SFREESIZ more . . . * * no space on dev x/y -- when * the free list is exhausted. */ alloc(dev) { int bno; register *bp, *ip, *fp; fp = getfs(dev); while(fp->s_flock) sleep(&fp->s_flock, PINOD); do { if(fp->s_nfree <= 0) goto nospace; bno = fp->s_free[--fp->s_nfree]; if(bno == 0) goto nospace; } while (badblock(fp, bno, dev)); if(fp->s_nfree <= 0) { fp->s_flock++; bp = bread(dev, bno); ip = bp->b_addr; fp->s_nfree = *ip++; bcopy(ip, fp->s_free, SFREESIZ*WORDSIZE); brelse(bp); fp->s_flock = 0; wakeup(&fp->s_flock); } bp = getblk(dev, bno); clrbuf(bp); fp->s_fmod = 1; return(bp); nospace: fp->s_nfree = 0; prdev("no space", dev); u->u_error = ENOSPC; return(NULL); } /* * place the specified disk block * back on the free list of the * specified device. */ free(dev, bno) { register *fp, *bp, *ip; fp = getfs(dev); fp->s_fmod = 1; while(fp->s_flock) sleep(&fp->s_flock, PINOD); if (badblock(fp, bno, dev)) return; if(fp->s_nfree <= 0) { fp->s_nfree = 1; fp->s_free[0] = 0; } if(fp->s_nfree >= SFREESIZ) { fp->s_flock++; bp = getblk(dev, bno); ip = bp->b_addr; *ip++ = fp->s_nfree; bcopy(fp->s_free, ip, SFREESIZ*WORDSIZE); fp->s_nfree = 0; bwrite(bp); fp->s_flock = 0; wakeup(&fp->s_flock); } fp->s_free[fp->s_nfree++] = bno; fp->s_fmod = 1; } /* * Check that a block number is in the * range between the I list and the size * of the device. * This is used mainly to check that a * garbage file system has not been mounted. * * bad block on dev x/y -- not in range */ badblock(afp, abn, dev) { register struct filsys *fp; register char *bn; fp = afp; bn = abn; if (bn < fp->s_isize+2 || bn >= fp->s_fsize) { prdev("bad block", dev); return(1); } return(0); } /* * Allocate an unused I node * on the specified device. * Used with file creation. * The algorithm keeps up to * SINODESZ spare I nodes in the * super block. When this runs out, * a linear search through the * I list is instituted to pick * up SINODESZ more. */ ialloc(dev) { register *fp, *bp, *ip; int i, j, k, ino; fp = getfs(dev); while(fp->s_ilock) sleep(&fp->s_ilock, PINOD); loop: if(fp->s_ninode > 0) { ino = fp->s_inode[--fp->s_ninode]; ip = iget(dev, ino); if (ip==NULL) return(NULL); if(ip->i_mode == 0) { for(bp = &ip->i_mode; bp < &ip->i_addr[8];) *bp++ = 0; fp->s_fmod = 1; return(ip); } /* * Inode was allocated after all. * Look some more. */ iput(ip); goto loop; } fp->s_ilock++; ino = 0; for(i=0; is_isize; i++) { bp = bread(dev, i+2); ip = bp->b_addr; for(j=0; js_inode[fp->s_ninode++] = ino; if(fp->s_ninode >= SINODESZ) break; cont:; } brelse(bp); if(fp->s_ninode >= SINODESZ) break; } fp->s_ilock = 0; wakeup(&fp->s_ilock); if (fp->s_ninode > 0) goto loop; prdev("Out of inodes", dev); u->u_error = ENOSPC; return(NULL); } /* * Free the specified I node * on the specified device. * The algorithm stores up * to SINODESZ I nodes in the super * block and throws away any more. */ ifree(dev, ino) { register *fp; fp = getfs(dev); if(fp->s_ilock) return; if(fp->s_ninode >= SINODESZ) return; fp->s_inode[fp->s_ninode++] = ino; fp->s_fmod = 1; } /* * getfs maps a device number into * a pointer to the incore super * block. * The algorithm is a linear * search through the mount table. * A consistency check of the * in core free-block and i-node * counts. * * bad count on dev x/y -- the count * check failed. At this point, all * the counts are zeroed which will * almost certainly lead to "no space" * diagnostic * panic: no fs -- the device is not mounted. * this "cannot happen" */ getfs(dev) { register struct mount *p; register char *n1, *n2; for(p = &mount[0]; p < &mount[NMOUNT]; p++) if(p->m_bufp != NULL && p->m_dev == dev) { p = p->m_bufp->b_addr; n1 = p->s_nfree; n2 = p->s_ninode; if(n1 > SFREESIZ || n2 > SINODESZ) { prdev("bad count", dev); p->s_nfree = 0; p->s_ninode = 0; } return(p); } panic("no fs"); } /* * update is the internal name of * 'sync'. It goes through the disk * queues to initiate sandbagged IO; * goes through the I nodes to write * modified nodes; and it goes through * the mount table to initiate modified * super blocks. */ update() { register struct inode *ip; register struct mount *mp; register *bp; if(updlock) return; updlock++; for(mp = &mount[0]; mp < &mount[NMOUNT]; mp++) if(mp->m_bufp != NULL) { ip = mp->m_bufp->b_addr; if(ip->s_fmod==0 || ip->s_ilock!=0 || ip->s_flock!=0 || ip->s_ronly!=0) continue; bp = getblk(mp->m_dev, 1); ip->s_fmod = 0; ip->s_time = time; bcopy(ip, bp->b_addr, BLKSIZE); bwrite(bp); } for(ip = &inode[0]; ip < &inode[NINODE]; ip++) if((ip->i_flag&ILOCK) == 0) { ip->i_flag =| ILOCK; iupdat(ip, time); prele(ip); } updlock = 0; bflush(NODEV); } # #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../inode.h" #include "../filsys.h" #include "../conf.h" #include "../buf.h" /* * Look up an inode by device,inumber. * If it is in core (in the inode structure), * honor the locking protocol. * If it is not in core, read it in from the * specified device. * If the inode is mounted on, perform * the indicated indirection. * In all cases, a pointer to a locked * inode structure is returned. * * printf warning: no inodes -- if the inode * structure is full * panic: no imt -- if the mounted file * system is not in the mount table. * "cannot happen" */ iget(dev, ino) { register struct inode *p; register *ip2; int *ip1; register struct mount *ip; loop: ip = NULL; for(p = &inode[0]; p < &inode[NINODE]; p++) { if(dev==p->i_dev && ino==p->i_number) { if((p->i_flag&ILOCK) != 0) { p->i_flag =| IWANT; sleep(p, PINOD); goto loop; } if((p->i_flag&IMOUNT) != 0) { for(ip = &mount[0]; ip < &mount[NMOUNT]; ip++) if(ip->m_inodp == p) { dev = ip->m_dev; ino = ROOTINO; goto loop; } panic("no imt"); } p->i_count++; p->i_flag =| ILOCK; return(p); } if(ip==NULL && p->i_count==0) ip = p; } if((p=ip) == NULL) { printf("Inode table overflow\n"); u->u_error = ENFILE; return(NULL); } p->i_dev = dev; p->i_number = ino; p->i_flag = ILOCK; p->i_count++; p->i_lastr = -1; ip = bread(dev, (ino + IOFFSET) / IPERBLK); /* * Check I/O errors */ if (ip->b_flags&B_ERROR) { brelse(ip); iput(p); return(NULL); } ip1 = ip->b_addr + ISIZE * ((ino + IOFFSET) % IPERBLK); ip2 = &p->i_mode; while(ip2 < &p->i_addr[8]) *ip2++ = *ip1++; brelse(ip); return(p); } /* * Decrement reference count of * an inode structure. * On the last reference, * write the inode out and if necessary, * truncate and deallocate the file. */ iput(p) struct inode *p; { register *rp; rp = p; if(rp->i_count == 1) { rp->i_flag =| ILOCK; if(rp->i_nlink <= 0) { itrunc(rp); rp->i_mode = 0; ifree(rp->i_dev, rp->i_number); } iupdat(rp, time); prele(rp); rp->i_flag = 0; rp->i_number = 0; } rp->i_count--; prele(rp); } /* * Check accessed and update flags on * an inode structure. * If either is on, update the inode * with the corresponding dates * set to the argument tm. */ iupdat(p, tm) int *p; int tm; { register *ip1, *ip2, *rp; int *bp, i; rp = p; if((rp->i_flag&(IUPD|IACC)) != 0) { if(getfs(rp->i_dev)->s_ronly) return; i = rp->i_number + IOFFSET; bp = bread(rp->i_dev, i / IPERBLK); ip1 = bp->b_addr + ISIZE * (i % IPERBLK); ip2 = &rp->i_mode; while(ip2 < &rp->i_addr[8]) *ip1++ = *ip2++; if(rp->i_flag&IACC) { *ip1++ = time; } else ip1 =+ 1; if(rp->i_flag&IUPD) { *ip1++ = tm; } bwrite(bp); } } /* * Free all the disk blocks associated * with the specified inode structure. * The blocks of the file are removed * in reverse order. This FILO * algorithm will tend to maintain * a contiguous free list much longer * than FIFO. */ itrunc(ip) int *ip; { register *rp, *bp, *cp; int *dp, *ep; rp = ip; if((rp->i_mode&(IFCHR&IFBLK)) != 0) return; for(ip = &rp->i_addr[7]; ip >= &rp->i_addr[0]; ip--) if(*ip) { if((rp->i_mode&ILARG) != 0) { bp = bread(rp->i_dev, *ip); for(cp = bp->b_addr+(BLKSIZE-WORDSIZE); cp >= bp->b_addr; cp--) if(*cp) { if(ip == &rp->i_addr[7]) { dp = bread(rp->i_dev, *cp); for(ep = dp->b_addr+(BLKSIZE-WORDSIZE); ep >= dp->b_addr; ep--) if(*ep) free(rp->i_dev, *ep); brelse(dp); } free(rp->i_dev, *cp); } brelse(bp); } free(rp->i_dev, *ip); *ip = 0; } rp->i_mode =& ~ILARG; rp->i_size = 0; rp->i_flag =| IUPD; } /* * Make a new file. */ maknode(mode) { register *ip; ip = ialloc(u->u_pdir->i_dev); if (ip==NULL) return(NULL); ip->i_flag =| IACC|IUPD; ip->i_mode = mode|IALLOC; ip->i_nlink = 1; ip->i_uid = u->u_uid; ip->i_gid = u->u_gid; wdir(ip); return(ip); } /* * Write a directory entry with * parameters left as side effects * to a call to namei. */ wdir(ip) int *ip; { register char *cp1, *cp2; sthalf(u->u_dent.u_ino, ip->i_number); cp1 = &u->u_dent.u_name[0]; for(cp2 = &u->u_dbuf[0]; cp2 < &u->u_dbuf[DIRSIZ];) *cp1++ = *cp2++; /* Need to do two writes because of hole in u_dent */ u->u_segflg = 1; u->u_base = &u->u_dent; u->u_count = 2; writei(u->u_pdir); u->u_base =+ 2; u->u_count = DIRSIZ; writei(u->u_pdir); iput(u->u_pdir); } urn(ip); } /* * Write a directory entry with *# #include "../manifest.h" #include "../param.h" #include "../inode.h" #include "../user.h" #include "../systm.h" #include "../buf.h" /* * Convert a pathname into a pointer to * an inode. Note that the inode is locked. * * func = function called to get next char of name * &uchar if name is in user space * &schar if name is in system space * flag = 0 if name is sought * 1 if name is to be created * 2 if name is to be deleted */ namei(func, flag) int (*func)(); { register struct inode *dp; register c; register char *cp; int eo, *bp; /* * If name starts with '/' start from * root; otherwise start from current dir. */ dp = u->u_cdir; if((c=(*func)()) == '/') dp = rootdir; iget(dp->i_dev, dp->i_number); while(c == '/') c = (*func)(); if(c == '\0' && flag != 0) { u->u_error = ENOENT; goto out; } cloop: /* * Here dp contains pointer * to last component matched. */ if(u->u_error) goto out; if(c == '\0') return(dp); /* * If there is another component, * dp must be a directory and * must have x permission. */ if((dp->i_mode&IFMT) != IFDIR) { u->u_error = ENOTDIR; goto out; } if(access(dp, IEXEC)) goto out; /* * Gather up name into * users' dir buffer. */ cp = &u->u_dbuf[0]; while(c!='/' && c!='\0' && u->u_error==0) { if(cp < &u->u_dbuf[DIRSIZ]) *cp++ = c; c = (*func)(); } while(cp < &u->u_dbuf[DIRSIZ]) *cp++ = '\0'; while(c == '/') c = (*func)(); if(u->u_error) goto out; /* * Set up to search a directory. */ u->u_offset = 0; u->u_segflg = 1; eo = 0; u->u_count = dp->i_size/(DIRSIZ+2); bp = NULL; eloop: /* * If at the end of the directory, * the search failed. Report what * is appropriate as per flag. */ if(u->u_count == 0) { if(bp != NULL) brelse(bp); if(flag==1 && c=='\0') { if(dp -> i_nlink == 0) { /* we don't want to allow * writing in a * disconnected directory */ u->u_error = ENOENT; goto out; } if(access(dp, IWRITE)) goto out; u->u_pdir = dp; if(eo) u->u_offset = eo-DIRSIZ-2; else dp->i_flag =| IUPD; return(NULL); } u->u_error = ENOENT; goto out; } /* * If offset is on a block boundary, * read the next directory block. * Release previous if it exists. */ if((u->u_offset % BLKSIZE) == 0) { if(bp != NULL) brelse(bp); bp = bread(dp->i_dev, bmap(dp, u->u_offset/BLKSIZE)); } /* * Note first empty directory slot * in eo for possible creat. * String compare the directory entry * and the current component. * If they do not match, go back to eloop. */ /* Note that u_ino and u_name are not contiguous. This may fairly be regarded as a bug */ bcopy(bp->b_addr+(u->u_offset%BLKSIZE), u->u_dent.u_ino, 2); bcopy(bp->b_addr+(u->u_offset%BLKSIZE)+2, u->u_dent.u_name, DIRSIZ); u->u_offset =+ DIRSIZ+2; u->u_count--; if(ldhalf(u->u_dent.u_ino) == 0) { if(eo == 0) eo = u->u_offset; goto eloop; } for(cp = &u->u_dbuf[0]; cp < &u->u_dbuf[DIRSIZ]; cp++) if(*cp != cp[u->u_dent.u_name - u->u_dbuf]) goto eloop; /* * Here a component matched in a directory. * If there is more pathname, go back to * cloop, otherwise return. */ if(bp != NULL) brelse(bp); if(flag==2 && c=='\0') { if(access(dp, IWRITE)) goto out; return(dp); } bp = dp->i_dev; iput(dp); dp = iget(bp, ldhalf(u->u_dent.u_ino)); if(dp == NULL) return(NULL); goto cloop; out: iput(dp); return(NULL); } /* * Return the next character from the * kernel string pointed at by dirp. */ schar() { return(*u->u_dirp++ & 0377); } /* * Return the next character from the * user string pointed at by dirp. */ uchar() { register c; c = fubyte(u->u_dirp++); if(c == -1) u->u_error = EFAULT; return(c); } { if(access(dp, IWRITE)) goto out; return(dp); } bp = dp->i_dev; iput(dp); dp = iget(bp, ldhalf(u->u_dent.u_ino)); if(dp == NULL) return(NULL); goto cloop; out: iput(dp); return(NULL); } /* * Return the next character from the * kernel string pointed at by dirp. */ schar() { return(*u->u_dirp# /* */ #include "../manifest.h" #include "../param.h" #include "../user.h" #include "../filsys.h" #include "../file.h" #include "../conf.h" #include "../inode.h" #include "../reg.h" int fiokludge; /* for the benefit of the switch bug */ /* * Convert a user supplied * file descriptor into a pointer * to a file structure. * Only task is to check range * of the descriptor. */ getf(f) { register *fp, rf; rf = f; if(rf<0 || rf>=NOFILE) goto bad; fp = u->u_ofile[rf]; if(fp != NULL) return(fp); bad: u->u_error = EBADF; return(NULL); } /* * Internal form of close. * Decrement reference count on * file structure and call closei * on last closef. * Also make sure the pipe protocol * does not constipate. */ closef(fp) int *fp; { register *rfp, *ip; rfp = fp; if(rfp->f_flag&FPIPE) { ip = rfp->f_inode; ip->i_mode =& ~(IREAD|IWRITE); wakeup(ip+1); wakeup(ip+2); } if(rfp->f_count <= 1) closei(rfp->f_inode, rfp->f_flag&FWRITE); rfp->f_count--; } /* * Decrement reference count on an * inode due to the removal of a * referencing file structure. * On the last closei, switchout * to the close entry point of special * device handler. * Note that the handler gets called * on every open and only on the last * close. */ closei(ip, rw) int *ip; { register *rip; register dev, maj; rip = ip; dev = rip->i_addr[0]; maj = rip->i_addr[0].d_major; if(rip->i_count <= 1) switch(rip->i_mode&IFMT) { case IFCHR: (*cdevsw[maj].d_close)(dev, rw); break; case IFBLK: (*bdevsw[maj].d_close)(dev, rw); } iput(rip); } /* * openi called to allow handler * of special files to initialize and * validate before actual IO. * Called on all sorts of opens * and also on mount. */ openi(ip, rw) int *ip; { register *rip; register dev, maj; rip = ip; dev = rip->i_addr[0]; maj = rip->i_addr[0].d_major; switch(rip->i_mode&IFMT) { case IFCHR: if(maj >= nchrdev) goto bad; (*cdevsw[maj].d_open)(dev, rw); break; case IFBLK: if(maj >= nblkdev) goto bad; (*bdevsw[maj].d_open)(dev, rw); } return; bad: u->u_error = ENXIO; } /* * Check mode permission on inode pointer. * Mode is READ, WRITE or EXEC. * In the case of WRITE, the * read-only status of the file * system is checked. * Also in WRITE, prototype text * segments cannot be written. * The mode is shifted to select * the owner/group/other fields. * The super user is granted all * permissions except for EXEC where * at least one of the EXEC bits must * be on. */ access(aip, mode) int *aip; { register *ip, m; ip = aip; m = mode; if(m == IWRITE) { if(getfs(ip->i_dev)->s_ronly != 0) { u->u_error = EROFS; return(1); } if(ip->i_flag & ITEXT) { u->u_error = ETXTBSY; return(1); } } if(u->u_uid == 0) { if(m == IEXEC && (ip->i_mode & (IEXEC | (IEXEC>>3) | (IEXEC>>6))) == 0) goto bad; return(0); } if(u->u_uid != ip->i_uid) { m =>> 3; if(u->u_gid != ip->i_gid) m =>> 3; } if((ip->i_mode&m) != 0) return(0); bad: u->u_error = EACCES; return(1); } /* * Look up a pathname and test if * the resultant inode is owned by the * current user. * If not, try for super-user. * If permission is granted, * return inode pointer. */ owner() { register struct inode *ip; extern uchar(); if ((ip = namei(uchar, 0)) == NULL) return(NULL); if(u->u_uid == ip->i_uid) return(ip); if (suser()) return(ip); iput(ip); return(NULL); } /* * Test if the current user is the * super user. */ suser() { if(u->u_uid == 0) return(1); u->u_error = EPERM; return(0); } /* * Allocate a user file descriptor. */ ufalloc() { register i; for (i=0; iu_ofile[i] == NULL) { u->u_arsp[R0] = i; return(i); } u->u_error = EMFILE; return(-1); } /* * Allocate a user file descriptor * and a file structure. * Initialize the descriptor * to point at the file structure. * * no file -- if there are no available * file structures. */ falloc() { register struct file *fp; register i; if ((i = ufalloc()) < 0) return(NULL); for (fp = &file[0]; fp < &file[NFILE]; fp++) if (fp->f_count==0) { u->u_ofile[i] = fp; fp->f_count++; fp->f_offset = 0; return(fp); } printf("no file\n"); u->u_error = ENFILE; return(NULL); } rn(-1); } /* * Allocate a user file descriptor * and a file structure. * Initialize the descriptor * to point at the file structure. * * no file -- if there are no available * file structures. */ falloc() { register struct file *fp; register i; if ((i = ufalloc()) < 0) return(NULL); for (fp = &file[0# /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../inode.h" #include "../file.h" #include "../reg.h" /* * Max allowable buffering per pipe. * This is also the max size of the * file created to implement the pipe. * If this size is bigger than 4096, * pipes will be implemented in LARG * files, which is probably not good. */ #define PIPSIZ 4096 /* * The sys-pipe entry. * Allocate an inode on the root device. * Allocate 2 file structures. * Put it all together with flags. */ pipe() { register *ip, *rf, *wf; int r; ip = ialloc(rootdev); if(ip == NULL) return; rf = falloc(); if(rf == NULL) { iput(ip); return; } r = u->u_arsp[R0]; wf = falloc(); if(wf == NULL) { rf->f_count = 0; u->u_ofile[r] = NULL; iput(ip); return; } u->u_arsp[R1] = u->u_arsp[R0]; u->u_arsp[R0] = r; wf->f_flag = FWRITE|FPIPE; wf->f_inode = ip; rf->f_flag = FREAD|FPIPE; rf->f_inode = ip; ip->i_count = 2; ip->i_flag = IACC|IUPD; ip->i_mode = IALLOC; } /* * Read call directed to a pipe. */ readp(fp) int *fp; { register *rp, *ip; rp = fp; ip = rp->f_inode; loop: /* * Very conservative locking. */ plock(ip); /* * If the head (read) has caught up with * the tail (write), reset both to 0. */ if(rp->f_offset == ip->i_size) { if(rp->f_offset != 0) { rp->f_offset = 0; ip->i_size = 0; if(ip->i_mode&IWRITE) { ip->i_mode =& ~IWRITE; wakeup(ip+1); } } /* * If there are not both reader and * writer active, return without * satisfying read. */ prele(ip); if(ip->i_count < 2) return; ip->i_mode =| IREAD; sleep(ip+2, PPIPE); goto loop; } /* * Read and return */ u->u_offset = rp->f_offset; readi(ip); rp->f_offset = u->u_offset; prele(ip); } /* * Write call directed to a pipe. */ writep(fp) { register *rp, *ip, c; rp = fp; ip = rp->f_inode; c = u->u_count; loop: /* * If all done, return. */ plock(ip); if(c == 0) { prele(ip); u->u_count = 0; return; } /* * If there are not both read and * write sides of the pipe active, * return error and signal too. */ if(ip->i_count < 2) { prele(ip); u->u_error = EPIPE; psignal(u->u_procp, SIGPIPE); return; } /* * If the pipe is full, * wait for reads to deplete * and truncate it. */ if(ip->i_size == PIPSIZ) { ip->i_mode =| IWRITE; prele(ip); sleep(ip+1, PPIPE); goto loop; } /* * Write what is possible and * loop back. */ u->u_offset = ip->i_size; u->u_count = min(c, PIPSIZ-u->u_offset); c =- u->u_count; writei(ip); prele(ip); if(ip->i_mode&IREAD) { ip->i_mode =& ~IREAD; wakeup(ip+2); } goto loop; } /* * Lock a pipe. * If its already locked, * set the WANT bit and sleep. */ plock(ip) int *ip; { register *rp; rp = ip; while(rp->i_flag&ILOCK) { rp->i_flag =| IWANT; sleep(rp, PPIPE); } rp->i_flag =| ILOCK; } /* * Unlock a pipe. * If WANT bit is on, * wakeup. * This routine is also used * to unlock inodes in general. */ prele(ip) int *ip; { register *rp; rp = ip; rp->i_flag =& ~ILOCK; if(rp->i_flag&IWANT) { rp->i_flag =& ~IWANT; wakeup(rp); } } akeup(ip+2); } goto loop; } /* * Lock a pipe. * If its already locked, * set the WANT bit and sleep. */ plock(ip) int *ip; { register *rp; rp = ip; while(rp->i_flag&ILOCK) { rp->i_flag =| IWANT; sleep(rp, PPIPE); } rp->i_flag =| ILOCK; } /* * Unlock a pipe. * If WANT bit is on, * wakeup. * This routine is also used * to unlock inodes in general. */ prele(ip) int *ip;# /* */ #include "../manifest.h" #include "../param.h" #include "../inode.h" #include "../user.h" #include "../buf.h" #include "../conf.h" #include "../systm.h" /* * Read the file corresponding to * the inode pointed at by the argument. * The actual read arguments are found * in the variables: * u_base core address for destination * u_offset byte offset in file * u_count number of bytes to read * u_segflg read to kernel/user */ readi(aip) struct inode *aip; { int *bp; int lbn, bn, on; register dn, n; register struct inode *ip; ip = aip; if(u->u_count == 0) return; ip->i_flag =| IACC; if((ip->i_mode&IFMT) == IFCHR) { (*cdevsw[ip->i_addr[0].d_major].d_read)(ip->i_addr[0]); return; } do { lbn = bn = u->u_offset>>9; on = u->u_offset & 0777; n = min(512-on, u->u_count); if((ip->i_mode&IFMT) != IFBLK) { dn = ip->i_size - u->u_offset; if(dn <= 0) return; n = min(n, dn); if ((bn = bmap(ip, lbn)) == 0) return; dn = ip->i_dev; } else { dn = ip->i_addr[0]; rablock = bn+1; } if (ip->i_lastr+1 == lbn) bp = breada(dn, bn, rablock); else bp = bread(dn, bn); ip->i_lastr = lbn; iomove(bp, on, n, B_READ); brelse(bp); } while(u->u_error==0 && u->u_count!=0); } /* * Write the file corresponding to * the inode pointed at by the argument. * The actual write arguments are found * in the variables: * u_base core address for source * u_offset byte offset in file * u_count number of bytes to write * u_segflg write to kernel/user */ writei(aip) struct inode *aip; { int *bp; int n, on; register dn, bn; register struct inode *ip; ip = aip; ip->i_flag =| IACC|IUPD; if((ip->i_mode&IFMT) == IFCHR) { (*cdevsw[ip->i_addr[0].d_major].d_write)(ip->i_addr[0]); return; } if (u->u_count == 0) return; do { bn = u->u_offset>>9; on = u->u_offset & 0777; n = min(512-on, u->u_count); if((ip->i_mode&IFMT) != IFBLK) { if ((bn = bmap(ip, bn)) == 0) return; dn = ip->i_dev; } else dn = ip->i_addr[0]; if(n == 512) bp = getblk(dn, bn); else bp = bread(dn, bn); iomove(bp, on, n, B_WRITE); if(u->u_error != 0) brelse(bp); else if ((u->u_offset&0777)==0) bawrite(bp); else bdwrite(bp); if(ip->i_size < u->u_offset && (ip->i_mode&(IFBLK&IFCHR)) == 0) { ip->i_size = u->u_offset; } ip->i_flag =| IUPD; } while(u->u_error==0 && u->u_count!=0); } /* * Move 'an' bytes at byte location * &bp->b_addr[o] to/from (flag) the * user/kernel (u.segflg) area starting at u.base. * Update all the arguments by the number * of bytes moved. */ iomove(bp, o, an, flag) struct buf *bp; { register char *cp; register int n, t; n = an; cp = bp->b_addr + o; if(u->u_segflg==0) { if (flag==B_WRITE) cp = copyin(u->u_base, cp, n); else cp = copyout(cp, u->u_base, n); if (cp) { u->u_error = EFAULT; return; } u->u_base =+ n; u->u_offset =+ n; u->u_count =- n; } else { if (flag==B_WRITE) { bcopy(u->u_base, cp, n); } else { bcopy(cp, u->u_base, n); } u->u_base =+ n; u->u_offset =+ n; u->u_count =- n; } } */ iomove(bp, o, an, flag) struct buf *bp; { register char *cp; register int n, t; n = an; cp = bp->b_addr + o; if(u->u_segflg==0) { if (flag==B_WRITE) cp = copyin(u->u_base, cp, n); else cp = copyout(cp, u->u_base, n); if (cp) { u->u_error = EFAULT; return; } u->u_base =+ n; u->u_offset =+ n; u->u_count =- n; } else { if (flag==B_WRITE) { bcopy(u->u_base, cp, n); } else { bcopy(cp, u->u_base, n); } u->u_base =+ n; u->u_offs# #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../reg.h" #include "../file.h" #include "../inode.h" #include "../370.h" /* * read system call */ read() { rdwr(FREAD); } /* * write system call */ write() { rdwr(FWRITE); } /* * common code for read and write calls: * check permissions, set base, count, and offset, * and switch out to readi, writei, or pipe code. */ rdwr(mode) { register *fp, m; m = mode; fp = getf(u->u_arsp[R0]); if(fp == NULL) return; if((fp->f_flag&m) == 0) { u->u_error = EBADF; return; } u->u_base = u->u_arg[0]; u->u_count = u->u_arg[1]; u->u_segflg = 0; if(fp->f_flag&FPIPE) { if(m==FREAD) readp(fp); else writep(fp); } else { u->u_offset = fp->f_offset; if(m==FREAD) readi(fp->f_inode); else writei(fp->f_inode); fp->f_offset =+ u->u_arg[1] - u->u_count; } u->u_arsp[R0] = u->u_arg[1]-u->u_count; } /* * open system call */ open() { register *ip; extern uchar; ip = namei(&uchar, 0); if(ip == NULL) return; u->u_arg[1]++; open1(ip, u->u_arg[1], 0); } /* * creat system call */ creat() { register *ip; extern uchar; ip = namei(&uchar, 1); if(ip == NULL) { if(u->u_error) return; ip = maknode(u->u_arg[1]&07777&(~ISVTX)); if (ip==NULL) return; open1(ip, FWRITE, 2); } else open1(ip, FWRITE, 1); } /* * common code for open and creat. * Check permissions, allocate an open file structure, * and call the device open routine if any. */ open1(ip, mode, trf) int *ip; { register struct file *fp; register *rip, m; int i; rip = ip; m = mode; if(trf != 2) { if(m&FREAD) access(rip, IREAD); if(m&FWRITE) { access(rip, IWRITE); if((rip->i_mode&IFMT) == IFDIR) u->u_error = EISDIR; } } if(u->u_error) goto out; if(trf) itrunc(rip); prele(rip); if ((fp = falloc()) == NULL) goto out; fp->f_flag = m&(FREAD|FWRITE); fp->f_inode = rip; i = u->u_arsp[R0]; openi(rip, m&FWRITE); if(u->u_error == 0) return; u->u_ofile[i] = NULL; fp->f_count--; out: iput(rip); } /* * close system call */ close() { register *fp; fp = getf(u->u_arsp[R0]); if(fp == NULL) return; u->u_ofile[u->u_arsp[R0]] = NULL; closef(fp); } /* * seek system call */ seek() { register *fp, t, n; fp = getf(u->u_arsp[R0]); if(fp == NULL) return; if(fp->f_flag&FPIPE) { u->u_error = ESPIPE; return; } t = u->u_arg[1]; n = u->u_arg[0]; if(t>2) { n =<< 9; } switch(t) { case 1: case 4: fp->f_offset =+ n; break; case 0: case 3: fp->f_offset = n; break; default: fp->f_offset = n + fp->f_inode->i_size; } } /* * link system call */ link() { register *ip, *xp; extern uchar; ip = namei(&uchar, 0); if(ip == NULL) return; if(ip->i_nlink >= 127) { u->u_error = EMLINK; goto out; } if((ip->i_mode&IFMT)==IFDIR && !suser()) goto out; /* * unlock to avoid possibly hanging the namei */ ip->i_flag =& ~ILOCK; u->u_dirp = u->u_arg[1]; xp = namei(&uchar, 1); if(xp != NULL) { u->u_error = EEXIST; iput(xp); } if(u->u_error) goto out; if(u->u_pdir->i_dev != ip->i_dev) { iput(u->u_pdir); u->u_error = EXDEV; goto out; } wdir(ip); ip->i_nlink++; ip->i_flag =| IUPD; out: iput(ip); } /* * mknod system call */ mknod() { register *ip; extern uchar; if(suser()) { ip = namei(&uchar, 1); if(ip != NULL) { u->u_error = EEXIST; goto out; } } if(u->u_error) return; ip = maknode(u->u_arg[1]); if (ip==NULL) return; ip->i_addr[0] = u->u_arg[2]; out: iput(ip); } /* * sleep system call * not to be confused with the sleep internal routine. */ sslep() { int d; stnsm(~ALLINT); d = time + u->u_arsp[R0]; while(d > time) { if(tout <= time || tout > d) tout = d; sleep(&tout, PSLEP); } stosm(ALLINT); } ster *ip; extern uchar; if(suser()) { ip = namei(&uchar, 1); if(ip != NULL) { u->u_error = EEXIST; goto out; } } if(u->u_error) return; ip = maknode(u->u_arg[1]); if (ip==NULL) return; ip->i_addr[0] = u->u_arg[2]; out: iput(ip); } /* * sleep system call * not to be co/* */ /* * This table is the switch used to transfer * to the appropriate routine for processing a system call. * Each row contains the number of arguments expected * and a pointer to the routine. */ int sysent[] { 0, &nullsys, /* 0 = indir */ 0, &rexit, /* 1 = exit */ 0, &fork, /* 2 = fork */ 2, &read, /* 3 = read */ 2, &write, /* 4 = write */ 2, &open, /* 5 = open */ 0, &close, /* 6 = close */ 0, &wait, /* 7 = wait */ 2, &creat, /* 8 = creat */ 2, &link, /* 9 = link */ 1, &unlink, /* 10 = unlink */ 2, &exec, /* 11 = exec */ 1, &chdir, /* 12 = chdir */ 0, >ime, /* 13 = time */ 3, &mknod, /* 14 = mknod */ 2, &chmod, /* 15 = chmod */ 2, &chown, /* 16 = chown */ 1, &sbreak, /* 17 = break */ 2, &stat, /* 18 = stat */ 2, &seek, /* 19 = seek */ 0, &getpid, /* 20 = getpid */ 3, &smount, /* 21 = mount */ 1, &sumount, /* 22 = umount */ 0, &setuid, /* 23 = setuid */ 0, &getuid, /* 24 = getuid */ 0, &stime, /* 25 = stime */ 3, &ptrace, /* 26 = ptrace */ 0, &nosys, /* 27 = x */ 1, &fstat, /* 28 = fstat */ 0, &nosys, /* 29 = x */ 1, &nullsys, /* 30 = smdate; inoperative */ 1, &stty, /* 31 = stty */ 1, >ty, /* 32 = gtty */ 2, &saccess, /* 33 = access */ 0, &nice, /* 34 = nice */ 0, &sslep, /* 35 = sleep */ 0, &sync, /* 36 = sync */ 1, &kill, /* 37 = kill */ 0, &getswit, /* 38 = switch */ 0, &nosys, /* 39 = x */ 0, &nosys, /* 40 = x */ 0, &dup, /* 41 = dup */ 0, &pipe, /* 42 = pipe */ 1, ×, /* 43 = times */ 4, &profil, /* 44 = prof */ 0, &nosys, /* 45 = tiu */ 0, &setgid, /* 46 = setgid */ 0, &getgid, /* 47 = getgid */ 2, &ssig, /* 48 = sig */ 0, &rsig, /* 49 = rsig */ 0, &nosys, /* 50 = x */ 0, &nosys, /* 51 = x */ 0, &nosys, /* 52 = x */ 0, &nosys, /* 53 = x */ 0, &nosys, /* 54 = x */ 0, &nosys, /* 55 = x */ 0, &nosys, /* 56 = x */ 0, &nosys, /* 57 = x */ 0, &nosys, /* 58 = x */ 0, &nosys, /* 59 = x */ 0, &nosys, /* 60 = x */ 0, &nosys, /* 61 = x */ 0, &nosys, /* 62 = x */ 1, &cpcmnd, /* 63 = cp command */ }; , &setgid, /* 46 = setgid */ 0, &getgid, /* 47 = getgid */ 2, &ssig, /* 48 = sig */ 0, &rsig, /* 49 = rsig */ 0, &nosys, /* 50 = x */ 0, &nosys, /* 51 = x */ 0, &nosys, /* 52 = x */ 0, &nosys, /* 53 = x */ 0, &nosys, /* 54 = x */ 0, &nosys, /* 55 = x */ 0, &nosys, /* 56 = x */ 0, &nosys, /* 57 = x */ 0, &nosys, /* 58 = x */ 0, &nosys, /* 59 = x */ 0, &nosys, /* 60 # /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../reg.h" #include "../buf.h" #include "../filsys.h" #include "../user.h" #include "../inode.h" #include "../file.h" #include "../conf.h" /* * the fstat system call. */ fstat() { register *fp; fp = getf(u->u_arsp[R0]); if(fp == NULL) return; stat1(fp->f_inode, u->u_arg[0]); } /* * the stat system call. */ stat() { register ip; extern uchar; ip = namei(&uchar, 0); if(ip == NULL) return; stat1(ip, u->u_arg[1]); iput(ip); } /* * The basic routine for fstat and stat: * get the inode and pass appropriate parts back. */ stat1(ip, ub) int *ip; { register i, *bp, *cp; iupdat(ip, time); bp = bread(ip->i_dev, (ip->i_number+IOFFSET) / IPERBLK); cp = bp->b_addr + ISIZE*((ip->i_number+IOFFSET) % IPERBLK) + (&0->i_lastr); ip = &(ip->i_dev); for(i=0; i<13; i++) { suword(ub, *ip++); ub =+ WORDSIZE; } for(i=0; i<2; i++) { suword(ub, *cp++); ub =+ WORDSIZE; } brelse(bp); } /* * the dup system call. */ dup() { register i, *fp; fp = getf(u->u_arsp[R0]); if(fp == NULL) return; if ((i = ufalloc()) < 0) return; u->u_ofile[i] = fp; fp->f_count++; } /* * the mount system call. */ smount() { int d; register *ip; register struct mount *mp, *smp; extern uchar; if(!suser()) return; d = getmdev(); if(u->u_error) return; u->u_dirp = u->u_arg[1]; ip = namei(&uchar, 0); if(ip == NULL) return; if(ip->i_count!=1 || (ip->i_mode&(IFBLK&IFCHR))!=0) goto out; smp = NULL; for(mp = &mount[0]; mp < &mount[NMOUNT]; mp++) { if(mp->m_bufp != NULL) { if(d == mp->m_dev) goto out; } else if(smp == NULL) smp = mp; } if(smp == NULL) goto out; (*bdevsw[d.d_major].d_open)(d, !u->u_arg[2]); if(u->u_error) goto out; mp = bread(d, 1); if(u->u_error) { brelse(mp); goto out1; } smp->m_inodp = ip; smp->m_dev = d; smp->m_bufp = getblk(NODEV); bcopy(mp->b_addr, smp->m_bufp->b_addr, BLKSIZE); smp = smp->m_bufp->b_addr; smp->s_ilock = 0; smp->s_flock = 0; smp->s_ronly = u->u_arg[2] & 1; brelse(mp); ip->i_flag =| IMOUNT; prele(ip); return; out: u->u_error = EBUSY; out1: iput(ip); } /* * the umount system call. */ sumount() { int d; register struct inode *ip; register struct mount *mp; update(); if(!suser()) return; d = getmdev(); if(u->u_error) return; for(mp = &mount[0]; mp < &mount[NMOUNT]; mp++) if(mp->m_bufp!=NULL && d==mp->m_dev) goto found; u->u_error = EINVAL; return; found: for(ip = &inode[0]; ip < &inode[NINODE]; ip++) if(ip->i_number!=0 && d==ip->i_dev) { u->u_error = EBUSY; return; } (*bdevsw[d.d_major].d_close)(d, 0); ip = mp->m_inodp; ip->i_flag =& ~IMOUNT; iput(ip); ip = mp->m_bufp; mp->m_bufp = NULL; brelse(ip); } /* * Common code for mount and umount. * Check that the user's argument is a reasonable * thing on which to mount, and return the device number if so. */ getmdev() { register d, *ip; extern uchar; ip = namei(&uchar, 0); if(ip == NULL) return; if((ip->i_mode&IFMT) != IFBLK) u->u_error = ENOTBLK; d = ip->i_addr[0]; if(ip->i_addr[0].d_major >= nblkdev) u->u_error = ENXIO; iput(ip); return(d); } saccess() { extern uchar; register *ip; register suid, sgid; suid = u->u_uid; sgid = u->u_gid; u->u_uid = u->u_ruid; u->u_gid = u->u_rgid; ip = namei(&uchar,0); if(ip != NULL) { if(u->u_arg[1]&(IREAD>>6)) access(ip,IREAD); if(u->u_arg[1]&(IWRITE>>6)) access(ip,IWRITE); if(u->u_arg[1]&(IEXEC>>6)) access(ip,IEXEC); iput(ip); } u->u_uid = suid; u->u_gid = sgid; } rror = E# /* */ #include "../manifest.h" #include "../param.h" #include "../conf.h" #include "../inode.h" #include "../user.h" #include "../buf.h" #include "../systm.h" #include "../370.h" /* * Bmap defines the structure of file system storage * by returning the physical block number on a device given the * inode and the logical block number in a file. * When convenient, it also leaves the physical * block number of the next block of the file in rablock * for use in read-ahead. */ bmap(ip, bn) struct inode *ip; int bn; { register *bp, *bap, nb; int *nbp, d, i; d = ip->i_dev; if(bn & ~077777) { u->u_error = EFBIG; return(0); } if((ip->i_mode&ILARG) == 0) { /* * small file algorithm */ if((bn & ~7) != 0) { /* * convert small to large */ if ((bp = alloc(d)) == NULL) return(NULL); bap = bp->b_addr; for(i=0; i<8; i++) { *bap++ = ip->i_addr[i]; ip->i_addr[i] = 0; } ip->i_addr[0] = bp->b_blkno; bdwrite(bp); ip->i_mode =| ILARG; goto large; } nb = ip->i_addr[bn]; if(nb == 0 && (bp = alloc(d)) != NULL) { bdwrite(bp); nb = bp->b_blkno; ip->i_addr[bn] = nb; ip->i_flag =| IUPD; } rablock = 0; if (bn<7) rablock = ip->i_addr[bn+1]; return(nb); } /* * large file algorithm */ large: i = bn>>8; if(bn & 0174000) i = 7; if((nb=ip->i_addr[i]) == 0) { ip->i_flag =| IUPD; if ((bp = alloc(d)) == NULL) return(NULL); ip->i_addr[i] = bp->b_blkno; } else bp = bread(d, nb); bap = bp->b_addr; /* * "huge" fetch of double indirect block */ if(i == 7) { i = ((bn>>8) & 0377) - 7; if((nb=bap[i]) == 0) { if((nbp = alloc(d)) == NULL) { brelse(bp); return(NULL); } bap[i] = nbp->b_blkno; bdwrite(bp); } else { brelse(bp); nbp = bread(d, nb); } bp = nbp; bap = bp->b_addr; } /* * normal indirect fetch */ i = bn & 0377; if((nb=bap[i]) == 0 && (nbp = alloc(d)) != NULL) { nb = nbp->b_blkno; bap[i] = nb; bdwrite(nbp); bdwrite(bp); } else brelse(bp); rablock = 0; if(i < 255) rablock = bap[i+1]; return(nb); } /* * Pass back c to the user at his location u_base; * update u_base, u_count, and u_offset. Return -1 * on the last character of the user's read. * u_base is in the user address space unless u_segflg is set. */ passc(c) char c; { if(u->u_segflg) *u->u_base = c; else if(subyte(u->u_base, c) < 0) { u->u_error = EFAULT; return(-1); } u->u_count--; u->u_offset++; u->u_base++; return(u->u_count == 0? -1: 0); } /* * Pick up and return the next character from the user's * write call at location u_base; * update u_base, u_count, and u_offset. Return -1 * when u_count is exhausted. u_base is in the user's * address space unless u_segflg is set. */ cpass() { register c; if(u->u_count == 0) return(-1); if(u->u_segflg) c = *u->u_base; else if((c=fubyte(u->u_base)) < 0) { u->u_error = EFAULT; return(-1); } u->u_count--; u->u_offset++; u->u_base++; return(c&0377); } /* * Routine which sets a user error; placed in * illegal entries in the bdevsw and cdevsw tables. */ nodev() { u->u_error = ENODEV; } /* * Null routine; placed in insignificant entries * in the bdevsw and cdevsw tables. */ nulldev() { } /* * modifies addresses saved on stack (saved r12 and r13) * when stack is moved. This makes the assumption that the * stack has ben dropped only 4 bytes for the argument */ fixstak(newu) int newu; { int diff, *p, top; diff = newu - u.integ; newu->u_arsp =+ diff >> 2; newu->u_orega =+ diff >> 2; top = newu->u_orega; p =(&newu)+1; p =+ diff >> 2; while(1) { *(p+13) =+ diff; if((*(p+12) =+ diff) >= top) break; p = *(p+12); }; *(newu->u_orega + 13) =+ diff; newu->u_rsav[0] =+ diff; newu->u_rsav[1] =+ diff; } s saved on stack (saved r12 and r13) * when stack is moved. This makes the assumption that the * stack has ben dropped only 4 bytes for the argument */ fixstak(newu) int newu; { int diff, *p, top; diff = newu - u.integ; newu->u_arsp =+ diff >> 2; newu->u_orega =+ diff >> 2; # /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../proc.h" #include "../buf.h" #include "../reg.h" #include "../inode.h" #include "../370.h" #define MAGIC1 #47f00020 #define MAGIC2 #47f00024 #define HDRLEN 32 /* * exec system call. * Because of the fact that an I/O buffer is used * to store the caller's arguments during exec, * and more buffers are needed to read in the text file, * deadly embraces waiting for free buffers are possible. * Therefore the number of processes simultaneously * running in exec has to be limited to NEXEC. */ #define EXPRI -1 exec() { int ap, na, nc, *bp; int ts, ds, sep; int c, *ip; char *cp; extern uchar; /* * pick up file names * and check various modes * for execute permission */ ip = namei(&uchar, 0); if(ip == NULL) return; while(execnt >= NEXEC) sleep(&execnt, EXPRI); execnt++; bp = getblk(NODEV); if(access(ip, IEXEC) || (ip->i_mode&IFMT)!=0) goto bad; /* * pack up arguments into * allocated disk buffer */ cp = bp->b_addr; na = 0; nc = 0; while(ap = fuword(u->u_arg[1])) { na++; if(ap == -1) goto bad; u->u_arg[1] =+ 4; for(;;) { c = fubyte(ap++); if(c == -1) goto bad; *cp++ = c; nc++; if(nc > BLKSIZE - WORDSIZE) { u->u_error = E2BIG; goto bad; } if(c == 0) break; } } if((nc&1) != 0) { *cp++ = 0; nc++; } /* * read in first 16 bytes * of file for segment * sizes: * w0 = magic number (MAGIC1 is normal) ( MAGIC2 is RO text) * w1 = text size * w2 = data size * w3 = bss size */ u->u_base = &u->u_arg[0]; u->u_count = 16; u->u_offset = 0; u->u_segflg = 1; readi(ip); u->u_segflg = 0; if(u->u_error) goto bad; sep = 0; if(u->u_arg[0] == MAGIC1) { u->u_arg[2] =+ u->u_arg[1]; u->u_arg[1] = 0; } else if(u->u_arg[0] != MAGIC2) { u->u_error = ENOEXEC; goto bad; } if(u->u_arg[1]!=0 && (ip->i_flag&ITEXT)==0 && ip->i_count!=1) { u->u_error = ETXTBSY; goto bad; } /* * find text and data sizes * try them out for possible * exceed of max sizes */ ts = ((u->u_arg[1]+PGSIZE-1)/PGSIZE) & PGMASK; ds = ((u->u_arg[3]+u->u_arg[2]+PGSIZE-1)/PGSIZE) & PGMASK; if(ts+ds+SSIZE > maxmem) goto bad; /* * allocate and clear core * at this point, committed * to the new image */ u->u_prof[3] = 0; xfree(); xalloc(ip); estabur(ds,SSIZE); protect(u->u_dpages,1); protect(u->u_spages,1); sureg(); /* * read in data segment */ u->u_base = 0; u->u_offset = HDRLEN + u->u_arg[1]; u->u_count = u->u_arg[2]; readi(ip); /* * initialize stack segment */ u->u_tsize = ts; u->u_dsize = ds; u->u_ssize = SSIZE; cp = bp->b_addr; ap = (-(nc+na*4+8)) & ~3; u->u_arsp[R13] = ap; suword(ap, na); c = -nc; while(na--) { suword(ap=+4, c); do subyte(c++, *cp); while(*cp++); } suword(ap+4, -1); /* * set SUID/SGID protections, if no tracing */ if ((u->u_procp->p_flag&STRC)==0) { if(ip->i_mode&ISUID) if(u->u_uid != 0) { u->u_uid = ip->i_uid; u->u_procp->p_uid = ip->i_uid; } if(ip->i_mode&ISGID) u->u_gid = ip->i_gid; } /* * clear sigs, regs and return */ c = ip; for(ip = &u->u_signal[0]; ip < &u->u_signal[NSIG]; ip++) if((*ip & 1) == 0) *ip = 0; for(ip=u->u_orega+2;ipu_orega+13;ip++) *ip = 0; u->u_arsp[R0]=u->u_arsp[R1]=u->u_arsp[R14]=u->u_arsp[R15] = 0; for(ip = &u->u_fsav[0]; ip < &u->u_fsav[8]; ip++) *ip = 0; u->u_arsp[RPC] = 0; ip = c; bad: iput(ip); brelse(bp); if(execnt >= NEXEC) wakeup(&execnt); execnt--; } /* * exit system call: * pass back caller's r0 */ rexit() { u->u_arg[0] = u->u_arsp[R0] << 8; exit(); } /* * Release resources. * Save u. area for parent to look at. * Enter zombie state. * Wake up parent and init processes, * and dispose of children. */ exit() { register int *q, a; register struct proc *p; u->u_procp->p_flag =& ~STRC; for(q = &u->u_signal[0]; q < &u->u_signal[NSIG];) *q++ = 1; for(q = &u->u_ofile[0]; q < &u->u_ofile[NOFILE]; q++) if(a = *q) { *q = NULL; closef(a); } iput(u->u_cdir); xfree(); q = u->u_procp; q->p_utime =+ u->u_cutime; q->p_textp = u->u_arg[0]; freesgs(u->u_dpages); freesgs(u->u_spages); q->p_addr = u->u_cstime; q->p_stat = SZOMB; loop: for(p = &proc[0]; p < &proc[NPROC]; p++) if(q->p_ppid == p->p_pid) { wakeup(&proc[1]); wakeup(p); for(p = &proc[0]; p < &proc[NPROC]; p++) if(q->p_pid == p->p_ppid) { p->p_ppid = 1; if (p->p_stat == SSTOP) setrun(p); } swtch(); /* no return */ } q->p_ppid = 1; goto loop; } /* * Wait system call. * Search for a terminated (zombie) child, * finally lay it to rest, and collect its status. * Look also for stopped (traced) children, * and pass back status from them. */ wait() { register f, *bp; register struct proc *p; f = 0; loop: for(p = &proc[0]; p < &proc[NPROC]; p++) if(p->p_ppid == u->u_procp->p_pid) { f++; if(p->p_stat == SZOMB) { u->u_arsp[R0] = p->p_pid; u->u_arsp[R1] = p->p_textp; p->p_textp = 0; u->u_cstime =+ p->p_addr; u->u_cutime =+ p->p_utime; p->p_stat = NULL; p->p_pid = 0; p->p_ppid = 0; p->p_sig = 0; p->p_ttyp = 0; p->p_flag = 0; return; } if(p->p_stat == SSTOP) { if((p->p_flag&SWTED) == 0) { p->p_flag =| SWTED; u->u_arsp[R0] = p->p_pid; u->u_arsp[R1] = (p->p_sig<<8) | 0177; return; } p->p_flag =& ~(STRC|SWTED); setrun(p); } } if(f) { sleep(u->u_procp, PWAIT); goto loop; } u->u_error = ECHILD; } /* * fork system call. */ fork() { register struct proc *p1, *p2; p1 = u->u_procp; for(p2 = &proc[0]; p2 < &proc[NPROC]; p2++) if(p2->p_stat == NULL) goto found; u->u_error = EAGAIN; goto out; found: if(newproc()) { u->u_arsp[R0] = p1->p_pid; u->u_cstime = 0; u->u_stime = 0; u->u_cutime = 0; return; } u->u_arsp[R0] = p2->p_pid; out: u->u_arsp[RPC] =+ 4; } /* * break system call. * -- bad planning: "break" is a dirty word in C. */ sbreak() { register n, d; /* * set n to new data size * set d to new minus old */ n = ((u->u_arg[0]+PGSIZE-1)/PGSIZE) & PGMASK; n =- u->u_tsize; d = n - u->u_dsize; sbreak1(d); /* call asm routine to do the dirty work */ sureg(); } ; found: if(newproc()) { u->u_arsp[R0] = p1->p_pid; u->u_cstime = 0; u->u_stime = 0; u->u_cutime = 0; return; } u->u_arsp[R0] = p2->p_pid; out: u->u_arsp[RPC] =+ 4; } /* * break system call. * -- bad planning: "break" is a dirty word in C. */ sbreak() { register n, d; /* * set n to new data size * set d to new minus old */ n = ((u->u_arg[0]+PGSIZE-1)/PGS# #include "../manifest.h" #include "../param.h" #include "../conf.h" /* * In case console is off, * panicstr contains argument to last * call to panic. */ char *panicstr; char prbuf[32]; /* Buffer for number conversion */ char prch[] "0123456789abcdef"; /* * Scaled down version of C Library printf. * Only %s %l %d (==%l) %o and %x are recognized. * Used to print diagnostic information * directly on console tty. * Since it is not interrupt driven, * all system activities are pretty much * suspended. * Printf should not be used for chit-chat. */ printf(fmt,x1,x2,x3,x4,x5,x6,x7,x8,x9,xa,xb,xc) char fmt[]; { register char *s; register *adx, c; adx = &x1; loop: while((c = *fmt++) != '%') { if(c == '\0') return; putchar(c); } c = *fmt++; if(c == 'd' || c == 'l') printn(*adx); if(c == 'o' || c == 'x'){ printl(*adx, c == 'o' ? 8 : 16); } if(c == 's') { s = *adx; while(c = *s++) putchar(c); } adx++; goto loop; } /* * Print an unsigned integer in base b. * b must be a power of 2. */ printl(n, b) { int m; char *p; p = prbuf; m = b == 8 ? 11 : 8; while(m--){ *p++ = prch[n & (b-1)]; n =/ b; } while(--p >= prbuf) putchar(*p); } /* * Print a decimal integer. */ printn(n) { char *p; p = prbuf; if(n < 0){ n = -n; putchar('-'); } while(n){ *p++ = prch[n % 10]; n = n / 10; } if(p == prbuf) putchar('0'); else while(--p >= prbuf) putchar(*p); } /* * Panic is called on unresolvable * fatal errors. * It syncs, prints "panic: mesg" and * then loops. */ panic(s) char *s; { panicstr = s; update(); printf("panic: %s\n", s); } /* * prdev prints a warning message of the * form "mesg on dev x/y". * x and y are the major and minor parts of * the device argument. */ prdev(str, dev) { printf("%s on dev %l/%l\n", str, dev.d_major, dev.d_minor); } /* * deverr prints a diagnostic from * a device driver. * It prints the device, block number, * and an octal word (usually some error * status register) passed as argument. */ /* deverror(bp, o1, o2) int *bp; { register *rbp; rbp = bp; prdev("err", rbp->b_dev); printf("bn%l er%o %o\n", rbp->b_blkno, o1, o2); } e * form "mesg on dev x/y". * x and y are the major and minor parts of * the device argument. */ prdev(str, dev) { printf("%s on dev %l/%l\n", str, dev.d_major, dev.d_minor); } /* * deverr prints a diagnostic from * a device driver. * It prints the device, block number, * and an octal word (usually some error * status register) passed as argument. */ /* deve* assemble routine used by ipl loadit csect using *,15 lpsw npsw npsw ds 0d dc x'00000000',a(kz) kz sr 0,0 l 1,4(,13) length l 2,0(,13) from address lr 3,1 or 3,2 mvc 0(16,3),inst lr 15,3 lr 3,1 sr 4,4 bctr 4,0 minus one lr 5,4 lr 6,4 fill all register with lr 7,4 this garbage lr 8,4 lr 9,4 lr 10,4 lr 11,4 lr 12,4 lr 13,4 lr 14,4 br 15 go and execute mvcl * inst mvcl 0,2 lr 15,4 fill remaining regs lr 0,4 lr 1,4 lr 2,4 lr 3,4 lpsw 0 go!! end ssed as argument. */ /* deve# /* */ /* * Everything in this file is a routine implementing a system call. */ #include "../manifest.h" #include "../param.h" #include "../user.h" #include "../reg.h" #include "../inode.h" #include "../systm.h" #include "../proc.h" #include "../370.h" getswit() { /* u->u_ar0[R0] = SW->integ; */ } gtime() { u->u_arsp[R0] = time; } stime() { if(suser()) { time = u->u_arsp[R0]; wakeup(tout); } } setuid() { register uid; uid = u->u_arsp[R0].lobyte; if(u->u_ruid == uid.lobyte || suser()) { u->u_uid = uid; u->u_procp->p_uid = uid; u->u_ruid = uid; } } getuid() { u->u_arsp[R0].lobyte = u->u_ruid; u->u_arsp[R0].hibyte = u->u_uid; } setgid() { register gid; gid = u->u_arsp[R0].lobyte; if(u->u_rgid == gid.lobyte || suser()) { u->u_gid = gid; u->u_rgid = gid; } } getgid() { u->u_arsp[R0].lobyte = u->u_rgid; u->u_arsp[R0].hibyte = u->u_gid; } getpid() { u->u_arsp[R0] = u->u_procp->p_pid; } sync() { update(); } nice() { register n; n = u->u_arsp[R0]; if(n > 20) n = 20; if(n < 0 && !suser()) n = 0; u->u_procp->p_nice = n; } /* * Unlink system call. * panic: unlink -- "cannot happen" */ unlink() { register *ip, *pp; extern uchar; pp = namei(&uchar, 2); if(pp == NULL) return; prele(pp); ip = iget(pp->i_dev, ldhalf(u->u_dent.u_ino)); if(ip == NULL) panic("unlink -- iget"); if((ip->i_mode&IFMT)==IFDIR && !suser()) goto out; u->u_offset =- DIRSIZ+2; u->u_base = &u->u_dent; u->u_count = DIRSIZ+2; sthalf(u->u_dent.u_ino, 0); writei(pp); ip->i_nlink--; ip->i_flag =| IUPD; out: iput(pp); iput(ip); } chdir() { register *ip; extern uchar; ip = namei(&uchar, 0); if(ip == NULL) return; if((ip->i_mode&IFMT) != IFDIR) { u->u_error = ENOTDIR; bad: iput(ip); return; } if(access(ip, IEXEC)) goto bad; iput(u->u_cdir); u->u_cdir = ip; prele(ip); } chmod() { register *ip; if ((ip = owner()) == NULL) return; ip->i_mode =& ~07777; if (u->u_uid) u->u_arg[1] =& ~ISVTX; ip->i_mode =| u->u_arg[1]&07777; ip->i_flag =| IUPD; iput(ip); } chown() { register *ip; if (!suser() || (ip = owner()) == NULL) return; ip->i_uid = u->u_arg[1].lobyte; ip->i_gid = u->u_arg[1].hibyte; ip->i_flag =| IUPD; iput(ip); } /* * Change modified date of file: * time to r0-r1; sys smdate; file * This call has been withdrawn because it messes up * incremental dumps (pseudo-old files aren't dumped). * It works though and you can uncomment it if you like. smdate() { register struct inode *ip; register int *tp; int tbuf[2]; if ((ip = owner()) == NULL) return; ip->i_flag =| IUPD; tp = &tbuf[2]; *--tp = u->u_ar0[R1]; *--tp = u->u_ar0[R0]; iupdat(ip, tp); ip->i_flag =& ~IUPD; iput(ip); } */ /* * Setup to catch a signal. */ ssig() { register a,p; a = u->u_arg[0]; if(a<=0 || a>=NSIG || a ==SIGKIL) { u->u_error = EINVAL; return; } u->u_arsp[R0] = u->u_signal[a]; /* * Clear high order byte of signal routine addr, * a non-zero value would give an invalid psw * when signal occured. */ u->u_signal[a] = u->u_arg[1] & PCMASK; if(u->u_procp->p_sig == a) u->u_procp->p_sig = 0; } /* * Return from signal. * Pops pc, ps, and r15 from user stack, * so process can restore last register * and branch at the same time. */ rsig() { int *sp; sp = u->u_arsp[R13]; u->u_arsp[RPC] = fuword(sp++) & PCMASK; u->u_arsp[RPS] =& ~PSMASK; u->u_arsp[RPS] =| (fuword(sp++) & PSMASK); u->u_arsp[R15] = fuword(sp); u->u_arsp[R13] =+ 12; } kill() { register struct proc *p, *q; register a; int f; f = 0; a = u->u_arsp[R0]; q = u->u_procp; for(p = &proc[0]; p < &proc[NPROC]; p++) { if(p == q) continue; if(a != 0 && p->p_pid != a) continue; if(a == 0 && (p->p_ttyp != q->p_ttyp || p <= &proc[1])) continue; if(u->u_uid != 0 && u->u_uid != p->p_uid) continue; f++; psignal(p, u->u_arg[0]); } if(f == 0) u->u_error = ESRCH; } times() { register *p; u->u_utime = u->u_procp->p_utime; for(p = &u->u_utime; p < &u->u_utime+4; u->u_arg[0] =+ WORDSIZE) { suword(u->u_arg[0], *p++); } } profil() { u->u_prof[0] = u->u_arg[0] & ~1; /* base of sample buf */ u->u_prof[1] = u->u_arg[1]; /* size of same */ u->u_prof[2] = u->u_arg[2]; /* pc offset */ u->u_prof[3] = (u->u_arg[3]>>1) & 017777777777; /* pc scale */ } /* * Issue a CP command. */ cpcmnd() { char *cp,*ap; char cpbuf[150]; cp = cpbuf; ap = u->u_arg[0]; if(suser()) { while(*cp++ = fubyte(ap++)); cpcmd(cpbuf); } } e; for(p = &u->u_utime; p < &u->u_utime+4; u->u_arg[0] =+ WORDSIZE) { suword(u->u_arg[0], *p++); } } profil() { u-# #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../proc.h" #include "../370.h" #define SCHMAG 10 char clokwork 0; /* * clock is called straight from * the real time clock interrupt. * * Functions: * reprime clock * implement callouts * maintain user/system times * maintain date * profile * tout wakeup (sys sleep) * lightning bolt wakeup (every 4 sec) * alarm clock signals */ clock(rsp,r14,r15,r0,r1,ps,pc) { register struct callo *p1, *p2; register struct proc *pp; int s; /* * restart clock */ clkstrt(); /* * callouts * if none, just return * else update first non-zero time */ if(callout[0].c_func == 0) goto out; p2 = &callout[0]; while(p2->c_time<=0 && p2->c_func!=0) p2++; p2->c_time--; /* * if ps is high, just return */ if((ps&ALLINT) != ALLINT) goto out; /* * callout */ s = stnsm(~IOINT); if(callout[0].c_time <= 0) { p1 = &callout[0]; while(p1->c_func != 0 && p1->c_time <= 0) { (*p1->c_func)(p1->c_arg); p1++; } p2 = &callout[0]; while(p2->c_func = p1->c_func) { p2->c_time = p1->c_time; p2->c_arg = p1->c_arg; p1++; p2++; } } /* * lightning bolt time-out * and time of day */ out: pp = u->u_procp; if((ps&UMODE) == UMODE) { ++pp->p_utime; if(u->u_prof[3]) incupc(pc, u->u_prof); } else u->u_stime++; if(++pp->p_cpu == 0) pp->p_cpu--; if(++lbolt >= HZ) { if(clokwork++ | ((ps&ALLINT) != ALLINT)) return; lbolt =- HZ; /* ++time; */ /* now done in clkstrt */ if(time==tout) wakeup(&tout); if((time&03) == 0) { runrun++; wakeup(&lbolt); } for(pp = &proc[0]; pp < &proc[NPROC]; pp++) if (pp->p_stat) { if(pp->p_time != 127) pp->p_time++; if((pp->p_cpu & 0377) > SCHMAG) pp->p_cpu =- SCHMAG; else pp->p_cpu = 0; if(pp->p_pri > PUSER) setpri(pp); } if((ps&UMODE) == UMODE) { u->u_arsp = &rsp; if(issig()) psig(); setpri(u->u_procp); } clokwork = 0; } } /* * timeout is called to arrange that * fun(arg) is called in tim/HZ seconds. * An entry is sorted into the callout * structure. The time in each structure * entry is the number of HZ's more * than the previous entry. * In this way, decrementing the * first entry has the effect of * updating all entries. */ timeout(fun, arg, tim) { register struct callo *p1, *p2; register t; int s; t = tim; p1 = &callout[0]; s = stnsm(~ALLINT); while(p1->c_func != 0 && p1->c_time <= t) { t =- p1->c_time; p1++; } p1->c_time =- t; p2 = p1; while(p2->c_func != 0) p2++; while(p2 >= p1) { (p2+1)->c_time = p2->c_time; (p2+1)->c_func = p2->c_func; (p2+1)->c_arg = p2->c_arg; p2--; } p1->c_time = t; p1->c_func = fun; p1->c_arg = arg; setsm(s); } first entry has the effect of * updating all entries. */ timeout(fun, arg, tim) { register struct callo *p1, *p2; register t; int s; t = tim; p1 = &callout[0]; s = stnsm(~ALLINT); while(p1->c_func != 0 && p1->c_time <= t) { t =- p1->c_time; p1++; } p1->c_time =- t; p2 # /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../proc.h" #include "../inode.h" #include "../reg.h" #include "../370.h" /* * Priority for tracing */ #define IPCPRI (-1) /* * Structure to access an array of integers. */ struct { int inta[]; }; /* * Tracing variables. * Used to pass trace command from * parent to child being traced. * This data base cannot be * shared and is locked * per user. */ struct { int ip_lock; int ip_req; int ip_addr; int ip_data; } ipc; /* * Send the specified signal to * all processes with 'tp' as its * controlling teletype. * Called by tty.c for quits and * interrupts. */ signal(tp, sig) { register struct proc *p; for(p = &proc[0]; p < &proc[NPROC]; p++) if(p->p_ttyp == tp) psignal(p, sig); } /* * Send the specified signal to * the specified process. */ psignal(p, sig) int *p; { register *rp; if(sig >= NSIG) return; rp = p; if(rp->p_sig != SIGKIL) rp->p_sig = sig; if(rp->p_pri > PUSER) rp->p_pri = PUSER; if(rp->p_stat == SWAIT) setrun(rp); } /* * Returns true if the current * process has a signal to process. * This is asked at least once * each time a process enters the * system. * A signal does not do anything * directly to a process; it sets * a flag that asks the process to * do something to itself. */ issig() { register n; register struct proc *p; p = u->u_procp; if(n = p->p_sig) { if (p->p_flag&STRC) { stop(); if ((n = p->p_sig) == 0) return(0); } if((u->u_signal[n]&1) == 0) return(n); } return(0); } /* * Enter the tracing STOP state. * In this state, the parent is * informed and the process is able to * receive commands from the parent. */ stop() { register struct proc *pp, *cp; loop: cp = u->u_procp; if(cp->p_ppid != 1) for (pp = &proc[0]; pp < &proc[NPROC]; pp++) if (pp->p_pid == cp->p_ppid) { wakeup(pp); cp->p_stat = SSTOP; swtch(); if ((cp->p_flag&STRC)==0 || procxmt()) return; goto loop; } exit(); } /* * Perform the action specified by * the current signal. * The usual sequence is: * if(issig()) * psig(); */ psig() { int n, p, s; int *rp; rp = u->u_procp; n = rp->p_sig; rp->p_sig = 0; if((p=u->u_signal[n]) != 0) { u->u_error = 0; if(n != SIGINS && n != SIGTRC) u->u_signal[n] = 0; s = u->u_arsp[R13] - 12; grow(s); suword(s+8, u->u_arsp[R15]); suword(s+4, u->u_arsp[RPS]); suword(s, u->u_arsp[RPC]); u->u_arsp[R13] = s; u->u_arsp[RPC] = p; u->u_arsp[R15] = p | (n << 24); return; } switch(n) { case 0: case SIGHUP: case SIGINT: break; case SIGQIT: case SIGINS: case SIGTRC: case SIGIOT: case SIGEMT: case SIGFPT: case SIGBUS: case SIGSEG: case SIGSYS: u->u_arg[0] = n; if(core()) n =| 0200; default:; } u->u_arg[0] = (u->u_arsp[R0]<<8) | n; exit(); } /* * Create a core image on the file "core" * If you are looking for protection glitches, * there are probably a wealth of them here * when this occurs to a suid command. * * It writes USIZE block of the * user.h area followed by the entire * data+stack segments. */ core() { register s, *ip; extern schar; u->u_error = 0; u->u_dirp = "core"; ip = namei(&schar, 1); if(ip == NULL) { if(u->u_error) return(0); ip = maknode(0666); if(ip == NULL) return(0); } if(!access(ip, IWRITE) && (ip->i_mode&IFMT) == 0 && u->u_uid == u->u_ruid) { itrunc(ip); u->u_offset = 0; u->u_base = u; u->u_count = USIZE*PGSIZE; u->u_segflg = 1; writei(ip); u->u_base = 0; u->u_count = u->u_dsize*PGSIZE; u->u_segflg = 0; writei(ip); u->u_count = u->u_ssize*PGSIZE; u->u_base = TWOPOW24 - u->u_count; writei(ip); } iput(ip); return(u->u_error==0); } /* * grow the stack to include the SP * true return if successful. */ grow(sp) char *sp; { register a; a = TWOPOW24 - u->u_ssize*PGSIZE; if(sp >= a )return(0); a = (a - sp + PGSIZE) / PGSIZE; if(a+u->u_dsize+u->u_ssize+u->u_tsize > maxmem) return(0); grow1(a); sureg(); return(1); } /* * sys-trace system call. */ ptrace() { register struct proc *p; if (u->u_arg[2] <= 0) { u->u_procp->p_flag =| STRC; return; } for (p=proc; p < &proc[NPROC]; p++) if (p->p_stat==SSTOP && p->p_pid==u->u_arg[0] && p->p_ppid==u->u_procp->p_pid) goto found; u->u_error = ESRCH; return; found: while (ipc.ip_lock) sleep(&ipc, IPCPRI); ipc.ip_lock = p->p_pid; ipc.ip_data = u->u_arsp[R0]; ipc.ip_addr = u->u_arg[1] & ~01; ipc.ip_req = u->u_arg[2]; p->p_flag =& ~SWTED; setrun(p); while (ipc.ip_req > 0) sleep(&ipc, IPCPRI); u->u_arsp[R0] = ipc.ip_data; if (ipc.ip_req < 0) u->u_error = EIO; ipc.ip_lock = 0; wakeup(&ipc); } /* * Code that the child process * executes to implement the command * of the parent process in tracing. */ procxmt() { register int i; register int *p; if (ipc.ip_lock != u->u_procp->p_pid) return(0); i = ipc.ip_req; ipc.ip_req = 0; wakeup(&ipc); switch (i) { /* read user I */ case 1: if (fuibyte(ipc.ip_addr) == -1) goto error; ipc.ip_data = fuiword(ipc.ip_addr); break; /* read user D */ case 2: if (fubyte(ipc.ip_addr) == -1) goto error; ipc.ip_data = fuword(ipc.ip_addr); break; /* read u */ case 3: i = ipc.ip_addr; if (i<0 || i >= (USIZE<<6)) goto error; ipc.ip_data = u.inta[i>>1]; break; /* write user I (for now, always an error) */ case 4: if (suiword(ipc.ip_addr, 0) < 0) goto error; suiword(ipc.ip_addr, ipc.ip_data); break; /* write user D */ case 5: if (suword(ipc.ip_addr, 0) < 0) goto error; suword(ipc.ip_addr, ipc.ip_data); break; /* write u */ /* Ignored for time being, cause we're not too sure where all our registers are. case 6: p = &u.inta[ipc.ip_addr>>1]; if (p >= u->u_fsav && p < &u->u_fsav[25]) goto ok; for (i=0; i<9; i++) if (p == &u->u_arsp[regloc[i]]) goto ok; goto error; ok: if (p == &u->u_arsp[RPS]) { ipc.ip_data =| 0170000; /* assure user space *//* ipc.ip_data =& ~0340; /* priority 0 *//* } *p = ipc.ip_data; break; */ /* set signal and continue */ case 7: u->u_procp->p_sig = ipc.ip_data; return(1); /* force exit */ case 8: exit(); default: error: ipc.ip_req = -1; } return(0); } nta[ipc.ip_addr>>1]; if (p >= u->u_fsav && p < &u->u_fsav[25]) goto ok; for (i=0; i<9; i++) if (p == &u->u_arsp[regloc[i]]) goto ok; goto error; ok: if (p == &u->u_arsp[RPS]) { ipc.ip_data =| 0170000; /* assure user space *//* ipc.ip_data =& ~0340; /* priority 0 *//* } *p = ipc.ip_data; break; */ /* set signal and continx & 6  zw   w  7" w@&  7"@ %% > ^#  wnw \ rf  74#7.#N > _ 7#7### Wp 1#Wp 1 # % J   J   K   K   K   K  MI7 I7 IK"7 "7 t" 7h"%0%9 \"`"P"  J"F"$-lH"n! ^ #bI XI ."L!& ^ Ĕ >  v% %_ RG@JG &    %#  *%   _%'%"_ | %   |%\  |  7  F_%B?x %  -\ >rf  7b N > _J&7  V7%0%9   _ | % _N |%_ ! >_w f  =4ww ! b %Nf b %)! b dww p`^e!+! >TFe!:e70wFw 4JE FEBEM% N9! > ww  E%ME E? EE  Dww  z%/7 4 h%*"&  /w  D%/-~z  ^ p7 f  7| yw T> -T0L $ B ^   ww D 55um5A ruWp Bm2Dԋ@e! f  we e' @ K! > `ʋ CԒ e!w >@.% 2 .\! >  ww w. %du! >w@f . ̋ | ̥( | %.׆ &  ̋ w p:@e5@ A H@e55  Ne A Aa1n@e50 :5%  ,5% f  5  Ne0 A Aa1Fu- ! >N |@ 5R @e25.}. .}. . @  =.5 @ @anfe2 .  u-u-@ @aF@e25..@. . |w w @5@ %) w@ȥ  u%)N  }- N ! > @5%,@w l5 u %)M ! >5 @ $f  @w2%\ >5@- %\@ ȕ\@ H?%\W 5%" %\@ ȕ\@ H 5%,%)  %"%'u%'@ ȕ' 5@- % %0! >'@ ȕ'@ H%( %) w N \ %_w w N %0%9w w  @5 A @  w w @A 9 x w w l5 5 & *%0%9%a!%fA@v%aee `@ `5u 4% ff %&  ww A@vA@v5uEff %@Ee00 ww F@e7@e5^% w\e   % uew6e_f@& %_%ue-l r00 5 h5.ƍ T55 5 5  r e!A! y"N J Z@YN ?N H:u6@E?F B-@e5=5e@ffe& e 5 @e5 f@& % ?܌@e5N 55 @5@@5rff %@ f@& %@ ff %@ @e7@@ <5_ %f& %w w  0 w 5  @A tE5 @e0?‹ w @t5E H@E5% e7@e0?l hwj w X 5 %9AWp Ameu:5 2%0 (@w" w   w w @e5 @ -䊷 A r }eA r 5 @e@-= e 0@? @e@-w~ w l  %B" r  %@ 5%f%F5 5 5 - 555 5 B5 @5 5 B5  B5 um555 B5 u-5uWA5 B5@5 % "0 . @@50 u-5 )55e0= @ @-. 5uu5 B5 u- A >E @ 5- dA r 5u-A 5re0= A 5ruA r 5 = w w 5  @ ȋ@ww  v%J" rAWp eu@  f l @Aep NQ"  A1@H@ w~w l %|" r %N"  AWp eu@ N"  @  f l @9 Gfe@&@p&f e  xv"@N"  @@@ @ A@ whw V" b Nf f fff b e  ^ w(w  @"7  y 5  f  5 Nf l @wf  5f pN"  w  % # r %@wrAWp eu@ N#  @ f l A@ HA1`@A@,@ & f 4%@ @@p&f X% @N'#  @@0 w ,AWp eu@Ap@A p N %xNef  %( Z = % $ !Ned 5%.@ pB#@Aedp@=@p @A ebp@=@%  @t5pw~w l 5 N  % N zwTw B ]%YAWp eu@  @   x<#& f 4%@ff X%@Ap@0 @ B# @%@ B# $@ 0 B#N Zw@ &f 4w ^D m " a2  ew.eEV# !݃%%5 %Je $w DeV#!c bb! c33ww zf X%\# X%t# X% wJ7    ӕ- ӕ0B~ӕ.  ӕ0~ B~7   ӕ-ӕ. B~ӕe ӕ- ӕ+ r e0e0Sf@w fww #wf@w#wf fww#wFf@ww#w$fwm#wwmfw#wwt f@wdw`#w f@wFwB#wf@fA w,w"e"w" ew"@lw"Alew &@t`e @& HADCBF7fFf@  xp z">#@z z$$ttyЋ7(@ f5w : * f5w $ }7 W&f 7 ~@% ~@A&@̜7@L> Be0# @є ̜    BA   W ̜e B@e0mb \~` eȐ9 ȕ0 ȕ1 *~* $~`0VV *L^P|t t t t t t t t t t t t t t t t t t t t t t t t t t | | | | | | t t t t t t t t t t t t t t t t t t t t t t t t t t **2*S*Arg countCan't find %sdefineincludeendififdefifndefibmIf-less endifUndefined controlNested 'include'Missing file %sControl syntax*F* %d: Line overflowtoo much definingtoo many definesExcessive define loopingdefine recursion loop define argument mismatchdefine prototype argument perrornon terminated macro callnon-terminated string! "" "!"%(!&!ttftoa ungetcungetc/unprintf: buffer full file %dBfcgetc cgetc: %d illegal file numbercgetc: %d not open to readcgetc: error on %dERROR arw@& :Ncopen: bad file %scputc cputc: %d not opencputc: writing %dt: argument count wrong That's all, folks М %ew@/dev> ed macro callnon-terminated string! "" "!"%(!&!ttftoa ungetcungetc/unprintf: buffer full file %dBfcgetc cgetc: %d illegal file numbercgetc: %d not open to readcgetc: error on %dERROR arw@& :Ncopen: bad file %scputc cputc: %d not opencputc: wr/* * Masks for ps (first half of psw) */ #define TRANS #04000000 #define ALLINT #03000000 #define IOINT #02000000 #define EXTINT #01000000 #define KEY 074000000 #define UMODE 0200000 #define PGSIZE 4096 #define PGMASK 07777 #define PTRADDR 0177760 #define PTINVAL 010 #define PTUSER 1 #define PTINIT #00080008 #define SGNPAG 16 #define SGNFLG 1 /* SGNPAG / 16 */ #define SGLEN 036000000000 #define SGPTA 077777770 #define SGINVAL 1 #define CLOCK 80 #define INTERVAL 02400 #define TWOPOW24 16777216 /* * for those of us who like (need) to use IBM's numbering convention * the the bytes in a word */ struct { char byte0; char byte1; char byte2; char byte3; }; /* * Hardware address for i/o */ #define CSW 64 #define IOADDR 186 /* #b8 */ /* * bits in the channel status word */ #define ATTENTION #80 #define STATMOD #40 #define CUEND #20 #define BUSY #10 #define CHEND #08 #define DEVEND #04 #define UNITCHEK #02 #define UNITEXCP #01 #define PCI #80 #define INTREQ #40 #define TIMEOUT #01 /* * Structure to pull CSW apart */ struct { char key_flag; char ccwadr1,ccwadr2,ccwadr3; char unitstat; char chanstat; char resid1,resid2; }; /* User controlled bits in the PSW */ #define PSMASK #00003700 #define PCMASK #00ffffff 186 /* #b8 */ /* * bits in the channel status word */ #define ATTENTION #80 #define STATMOD #40 #define CUEND #20 #define BUSY #10 #define CHEND #08 #define DEVEND #04 #define UNITCHEK #02 #define UNITEXCP #01 #define PCI #80 #define INTREQ #40 #define TIMEOUT #01 /* * Used to dissect integer device code * into major (driver designation) and * minor (driver parameter) parts. */ struct { char d_fill1,d_fill2; char d_major; char d_minor; }; /* * Declaration of block device * switch. Each entry (row) is * the only link between the * main unix code and the driver. * The initialization of the * device switches is in the * file conf.c. */ extern struct bdevsw { int (*d_open)(); int (*d_close)(); int d_baseaddr; int (*d_strategy)(); int *d_tab; } bdevsw[]; /* * Nblkdev is the number of entries * (rows) in the block switch. It is * set in binit/bio.c by making * a pass over the switch. * Used in bounds checking on major * device numbers. */ extern int nblkdev; /* * Character device switch. */ extern struct cdevsw { int (*d_open)(); int (*d_close)(); int d_baseaddr; int (*d_read)(); int (*d_write)(); int (*d_sgtty)(); } cdevsw[]; /* * Number of character switch entries. * Set by cinit/tty.c */ extern int nchrdev; ; int *d_tab; } bdevswd. q n s ?;+-;0"o;S @=&541 2$(*>?"#< 3!9u @ABCD! n@ k h e0X a` / /2 / /4rux .F CvXm [ :  Bv p Avm  @v  F ?v&  ) ka p =vd vspm j  Og d _J \` Y ^[@ X]Z W t h m @ABCD! n@ k h e0X a` / /2 / /4rux .F CvXm [ :  Bv p Avm  @v  F ?v&  ) ka p =v/* * One file structure is allocated * for each open/creat/pipe call. * Main use is to hold the read/write * pointer associated with each open * file. */ extern struct file { char f_flag; char f_count; /* reference count */ int f_inode; /* pointer to inode structure */ char *f_offset; /* read/write character pointer */ } file[NFILE]; /* flags */ #define FREAD 01 #define FWRITE 02 #define FPIPE 04  @v  F ?v&  ) ka p =v# /* */ #include "../manifest.h" #include "../param.h" #include "../user.h" #include "../proc.h" #include "../text.h" #include "../systm.h" #include "../file.h" #include "../inode.h" #include "../buf.h" #include "../370.h" #define MAXPID 32767 /* * Give up the processor till a wakeup occurs * on chan, at which time the process * enters the scheduling queue at priority pri. * The most important effect of pri is that when * pri<0 a signal cannot disturb the sleep; * if pri>=0 signals will be processed. * Callers of this routine must be prepared for * premature return, and check that the reason for * sleeping has gone away. */ sleep(chan, pri) { register *rp, s; s = stosm(0); rp = u->u_procp; if(pri >= 0) { if(issig()) goto psig; stnsm(~ALLINT); rp->p_wchan = chan; rp->p_stat = SWAIT; rp->p_pri = pri; stosm(ALLINT); swtch(); if(issig()) goto psig; } else { stnsm(~ALLINT); rp->p_wchan = chan; rp->p_stat = SSLEEP; rp->p_pri = pri; stosm(ALLINT); swtch(); } setsm(s); return; /* * If priority was low (>=0) and * there has been a signal, * execute non-local goto to * the qsav location. * (see trap1/trap.c) */ psig: aretu(u->u_qsav); } /* * Wake up all processes sleeping on chan. */ wakeup(chan) { register struct proc *p; register c, i; c = chan; p = &proc[0]; i = NPROC; do { if(p->p_wchan == c) { setrun(p); } p++; } while(--i); } /* * Set the process running. */ setrun(p) { register struct proc *rp; rp = p; rp->p_wchan = 0; rp->p_stat = SRUN; if(rp->p_pri < curpri) runrun++; } /* * Set user priority. */ setpri(up) { register *pp, p; pp = up; p = (pp->p_cpu & 0377)/16; p =+ PUSER + pp->p_nice; if(p > 127) p = 127; pp->p_pri = p; if(p < curpri) runrun++; } /* * This routine is called to reschedule the CPU. * if the calling process is not in RUN state, * arrangements for it to restart must have * been made elsewhere, usually by calling via sleep. */ swtch() { static struct proc *p,*rp; register i, n; static struct proc *oldpid; static struct user *oldu; oldu = u; oldpid = u->u_procp; runrun = 0; if(p == NULL) p = &proc[0]; /* * Remember stack of caller */ savu(u->u_rsav); /* * Switch to scheduler's stack */ retu(proc[0].p_addr); /* Free U struct in case of a dying process. */ if(oldpid->p_stat == SZOMB) freepag(oldu); loop: rp = p; p = NULL; n = 128; /* * Search for highest-priority runnable process */ i = NPROC; do { rp++; if(rp >= &proc[NPROC]) rp = &proc[0]; if(rp->p_stat==SRUN /* && (rp->p_flag&SLOAD)!=0 */ ) { if(rp->p_pri < n) { p = rp; n = rp->p_pri; } } } while(--i); /* * If no process is runnable, idle. */ if(p == NULL) { p = rp; idle(); goto loop; } rp = p; curpri = n; /* * Switch to stack of the new process and set up * his segmentation registers. */ retu(rp->p_addr); u = rp->p_addr; sureg(); /* * If the new process paused because it was * swapped out, set the stack level to the last call * to savu(u_ssav). This means that the return * which is executed immediately after the call to aretu * actually returns from the last routine which did * the savu. * * You are not expected to understand this. * NO swapping for the 370. * if(rp->p_flag&SSWAP) { * rp->p_flag =& ~SSWAP; * aretu(u->u_ssav); * } */ /* * The value returned here has many subtle implications. * See the newproc comments. */ return(1); } /* * Create a new process-- the internal version of * sys fork. * It returns 1 in the new process. * How this happens is rather hard to understand. * The essential fact is that the new process is created * in such a way that appears to have started executing * in the same call to newproc as the parent; * but in fact the code that runs is that of swtch. * The subtle implication of the returned value of swtch * (see above) is that this is the value that newproc's * caller in the new process sees. */ newproc() { int a1, a2; struct proc *p, *up; register struct proc *rpp; struct user *newu; register *rip, n; p = NULL; /* * First, just locate a slot for a process * and copy the useful info from this process into it. * The panic "cannot happen" because fork has already * checked for the existence of a slot. */ retry: if(++mpid > MAXPID) mpid = 1; for(rpp = &proc[0]; rpp < &proc[NPROC]; rpp++) { if(rpp->p_stat == NULL && p==NULL) p = rpp; if (rpp->p_pid==mpid) goto retry; } if ((rpp = p)==NULL) panic("no procs"); /* * make proc entry for new proc */ rip = u->u_procp; up = rip; rpp->p_stat = SRUN; rpp->p_flag = SLOAD; rpp->p_uid = rip->p_uid; rpp->p_ttyp = rip->p_ttyp; rpp->p_nice = rip->p_nice; rpp->p_textp = rip->p_textp; rpp->p_pid = mpid; rpp->p_ppid = rip->p_pid; rpp->p_time = 0; rpp->p_utime = 0; rpp->p_cpu = 0; /* * make duplicate entries * where needed */ for(rip = &u->u_ofile[0]; rip < &u->u_ofile[NOFILE];) if((rpp = *rip++) != NULL) rpp->f_count++; if((rpp=up->p_textp) != NULL) { rpp->x_count++; } u->u_cdir->i_count++; /* * Partially simulate the environment * of the new process so that when it is actually * created (by copying) it will look right. */ savu(u->u_rsav); newu = getpage(); a1 = u->u_dpages; a2 = u->u_spages; u->u_dpages = u->u_spages = 0; estabur(u->u_dsize,u->u_ssize); protect(u->u_dpages,1); protect(u->u_spages,1); copypag(u,newu); copysgs(a1,u->u_dpages); copysgs(a2,u->u_spages); fixstak(newu); u->u_dpages = a1; u->u_spages = a2; newu->u_procp = p; p->p_addr = newu; return(0); } /* * Partially simulate the environment * of the new process so that when it is actually * created (by copying) it will look right. */ savu(u->u_rsav); newu = getpage(); a1 = u->u_dpages; a2 = u->u_spages; u->u_dpages = u->u_spages = 0; estabur(u->u_dsize,u->u_ssize); protect(u->u_dpages,1); protect(u->u_spages,1); copypag(u,newu); copysgs(a1,u->u_dpages); copysgs(a2,u->u_spages); fixstak(newu); u->u_dpages = a1; u->u_spages = a2; newu->u_procp = p; p->p_addr = newu;# # include "../370.h" # include "../param.h" # include "../systm.h" # define PGPTABS 102 /* PGSIZE/sizeof pagetab */ # define NULL 0 struct pagetab { struct pagetab *pt_next; int pt_info[9]; }; char **ffpage; struct pagetab *ffptab NULL; getpage() { char **p; if((p=ffpage)==NULL) panic("no pages"); ffpage = *ffpage; --pagecnt; return(p); } freepag(p) char **p; { char *q; q = p; if(q%PGSIZE) panic("bad freepage addr"); *p = ffpage; ffpage = p; pagecnt++; return(1); } getptab() { struct pagetab *p; int i; if(!ffptab) { if(p=getpage()){ ffptab = p; while(p+1 < ffptab+PGPTABS) p = p->pt_next = p+1; p->pt_next = NULL; }else return(NULL); } p = ffptab; ffptab = ffptab->pt_next; p->pt_next = NULL; p->pt_info[0] = 0; for(i=1;i<9;i++) p->pt_info[i] = PTINIT; return(p); } freptab(p) struct pagetab *p; { p->pt_next = ffptab; ffptab = p; return(1); } p; if(q%PGSIZE) panic("bad freepage addr"); *p = ffpage; ffpage = p; pagecnt++; return(1); } getptab() { struct pa macro pagetab pagetab dsect ptnext ds f pointer to next pagetab ptflags ds h unused ptvaddr ds h virtual address ptinfo ds 16h actual page table ptimask equ x'0008' mask for invalid bit ptamask equ x'fff0' mask for real address o[0] = 0; for(i=1;i<9;i++) p->pt_info[i] = PTINIT; return(p); } freptab(p) struct pagetab *p; { p->pt_next = ffptab; ffptab = p; return(1); } p; if(q%PGSIZE) panic("bad freepage addr"); *p = ffpage; ffpage = p; pagecnt++; return(1); } getptab() { struct pa macro user user dsect ursav ds 2f ufsav ds 8f utpages ds f udpages ds f uspages ds f utsize ds f udsize ds f ussize ds f info ds 16h actual page table ptimask equ x'0008' mask for invalid bit ptamask equ x'fff0' mask for real address o[0] = 0; for(i=1;i<9;i++) p->pt_info[i] = PTINIT; return(p); } freptab(p) struct pagetab *p; { p->pt_next = ffptab; ffptab = p; return(1); } p; if(q%PGSIZE) panic("bad freepage addr"); *p = ffpage; ffpage = p; pagecnt++; return(1); } getptab() { struct pa# #include "../manifest.h" #include "../param.h" #include "../user.h" #include "../systm.h" #include "../proc.h" #include "../inode.h" #include "../370.h" #define ICODESZ 40 /* byte size of icode */ /* * Icode is the bootstrap * program executed in user mode * to bring up the system. */ extern int icode[]; /* * Initialization code. * Called from m370.a as soon as stack * has been established. * Functions: * start the clock * hand craft 0th process * call all initialization routines * fork - process 0 to scheduler * - process 1 execute bootstrap * * loop at loc 6 in user mode -- /etc/init * cannot be executed. */ #define DROPSIZE 17 /* # words stack is dropped by on entry */ main370(dummy) { printf("Unix/370\n"); printf("Storage = %dk\n",(memlim+1)/1024); printf("%d free pages\n", maxmem); pagecnt = maxmem; /* * start clock */ clkstrt(); /* * set up system process */ proc[0].p_addr = u; proc[0].p_stat = SRUN; proc[0].p_flag =| SLOAD|SSYS; u->u_procp = &proc[0]; u->u_orega = (&dummy) - DROPSIZE; /* for fixstak */ /* * set up 'known' i-nodes */ paginit(); cinit(); binit(); iinit(); rootdir = iget(rootdev, ROOTINO); rootdir->i_flag =& ~ILOCK; u->u_cdir = iget(rootdev, ROOTINO); u->u_cdir->i_flag =& ~ILOCK; /* * make init process * enter scheduling loop * with system process */ if(newproc()) { estabur(1,0); sureg(); copyout(icode, 0, ICODESZ); /* * Return goes to loc. 0 of user init * code just copied out. */ return; } sched(); } estabur(ds,ss) { freesgs(u->u_dpages); freesgs(u->u_spages); u->u_dpages = u->u_spages = 0; u->u_dsize = u->u_ssize = 0; if(ds > 0) sbreak1(ds); if(ss > 0) grow1(ss); u->u_dsize = ds; u->u_ssize = ss; } /* * Scheduling routine. * No scheduling for now, * so just sleep forever. */ sched() { for(;;) sleep(sched,-1); } if(newproc()) { estabur(1,0); sureg(); copyout(icode, 0, ICODESZ); /* * Return goes to loc. 0 of user init * code just copied out. */ return; } sch user pagetab entry sbreak1,freesgs,protect,grow1,copysgs entry zeropag,copypag gbla &fnum eject sbreak1 startup sbreak1 prolog 4 0 args l r7,=v(u) find u pointer l r7,0(,r7) address u struct using user,r7 tell assembler l r2,ss$&fnum+0(r12) load amount to be added to data ltr r2,r2 is it positive? bl less no, branch using pagetab,r3 * * The following code finds the end of the current * string of page tables, so we know where to add more. * more l r3,udpages morelp ltr r3,r3 any more pagetabs bz nomore no, branch sr r4,r4 zero count morent lh r5,ptinfo(r4) load page entry n r5,=a(ptimask) look at invalid bit ltr r5,r5 is it set? bnz addmore yes, branch la r4,2(,r4) bump page entry index c r4,=f'30' last one? bnh morent no, branch l r3,ptnext yes, get next pagetab b morelp branch back * * Loop to add pages, decreasing the amount (r2) by one * each time until we hit zero. * addmore l r15,=v(getpage) prepare to getpage balr r14,r15 call getpage srl r0,8 shift page addr to 16 bits sth r0,ptinfo(r4) stuff in the page table bct r2,incmore decrement count, br > 0 b return if zero, return incmore la r4,2(,r4) bump page entry index c r4,=f'30' last one? bnh addmore no, branch nomore l r15,=v(getptab) yes, get another pagetab balr r14,r15 call getptab ltr r3,r3 any pagetabs already? bz ptab1 no, branch st r0,ptnext yes, link in new one b $1 forward ptab1 st r0,udpages first, so store in u_dpages $1 lr r3,r0 point to it sr r4,r4 zero entry index b addmore go add some more pages * * The amount of pages to be added was negative, so * find how many pages will remain and remove all * pages beyond that point. * less a r2,udsize find # of pages l r3,udpages point at first data pagetab lesslp ltr r3,r3 any more pagetabs? bz return no, branch c r2,=f'16' more than 1 table left? bnl nextpt yes, branch sll r2,1 * 2 for index b frepag start freeing pages nextpt l r3,ptnext get next pagetab s r2,=f'16' decrement page count b lesslp branch back freelp l r3,ptnext get next pagetab ltr r3,r3 any more pagetabs? bz return no, return frepag lh r4,ptinfo(r2) get page entry n r4,=a(ptamask) look at the addr sll r4,8 make 24 bit addr ltr r4,r4 is it zero? bz skip yes, don't free it st r4,0(SP) store it as argument l r15,=v(freepag) prepare to free page balr r14,r15 call freepag l r4,=a(ptimask) load invalid entry model sth r4,ptinfo(r2) mark entry as invalid skip la r2,2(,r2) increment entry index c r2,=f'30' last one? bnh frepag no, branch b freelp yes, go to next table return epilog 64 eject * * Free an entire chain of pages and page tables. * space drop freesgs prolog 4 1 arg l r2,ss$&fnum+0(R12) pointer to chain to be freed loop ltr r2,r2 any pagetabs left? bz none no, branch using pagetab,r2 address page table sr r3,r3 zero entry index free lh r4,ptinfo(r3) load page entry lr r5,r4 save for posterity n r4,=a(ptimask) extract invalid bit ltr r4,r4 is it invalid? bnz inv yes, branch n r5,=a(ptamask) extract address ltr r5,r5 is it zero? bz inv yes, branch sll r5,8 make real address st r5,0(sp) pass as argument l r15,=v(freepag) prepare to free page balr r14,r15 call freepag inv la r3,2(,r3) bump entry index c r3,=f'30' last one? bnh free no, branch l r3,ptnext yes, remember next table st r2,0(sp) store pagetab addr as arg l r15,=v(freptab) prepare to call freptab balr r14,r15 call freptab lr r2,r3 point at next pagetab b loop go free it none epilog 64 say goodbye eject * * Protect a chain of pages by setting their protect keys * to a given value. * drop protect prolog 0 2 args l r2,ss$&fnum+0(r12) load pointer to chain l r7,ss$&fnum+4(r12) load protect key value sll r7,4 shift into proper position ploop ltr r2,r2 any pages remaining? bz nope no, branch using pagetab,r2 establish addr for pagetab sr r3,r3 zero entry index setkey lh r4,ptinfo(r3) load page entry lr r5,r4 save for posterity n r4,=a(ptimask) extract invalid bit ltr r4,r4 is it set? bnz pinv yes, branch n r5,=a(ptamask) extract address ltr r5,r5 is it zero? bz pinv yes, branch sll r5,8 make real addr ssk r7,r5 set key on first 2k la r5,2048(,r5) add 2048 ssk r7,r5 set key on second 2k pinv la r3,2(,r3) point to next page entry c r3,=f'30' last one? bnh setkey no, branch l r2,ptnext point at next pagetab b ploop go set its keys nope epilog 64 eject * * Grow the stack by adding r2 pages to the bottom. * drop grow1 prolog 4 1 arg l r2,ss$&fnum+0(r12) load amount to be grown l r7,=v(u) find u pointer l r7,0(,r7) address u struct using user,r7 tell assembler l r3,uspages load pointer to stack chain using pagetab,r3 establish addr ltr r3,r3 any there? bz create no, branch trundle l r4,ptnext get pointer to next pagetab ltr r4,r4 is it zero? bz fndlast yes,branch lr r3,r4 trundle down the chain b trundle branch back fndlast la r4,30 load entry index findlp lh r5,ptinfo(r4) load page entry n r5,=a(ptimask) look at invalid bit ltr r5,r5 is it set? bnz grow yes, branch s r4,=f'2' look at previous entry bnm findlp branch back if there create l r15,=v(getptab) prepare to get pagetab balr r14,r15 branch to getptab ltr r3,r3 did prev pagetab exist? bz $2 st r0,ptnext link in this pagetab b $3 $2 st r0,uspages store where we can find it $3 lr r3,r0 point at new pagetab la r4,30 load entry index grow l r15,=v(getpage) prepare to get page balr r14,r15 call getpage srl r0,8 shift for pagetab format sth r0,ptinfo(r4) store in pagetab bct r2,moregr dec page count & branch b growret go away happy moregr s r4,=f'2' point at prev entry bnm grow branch if there b create get pagetab if not growret epilog 64 eject * * Copy one chain of pages to another. * next equ ptnext-pagetab info equ ptinfo-pagetab drop copysgs prolog 8 2 args lm r3,r4,ss$&fnum+0(r12) load from & to pointers ptlp ltr r3,r3 any to copy? bz quit no, quit sr r5,r5 zero entry index copylp lh r6,info(r5,r3) load from entry lr r7,r6 save for posterity n r6,=a(ptimask) extract invalid bit ltr r6,r6 is it set? bnz nocopy yes, don't copy it lh r6,info(r5,r4) load to entry n r6,=a(ptamask) mask addr sll r6,8 shift to to 24 bits sll r7,8 shift from to 24 bits st r7,0(,sp) store arg 1 st r6,4(,sp) store arg 2 l r15,=v(copypag) prepare to copy page balr r14,r15 call copypag nocopy la r5,2(,r5) point at next entry c r5,=f'30' is it last one? bnh copylp no, branch l r3,next(,r3) get next from pagetab l r4,next(,r4) get next to pagetab b ptlp branch back quit epilog 64 eject * * Zero an entire page. * drop zeropag prolog 0 1 arg l r2,ss$&fnum+0(r12) load addr of page l r3,=f'4096' load length of page sr r4,r4 no second addr for mvcl sr r5,r5 zero length mvcl r2,r4 pad page with zeroes epilog 64 go away space 5 * * Copy one page to another. * drop copypag prolog 0 2 args l r2,ss$&fnum+4(r12) "to" l r4,ss$&fnum+0(r12) "from" l r3,=f'4096' page length lr r5,r3 for both pages mvcl r2,r4 copy epilog 64 go away end branch back quit epilog 64 eject * * Zero an entire page. * drop zeropag prolog 0 1 arg l r2,ss$&fnum+0(r12) load addr of page l r3,=f'4096' load length of page sr r4,r4 no second addr for mvcl sr r5,r5 zero length mvcl r2,r4 pad page with zeroes epilog 64 go away space 5 * * Copy one page to another. * drop copypag prolog 0 2 args l r2,ss$&fnum+4(r12) "to" l r4,ss$&fnum+0(r12) "from" l r3,=f user pagetab sureg startup entry sureg sureg prolog 0 no args l r7,=v(u) find u pointer l r7,0(,r7) address u struct using user,r7 tell assembler clear l r2,sgtaddr get segment table addr mvc 0(4,r2),=f'1' mark first segment invalid mvc 4(252,r2),0(r2) propagate thru rest of table mvc 256(256,r2),0(r2) mvc 512(256,r2),0(r2) mvc 768(256,r2),0(r2) text sr r1,r1 zero segtab index l r2,sgtaddr get segtab addr l r3,utpages get text pagetab chain using pagetab,r3 establish addr textlp ltr r3,r3 any text remaining? bz data no, go do data la r4,ptinfo get actual pagetab addr o r4,=x'f0000000' set length indicator st r4,0(r1,r2) store in segtab la r1,4(,r1) bump segtab index l r3,ptnext get next pagetab b textlp branch back data l r3,udpages get data pagetab chain datalp ltr r3,r3 any data remaining? bz stack no, go do stack la r4,ptinfo get actual pagetab addr o r4,=x'f0000000' set length indicator st r4,0(r1,r2) store in segtab la r1,4(,r1) bump segtab index l r3,ptnext get next data pagetab b datalp go back stack la r1,1020 index to top of segtab l r3,uspages get stack pagetab chain stacklp ltr r3,r3 any stack remaining? bz purge no, go purge tlb la r4,ptinfo get actual pagetab addr o r4,=x'f0000000' set length indicator st r4,0(r1,r2) store in segtab s r1,=f'4' decrement segtab index l r3,ptnext get next stack pagetab b stacklp branch back purge ptlb reset translation info epilog 64 eject entry paginit drop paginit prolog 4 no args l r15,=v(getpage) prepare to get page balr r14,r15 call getpage st r0,sgtaddr store segment table addr stctl 0,0,cr0 get control reg 0 l r1,cr0 load it n r1,=x'ff07ffff' turn off dat bits o r1,=x'00800000' set dat bits to p=4k,s=64k o r1,=x'80000000' set channels to bmx o r1,=x'00000400' enable cpu timer n r1,=x'ffffff7f' disable interval timer st r1,cr0 store new cr0 mvi cr1,x'0f' segtab length for cr1 mvc cr1+1(3),sgtaddr+1 segtab addr for cr1 lctl 0,1,cr0 load control regs 0&1 lr r2,r0 move page addr la r2,1024(,r2) addr unused portion la r3,76 76 pagetabs in 3k freept st r2,0(,sp) store addr as arg l r15,=v(freptab) prepare to free pagetab balr r14,r15 call freptab la r2,40(,r2) point at next pagetab bct r3,freept branch if any left epilog 64 return sgtaddr ds f segment table address cr0 ds f work space for cr0 cr1 ds f work space for cr1 end 0f' segtab length for cr1 mvc cr1+1(3),sgtaddr+1 segtab addr for cr1 lctl 0,1,cr0 load con# /* */ #include "../manifest.h" #include "../param.h" #include "../systm.h" #include "../user.h" #include "../proc.h" #include "../text.h" #include "../inode.h" #include "../370.h" /* * Swap out process p. * The ff flag causes its core to be freed-- * it may be off when called to create an image for a * child process in newproc. * Os is the old size of the data area of the process, * and is supplied during core expansion swaps. * * panic: out of swap space * panic: swap error -- IO error * * This routine goes away for the 370. xswap(p, ff, os) int *p; { register *rp, a; rp = p; if(os == 0) os = rp->p_size; a = malloc(swapmap, (rp->p_size+7)/8); if(a == NULL) panic("out of swap space"); xccdec(rp->p_textp); rp->p_flag =| SLOCK; if(swap(a, rp->p_addr, os, 0)) panic("swap error"); if(ff) mfree(coremap, os, rp->p_addr); rp->p_addr = a; rp->p_flag =& ~(SLOAD|SLOCK); rp->p_time = 0; if(runout) { runout = 0; wakeup(&runout); } } */ /* * relinquish use of the shared text segment * of a process. */ xfree() { register *xp, *ip; if((xp=u->u_procp->p_textp) != NULL) { u->u_procp->p_textp = NULL; if(--xp->x_count == 0) { ip = xp->x_iptr; if((ip->i_mode&ISVTX) == 0) { xp->x_iptr = NULL; freesgs(xp->x_tpages); ip->i_flag =& ~ITEXT; iput(ip); } } } } /* * Attach to a shared text segment. * If there is no shared text, just return. * If there is, hook up to it: * if it is not currently being used, it has to be read * in from the inode (ip) and established in the swap space. * If it is being used, but is not currently in core, * a swap has to be done to get it back. * The full coroutine glory has to be invoked-- * see slp.c-- because if the calling process * is misplaced in core the text image might not fit. * Quite possibly the code after "out:" could check to * see if the text does fit and simply swap it in. * * panic: out of swap space * * None of the swapping remarks apply for the 370. * */ xalloc(ip) int *ip; { register struct text *xp; register *rp, ts; if(u->u_arg[1] == 0) return; rp = NULL; for(xp = &text[0]; xp < &text[NTEXT]; xp++) if(xp->x_iptr == NULL) { if(rp == NULL) rp = xp; } else if(xp->x_iptr == ip) { xp->x_count++; u->u_procp->p_textp = xp; goto out; } if((xp=rp) == NULL) panic("out of text"); xp->x_count = 1; xp->x_iptr = ip; ts = (u->u_arg[1]+PGSIZE-1)%PGSIZE; xp->x_size = ts; estabur(ts,0); u->u_count = u->u_arg[1]; u->u_offset[1] = 020; u->u_base = 0; u->u_segflg = 0; readi(ip); u->u_tpages = xp->x_tpages = u->u_dpages; u->u_dpages = NULL; protect(xp->x_tpages,2); rp = u->u_procp; rp->p_textp = xp; rp = ip; rp->i_flag =| ITEXT; rp->i_count++; out: return; } /* * Decrement the in-core usage count of a shared text segment. * When it drops to zero, free the core space. * * This routine also goes away for the 370 xccdec(xp) int *xp; { register *rp; if((rp=xp)!=NULL && rp->x_ccount!=0) if(--rp->x_ccount == 0) mfree(coremap, rp->x_size, rp->x_caddr); } */ di(ip); u->u_tpages = xp->x_tpages = u->u_dpages; u->u_dpages = NULL; protect(xp->x_tpages,2); rp = u->u_procp; rp->p_textp = xp; rp = ip; rp->i_flag =| ITEXT; rp->i_count++; out: return; } /* * Decrement the in-core usage count of a shared text segment. * When it drops to zero, free the core space. * * This routine also goes away for the 370 xccdec(xp) int *xp; { register *rp; if((rp=xp)!=NULL && rp->x_ccount!=0) if(--rp->x_ccount == 0) mfree(coremap, rp->x_size, rp->x_caddr)# /* * input/output lookup table for interupts... not very elegant, but * neither is working down to the wire... sorry... */ #include "../dsk.h" #define MAJOR 256 #define DISK195 #195 #define DISK222 #222 #define CONSOLE #009 #define TRMBASE #030 int iolkptab[] { DISK222, &dskintr, 0*MAJOR+0, DISK195, &dskintr, 1*MAJOR+0, CONSOLE, &conintr, 0*MAJOR+0, TRMBASE+0, &trmintr, 2*MAJOR+0, TRMBASE+1, &trmintr, 2*MAJOR+1, TRMBASE+2, &trmintr, 2*MAJOR+2, TRMBASE+3, &trmintr, 2*MAJOR+3, TRMBASE+4, &trmintr, 2*MAJOR+4, TRMBASE+5, &trmintr, 2*MAJOR+5, TRMBASE+6, &trmintr, 2*MAJOR+6, TRMBASE+7, &trmintr, 2*MAJOR+7, TRMBASE+8, &trmintr, 2*MAJOR+8, TRMBASE+9, &trmintr, 2*MAJOR+9, TRMBASE+10, &trmintr, 2*MAJOR+10, TRMBASE+11, &trmintr, 2*MAJOR+11, TRMBASE+12, &trmintr, 2*MAJOR+12, TRMBASE+13, &trmintr, 2*MAJOR+13, TRMBASE+14, &trmintr, 2*MAJOR+14, TRMBASE+15, &trmintr, 2*MAJOR+15, /* not yet available... * #00c, &crdintr, -1, * #00d, &punintr, -1, * #00e, &prtintr, -1, */ -1, &nointr, -1 }; int (*bdevsw[])() { &dskopen, &nulldev, DISK222, &dskstrat, &root_tab, &dskopen, &nulldev, DISK195, &dskstrat, &mnt_tab, 0 }; int (*cdevsw[])() { &nulldev, &nulldev, CONSOLE, &nulldev, &conswrt, &nulldev, /* cons */ &nulldev, &nulldev , 0, &mmread , &mmwrite , &nodev , /* mem */ &trmopen,&trmclose,TRMBASE,&trmread,&trmwrite,&trmsgtty, /* terms */ 0 }; int nchrdev; int nblkdev; int root_tab[] { 0, 0, 0, 0, &root_dsc, 0 }; int mnt_tab[] { 0, 0, 0, 0, &mnt_dsc, 0 }; struct devdesc root_dsc; struct devdesc mnt_dsc; int dskdesc[] { 12, 12, 353, 679, 140 }; n, &nulldev, DISK195, &dskstrat, &mnt_tab, 0 }; int (*cdevsw[])() { &nulldev, &nulldev, CONSOLE, &nulldev, &conswrt, &nulldev, /* cons */ &nulldev, &nulldev , 0, &mmread , &mmwrite , &nodev , /* mem */ &trmopen,&trmclose,TRMBASE,&trmread,&trmwrite,&trmsgtty, /* terms */ 0 }; int nchrdev; int nblkdev; int root_tab[] { 0, 0, 0, 0, &root_dsc, 0 }; int mnt_tab[] { 0, 0, 0, 0, &mnt_dsc, 0 }; struct# #define CSW 64 conintr(majmin) int majmin; { extern char consbusy; char *p; p = CSW + 4; if(*p & 4) consbusy = 0; } conswrt() { char c; while((c = cpass()) >= 0) putchar(c); } E, &nulldev, &conswrt, &nulldev, /* cons */ &nulldev, &nulldev , 0, &mmread , &mmwrite , &nodev , /* mem */ &trmopen,&trmclose,TRMBASE,&trmread,&trmwrite,&trmsgtty, /* terms */ 0 }; int nchrdev; int nblkdev; int root_tab[] { 0, 0, 0, 0, &root_dsc, 0 }; int mnt_tab[] { 0, 0, 0, 0, &mnt_dsc, 0 }; structmain(argc,argv) int argc; char **argv; { int cc,hh,xr,tmp; char *cp,c; cp = argv[1]; tmp = 0; while(c = *cp++) switch(c){ case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': tmp = tmp*16 + c - '0'; break; case 'a':case 'b':case 'c':case 'd':case 'e': case 'f': tmp = tmp*16 + c - 'a' + 10; break; } xr = tmp%12 + 1; tmp = tmp/12+1; /* skip first record and track */ hh = tmp%12; cc = tmp/12; printf("%x %x %x\n",cc,hh,xr); } 0 }; structcharq startup dataloc codeloc dataloc codeloc dataloc codeloc dataloc codeloc dataloc codeloc getc PROLOG 4 1 args l 1,ss$&fnum+0(R12) l 2,0(1) ltr 2,2 bne $2 l 2,=f'-1' lr 0,2 B $1 $2 equ * l 2,=x'FCFFFFFF' st 2,0(SP) lfunc 15,@stnsm la 1,4 BCALL st 0,76(R12) l 2,ss$&fnum+0(R12) l 0,0(2) lr 1,0 s 1,=f'1' st 1,0(2) lr 2,0 l 2,@cfree st 2,68(R12) l 2,ss$&fnum+0(R12) l 2,4(2) l 4,ss$&fnum+0(R12) l 4,4(4) s 4,68(R12) srda 4,32 d 4,=f'64' sr 2,4 st 2,64(R12) l 2,ss$&fnum+0(R12) l 0,4(2) lr 1,0 a 1,=f'1' st 1,4(2) lr 2,0 st 2,68(R12) l 2,ss$&fnum+0(R12) l 2,4(2) l 3,64(R12) a 3,=f'63' cr 2,3 bh $10000 l 1,ss$&fnum+0(R12) l 2,0(1) ltr 2,2 bne $3 $10000 equ * l 2,ss$&fnum+0(R12) l 1,64(R12) l 3,0(1) a 3,=f'4' st 3,4(2) lr 2,3 l 1,@cfreeli l 2,0(1) l 1,64(R12) st 2,0(1) l 2,64(R12) l 1,@cfreeli st 2,0(1) $3 equ * l 1,68(R12) lc 2,0(1) stc 2,72(R12) l 2,76(R12) st 2,0(SP) lfunc 15,@setsm la 1,4 BCALL sr 2,2 ic 2,72(R12) lr 0,2 B $1 $1 EPILOG 80 DC C'END getc' putc PROLOG 4 2 args sr 2,2 st 2,76(R12) l 2,=x'FCFFFFFF' st 2,0(SP) lfunc 15,@stnsm la 1,4 BCALL st 0,72(R12) l 1,ss$&fnum+4(R12) l 2,0(1) ltr 2,2 bne $5 l 1,@cfreeli l 2,0(1) st 2,64(R12) ltr 2,2 bne $6 l 2,76(R12) lr 0,2 s 0,=f'1' st 0,76(R12) B $7 $6 equ * l 1,@cfreeli l 2,0(1) l 2,0(2) l 1,@cfreeli st 2,0(1) l 2,=f'1' l 1,ss$&fnum+4(R12) st 2,0(1) l 2,ss$&fnum+4(R12) l 3,ss$&fnum+4(R12) l 4,64(R12) a 4,=f'4' st 4,8(3) lr 3,4 st 3,4(2) lr 2,3 l 2,64(R12) l 3,ss$&fnum+0(R12) stc 3,4(2) lr 2,3 B $8 $5 equ * l 2,ss$&fnum+4(R12) l 0,0(2) lr 1,0 a 1,=f'1' st 1,0(2) lr 2,0 l 2,@cfree st 2,68(R12) l 2,ss$&fnum+4(R12) l 2,8(2) l 4,ss$&fnum+4(R12) l 4,8(4) s 4,68(R12) srda 4,32 d 4,=f'64' sr 2,4 st 2,64(R12) l 2,ss$&fnum+4(R12) l 0,8(2) a 0,=f'1' st 0,8(2) lr 2,0 st 2,68(R12) l 2,64(R12) a 2,=f'63' c 2,68(R12) bnl $9 l 1,@cfreeli l 2,0(1) l 1,64(R12) st 2,0(1) ltr 2,2 bne $10 l 2,76(R12) lr 0,2 s 0,=f'1' st 0,76(R12) B $7 $10 equ * l 1,@cfreeli l 2,0(1) l 2,0(2) l 1,@cfreeli st 2,0(1) l 1,64(R12) l 2,0(1) st 2,64(R12) l 2,ss$&fnum+4(R12) l 3,64(R12) a 3,=f'4' st 3,8(2) lr 2,3 st 2,68(R12) $9 equ * l 2,ss$&fnum+0(R12) l 1,68(R12) stc 2,0(1) $8 equ * $7 equ * l 2,72(R12) st 2,0(SP) lfunc 15,@setsm la 1,4 BCALL l 2,76(R12) lr 0,2 B $4 $4 EPILOG 80 DC C'END putc' tvecloc entry getc @getc INTFUNC getc @bfreeli EXTADDR bfreelis @cfreeli EXTADDR cfreelis entry putc @putc INTFUNC putc @cfree EXTADDR cfree @setsm EXTFUNC setsm @stnsm EXTFUNC stnsm @partab EXTADDR partab @buf EXTADDR buf codeloc litloc ltorg end R12) l 3,64(R12) a 3,=f'4' st 3,8(2) lr 2,3 st 2,68(R12) $9 equ * l 2,ss$&fnum+0(R12) l 1,68(R12) stc 2,0(1) $8 equ * $7 equ * l 2,72(R12) st 2,0(SP) lfunc 15,@setsm la 1,4 BCALL l 2,76(R12) lr 0,2 B $4 $4 EPILOG 80 DC C'END putc' tvecloc entry getc @getc INTFUNC getc @bfreeli EXTADDR bfreelis @cfreeli EXTADDR cfreelis entry putc @putc Idio title '***---> dskio -- assembler language assists <---***' dskio csect * * stack contents: * devdesc address * buffer address * cylinder number * head number * record number * sector number * equate regs sp equ r13 oxxx equ b'0111' caw equ x'48' using dskio,r15 using arglist,r1 arglist dsect xdevdsc ds f xbufadr ds f xcc ds f xhh ds f xxr ds f xsector ds f * dskio csect using ddevdsc,devdsc devdsc equ r4 tmp equ r2 tmp1 equ r3 ddevdsc dsect dmaxblk ds f ddevadr ds f dcurcyl ds f ddirect ds x dspares ds 3x dchlpgm equ *,9*8 dccw1 ds d dccw2 ds d dccw3 ds d dccw4 ds d dccw5 ds d dccw6 ds d dccw7 ds d dccw8 ds d dccw9 ds d dbbcc ds f dhh ds h dr ds x dsector ds x * buf dsect buffill ds 4f b$flags ds f b$dev ds f b$count ds f b$addr ds f b$blkno ds f b$resid ds f b$error ds x B$READ equ x'01' * * sense equ x'04' seek equ x'07' rddata equ x'06' tic equ x'08' wrtdata equ x'05' setsctr equ x'23' srchide equ x'31' noop equ x'03' * cc equ x'40' sli equ x'20' skip equ x'10' * dskio csect lr r1,sp sl sp,=f'64' stm r0,r15,0(sp) l devdsc,xdevdsc mvc dchlpgm,stdioseq * * now link the whole bloody mess together... * la tmp,dbbcc stcm tmp,oxxx,dccw1+1 la tmp,dsector stcm tmp,oxxx,dccw2+1 stcm tmp,oxxx,dccw6+1 la tmp,dbbcc+2 stcm tmp,oxxx,dccw3+1 stcm tmp,oxxx,dccw7+1 la tmp,dccw3 stcm tmp,oxxx,dccw4+1 la tmp,dccw7 stcm tmp,oxxx,dccw8+1 l tmp,xbufadr using buf,tmp l tmp1,b$addr stcm tmp1,oxxx,dccw5+1 stcm tmp1,oxxx,dccw9+1 tm b$flags+3,B$READ drop tmp bz dowrite mvi dccw5,rddata mvi dccw6,noop * dowrite $null l tmp,xcc st tmp,dbbcc l tmp,xhh sth tmp,dhh l tmp,xxr stc tmp,dr l tmp,xsector stc tmp,dsector * * here we go... ...going... ...going... ...gone * la tmp,dchlpgm st tmp,caw  l tmp,ddevadr sr tmp1,tmp1 clear for popping into diag... sio 0(tmp) bz return diag tmp1,tmp1,x'8' * return $null lm r0,r15,0(sp) al sp,=f'64' br r14 note that r15 still has entry value stdioseq ds 0d ccw1 ccw seek,0,cc,6 ccw2 ccw setsctr,0,cc,1 ccw3 ccw srchide,0,cc,5 ccw4 ccw tic,0,cc,0 ccw5 ccw wrtdata,0,cc,512 ccw6 ccw setsctr,0,cc,1 ccw7 ccw srchide,0,cc,5 ccw8 ccw tic,0,cc,0 ccw9 ccw rddata,0,skip,512 * end tor * * here we go... ...going... ...going... ...gone * la tmp,dchlpgm st tmp,caw  l tmp,ddevadr sr tmp1,tmp1 clear for popping into diag... sio 0(tmp) bz return diag tmp1,tmp1,x'8' * return $null lm r0,r15,0(sp) al sp,=f'64' br r14 note that r15 still has entry value stdioseq ds 0d ccw1 ccw seek,0,cc,6 ccw2 ccw setsctr,0,cc,1 ccw3 ccw srchide,0,cc,5 ccw4 ccw tic,0,cc,0 ccw5 ccw# /* */ #include "../manifest.h" #include "../param.h" #include "../user.h" #include "../buf.h" #include "../conf.h" #include "../systm.h" #include "../proc.h" #include "../370.h" /* * This is the set of buffers proper, whose heads * were declared in buf.h. There can exist buffer * headers not pointing here that are used purely * as arguments to the I/O routines to describe * I/O to be done-- e.g. swbuf, just below, for * swapping. */ extern char buffers[NBUF][BLKSIZE]; struct buf swbuf; /* * Declarations of the tables for the magtape devices; * see bdwrite. */ int tmtab; int httab; /* * The following several routines allocate and free * buffers with various side effects. In general the * arguments to an allocate routine are a device and * a block number, and the value is a pointer to * to the buffer header; the buffer is marked "busy" * so that no on else can touch it. If the block was * already in core, no I/O need be done; if it is * already busy, the process waits until it becomes free. * The following routines allocate a buffer: * getblk * bread * breada * Eventually the buffer must be released, possibly with the * side effect of writing it out, by using one of * bwrite * bdwrite * bawrite * brelse */ /* * Read in (if necessary) the block and return a buffer pointer. */ bread(dev, blkno) { register struct buf *rbp; rbp = getblk(dev, blkno); if (rbp->b_flags&B_DONE) return(rbp); rbp->b_flags =| B_READ; rbp->b_count = BLKSIZE; (*bdevsw[dev.d_major].d_strategy)(rbp); iowait(rbp); return(rbp); } /* * Read in the block, like bread, but also start I/O on the * read-ahead block (which is not allocated to the caller) */ breada(adev, blkno, rablkno) { register struct buf *rbp, *rabp; register int dev; dev = adev; rbp = 0; if (!incore(dev, blkno)) { rbp = getblk(dev, blkno); if ((rbp->b_flags&B_DONE) == 0) { rbp->b_flags =| B_READ; rbp->b_count = BLKSIZE; (*bdevsw[adev.d_major].d_strategy)(rbp); } } if (rablkno && !incore(dev, rablkno)) { rabp = getblk(dev, rablkno); if (rabp->b_flags & B_DONE) brelse(rabp); else { rabp->b_flags =| B_READ|B_ASYNC; rabp->b_count = BLKSIZE; (*bdevsw[adev.d_major].d_strategy)(rabp); } } if (rbp==0) return(bread(dev, blkno)); iowait(rbp); return(rbp); } /* * Write the buffer, waiting for completion. * Then release the buffer. */ bwrite(bp) struct buf *bp; { register struct buf *rbp; register flag; rbp = bp; flag = rbp->b_flags; rbp->b_flags =& ~(B_READ | B_DONE | B_ERROR | B_DELWRI); rbp->b_count = BLKSIZE; (*bdevsw[rbp->b_dev.d_major].d_strategy)(rbp); if ((flag&B_ASYNC) == 0) { iowait(rbp); brelse(rbp); } else if ((flag&B_DELWRI)==0) geterror(rbp); } /* * Release the buffer, marking it so that if it is grabbed * for another purpose it will be written out before being * given up (e.g. when writing a partial block where it is * assumed that another write for the same block will soon follow). * This can't be done for magtape, since writes must be done * in the same order as requested. */ bdwrite(bp) struct buf *bp; { register struct buf *rbp; register struct devtab *dp; rbp = bp; dp = bdevsw[rbp->b_dev.d_major].d_tab; if (dp == &tmtab || dp == &httab) bawrite(rbp); else { rbp->b_flags =| B_DELWRI | B_DONE; brelse(rbp); } } /* * Release the buffer, start I/O on it, but don't wait for completion. */ bawrite(bp) struct buf *bp; { register struct buf *rbp; rbp = bp; rbp->b_flags =| B_ASYNC; bwrite(rbp); } /* * release the buffer, with no I/O implied. */ brelse(bp) struct buf *bp; { register struct buf *rbp, **backp; register int sps; rbp = bp; if (rbp->b_flags&B_WANTED) wakeup(rbp); if (bfreelist.b_flags&B_WANTED) { bfreelist.b_flags =& ~B_WANTED; wakeup(&bfreelist); } if (rbp->b_flags&B_ERROR) rbp->b_dev.d_minor = NODEV; /* no assoc. on error */ backp = &bfreelist.av_back; sps = stnsm(~ALLINT); rbp->b_flags =& ~(B_WANTED|B_BUSY|B_ASYNC); (*backp)->av_forw = rbp; rbp->av_back = *backp; *backp = rbp; rbp->av_forw = &bfreelist; setsm(sps); } /* * See if the block is associated with some buffer * (mainly to avoid getting hung up on a wait in breada) */ incore(adev, blkno) { register int dev; register struct buf *bp; register struct devtab *dp; dev = adev; dp = bdevsw[adev.d_major].d_tab; for (bp=dp->b_forw; bp != dp; bp = bp->b_forw) if (bp->b_blkno==blkno && bp->b_dev==dev) return(bp); return(0); } /* * Assign a buffer for the given block. If the appropriate * block is already associated, return it; otherwise search * for the oldest non-busy buffer and reassign it. * When a BLKSIZE-byte area is wanted for some random reason * (e.g. during exec, for the user arglist) getblk can be called * with device NODEV to avoid unwanted associativity. * Since the scheduler never switches in system state, * no one could mark the block BUSY between our finding * it and allocating it, so long as the interrupt routines don't. */ getblk(dev, blkno) { register struct buf *bp; register struct devtab *dp; extern lbolt; if(dev.d_major >= nblkdev && dev != NODEV) panic("blkdev"); loop: if (dev < 0) dp = &bfreelist; else { dp = bdevsw[dev.d_major].d_tab; if(dp == NULL) panic("devtab"); for (bp=dp->b_forw; bp != dp; bp = bp->b_forw) { if (bp->b_blkno!=blkno || bp->b_dev!=dev) continue; stnsm(~ALLINT); if (bp->b_flags&B_BUSY) { bp->b_flags =| B_WANTED; sleep(bp, PRIBIO); stosm(ALLINT); goto loop; } stosm(ALLINT); notavail(bp); return(bp); } } stnsm(~ALLINT); if (bfreelist.av_forw == &bfreelist) { bfreelist.b_flags =| B_WANTED; sleep(&bfreelist, PRIBIO); stosm(ALLINT); goto loop; } stosm(ALLINT); notavail(bp = bfreelist.av_forw); if (bp->b_flags & B_DELWRI) { bp->b_flags =| B_ASYNC; bwrite(bp); goto loop; } bp->b_flags = B_BUSY | B_RELOC; bp->b_back->b_forw = bp->b_forw; bp->b_forw->b_back = bp->b_back; bp->b_forw = dp->b_forw; bp->b_back = dp; dp->b_forw->b_back = bp; dp->b_forw = bp; bp->b_dev = dev; bp->b_blkno = blkno; return(bp); } /* * Wait for I/O completion on the buffer; return errors * to the user. */ iowait(bp) struct buf *bp; { register struct buf *rbp; rbp = bp; stnsm(~ALLINT); while ((rbp->b_flags&B_DONE)==0) sleep(rbp, PRIBIO); stosm(ALLINT); geterror(rbp); } /* * Unlink a buffer from the available list and mark it busy. * (internal interface) */ notavail(bp) struct buf *bp; { register struct buf *rbp; register int sps; rbp = bp; sps = stnsm(~ALLINT); rbp->av_back->av_forw = rbp->av_forw; rbp->av_forw->av_back = rbp->av_back; rbp->b_flags =| B_BUSY; setsm(sps); } /* * Mark I/O complete on a buffer, release it if I/O is asynchronous, * and wake up anyone waiting for it. * We seem to do that even if no one is actually waiting, but * that is all right since we just trundle thru the process table * looking for matches, not mandating that they be found (still, * it seems somewhat sloppy to me...) */ iodone(bp) struct buf *bp; { register struct buf *rbp; rbp = bp; rbp->b_flags =| B_DONE; if (rbp->b_flags&B_ASYNC) brelse(rbp); else { rbp->b_flags =& ~B_WANTED; wakeup(rbp); } } /* * Zero the core associated with a buffer. */ /* this routine is now done in assembler clrbuf(bp) int *bp; { register *p; register c; p = bp->b_addr; c = 256; do *p++ = 0; while (--c); } */ /* * Initialize the buffer I/O system by freeing * all buffers and setting all device buffer lists to empty. */ binit() { register struct buf *bp; register struct devtab *dp; register int i; struct bdevsw *bdp; bfreelist.b_forw = bfreelist.b_back = bfreelist.av_forw = bfreelist.av_back = &bfreelist; for (i=0; ib_dev = NODEV; bp->b_addr = buffers[i]; bp->b_back = &bfreelist; bp->b_forw = bfreelist.b_forw; bfreelist.b_forw->b_back = bp; bfreelist.b_forw = bp; bp->b_flags = B_BUSY; brelse(bp); } i = 0; for (bdp = bdevsw; bdp->d_open; bdp++) { dp = bdp->d_tab; if(dp) { dp->b_forw = dp; dp->b_back = dp; } i++; } nblkdev = i; } /* * make sure all write-behind blocks * on dev (or NODEV for all) * are flushed out. * (from umount and update) */ bflush(dev) { register struct buf *bp; loop: stnsm(~ALLINT); for (bp = bfreelist.av_forw; bp != &bfreelist; bp = bp->av_forw) { if (bp->b_flags&B_DELWRI && (dev == NODEV||dev==bp->b_dev)) { bp->b_flags =| B_ASYNC; notavail(bp); bwrite(bp); goto loop; } } stosm(ALLINT); } /* * Raw I/O. The arguments are * The strategy routine for the device * A buffer, which will always be a special buffer * header owned exclusively by the device for this purpose * The device number * Read/write flag * Essentially all the work is computing physical addresses and * validating them. */ /* this routine has been removed because it needs * substantial rewriting for the 370 */ /* * Pick up the device's error number and pass it to the user; * if there is an error but the number is 0 set a generalized * code. Actually the latter is always true because devices * don't yet return specific errors. */ geterror(abp) struct buf *abp; { register struct buf *bp; bp = abp; if (bp->b_flags&B_ERROR) if ((u->u_error = bp->b_error)==0) u->u_error = EIO; } resses and * validating them. */ /* this routine has been removed because it needs * substantial rewriting for the 370 */ /* * Pick up the device's error number and pass it to the user; * if there is an error but the number is 0 set a generalized * code. Actually the latter is always true because devices * don't yet return sp# /* * (3340) disk driver */ #include "../manifest.h" #include "../param.h" #include "../370.h" #include "../conf.h" #include "../user.h" /* for appropriate errors */ #include "../buf.h" #include "../dsk.h" #define DSKBLKS 1428 /* 10 cyls of 3340 - track 0 */ dskopen(majmin,direction) { struct devdesc *ddp; ddp = bdevsw[majmin.d_major].d_tab->dd_ptr; if (ddp->d_init) return; ddp->d_init++; ddp->d_maxblk = DSKBLKS; /* * Actually, I wanted to find out the blocks on the (mini) disk * and not hard code a constant... Also, the channel program would * be copied and linked together, requiring the dskio subroutine to * simply zap a bit or two... */ ddp->d_devadr = bdevsw[majmin.d_major].d_baseaddr; ddp->d_curcyl = ddp->d_direct = 0; } dskstrategy(bp) struct buf *bp; { struct devtab *dtp; int oldsm; dtp = bdevsw[bp->b_dev.d_major].d_tab; if (bp->b_blkno > dtp->dd_ptr->d_maxblk) { bp->b_flags =| B_ERROR; iodone(bp); return; } oldsm = stnsm(~ALLINT); if (dtp->d_actf == 0) dtp->d_actf = bp; else dtp->d_actl->av_forw = bp; bp->av_forw = 0; dtp->d_actl = bp; if (dtp->d_active == 0) dskstart(dtp); setsm(oldsm); } dskstart(dtp) struct devtab *dtp; { struct dskdesc *dskdsc; struct devdesc *devdsc; struct buf *bp; int cc,hh,xr,sector,tmp; devdsc = dtp->dd_ptr; if ((bp = dtp->d_actf) == 0) return; dskdsc = &dskdesc[bp->b_dev.d_minor]; dtp->d_active++; /* note that queue is active */ xr = ((tmp = bp->b_blkno)%dskdsc->d_blktrk)+1; tmp = tmp/dskdsc->d_blktrk+1; /* skip first record and track */ hh = tmp%dskdsc->d_trkcyl; cc = tmp/dskdsc->d_trkcyl; sector = (dskdsc->d_const+(xr-1)*dskdsc->d_factor)/dskdsc->d_divsor; dskio(devdsc,bp,cc,hh,xr,sector); } /* * interrupt routine which recieves control in disabled state. */ dskintr(majmin) { struct devtab *dtp; struct buf *bp; dtp = bdevsw[majmin.d_major].d_tab; if (dtp->d_active == 0) { printf("dsk: unsolicited interrupt %x\n",IOADDR->integ); return; } bp = dtp->d_actf; if (CSW->unitstat&(ATTENTION|BUSY|UNITCHEK|UNITEXCP)) panic("dsk: unit stat"); if (CSW->chanstat) panic("dsk: chan stat"); if (CSW->unitstat&DEVEND == 0) return; /* wait for device end */ dtp->d_active = 0; dtp->d_actf = bp->av_forw; iodone(bp); if (dtp->d_actf) dskstart(dtp); } nointr(parm) { printf("no iolkptab entry: %x\n",IOADDR->integ); } devsw[majmin.d_major].d_tab; if (dtp->d_active == 0) { printf("dsk: unsolicited interrupt %x\n",IOADDR->integ); return; } bp = dtp->d_actf; if (CSW->unitstat&(ATTENTION|BUSY# #include "../filsys.h" #include "../ino.h" #define BLKSIZE 512 /* bytes */ #define BLKSIZEW (BLKSIZE/4) #define DIRENTSZ 16 #define DIRSIZ 14 #define MAXF_N 200 struct filsys filsys; struct minode { int i_number; struct inode inoed; }; fortran bio(),blkrd(),blkwrt(),indir(),filsiz(); extern cin; extern urnleft; extern char etatab[]; char *charp; int buf[BLKSIZEW]; char string[50]; char *fsys; char *proto; int diskaddr; /* * for 3340's... */ int f_n 144; int f_m 3; int f_l 12; /* blocks per track */ main(argc, argv) char **argv; { int f, n; /* * open relevent files */ if(argc != 3) { printf("arg count\n"); cexit(12); } fsys = argv[1]; proto = argv[2]; scanf(-1,fsys,"%x",&diskaddr); /* initialize disk routine */ if (bio(diskaddr)) { printf("BIO error\n"); cexit(12); } cin = copen(proto,'r'); if(cin < 0) { n = 0; for (f=0; proto[f]; f++) { if (proto[f]<'0' || proto[f]>'9') { printf("%s: cannoot open\n",proto); cexit(12); } n = n*10 + proto[f]-'0'; } filsys.s_fsize = n; filsys.s_isize = n/(43+n/1000)<<1; printf("isize = %d\n",filsys.s_isize); if (f_n != 1) printf("free list %d/%d\n", f_m, f_n); charp = "d--777 0 0 $ "; goto f3; } /* * get name of file * and read onto block 0 */ getstr(); f = copen(string,'r'); if (f<0) { printf("%s: cannot open block 0 file\n",string); goto f2; } for (n=0; n filsys.s_fsize || filsys.s_fsize-filsys.s_isize-2 < filsys.s_isize) { printf("%d/%d: bad ratio\n", filsys.s_fsize, filsys.s_isize); cexit(12); } /* * initialize block free list */ bflist(); /* * initialize files */ for(n=0; n'7') { printf("%c/%s: bad digit\n", string[i], string); cexit(12); } in.inoed.i_mode =| (string[i]-'0')<<(15-3*i); } in.inoed.i_uid = getnum(); in.inoed.i_gid = getnum(); /* * general initialization prior to * switching on format */ in.i_number = ++ino; if (ino/IPERBLK > filsys.s_isize) { printf("too many inodes\n"); cexit(12); } in.inoed.i_nlink = 1; in.inoed.i_size = 0; for(i=0; i 0) { in.inoed.i_size =+ i; newblk(&dbc, db, &ibc, ib); if (in.inoed.i_size >= tmp) break; } cclose(f); in.inoed.i_size = tmp; break; case IFBLK: case IFCHR: /* * special file * content is maj/min types */ in.inoed.i_addr[0] = getnum()<<8; in.inoed.i_addr[0] =| getnum(); break; case IFDIR: /* * directory * put in extra links * call recursively until * name of "$" found */ par->inoed.i_nlink++; direntry(par->i_number, "..", &dbc, db, &ibc, ib); in.inoed.i_nlink++; direntry(in.i_number, ".", &dbc, db, &ibc, ib); in.inoed.i_size = DIRENTSZ<<1; for(;;) { getstr(); if(string[0]=='$' && string[1]=='\0') break; direntry(ino+1, string, &dbc, db, &ibc, ib); in.inoed.i_size =+ DIRENTSZ; cfile(&in); } break; } if(dbc != 0) newblk(&dbc, db, &ibc, ib); if(ibc > IADDRSIZ) { in.inoed.i_mode =| ILARG; dbc = alloc(); wtfs(dbc, ib); in.inoed.i_addr[0] = dbc; } else for(i=0; i'9') { printf("%s: bad number\n", string); cexit(12); } n = n*10 + string[i] - '0'; } return(n); } getstr() { int i, c; loop: switch(c=getch()) { case ' ': case '\t': case '\n': goto loop; case '\0': printf("EOF\n"); cexit(12); case ':': while(getch() != '\n'); goto loop; } i = 0; do { string[i++] = c; c = getch(); } while(c!=' '&&c!='\t'&&c!='\n'&&c!='\0'); string[i] = '\0'; } rdfs(bno,bf) { indir(blkrd,bf,bno); } wtfs(bno, bf) { indir(blkwrt,bf,bno); } alloc() { int bno, i; filsys.s_nfree--; bno = filsys.s_free[filsys.s_nfree]; filsys.s_free[filsys.s_nfree] = 0; if(bno == 0) { printf("out of free space\n"); cexit(12); } if(filsys.s_nfree <= 0) { rdfs(bno, buf); filsys.s_nfree = buf[0]; for(i=0; i= SFREESIZ) { buf[0] = filsys.s_nfree; for(i=0; i= BLKSIZEW) newblk(adbc, db, aibc, ib); } strhalf(target,source) char *target; { struct { char z_fill1,z_fill2; char highbyte,lowbyte; }; target[0] = source.highbyte; target[1] = source.lowbyte; } newblk(adbc, db, aibc, ib) int *adbc, *db, *aibc, *ib; { int bno, i; bno = alloc(); wtfs(bno, db); for(i=0; i= BLKSIZEW) { printf("indirect block full\n"); cexit(12); } } getch() { if(charp) return(*charp++); return(getchar()); } bflist() { char flg[MAXF_N], adr[MAXF_N]; register i, j; char *low, *high; if(f_n > MAXF_N) f_n = MAXF_N; for(i=0; i= low+f_n; i =- f_n) for(j=0; j= low; i--) free(i); } (f_n > MAXF_N) f_n = MAXF_N; for(i=0; iu_arsp[R1]; suword(up, *vp++); suword(++up, *vp++); suword(++up, *vp++); } /* * The routine implementing the stty system call. * Read in values and call lower level. */ stty() { register int *up; up = u->u_arsp[R1]; u.u_arg[0] = fuword(up); u.u_arg[1] = fuword(++up); u.u_arg[2] = fuword(++up); sgtty(0); } /* * Stuff common to stty and gtty. * Check legality and switch out to individual * device routine. * v is 0 for stty; the parameters are taken from u.u_arg[]. * c is non-zero for gtty and is the place in which the device * routines place their information. */ sgtty(v) int *v; { register struct file *fp; register struct inode *ip; if ((fp = getf(u.u_arsp[R0])) == NULL) return; ip = fp->f_inode; if ((ip->i_mode&IFMT) != IFCHR) { u.u_error = ENOTTY; return; } (*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], v); } /* * Wait for output to drain, then flush input waiting. */ wflushtty(atp) struct tty *atp; { register struct tty *tp; int sps; tp = atp; sps = stnsm(~IOINT); while (tp->t_outq.c_cc) { sleep(&tp->t_outq, TTOPRI); } flushtty(tp); setsm(sps); } /* * Initialize clist by freeing all character blocks, then count * number of character devices. (Once-only routine) */ cinit() { register int ccp; register struct cblock *cp; register struct cdevsw *cdp; cfreelist = 0; cp = cfree; for(cp=cfree;cp<= &cfree[NCLIST-1];cp++){ cp->c_next = cfreelist; cfreelist = cp; } ccp = 0; for(cdp = cdevsw; cdp->d_open; cdp++) ccp++; nchrdev = ccp; trminit(); } /* * flush all TTY queues */ flushtty(atp) struct tty *atp; { register struct tty *tp; register int sps; tp = atp; while (getc(&tp->t_canq) >= 0); while (getc(&tp->t_outq) >= 0); wakeup(&tp->t_rawq); wakeup(&tp->t_outq); sps = stnsm(~ALLINT); while (getc(&tp->t_rawq) >= 0); tp->t_delct = 0; setsm(sps); } /* * transfer raw input list to canonical list, * doing erase-kill processing and handling escapes. * It waits until a full line has been typed in cooked mode, * or until any character has been typed in raw mode. */ canon(atp) struct tty *atp; { register char *bp; char *bp1; register struct tty *tp; register int c; int sps; tp = atp; sps = stnsm(~IOINT); while (tp->t_delct==0) { if(tp->t_state == 0) return(0); sleep(&tp->t_rawq, TTIPRI); } setsm(sps); loop: bp = &canonb[2]; while ((c=getc(&tp->t_rawq)) >= 0) { if (c==CDLM) { tp->t_delct--; break; } if ((tp->t_flags&RAW)==0) { if (bp[-1]!='\\') { if (c==tp->t_erase) { if (bp > &canonb[2]) bp--; continue; } if (c==tp->t_kill) goto loop; if (c==CEOT) continue; } else if (maptab[c] && (maptab[c]==c || (tp->t_flags&LCASE))) { if (bp[-2] != '\\') c = maptab[c]; bp--; } } *bp++ = c; if (bp>=canonb+CANBSIZ) break; } bp1 = bp; bp = &canonb[2]; c = &tp->t_canq; while (bpt_flags; if ((c =& 0177) == '\r' && t_flags&CRMOD){ c = '\n'; putc(c, &tp->t_outq); } if ((t_flags&RAW)==0 && (c==CQUIT || c==CINTR)) { signal(tp, c==CINTR? SIGINT:SIGQIT); flushtty(tp); return; } if (tp->t_rawq.c_cc>=TTYHOG) { flushtty(tp); return; } if (t_flags&LCASE && c>='A' && c<='Z') c =+ 'a'-'A'; putc(c, &tp->t_rawq); if(t_flags&RAW || c=='\n' || c==CEOT){ wakeup(&tp->t_rawq); if (putc(CDLM, &tp->t_rawq)==0) tp->t_delct++; } /* if (t_flags&ECHO) { ttyoutput(c, tp); ttstart(tp); } */ } /* * put character on TTY output queue, adding delays, * expanding tabs, and handling the CR/NL bit. * It is called both from the top half for output, and from * interrupt level for echoing. * The arguments are the character and the tty structure. */ ttyoutput(ac, tp) struct tty *tp; { register int c; register struct tty *rtp; register char *colp; int ctype; rtp = tp; c = ac&0177; /* * Ignore EOT in normal mode to avoid hanging up * certain terminals. */ if (c==CEOT && (rtp->t_flags&RAW)==0) return; /* * Turn tabs to spaces as required */ if (c=='\t' && rtp->t_flags&XTABS) { do ttyoutput(' ', rtp); while (rtp->t_col&07 && --c); /* the --c is to prevent hanging if no buffer space */ return; } /* * for upper-case-only terminals, * generate escapes. */ if (rtp->t_flags&LCASE) { colp = "({)}!|^~'`"; while(*colp++) if(c == *colp++) { ttyoutput('\\', rtp); c = colp[-2]; break; } if ('a'<=c && c<='z') c =+ 'A' - 'a'; } /* * turn to if desired. */ if (c=='\n' && rtp->t_flags&CRMOD) ttyoutput('\r', rtp); if (putc(c, &rtp->t_outq)) return; /* * Calculate delays and current column... * The numbers here represent clock ticks * and are not necessarily optimal for all terminals. * The delays are indicated by characters above 0200, * thus (unfortunately) restricting the transmission * path to 7 bits (sign bit also toyed with during normal input) */ colp = &rtp->t_col; ctype = partab[c]; c = 0; switch (ctype&077) { /* ordinary */ case 0: break; (*colp)++; /* non-printing */ case 1: break; /* backspace */ case 2: if (*colp) (*colp)--; break; /* newline (i.e., line feed) */ case 3: if(rtp->t_flags&(NLDELAY&(NLDELAY>>1))) { /* tty 37 */ if (*colp) c = max((*colp>>4) + 3, 6); } else if(rtp->t_flags&(NLDELAY&(NLDELAY<<1))) { /* vt05 */ c = 6; } *colp = 0; break; /* tab */ case 4: if(rtp->t_flags&(TBDELAY&(TBDELAY>>1))) { /* tty 37 */ c = 1 - (*colp | ~07); if(c < 5) c = 0; } *colp =| 07; (*colp)++; break; /* vertical motion */ case 5: if(rtp->t_flags & VTDELAY) /* tty 37 */ putc(PADCHAR,&rtp->t_outq); break; /* carriage return */ case 6: if(rtp->t_flags&(CRDELAY&(CRDELAY>>1))) { /* tn 300 */ c = 5; } else if(rtp->t_flags&(CRDELAY&(CRDELAY<<1))) { /* ti 700 */ c = 10; } *colp = 0; } c = (c+1)/2; for(; c; c--) putc(PADCHAR,&rtp->t_outq); } /* * Restart typewriter output following a delay * timeout. * The name of the routine is passed to the timeout * subroutine and it is called during a clock interrupt. * * Goes away for 370. ttrstrt(atp) { register struct tty *tp; tp = atp; tp->t_state =& ~TIMEOUT; ttstart(tp); } */ /* * Start output on the typewriter. It is used from the top half * after some characters have been put on the output queue. */ ttstart(atp) struct tty *atp; { register int *addr, c; register struct tty *tp; struct { int (*func)(); }; int pssave; tp = atp; addr = tp->t_addr; /* if (tp->t_state&SSTART) { */ (*addr.func)(tp); return; /* } */ /***out we go... pssave = stnsm(~ALLINT); if(addr->tttcsr&DONE && (tp->t_state&TIMEOUT) == 0 && (c=getc(&tp->t_outq)) >= 0) { if (c<=0177) addr->tttbuf = c | (partab[c]&0200); else { timeout(ttrstrt, tp, c&0177); tp->t_state =| TIMEOUT; } } setsm(pssave); ...***/ } /* * Called from device's read routine after it has * calculated the tty-structure given as argument. * The pc is backed up for the duration of this call. * In case of a caught interrupt, an RTI will re-execute. */ ttread(atp) struct tty *atp; { register struct tty *tp; tp = atp; if(tp->t_state == 0) return; if (tp->t_canq.c_cc || canon(tp)) while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0); } /* * Called from the device's write routine after it has * calculated the tty-structure given as argument. */ ttwrite(atp) struct tty *atp; { register struct tty *tp; register int c; tp = atp; if(tp->t_state == 0) return; while ((c=cpass())>=0) { stnsm(~ALLINT); while (tp->t_outq.c_cc > TTHIWAT) { ttstart(tp); sleep(&tp->t_outq, TTOPRI); } stosm(ALLINT); ttyoutput(c, tp); } ttstart(tp); } /* * Common code for gtty and stty functions on typewriters. * If v is non-zero then gtty is being done and information is * passed back therein; * if it is zero stty is being done and the input information is in the * u_arg array. */ ttystty(atp, av) int *atp, *av; { register *tp, *v; tp = atp; if(v = av) { *v++ = tp->t_speeds; v->lobyte = tp->t_erase; v->hibyte = tp->t_kill; v[1] = tp->t_flags; return(1); } wflushtty(tp); v = u->u_arsp[R1]; tp->t_speeds = *v++; tp->t_erase = v->lobyte; tp->t_kill = v->hibyte; tp->t_flags = v[1]; return(0); } v is non-zero then gtty is being done and information is * passed back therein; * if it is zero stty is being done and the #include "../conf.h" #include "../manifest.h" #include "../param.h" #include "../user.h" /* * Memory special file * minor device 0 is physical memory * minor device 1 is EOF/RATHOLE */ mmread(dev) { char *cp; extern char *memlim; if(dev.d_minor == 1) return; do{ cp = u->u_offset; if(cp > memlim) return; }while(u->u_error == 0 && passc(*cp) >= 0); } mmwrite(dev) { char c; extern char *memlim; if(dev.d_minor == 1){ u->u_base =+ u->u_count; u->u_offset =+ u->u_count; u->u_count = 0; return; } while( u->u_offset <= memlim && (c = cpass()) >= 0 && u->u_error == 0) *u->u_offset = c; } ial file * minor device 0 is physical memory * minor device 1 is EOF/RATHOLE */ mmread(dev) { char *cp; extern char *memlim; if(dev.d_minor == 1) return; do{ cp = u->u_offset; if(cp > memlim) return; }while(u->u_error == 0 && passc(*cp) >= 0); } mmwrite(dev) { char c; extern char *memlim; if(dev.d_minor == 1){ u->u_base =+ u->u_count; u->u_offset =+ u->u_count; u->u_count = 0; #include "../370.h" #include "../param.h" #include "../buf.h" #include "../tty.h" extern struct cblock cfree[]; extern struct cblock *cfreelist; getc(q) struct clist *q; { struct cblock *cbp; char *cp,c; int sps; if(q->c_cc == 0) return(-1); sps = stnsm(~ALLINT); q->c_cc--; cp = cfree; /* kludge */ cbp = q->c_cf - (q->c_cf - cp) % sizeof *cbp; cp = q->c_cf++; if(q->c_cf > &cbp->info[NINFO-1] || q->c_cc == 0 ){ q->c_cf = &cbp->c_next->info[0]; cbp->c_next = cfreelist; cfreelist = cbp; } c = *cp; setsm(sps); return(c); } putc(c,q) char c; struct clist *q; { struct cblock *cbp; char *cp; int sps,rc; rc = 0; sps = stnsm(~ALLINT); if(q->c_cc == 0) { if((cbp = cfreelist) == 0){ rc--; goto out; } cfreelist = cfreelist->c_next; q->c_cc = 1; q->c_cf = q->c_cl = &cbp->info[0]; cbp->info[0] = c; } else { q->c_cc++; cp = cfree; /* kludge */ cbp = q->c_cl - (q->c_cl - cp) % sizeof *cbp; cp = ++q->c_cl; if(cp > &cbp->info[NINFO-1]){ if((cbp->c_next = cfreelist) == 0){ rc--; goto out; } cfreelist = cfreelist->c_next; cbp = cbp->c_next; cp = q->c_cl = &cbp->info[0]; } *cp = c; } out: setsm(sps); return(rc); } ((cbp = cfreelist) == 0){ rc--; goto out; } cfreelist = cfreelist->c_next; q->c_cc = 1; q->c_cf = q->c_cl = &cbp->info[0]; cbp->info[0] = c; } else { q->c_cc++; cp = cfree; /* kludge */ cbp = q->c_cl - (q->c_cl - cp) % sizeof *cbp; cp = ++q->c_cl; if(cp > &cbp->info[NINFO-1]){ if((cbp->c_next = cfreelist) =main() { int i,n; extern int fout; char buf[512]; for(;;){ fout = open("/dev/tty0",2); printf("\n\nUnix/370\n"); i=0; while((n=read(fout,buf,512)) && i++ < 20) write(fout,buf,n); printf("\nBye now! Y'all come back now, hear?\n"); close(fout); } } c = 1; q->c_cf = q->c_cl = &cbp->info[0]; cbp->info[0] = c; } else { q->c_cc++; cp = cfree; /* kludge */ cbp = q->c_cl - (q->c_cl - cp) % sizeof *cbp; cp = ++q->c_cl; if(cp > &cbp->info[NINFO-1]){ if((cbp->c_next = cfreelist) =# #include "../370.h" #include "../param.h" #include "../buf.h" #include "../conf.h" #include "../manifest.h" #include "../proc.h" #include "../systm.h" #include "../tty.h" #include "../user.h" /* States that the terminal driver is in when expecting an interrupt */ #define ENABLING 1 #define WAITING 2 #define READING 3 #define HALTING 4 #define WRITING 5 #define SENSING 6 #define PREPARING 7 #define DISABLING 8 struct tty trms[NTRMS]; extern char revbit[]; /* * Upper level routines: trmopen, trmclose, trmread, trmwrite, * trmsgtty, and trmstart. */ /* * Open a terminal. If already open, do nothing, otherwise set * default values and issue enable for line. */ trmopen(dev,flag) { struct tty *tp; int sps,trmstart(); if(dev.d_minor >= NTRMS){ u->u_error = ENXIO; return; } tp = &trms[dev.d_minor]; if(tp->t_state) return; /* already open */ tp->t_dev = dev; tp->t_devaddr = cdevsw[dev.d_major].d_baseaddr + dev.d_minor; tp->t_addr = &trmstart; tp->t_flags = XTABS|CRMOD; tp->t_erase = CERASE; tp->t_kill = CKILL; /* Get input and output buffers */ tp->t_ibp = getblk(NODEV); tp->t_obp = getblk(NODEV); /* Enable line */ sps = stnsm(~IOINT); enb: switch(trmenbl(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY)goto enb; /* busy */ break; /* shouldn't happen */ case 2: /* channel busy */ goto enb; case 3: /* not operational */ setsm(sps); u->u_error = EIO; brelse(tp->t_ibp); brelse(tp->t_obp); return; } tp->t_state = ENABLING; setsm(sps); if(u->u_procp->p_ttyp == 0) u->u_procp->p_ttyp = tp; } /* * Close a terminal. Flush queues, halt activity, and disable line. */ trmclose(dev) { struct tty *tp; int sps; tp = &trms[dev.d_minor]; wflushtty(tp); /* flush */ sps = stnsm(~IOINT); if(tp->t_state != WRITING){ tp->t_state = DISABLING; /* There should now be a read active, which we halt before disabling */ hlt: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ stosm(IOINT); /* let 'er rip */ stnsm(~IOINT); goto hlt; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto hlt; case 3: /* not operational */ trmerr(tp,"halting for disable"); } } else tp->t_state = DISABLING; /* * During sleep, we get an interrupt indicating * I/O was halted, or write completed. We are * still disabled when we return. */ while(tp->t_state) sleep(&tp->t_state, TTOPRI); tp->t_state = DISABLING; dsbl: switch(trmdsbl(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto dsbl; /* busy */ break; /* shouldn't happen */ case 2: /* channel busy */ goto dsbl; case 3: /* not operational */ trmerr(tp,"disabling"); } setsm(sps); brelse(tp->t_ibp); brelse(tp->t_obp); } trmread(dev) { ttread(&trms[dev.d_minor]); } trmwrite(dev) { ttwrite(&trms[dev.d_minor]); } trmsgtty(dev,v) { ttystty(&trms[dev.d_minor],v); } /* * Start output to a terminal. * If we waiting for something to be typed, * halt read and start writing. */ trmstart(tp) struct tty *tp; { int sps; sps = stnsm(~IOINT); if(tp->t_state == WAITING){ hrd: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ stosm(IOINT); /* let 'er rip */ stnsm(~IOINT); if(tp->t_state == WAITING) goto hrd; break; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto hrd; case 3: /* not operational */ trmdead(tp); } tp->t_state = HALTING; } setsm(sps); } /* * Interrupt level routines: trmintr, trmnorm, trmrd, and trmsens. */ /* * Terminal interrupt routine. Entire routine is run disabled. */ trmintr(dev) { struct tty *tp; int sps,len; char *cp; tp = &trms[dev.d_minor]; /* Huge switch by current state */ switch(tp->t_state){ case 0: /* nothing happening */ return; case ENABLING: /* someone has dialed up */ if(CSW->unitstat == CHEND+DEVEND) trmnorm(tp); else trmdead(tp); /* shouldn't happen */ return; case WAITING: case READING: /* terminal input */ if(CSW->unitstat == 0 && CSW->chanstat&PCI){ /* typing begun, but not finished */ tp->t_state = READING; return; } len = tp->t_il - ((CSW->resid1<<8) | CSW->resid2); cp = tp->t_ibp->b_addr; while(len--) ttyinput(revbit[*cp++],tp); /* process chars */ if(CSW->unitstat == CHEND+DEVEND){ /* normally terminated read */ trmnorm(tp); return; } if(CSW->unitstat == CHEND+DEVEND+UNITCHEK){ /* read terminated by break */ trmsens(tp); return; } trmerr(tp,"reading"); trmnorm(tp); return; case HALTING: /* read was halted to begin write */ trmnorm(tp); return; case WRITING: /* data written */ tp->t_wresid = (CSW->resid1 << 8) | CSW->resid2; if(CSW->unitstat == CHEND+DEVEND){ /* normal write */ trmnorm(tp); return; } if(CSW->unitstat == CHEND+DEVEND+UNITCHEK){ /* write interrupted by break */ trmsens(tp); return; } trmerr(tp,"writing"); trmnorm(tp); return; case SENSING: /* getting sense info */ if(tp->t_sense & INTREQ){ /* * Either break was hit, or the line went dead. * In order to tell the difference, we issue a * prepare command and attempt to halt it. If the * halt works, the line is allright, if not, we'll * get a later interrupt telling us the line is okay, * or the line timed out. */ tp->t_state = PREPARING; prep: switch(trmprep(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat==STATMOD+CUEND+BUSY)goto prep; break; /* shouldn't happen */ case 2: /* channel busy */ goto prep; case 3: /* not operational */ trmdead(tp); return; } /* now attempt halt */ halt: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ return; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto halt; case 3: /* not operational */ trmdead(tp); return; } return; } if(tp->t_sense & TIMEOUT){ /* * Line has been dead for 28 seconds, so assume hangup. */ trmhup(tp); return; } printf("dev=%x,sense=%x\n",tp->t_devaddr,tp->t_sense); trmnorm(tp); return; case PREPARING: /* Waiting for line to come to life. */ switch(CSW->unitstat){ case CHEND+DEVEND: case CHEND+DEVEND+UNITEXCP: trmrd(tp); /* * Make state READING rather than WAITING, so * that read will not be halted in trmstart. */ tp->t_state = READING; return; case CHEND+DEVEND+UNITCHEK: trmsens(tp); return; default: trmerr(tp,"preparing"); trmnorm(tp); return; } case DISABLING: tp->t_state = 0; wakeup(&tp->t_state); return; } /* end of state switch */ } /* * Perform normal action for the terminal. * If we need to restart an interrupted write, do so, * if there is anything to write, write it, * else read. */ trmnorm(tp) struct tty *tp; { int i,len; char *cp; if(tp->t_wresid > 0) { /* restart write */ wr: switch(wrterm(tp->t_devaddr,tp->t_obp->b_addr+tp->t_wresid, tp->t_wresid)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto wr; break; /* shouldn't happen */ case 2: /* channel busy */ goto wr; case 3: /* not operational */ trmdead(tp); return; } tp->t_state = WRITING; return; } if(tp->t_outq.c_cc == 0){ /* read */ trmrd(tp); return; } /* write */ cp = tp->t_obp->b_addr; len = min(BLKSIZE, tp->t_outq.c_cc); for(i=0;it_outq)]; tp->t_state = WRITING; w: switch(wrterm(tp->t_devaddr,tp->t_obp->b_addr,len)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto w; break; /* shouldn't happen */ case 2: /* channel busy */ goto w; case 3: /* not operational */ trmdead(tp); return; } wakeup(&tp->t_outq); } /* * Read! */ trmrd(tp) struct tty *tp; { tp->t_state = WAITING; tp->t_il = BLKSIZE; r: switch(rdterm(tp->t_devaddr,tp->t_ibp->b_addr,tp->t_il)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto r; break; /* shouldn't happen */ case 2: /* channel busy */ goto r; case 3: /* not operational */ trmdead(tp); return; } } /* * Get the sense information for a terminal. */ trmsens(tp) struct tty *tp; { tp->t_state = SENSING; s: switch(trmsns(tp->t_devaddr, &tp->t_sense)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ trmdead(tp); return; } } /* * Error routines: trmdead, trmhup, and trmerr. */ trmdead(tp) struct tty *tp; { printf("line %x not operational\n",tp->t_devaddr); trmhup(tp); } trmhup(tp) struct tty *tp; { tp->t_state = 0; signal(tp, SIGHUP); } trmerr(tp,s) struct tty *tp; char *s; { printf("term err: dev=%x,csw=%x,sense=%x,id=%s\n", tp->t_devaddr,(CSW+4)->integ,tp->t_sense,s); } tstat == STATMOD+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ struct tty { struct clist t_rawq; 0 struct clist t_canq; 12 struct clist t_outq; 24 struct buf *t_ibp; 36 struct buf *t_obp; 40 int t_flags; 44 int t_addr; 48 char t_delct; 52 char t_col; 53 char t_erase; 54 char t_kill; 55 int t_state; 56 int t_speeds; 60 int t_dev; 64 int t_devaddr; 68 int t_il; 72 int t_wresid; 76 char t_sense; 80 char t_char; 81 }; length: 84 D+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ send trm,tty struct clist t_rawq; 0 struct clist t_canq; 12 struct clist t_outq; 24 struct buf *t_ibp; 36 struct buf *t_obp; 40 int t_flags; 44 int t_addr; 48 char t_delct; 52 char t_col; 53 char t_erase; 54 char t_kill; 55 int t_state; 56 int t_speeds; 60 int t_dev; 64 int t_devaddr; 68 int t_il; 72 int t_wresid; 76 char t_sense; 80 char t_char; 81 }; length: 84 D+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ /* * Each buffer in the pool is usually doubly linked into 2 lists: * the device with which it is currently associated (always) * and also on a list of blocks available for allocation * for other use (usually). * The latter list is kept in last-used order, and the two * lists are doubly linked to make it easy to remove * a buffer from one list when it was found by * looking through the other. * A buffer is on the available list, and is liable * to be reassigned to another disk block, if and only * if it is not marked BUSY. When a buffer is busy, the * available-list pointers can be used for other purposes. * Most drivers use the forward ptr as a link in their I/O * active queue. * A buffer header contains all the information required * to perform I/O. * Most of the routines which manipulate these things * are in bio.c. */ extern struct buf { struct buf *b_forw; /* headed by devtab of b_dev */ struct buf *b_back; /* " */ struct buf *av_forw; /* position on free list, */ struct buf *av_back; /* if not BUSY*/ int b_flags; /* see defines below */ int b_dev; /* major+minor device name */ int b_count; /* transfer count (usu. bytes) */ int b_addr; /* core address */ int b_blkno; /* block # on device */ int b_resid; /* bytes not transferred after error */ char b_error; /* returned after I/O */ } buf[NBUF]; /* * Each block device has a devtab, which contains private state stuff * and 2 list heads: the b_forw/b_back list, which is doubly linked * and has all the buffers currently associated with that major * device; and the d_actf/d_actl list, which is private to the * device but in fact is always used for the head and tail * of the I/O queue for the device. * Various routines in bio.c look at b_forw/b_back * (notice they are the same as in the buf structure) * which point to the devtab entry when when the queue is empty * but the rest is private to each device driver. */ struct devtab { struct buf *b_forw; /* first buffer for this dev */ struct buf *b_back; /* last buffer for this dev */ struct buf *d_actf; /* head of I/O queue */ struct buf *d_actl; /* tail of I/O queue */ int *dd_ptr; /* random ptr (devdesc for disks) */ char d_active; /* busy flag */ char d_errcnt; /* error count (for recovery) */ }; /* * This is the head of the queue of available * buffers-- all unused except for the 2 list heads. * AND b_flags (to see if buffer wanted) */ extern struct buf bfreelist; /* * These flags are kept in b_flags. */ #define B_WRITE 0 /* non-read pseudo-flag */ #define B_READ 01 /* read when I/O occurs */ #define B_DONE 02 /* transaction finished */ #define B_ERROR 04 /* transaction aborted */ #define B_BUSY 010 /* not on av_forw/back list */ #define B_PHYS 020 /* Physical IO potentially using UNIBUS map */ #define B_MAP 040 /* This block has the UNIBUS map allocated */ #define B_WANTED 0100 /* issue wakeup when BUSY goes off */ #define B_RELOC 0200 /* no longer used */ #define B_ASYNC 0400 /* don't wait for I/O completion */ #define B_DELWRI 01000 /* don't write till block leaves available list */ ccurs */ #define B_DONE 02 /* transaction finished */ #define B_ERROR 04 /* transaction aborted */ #define B_BUSY 010 /* not on av_forw/back list */ #define B_PHYS 020 /* Physical IO potentially using UNIBUS map */ #define B_MAP 040 /* This block has the UNIBUS map allocated */ #define B_WANTED 0100 /* issue wakeup when BUSY goes off */ #define B_RELOC 0200 /* no longer used */ #define B_ASYNC 0400 /* don't wait for I/O completion */ #define B_DELWRI 0100/* * Definition of the unix super block. * The root super block is allocated and * read in iinit/alloc.c. Subsequently * a super block is allocated and read * with each mount (smount/sys3.c) and * released with unmount (sumount/sys3.c). * A disk block is ripped off for storage. * See alloc.c for general alloc/free * routines for free list and I list. */ #define SFREESIZ 50 #define SINODESZ 50 #define SPADSIZE 22 struct filsys { int s_isize; /* size in blocks of I list */ int s_fsize; /* size in blocks of entire volume */ int s_nfree; /* number of in core free blocks (0-FREESIZ) */ int s_free[SFREESIZ]; /* in core free blocks */ int s_ninode; /* number of in core I nodes (0-FREESIZ) */ int s_inode[SINODESZ]; /* in core free I nodes */ char s_flock; /* lock during free list manipulation */ char s_ilock; /* lock during I list manipulation */ char s_fmod; /* super block modified flag */ char s_ronly; /* mounted read-only flag */ int s_time; /* current date of last update */ int pad[SPADSIZE]; }; entire volume */ int s_nfree; /* number of in core free blocks (0-FREESIZ) */ int s_free[SFREESIZ]; /* in core free blocks */ int s_ninode; /* number of in core I nodes (0-FREESIZ) */ int s_inode[SINODESZ]; /* in core free I nodes */ char s_flock; /* lock during free list manipulation */ char s_ilock; /* lock during I list manipulation */ char s_fmod; /* super block modified flag */ char s_ronly; /* mounted read-only flag */ int s_time; /* current date of last update */ int pad[SPADS/* * Inode structure as it appears on * the disk. Not used by the system, * but by things like check, df, dump. */ /* inode is actually now 64 bytes long on 370 */ #define ISIZE 64 #define ISIZEW (ISIZE/4) #define IPERBLK (BLKSIZE/ISIZE) #define IOFFSET (2*IPERBLK-1) #define IADDRSIZ 8 #define ISPARESZ 3 struct inode { int i_mode; char i_nlink; char i_uid; char i_gid; /* char i_size0; char *i_size1; */ char i_fil; int i_size; int i_addr[IADDRSIZ]; /* int i_atime[2]; int i_mtime[2]; */ int i_atimei; int i_mtimei; int i_spare[ISPARESZ]; }; /* modes */ #define IALLOC 0100000 #define IFMT 060000 #define IFDIR 040000 #define IFCHR 020000 #define IFBLK 060000 #define ILARG 010000 #define ISUID 04000 #define ISGID 02000 #define ISVTX 01000 #define IREAD 0400 #define IWRITE 0200 #define IEXEC 0100 uct inode { int i_mode; char i_nlink; char i_uid; char i_gid; /* char i_size0; char *i_size1; */ char i_fil; int i_size; int i_addr[IADDRSIZ]; /* int i_atime[2]; int i_mtime[2]; */ in/* * The I node is the focus of all * file activity in unix. There is a unique * inode allocated for each active file, * each current directory, each mounted-on * file, text file, and the root. An inode is 'named' * by its dev/inumber pair. (iget/iget.c) * Data, from mode on, is read in * from permanent inode on volume. */ #define ISIZE 64 #define IADDRSIZ 8 #define ISIZEW (ISIZE/2) #define IPERBLK (BLKSIZE/ISIZE) #define IOFFSET (2*IPERBLK-1) extern struct inode { char i_flag; char i_count; /* reference count */ int i_dev; /* device where inode resides */ int i_number; /* i number, 1-to-1 with device address */ int i_mode; char i_nlink; /* directory entries */ char i_uid; /* owner */ char i_gid; /* group of owner */ /* char i_size0; /* most significant of size */ char i_fil; /* char *i_size1; /* least sig */ int i_size; int i_addr[IADDRSIZ]; /* device addresses constituting file */ /* or major/minor device number */ int i_lastr; /* last logical block read (for read-ahead) */ } inode[NINODE]; /* flags */ #define ILOCK 01 /* inode is locked */ #define IUPD 02 /* inode has been modified */ #define IACC 04 /* inode access time to be updated */ #define IMOUNT 010 /* inode is mounted on */ #define IWANT 020 /* some process waiting on lock */ #define ITEXT 040 /* inode is pure text prototype */ /* modes */ #define IALLOC 0100000 /* file is used */ #define IFMT 060000 /* type of file */ #define IFDIR 040000 /* directory */ #define IFCHR 020000 /* character special */ #define IFBLK 060000 /* block special, 0 is regular */ #define ILARG 010000 /* large addressing algorithm */ #define ISUID 04000 /* set user id on execution */ #define ISGID 02000 /* set group id on execution */ #define ISVTX 01000 /* save swapped text even after use */ #define IREAD 0400 /* read, write, execute permissions */ #define IWRITE 0200 #define IEXEC 0100 le is used */ #define IFMT 060000 /* type of file */ #define IFDIR 040000 /* directory */ #define IFCHR 020000 /* character special */ #/* * manifest constants * the following attempt to take into account things like * system blocksize, the word length on the machine running UNIX * and the resultant change in some of the structures. Quite beneficial * when used in conjunction with "sizeof" in the c compiler for attempting * fairly machine independent code... */ #define BLKSIZE 512 #define BLKSIZEW (BLKSIZE/4) /* blocksize in words */ #define DIRENTSZ 16 /* total bytes in directory entry */ #define WORDSIZE 4 /* four byte words */ /* * signals * dont change */ /* actually these will have to change slightly for 370 implentation. * certain things like iot and emt dont exist, but other kinds of program * interrupts do */ #define SIGHUP 1 /* hangup */ #define SIGINT 2 /* interrupt (rubout) */ #define SIGQIT 3 /* quit (FS) */ #define SIGINS 4 /* illegal instruction */ #define SIGTRC 5 /* trace or breakpoint */ #define SIGIOT 6 /* iot */ #define SIGEMT 7 /* emt */ #define SIGFPT 8 /* floating exception */ #define SIGKIL 9 /* kill */ #define SIGBUS 10 /* bus error */ #define SIGSEG 11 /* segmentation violation */ #define SIGSYS 12 /* sys */ #define SIGPIPE 13 /* end of pipe */ #define SIGPGM 14 /* 370 program interrupt */ #define NSIG (SIGPGM+16) /* * fundamental constants * cannot be changed */ #define USIZE 1 /* size of user struct in pages */ #define NULL 0 #define NODEV (-1) #define ROOTINO 1 /* i number of all roots */ #define DIRSIZ 14 /* max characters per directory */ /* * structure to access an * integer in bytes */ struct { char fil1byte; char fil2byte; char hibyte; char lobyte; }; /* * structure to access an integer */ struct { int integ; }; /* * Certain processor registers */ /* these don't exist at all on 370 #define PS 0177776 #define KL 0177560 #define SW 0177570 */ ne USIZE 1 /* size of user struct in pages */ #define NULL 0 #define NODEV (-1) #define ROOTINO 1 /* i number of all roots */ #define DIRSIZ 14 /* max characters per directory */ /* * structure to access /* * tunable variables */ #define NBUF 60 /* size of buffer cache */ #define NINODE 100 /* number of in core inodes */ #define NFILE 100 /* number of in core file structures */ #define NMOUNT 2 /* number of mountable file systems */ #define NEXEC 3 /* number of simultaneous exec's */ #define MAXMEM 256 /* max core per process in units of pages */ #define SSIZE 1 /* initial stack size in pages */ #define SINCR 1 /* increment of stack in pages */ #define NOFILE 15 /* max open files per process */ #define CANBSIZ 256 /* max size of typewriter line */ #define MAXMSG 128 /* max size of message to ourselves */ /* for pugs #define CMAPSIZ 100 /* size of core allocation area */ /* for pugs #define SMAPSIZ 100 /* size of swap allocation area */ #define NCALL 4 /* max simultaneous time callouts */ #define NPROC 50 /* max number of processes */ #define NTEXT 40 /* max number of pure texts */ #define NCLIST 100 /* max total clist size */ #define NINFO 60 /* number of chars per cblock */ #define HZ 1 /* Ticks/second of the clock */ #define NTRMS 16 /* * priorities * probably should not be * altered too much */ #define PSWP -100 #define PINOD -90 #define PRIBIO -50 #define PPIPE 1 #define PWAIT 40 #define PSLEP 90 #define PUSER 100 area */ #define NCALL 4 /* max simultaneous time callouts */ #define NPROC 50 /* max number of processes */ #define NTEXT 40 /* max number of pure texts */ #define NCLIST 100 /* max total clist size */ #define NINFO 60 /* number of chars per cblock */ #define HZ /* * One structure allocated per active * process. It contains all data needed * about the process while the * process may be swapped out. * Other per process data (user.h) * is swapped with the process. */ extern struct proc { char p_stat; char p_flag; char p_sig; /* signal number sent to this process */ char p_uid; /* user id, used to direct tty signals */ int p_pri; /* priority, negative is high */ char p_time; /* resident time for scheduling */ char p_cpu; /* cpu usage for scheduling */ char p_nice; /* nice for scheduling */ int p_ttyp; /* controlling tty */ int p_pid; /* unique process id */ int p_ppid; /* process id of parent */ int p_addr; /* address of swappable image */ /* actually real core address of u structure */ /* this not needed int p_size; /* size of swappable image (*64 bytes) */ int p_wchan; /* event process is awaiting */ int *p_textp; /* pointer to text structure */ int p_utime; /* user cpu time for a process */ } proc[NPROC]; /* stat codes */ #define SSLEEP 1 /* sleeping on high priority */ #define SWAIT 2 /* sleeping on low priority */ #define SRUN 3 /* running */ #define SIDL 4 /* intermediate state in process creation */ #define SZOMB 5 /* intermediate state in process termination */ #define SSTOP 6 /* process being traced */ /* flag codes */ #define SLOAD 01 /* in core */ #define SSYS 02 /* scheduling process */ #define SLOCK 04 /* process cannot be swapped */ #define SSWAP 010 /* process is being swapped out */ #define STRC 020 /* process is being traced */ #define SWTED 040 /* another tracing flag */ ng on low priority */ #define SRUN 3 /* running */ #define SIDL 4 /* intermediate state in process creation */ #define SZOMB 5 /* intermediate state in process termination */ #define SSTOP 6 /* process being traced */ /* flag codes */ #define SLOAD 01 /* in core */ #define SSYS 02 /* scheduling process */ #define SLOCK 04 /* process cannot be swapped */ #define SSWAP 010 /* process is being swapped out */ #define STRC 020 /* pr/* * Location of the users' stored * registers relative to user stack pointer (R13). * Usage is u->u_arsp[XX]. */ #define R0 (3) #define R1 (4) /* while R2-R12 are on the stack at the location that the * prologue macro of trap(...) stored them, the location relative to R0, etc. * depends on the size of automatic storage of trap(...). You don't need * them anyway, except for exec (see u_orega) */ #define R13 (0) /* this is also the user stack pointer if * the process is not in emulate mode */ #define R14 (1) #define R15 (2) #define RPS (5) /* this is the forst word of the psw */ #define RPC (6) /* this is the second word of the psw */ /* while R2-R12 are on the stack at the location that the * prologue macro of trap(...) stored them, the location relative to R0, etc. * depends on the size of automatic storage of trap(...). You don't need * them anyway, except for exec (see u_orega) */ #define R13 (0) /* this is also the user stack pointer if * the process is not in emulate mode /* * Random set of variables * used by more than one * routine. */ extern char canonb[CANBSIZ]; /* buffer for erase and kill (#@) */ extern int *rootdir; /* pointer to inode of root directory */ /* ibm, of course int cputype; /* type of cpu =40, 45, or 70 */ extern int execnt; /* number of processes in exec */ extern int lbolt; /* time of day in 60th not in time */ extern int time; /* time in sec from 1970 */ extern int tout; /* time of day of next sleep */ /* * The callout structure is for * a routine arranging * to be called by the clock interrupt * (clock.c) with a specified argument, * in a specified amount of time. * Used, for example, to time tab * delays on teletypes. */ extern struct callo { int c_time; /* incremental time */ int c_arg; /* argument to routine */ int (*c_func)(); /* routine */ } callout[NCALL]; /* * Mount structure. * One allocated on every mount. * Used to find the super block. */ extern struct mount { int m_dev; /* device mounted */ int *m_bufp; /* pointer to superblock */ int *m_inodp; /* pointer to mounted on inode */ } mount[NMOUNT]; extern int mpid; /* generic for unique process id's */ /* for swap only extern char runin; /* scheduling flag */ /* for swap only extern char runout; /* scheduling flag */ extern char curpri; /* more scheduling */ extern int maxmem; /* actual max memory per process in pages */ extern int pagecnt; /* number of free pages */ extern int memlim; /* highest physical byte address */ extern int rootdev; /* dev of root see conf.c */ /* no swap yet int swapdev; /* dev of swap see conf.c */ /* int swplo; /* block number of swap space */ /* int nswap; /* size of swap space */ extern int updlock; /* lock for sync */ extern int rablock; /* block to be read ahead */ /* trap has been changed extern char regloc[]; /* locs. of saved user registers (trap.c) */ #ifndef extern extern char runrun; /* scheduling flag */ #endif pages */ extern int memlim; /* highest physical byte address */ extern int rootdev; /* dev of root see/* * Text structure. * One allocated per pure * procedure on swap device. * Manipulated by text.c */ extern struct text { /* int x_daddr; /* disk address of segment */ /* int x_caddr; /* core address, if loaded */ int *x_tpages; /* pages for text segment */ int x_size; /* size (bytes) */ int *x_iptr; /* inode of prototype */ char x_count; /* reference count */ /* char x_ccount; /* number of loaded references */ } text[NTEXT]; highest physical byte address */ extern int rootdev; /* dev of root see/* * A clist structure is the head * of a linked list queue of characters. * The characters are stored in 16-word * blocks containing a link and 60 characters. * The routines getc and putc (machine code assists) * manipulate these structures. */ struct clist { int c_cc; /* character count */ int c_cf; /* pointer to first block */ int c_cl; /* pointer to last block */ }; /* * The actual structure of a clist block manipulated by * getc and putc (mch.s) */ struct cblock { struct cblock *c_next; char info[NINFO]; }; /* * A tty structure is needed for * each UNIX character device that * is used for normal terminal IO. * The routines in tty.c handle the * common code associated with * these structures. * The definition and device dependent * code is in each driver. (trm.c) */ struct tty { struct clist t_rawq; /* input chars right off device */ struct clist t_canq; /* input chars after erase and kill */ struct clist t_outq; /* output list to device */ struct buf *t_ibp; /* input buffer */ struct buf *t_obp; /* output buffer */ int t_flags; /* mode, settable by stty call */ int t_addr; /* device address (register or startup fcn) */ char t_delct; /* number of delimiters in raw q */ char t_col; /* printing column of device */ char t_erase; /* erase character */ char t_kill; /* kill character */ int t_state; /* internal state, not visible externally */ int t_speeds; /* output+input line speed */ int t_dev; /* device name (major/minor) */ int t_devaddr; /* device address */ int t_il; /* requested input length */ int t_wresid; /* residual write count */ char t_sense; /* sense info */ char t_char; /* character temporary */ }; extern char partab[]; /* ASCII table: parity, character class */ #define TTIPRI 10 #define TTOPRI 20 #define CERASE 010 /* backspace */ #define CEOT 017 /* control O */ #define CKILL '@' #define CQUIT 034 /* FS, cntl shift L */ #define CESC 033 #define CINTR 003 /* control C - interrupt */ #define CDLM 0377 #define PADCHAR #fb /* 270X quiet line character(reversed)*/ /* limits */ #define TTHIWAT 512 #define TTYHOG 1024 /* modes */ #define HUPCL 01 #define XTABS 02 #define LCASE 04 #define ECHO 010 #define CRMOD 020 #define RAW 040 #define ODDP 0100 #define EVENP 0200 #define NLDELAY 001400 #define TBDELAY 006000 #define CRDELAY 030000 #define VTDELAY 040000 trol O */ #define CKILL '@' #define CQUIT 034 /* FS, cntl shift L */ #define CESC 033 #define CINTR 003 /* control C - interrupt */ #define CDLM 0377 #define PADCHAR #fb /* 270X quiet l/* * The user structure. * One allocated per process. * Contains all per process data * that doesn't need to be referenced * while the process is swapped. * The user block is USIZE pages * long; is pointed to by the external * variable u; contains the system * stack per user; is cross referenced * with the proc structure for the * same process. */ extern struct user { int u_rsav[2]; /* save r5,r6 when exchanging stacks */ int u_fsav[8]; /* save fp registers */ int u_tpages; /* pages for text segment */ int u_dpages; /* pages for data segment */ int u_spages; /* pages for stack segment */ int u_tsize; /* text size (bytes) */ int u_dsize; /* data size (bytes) */ int u_ssize; /* stack size (bytes) */ /* rsav thru ssize must be first */ char u_segflg; /* flag for IO; user or kernel space */ char u_error; /* return error code */ char u_uid; /* effective user id */ char u_gid; /* effective group id */ char u_ruid; /* real user id */ char u_rgid; /* real group id */ int u_procp; /* pointer to proc structure */ char *u_base; /* base address for IO */ char *u_count; /* bytes remaining for IO */ char *u_offset; /* offset in file for IO */ int *u_cdir; /* pointer to inode of current directory */ char u_dbuf[DIRSIZ]; /* current pathname component */ char *u_dirp; /* current pointer to inode */ struct { /* current directory entry */ char u_ino[2]; /* half word integer for inode number */ char u_name[DIRSIZ]; } u_dent; int *u_pdir; /* inode of parent directory of dirp */ /* int u_uisa[16]; /* prototype of segmentation addresses */ /* int u_uisd[16]; /* prototype of segmentation descriptors */ int u_ofile[NOFILE]; /* pointers to file structures of open files */ int u_arg[5]; /* arguments to current system call */ /* int u_sep; /* flag for I and D separation */ int u_qsav[2]; /* label variable for quits and interrupts */ /* int u_ssav[2]; /* label variable for swapping */ int u_signal[NSIG]; /* disposition of signals */ int u_utime; /* this process user time */ int u_stime; /* this process system time */ int u_cutime; /* sum of childs' utimes */ int u_cstime; /* sum of childs' stimes */ /* int *u_ar0; /* address of users saved R0 */ int *u_arsp; int *u_orega; /* address of regs in trap auto area */ int u_prof[4]; /* profile arguments */ char u_intflg; /* catch intr from sys */ /* kernel stack per user * extends from u + USIZE*64 * backward not to reach here */ } *u; /* u_error codes */ #define EFAULT 106 #define EPERM 1 #define ENOENT 2 #define ESRCH 3 #define EINTR 4 #define EIO 5 #define ENXIO 6 #define E2BIG 7 #define ENOEXEC 8 #define EBADF 9 #define ECHILD 10 #define EAGAIN 11 #define ENOMEM 12 #define EACCES 13 #define ENOTBLK 15 #define EBUSY 16 #define EEXIST 17 #define EXDEV 18 #define ENODEV 19 #define ENOTDIR 20 #define EISDIR 21 #define EINVAL 22 #define ENFILE 23 #define EMFILE 24 #define ENOTTY 25 #define ETXTBSY 26 #define EFBIG 27 #define ENOSPC 28 #define ESPIPE 29 #define EROFS 30 #define EMLINK 31 #define EPIPE 32 ENT 2 #define ESRCH 3 #define EINTR 4 #define EIO 5 #define ENXIO 6 #define E2BIG 7 #define ENOEXEC 8 #define EBADF 9 #define ECHILD 10 #define EAGAIN 11 #define ENOMEM 12 #define EACCES 13 #define ENOTBLK 15 #define EBUSY 16 #define EEXIST 17 #define EXDEV 18 #define ENODEV 19 #define ENOTDIR 20 #define EISDIR 21 #define EINVAL 22 #define ENFILE 23 #define EMFILE 24 #define ENOTTY 25 #define ETXTBSY 26 #define EFBIG 27 #define ENOSPC 28 #define ESPIPE 29 #define Ezero title '***---> zero -- multifaceted machine assists <---***' zeroc csect * r0 equ 0 r1 equ 1 r2 equ 2 r3 equ 3 r4 equ 4 r5 equ 5 r6 equ 6 r7 equ 7 r8 equ 8 r9 equ 9 r10 equ 10 r11 equ 11 r12 equ 12 r13 equ 13 sp equ 13 r14 equ 14 r15 equ 15 * *format of low core * resnpsw ds 0d ipsw dc x'030c0000',a(main) resopsw ds 0d iccw1 ds d iccw2 ds d extopsw ds d svcopsw ds d pgmopsw ds d mckopsw ds d ioopsw ds d csw ds d caw ds f ds f timer dc f'-1' ds f extnpsw ds 0d dc x'000c0000',a(exttrap) svcnpsw ds 0d dc x'000c0000',a(svctrap) pgmnpsw ds 0d dc x'000c0000',a(pgmtrap) mcknpsw ds d ionpsw ds 0d dc x'000c0000',a(iotrap) ds f ds h "processor address" extcode ds h svccode ds f pgmcode ds f program interrupt code in low order byte * ilc at x'60000' * 1 operation * 2 priv. operation * 3 execute * 4 protection * 5 addressing * 6 specification * 7 data * 8 fixed point overflow * 9 fixed point divide * 10 decimal overflow * 11 decimal divide * 12 exponent overflow * 13 exponent underflow * 14 significance * 15 flt. pt. divide * 16 Segment tranlation * 17 Page tranlation * 18 translation specification * 19 spec. op. * 64 monitor event * 128+ program event tranexcp ds f translation exection addres monclass ds h monitor class number percode ds h peraddr ds f moncode ds f ds 2f chanid ds f ioel ds f ds f limited channel logout ds f $ioaddr ds f i/o address diag1 ds cl48 diagarea ds cl256 * ustack ds f temporary for user stack pointer entry u u ds f pointer to u area of currently running process a$trap dc v(trap) address of c trap routine a$clock dc v(clock) address of c clock routine a$swtch dc v(swtch) address of c routine to schedule * cons28 dc f'28' f32 dc f'32' fm1 dc f'-1' entry runrun runrun ds f entry sysmask sysmask ds h * from user mode opswtem ds d temp used when returning from system to system * (asynchronous system entry while in system state) * * * *clock & synchronous system entry * (external, svc, and program interrupts) USIZE equ 1 PGSIZE equ 4096 * entry svctrap entry pgmtrap entry exttrap using zeroc,0 * this routine resides in low core, and therefor needs no base register exttrap equ * tm extopsw+1,x'01' bz $1 st r13,ustack l r13,u la r13,USIZE*PGSIZE-28(,r13) b $2 $1 s r13,cons28 $2 mvc 20(8,r13),extopsw stm r14,r1,4(r13) l r0,ustack get user sp stosm sysmask,x'02' reenable i/o intrs l r15,a$clock tcom1 st r0,0(,r13) lr r1,r13 preserve args on return balr r14,r15 * * clock/trap (usp,r14,r15,r0,r1,ps,pc) * ni 20+1(r13),x'fd' turn off wait bit tm 20+1(r13),x'01' return to user or system??? bz $5 system. $3 stnsm sysmask,x'44' user, disable interrupts tm runrun,x'ff' test sched flag bnz $4 call scheduler mvc svcopsw(8),20(r13) ni svcopsw+2,x'7f' clear flag that was set on pgm intrpt lm r13,r1,0(r13) lpsw svcopsw * $4 equ * ssm sysmask * save fp registers here l r15,a$swtch lr r1,r13 balr r14,r15 *restore fpr here b $3 * $5 stnsm sysmask,x'44' mvc opswtem(8),20(r13) ni opswtem+1,x'fd' turn off wait state lm r14,r1,4(r13) la r13,28(,r13) lpsw opswtem * svctrap equ * tm svcopsw+1,x'01' bz $11 st r13,ustack l r13,u la r13,USIZE*PGSIZE-28(,r13) b $12 $11 sl r13,cons28 $12 mvc 20(8,r13),svcopsw tcom2 stm r14,r1,4(r13) l r0,ustack stosm sysmask,x'03' reenable interrupts l r15,a$trap b tcom1 * pgmtrap equ * oi pgmopsw+2,x'80' set bit to signify pgm trap tm pgmopsw+1,x'01' system or user mode?? bz $21 st r13,ustack l r13,u la r13,USIZE*PGSIZE-28(,r13) b $22 $21 sl r13,cons28 $22 mvc 20(8,r13),pgmopsw b tcom2 * iotrap $null extrn iolkptab st sp,ustack hold onto sp (r13) tm ioopsw+1,probstat q/ in problem(user) state bz $101 no --> system... use old stack l sp,u yes -- set up new stack la sp,usize*pgsize-32(,sp) calculate end of stack b $104 $101 sl sp,f32 drop for 32 bytes of arguments $104 mvc 24(8,sp),ioopsw save from where we came stm r14,r1,8(sp) hold onto volatile registers l r14,ustack get old r13 value st r14,4(,sp) and save for subsequent restoration l r14,=a(iolkptab) get us a base for lookup table using iolkpent,r14 let asm in on it l r15,$ioaddr pick device address from io comm area l r0,fm1 pick up end of table indicator b $105 space $102 la r14,12(,r14) (bump for next table entry) $105 c r0,ioaddr q/ end of table be $103 yes --> take the default c r15,ioaddr q/ this beastie bne $102 no --> go 'round again space $103 $null l r0,iomajmin pick up the sole argument st r0,0(,sp) and pass it along on the stack la r1,4 say four bytes for the ... l r15,iointrtn pick up the routine to be called drop r14 balr r14,r15 and pass control disabled * * lock out interrupt to return where ever... * stnsm sysmask,per+trans leave those enabled mvc resopsw,24(sp) put the restart psw somewhere ni resopsw+1,x'fd' turn off wait state lm r13,r1,4(sp) restore registers lpsw resopsw and fly away... space per equ x'40' trans equ x'04' probstat equ x'01' iolkpent dsect ioaddr ds f device address iointrtn ds f interrupt routine address iomajmin ds f major/minor device address zeroc csect resume where we left off eject entry main,stnsm,stosm,setsm,savu,retu,aretu entry idle,fubyte,fuibyte,fuword,fuiword entry subyte,suibyte,suword,suiword,copyin entry copyout,icode * main equ * l r2,=v(edata) get bss start addr lr r3,r2 dup s r3,=v(end) get neg length of bss lpr r3,r3 absolute value sr r4,r4 no second op sr r5,r5 mvcl r2,r4 zero bss segment mvc adpswsav(4),pgmnpsw+4 save new pgm psw lower half la r1,addrint get addr of interrupt entry st r1,pgmnpsw+4 set addr intr entry mvi pgmnpsw,x'03' allow interrupts l r1,=v(end) get addr of kernel end la r1,4095(,r1) round up to n r1,=x'fffff000' page boundary l r8,=v(ffpage) get first free page ptr addr st r1,0(,r8) store addr of first free page lr r7,r1 save ffpage ptr sr r4,r4 zero count of pages strpage lr r2,r1 dup r1 a r2,=f'4096' bump r2 to next page l r3,0(,r2) attempt addr exception st r2,0(,r1) chain this page l r3,=x'000000f0' ssk r3,r2 la r6,2048(,r2) ssk r3,r6 l r3,=f'4096' sr r14,r14 sr r15,r15 lr r1,r2 bump chain ptr mvcl r2,r14 la r4,1(,r4) increment page count b strpage try next page addrint mvc 0(4,r1),=f'0' zero last link in chain mvc pgmnpsw+4(4),adpswsav restore psw mvi pgmnpsw,x'00' ditto l r3,=v(maxmem) get maxmem var addr st r4,0(,r3) store page count l r3,=v(memlim) la r1,4095(,r1) get highest addr st r1,0(,r3) store in memlim mvc 0(4,r8),0(r7) delink first page st r7,u use it as u struct ptr lr r2,r7 dup u ptr l r3,=f'4096' page length sr r4,r4 zero second oper sr r5,r5 zero second length mvcl r2,r4 fill page with zeroes la r12,USIZE*PGSIZE-28(,r7) initialize auto ptr lr sp,r12 initialize stack ptr l r15,=v(main370) get main.c addr balr r14,r15 call main.c lpsw userinit load initial user psw userinit ds 0d dc x'071d0f00' dc f'0' adpswsav ds f temp * stnsm equ * 1 arg ic r1,0(,sp) get argument ex r1,stnsmi execute the stnsm ic r0,0(sp) get the previous mask sll r0,24 shift it into word br r14 return * stnsmi stnsm 0(sp),0 prototype inst stosm equ * 1 arg ic r1,0(,sp) get argument ex r1,stosmi execute the stosm ic r0,0(sp) get the previos mask sll r0,24 shift it into word br r14 return stosmi stosm 0(sp),0 prototype inst * setsm equ * 1 arg ssm 0(sp) set sysmask to arg br r14 return * savu equ * noint equ x'44' stnsm sysmask,noint turn off intr and save status l r1,0(,sp) get save addr st sp,0(,r1) save stack pointer st r12,4(,r1) save r12 too ssm sysmask restore intr status br r14 return * retu equ * l r1,0(,sp) get u struct save addr * la r1,RSAV(,r1) get rsav offset *RSAV equ 0 b 12(,r15) ----------- branch into aretu aretu l r1,0(,sp) | load save area addr stnsm sysmask,noint <---- turn off intr and save status l r12,4(,r1) load saved r12 l sp,0(,r1) ssm sysmask restore intr status br r14 return * idle equ * no args lpsw wait load wait state psw return br r14 return if awakened wait ds 0d dc x'030e0000' dc a(return) * fubyte equ * 1 arg l r1,0(,sp) get user addr lra r1,0(,r1) load real addr bnz badbyte branch if not found sr r0,r0 zero r0 ic r0,0(,r1) insert byte b byteret return badbyte l r0,=f'-1' load bad indication byteret br r14 return * fuword equ * 1 arg l r1,0(,sp) get user addr lra r1,0(,r1) load real addr bnz badword branch if not found l r0,0(,r1) load word b wordret go back badword l r0,=f'-1' load bad indication wordret br r14 return fuibyte equ fubyte fuiword equ fuword subyte equ * 2 args l r1,0(,sp) get user virtual addr l r0,4(,sp) get byte to set lra r1,0(,r1) load real addr bnz badsub branch if not found stc r0,0(,r1) store the byte sr r0,r0 indicate no error b subret return badsub l r0,=f'-1' indicate error subret br r14 return suword equ * l r1,0(,sp) get user virtual addr l r0,4(,sp) get word to set to lra r1,0(,r1) load real addr bnz badsuw branch if not found st r0,0(,r1) store word sr r0,r0 indicate no error b suwret return badsuw l r0,=f'-1' indicate error suwret br r14 return suibyte equ subyte space entry raddr raddr lra 0,0(,sp) br r14 suiword equ suword PGMASK equ x'fffff000' * copyin s sp,=f'20' drop stack stm r2,r6,0(sp) save volatile regs l r1,28(,sp) get length l r2,24(,sp) get system addr l r6,20(,sp) get user addr inlp ltr r1,r1 any to move? bz indone no, branch l r3,=a(PGSIZE) page length ar r3,r6 add to user addr n r3,=a(PGMASK) mask off lower bits sr r3,r6 sub addr for len cr r3,r1 take minimum of bnh inmove r1 and r3 for lr r3,r1 length to be moved inmove lr r5,r3 dup length for mvcl lra r4,0(,r6) get user's real addr bnz inbad branch if non-existent sr r1,r3 dec len by len being moved ar r6,r3 inc user addr by len mvcl r2,r4 move and inc r2 (sys addr) b inlp branch back inbad la r1,1 load error ind indone lr r0,r1 load return code lm r2,r6,0(sp) restore regs la sp,20(,sp) pop stack br r14 return * copyout s sp,=f'20' drop stack stm r2,r6,0(sp) save registers l r1,28(,sp) get length to move l r2,20(,sp) get system addr l r6,24(,sp) get user addr outlp ltr r1,r1 length zero? bz outdone yes, branch l r3,=a(PGSIZE) page length ar r3,r6 add to user addr n r3,=a(PGMASK) mask off lower bits sr r3,r6 sub addr for length cr r3,r1 compare with arg len bnh outmove branch if <= arg len lr r3,r1 minimum of r3 & r1 outmove lr r5,r3 dup len for mvcl lra r4,0(,r6) get real user addr bnz outbad branch if non-existent sr r1,r3 decrement length ar r6,r3 increment user addr mvcl r4,r2 move and inc sys addr b outlp outbad la r1,1 load error indication outdone lr r0,r1 load return code lm r2,r6,0(sp) restore regs la sp,20(,sp) pop stack br r14 return * entry bcopy bcopy equ * l 0,0(,13) st 14,0(,13) l 14,4(,13) l 1,8(,13) lr 15,1 mvcl 14,0 l 14,0(,13) br 14 * entry sthalf sthalf equ * l 1,0(,13) l 0,4(,13) sth 0,0(,1) br 14 entry ldhalf ldhalf l 1,0(,13) lh 0,0(,1) br 14 * entry min min l 0,0(,13) c 0,4(,13) blr 14 l 0,4(,13) br 14 * entry max max l 0,0(,13) c 0,4(,13) bhr 14 l 0,4(,13) br 14 * entry incupc incupc equ * l r1,0(,r13) l r15,4(,r13) s r1,8(,r15) offset blr 14 bellow buffer m r0,12(,r15) scale factor srda r0,30 la r1,3(,r1) round up n r1,=f'-4' c r1,4(,r15) outsize buffer? bnlr r14 yes - return a r1,0(,r15) base lra r2,0(,r1) get real address bnzr 14 return if not valid - i assume that * the buffer is not in sharable text l r0,0(,r15) a r0,=f'1' st r0,0(,r15) br 14 * entry clrbuf clrbuf l r1,0(,sp) l r1,28(,r1) bp->b_addr mvi 0(r1),0 mvc 1(255,r1),0(r1) mvc 256(256,r1),0(r1) br 14 * entry putchar putchar equ * sr r0,r0 ic r0,3(,sp) l r1,=v(atetab) ar r1,r0 ic r0,0(,r1) cli 0(r1),x'15' new line? be consput yes, branch cli 0(r1),x'ad' right bracket? bne putchr1 ic r0,=x'4a' change to cent-sign for cp putchr1 equ * l r1,cnslnp stc r0,0(,r1) la r1,1(,r1) st r1,cnslnp c r1,=a(cnsln+132) bnl consput br r14 consput tm consbusy,1 bo consput mvi consbusy,1 la r0,cnsln l r1,cnslnp sr r1,r0 sth r1,consccw+6 st r0,cnslnp mvc caw(4),=a(consccw) sio consaddr br r14 consccw ccw x'09',cnsln,0,*-* cnslnp dc a(cnsln) ds 0d cnsln ds cl132 entry consbusy consbusy dc x'00' consaddr equ x'009' * entry clkstrt clkstrt spt interval set cpu timer interval stck clk get tod value ni clk,x'7f' since may 11, 1971 lm r0,r1,clk srdl r0,12 micro seconds d r0,=f'1000000' seconds a r1,=f'42825414' since jan 1, 1970 l r15,=v(time) st r1,0(r15) br r14 ds 0d *interval dc f'0',x'0411b000' 1/60 sec interval dc x'00000000f4240000' 1 second clk ds d * entry ldiv,lrem ldiv l r1,0(,r13) sr r0,r0 d r0,4(,r13) lr r0,r1 br r14 lrem l r1,0(,r13) sr r0,r0 d r0,4(,r13) lr r0,r1 c r0,=f'10' blr r14 la r0,7(,r1) br r14 * entry cpcmd cpcmd ds 0h l r0,0(,sp) lr r1,r0 s sp,=f'8' stm r2,r3,0(sp) la r2,cmdbuf getdlm cli 0(r1),x'00' be gotdlm sr r3,r3 ic r3,0(,r1) a r3,=v(atetab) mvc 0(1,r2),0(r3) la r1,1(,r1) la r2,1(,r2) b getdlm gotdlm sr r1,r0 la r2,cmdbuf diag r2,r1,x'8' lm r2,r3,0(sp) a sp,=f'8' br r14 cmdbuf dc cl100' ' * icode equ * using icode,0 la r1,parmlist-icode svc 11 exec /etc/init svc 1 exit if exec failed parmlist dc a(name-icode) dc a(args-icode) name dc x'2f6574632f696e697400' /etc/init in hex & ascii args dc a(name-icode) dc a(0) end p,=f'8' stm r2,r3,0(sp) la r2,cmdbuf getdlm cli 0(r1),x'00' be gotdlm sr r3,r3 ic r3,0(,r1) a r3,=v(atetab) mvc 0(1,r2),0(r3) la r1,1(,r1) la r2,1(,r2) b getdlm gotdlm sr r1,r0 la r2,cmdbuf diag r2,r1,x'8' lm r2,r3,0(sp) a sp,=f'8' br r14 cmdbuf dc cl100' ' * icode equ * using icode,0 la r1,parmlist-icode svc 11 exec /etc/init svc 1 exit if exec failed parmlist dc a(name-icode) dc a(args-icode) name dc x'2f6574632f696e697400' /etc/init in hex & user addr by len mvcl r2,r4 move and inc r2 (sys addr) b inlp branch back inbad la r1,1 load error ind indone lr r0,r1 load return code lm r2,r6,0(sp) restore regs la sp,20(,sp) pop stack br r14 return * copyout s sp,=f'20' drop stack stm r2,r6,0(sp) save registers l r1,28(,sp) get length to move l r2,20(,sp) get system addr l r6,24(,sp) get user addr outlp ltr r1,r1 length zero? bz outdone yes, branch l r3,=a(PGSIZE) page length ar r3,r6 add to user addr n r3,=a(PGMASK) mask off lower bits sr r3,r6 sub addr for length cr r3,r1 compare with arg len bnh outmove branch if <= arg len lr r3,r1 minimum of r3 & r1 outmove lr r5,r3 dup len for mvcl lra r4,0(,r6) get real user addr bnz outbad branch if non-existent sr r1,r3 decrement length ar r6,r3 increment user addr mvcl r4,r2 move and inc sys addr b outlp outbad la r1,1 load error indication outdone lr r0,r1 load return code lm r2,r6,0(sp) restore regs la sp,20(,sp) pop stack br r14 return * entry bcopy bcopy equ * l 0,0(,13) st 14,0(,13) l 14,4(,13) l 1,8(,13) lr 15,1 mvcl 14,0 l 14,0(,13) br 14 * entry sthalf sthalf equ * l 1,0(,13) l 0,4(,13) sth 0,0(,1) br 14 entry ldhalf ldhalf l 1,0(,13) lh 0,0(,1) br 14 * entry min min l 0,0(,13) c 0,4(,13) blr 14 l 0,4(,13) br 14 * entry max max l 0,0(,13) c 0,4(,13) bhr 14 l 0,4(,13) br 14 * entry incupc incupc equ * l r1,0(,r13) l r15,4(,r13) s r1,8(,r15) offset blr 14 bellow buffer m r0,12(,r15) scale factor srda r0,30 la r1,3(,r1) round up n r1,=f'-4' c r1,4(,r15) outsize buffer? bnlr r14 yes - return a r1,0(,r15) base lra r2,0(,r1) get real address bnzr 14 return if not valid - i assume that * the buffer is not in sharable text l r0,0(,r15) a r0,=f'1' st r0,0(,r15) br 14 * entry clrbuf clrbuf l r1,0(,sp) l r1,28(,r1) bp->b_addr mvi 0(r1),0 mvc 1(255,r1),0(r1) mvc 256(256,r1),0(r1) br 14 * entry putchar putchar equ * sr r0,r0 ic r0,3(,sp) l r1,=v(atetab) ar r1,r0 ic r0,0(,r1) cli 0(r1),x'15' new line? be consput yes, branch cli 0(r1),x'ad' right bracket? bne putchr1 ic r0,=x'4a' change to cent-sign for cp putchr1 equ * l r1,cnslnp stc r0,0(,r1) la r1,1(,r1) st r1,cnslnp c r1,=a(cnsln+132) bnl consput br r14 consput tm consbusy,1 bo consput mvi consbusy,1 la r0,cnsln l r1,cnslnp sr r1,r0 sth r1,consccw+6 st r0,cnslnp mvc caw(4),=a(consccw) sio consaddr br r14 consccw ccw x'09',cnsln,0,*-* cnslnp dc a(cnsln) ds 0d cnsln ds cl132 entry consbusy consbusy dc x'00' consaddr equ x'009' * entry clkstrt clkstrt spt interval set cpu timer interval stck clk get tod value ni clk,x'7f' since may 11, 1971 lm r0,r1,clk srdl r0,12 micro seconds d r0,=f'1000000' seconds a r1,=f'42825414' since jan 1, 1970 l r15,=v(time) st r1,0(r15) br r14 ds 0d *interval dc f'0',x'0411b000' 1/60 sec interval dc x'00000000f4240000' 1 second * entry ldiv,lrem ldiv l r1,0(,r13) sr r0,r0 d r0,4(,r13) lr r0,r1 br r14 lrem l r1,0(,r13) sr r0,r0 d r0,4(,r13) lr r0,r1 c r0,=f'10' blr r14 la r0,7(,r1) br r14 * entry cpcmd cpcmd ds 0h l r0,0(,sp) lr r1,r0 s sp,=f'8' stm r2,r3,0(sp) la r2,cmdbuf getdlm cli 0(r1),x'00' be gotdlm sr r3,r3 ic r3,0(,r1) a r3,=v(atetab) mvc 0(1,r2),0(r3) la r1,1(,r1) la r2,1(,r2) b getdlm gotdlm sr r1,r0 la r2,cmdbuf diag r2,r1,x'8' lm r2,r3,0(sp) a sp,=f'8' br r14 cmdbuf dc cl100' ' * icode equ * using icode,0 la r1,parmlist-icode svc 11 exec /etc/init svc 1 exit if exec failed parmlist dc a(name-icode) dc a(args-icode) name dc x'2f6574632f696e697400' /etc/init in hex & ascii args dc a(name-icode) dc a(0) end p,=f'8' stm r2,r3,0(sp) la r2,cmdbuf getdlm cli 0(r1),x'00' be gotdlm sr r3,r3 ic r3,0(,r1) a r3,=v(atetab) mvc 0(1,r2),0(r3) la r1,1(,r1) la r2,1(,r2) b getdlm gotdlm sr r1,r0 la r2,cmdbuf diag r2,r1,x'8' lm r2,r3,0(sp) a sp,=f'8' br r14 cmdbuf dc cl100' ' * icode equ * using icode,0 la r1,parmlist-icode svc 11 exec /etc/init svc 1 exit if exec failed parmlist dc a(name-icode) dc a(args-icode) name dc x'2f6574632f696e697400' /etc/init in hex & ascii argPa,R" & 6  _w d_%ss7 7 ƭ5 @-* @5   N c $ 0  xae@@m%c $ [ <r@& tY 7J @c $  [e  ew@& tY%7ȋ7حc $ [ % Xa>%FԔ  `*e7 p@e&7` j@5@  =@c VP c VP  n .!  N+c VP cdc VP   [wj]w X]$_d VP Nff f fff VPe  \Y rw]w ]`dާ@e5@e5lƧ ,*_@4 "  V'%(  U   N^ H^  e 5%% %e 5 %N @e5  %-@% @%!N@&@&$ e5@ (A1uecf& e& &  e?TeNF   >% ĩ& %&  e?e& d $ N+ ^- N+t& &  e ?ƥe%#d $ [ __ e? _: % 4  %/#$'  '%e.\d  _ ̨  - xa _  jtE5% % uu- u-3 5j-%  b7f\ y @e@-7d $ [ee}_@548[ B>@%4N _ & & & e?ed%_% _8b7[ y N _Kd $ X# wXw X 5 C% 76%7 &7nZ%% ܧf ff ze5` Ҧ%@$ %% %%7 7 %%% @ ^ @wXw XC5 % 7l B%7Y%%Y _ \5_  tAE@P@t55D] 5 ,% % &%% % %% %2!% %2! _ 2 % %  U5J%  \VRJ  p 5 %' (5 % @eE@5` @eE5 r- PrE%% % % %]d $% U% j )7`^B%W N+W Wjd VP  )vd VP  N+!%E%5 E%%d $ Ф̤@ wvUw dU "_> tU_Z  "%_N  t z"[ 7 zr & &  e\ \ F"%UtU 0"%'7V 72-  Awp@ 1   !%$ʣ ģtU  xPb7VVb7W y ^ w0Tw Td $N X#wTw Tpe d $ wSw S@  0 J xfbwSw S %( $%  %!%%& &  e?VeP@ 5j562  % (3 3 3e_v%#  %d 5 @_e_v& & f e ?e_v%Xe $u_E%le $&  &d e ?2e,_v%#e_vE%e $& j &$ e?ڜeԜ_v%$ (1e_v%& : &f e?xer_ve $_ %F%Gs:e4 ( $ %%e $eu@E%N '5f %#  &  e?e( @E%$ _v  r edA! y$5 5 (5  (5 f % &f e?e_v%52 5 _N N&&  Wp eAku@E5@E5 @5%P5%_ _%%D-]%?5%%e $I%5 55 <%)u5 2 !5% 5 5 (ff ze (ff ze5%= %%@zd55 f % 3 &ff e ?.e(  (f&  ze?ewNw N@2*+N & &  ef& f& e "34N& f& e  xee $@wMw rMDCB%   && %  && %& %#  xewMw LDE%& p &# ewLw LCE%%%vep&  & %# LHwfLw TL@%%e $w@Lw .LD%E%h E!Ee wKw KDee–@   wKw Ke 0 ,e $ [wKw KD%%$ f $w~Kw lKD%  % N&f %J@@/`53BpC/ r* rC%@ @!Px @ t t  % x$e?ewJw JDC%% %% _L%5 % @dumumu cuu ZA@@ A 5@u@Ou]u]H@5x@@5x@;A@  5u/A@@ A 5u$A@5v5u@ &A@v5u  % x>eJr?*e$w>Iw ,I %f $ wIw I N+H%h_hE%_%\E%0.f&e  %=e 3f VP %e E%SfXf&e Af VPe%%Ɨe E%oftf&e ]f VPee yf VP e N+wHw H 5 %@A pf7 H-  N+f VP N+f VPf VPf VPf VPf VP N+77wGw ~G@5 Au @  = u-><wFGw 4G  _|%_|5 7 j7 h%8%tR R%^> <J5%_X5 % fe L$ 5 7rH%  %_X7XH7 Е f& fE% % ze _|e 5U7 @E% b7G%%GN tUU0f  c $ UUE%  5 '2 (% ACpWturfff e N+ % _%%`g $ mg $ X# ,wEw DN7 vP> v#F< FN , *"g VP%Ng VP 7wDw D N+B 7\F% %PF ԓ ГABpNeEfg VP%f %g $%%g VP Ng VP t%   \% %7E7E   C- ABpNg VP %@E% @E%h $h VPwCw zC 7BE% 6E% N+  N+"h VP e `%D@Y  N+7 @>z|&& & k e & h( 4%z|&& & h e & h( %%)xDpD N >%    xf @wrBw `B.h $wbBw PB _!Sh $_!  bh $ N+nfh VPe x b T%_!7C ,h $_!B_@!   ( _!  _!_! _! 8 5 %cX 0 >C 8Cf 8(% :  (* %;  C Cf 8(% X  ( xp L*f 8(% Z%_! J%%R_4B_!   xfB B |Bf 8(% , %%TB PB ( ( , (_!72B (_!B7 B ( B7& A & 8(% ,r (l (b`_!X L*P (_!B L*: (_!.,A$ AA AA5 |A ( , ( %% f j& 8(%ȍ (_!_! $ %_! h $_4%ܭh $@tpef@ @ (_4V@P @ 5N (z|f& & n e & h( _! h $ %_!\@7 R@ (_4ҌЌ>@Ȍ 8@4@ .@ ^ (]   xfh $ D %:&7 z i $_4 ? ? ( : 7?%#_4g7bE y 7? & h(  %i $ X#w=w =@ v# 00 ܋7֋%3E%%3  )`H%h< 6%E% 3EU3% e  i $ % e :i $ e fw:  N+;7 ;i VP %i VPi VP i VPeb VP i VP  N+w9w 92 %_"' %r; & h( %_"'և ( %8;&  L&g Ze & h( %g 7:% : ,l ( T`:X : 5 L%@ƒ< % i $(e,7Ce- ,w|Ce-nچ (f h( N ( wh8w V8 %  < % i $ X# w,8w 8n5 d7Z95 9 (J7 F , <24, (N (& RC  ( ww7w 7D5i $ tEE@Pwz7w h7@tEAE@PUwV7w D7fffg :e & h( w&7w 7 N PG57 rff 4%w6w 6Nj VP w6w 6Nj VP w6w 6B5  '2 (2w6w 6BE%  ' E% 4%t-Et%#tt ACp@#j $   xi1j $ w5w 5N (eEw5w 5 77%& P%;!    X7 R7 % %<7 wh5w V5 <3%~ %7wB5w 05 Ij $w,5w 5 ?e$  (z|&& & f e & h( w4w 4 l%;z| && & n e & h(  (w4w 4 % $%" $5%"@ \Y^j VP $%) `j $w@4w .4-7@(tj VP+~j VP&j VP!j VPj VPj VPj VP j $  xiw(w3w 35 5 >%FE5`A rWpDeHe ʋ6%E%555 >%FE@ E%< %e%hH D a%ao $ [ 4 4 4 4 > 0Ue %FҔw2w 2 f4 `4Z4%7 _1 ڀԀ7 Ѐ  ̀ _1 " <3_1Ѐ ~" Ā 47 h" _1(& 1%_12)> e&& 1%_1&  1% P_1 $%(%1e_1%P<_17l3P_1& & 1% ._1>?= 1%_1& & 1% -_1@A= 1%_1="= 1%_1 * 1% +_1 8! Zo $ _1 j 27 ` Z !%/n7? y _,- %0 %9._/7~'_15 5 5 %0 5%0'%9$&  fff eee@ ee@ 5u@0 D %.%%d"%D%e%E7H~  @e7B~_1@72~_15 2@0 3@% @c o $%@0   r enA! y, d @% @c @0 7V}@0 V}_1_1 _1> D}ӕ.7 :} <3%{ <3%| %_#%F %F7| 7|%||%:|7|2/5/& 1%%60| 1%o $ D_,-  r eoA! y. <3w-w - 8| 7.|w-(|@7 |@w-w -|n/7| d/p VP p VP " 2 % p VP"p VP { %{'p VP*p VPw>-w ,-7 { ' 2  %{ta7z{%9p $w,w ,C  D.Qp $7({9  1 .+ ) & { `.7 {o7< y o7< y%{mEwR,w @, {@m&l@&lw,,w ,C  _4@ 5j B %,%J _455N& dA 55@5Nf dA 5}-   _4e@ S%@%$IN& dA 5E@ :"7& A 5-@ 5j E@-%@%$f A 5 e e  w*w *%%6q $_D6BC c_D6e nEec_D6%#3% % %Mq $;z| H6%ch;z| H6%c] 5j4p N 4% dq VP < 0E~p7: yN H6% $%z|Nz| H6% %&nq VP%c zq $ wF)w 4)%%q $_">5 Bu5%d_7%$2q $% x7 xx mx7x5 %  B 5` B 5`w7 wu %h9 d9  5 Nq VP %  > >7lwerTw 7Nw@ 5j %5%6%"8%4N)5& z) nEeNf>t 4%Z) (N (NfFt 4%8) 4) (@_">%ZQ%_">N& )5& ( nEeNf 4%5(5 ( (N (Nf 4%5u- Nfq VP%uN (_:N C 5@ 5Nf >% _:p7Nf b3%5_">@55  5j@5 _=%eY%lu7 fu%`u%fueq VP Tuq VP Buq VP r VP,u r VP " r VP #r VP,r VP   6 N4r VP 5 %Cr $ B %+%I %% _">@_">@ ȥ1D <@_L9@ " B d \Y_L9 5j%$ @1Dp75 y%c \Y B5@ \Y_L9@ ȥ1@fr VP _L9R  _ _L9Nr VP _L9  r epA! y$N \Y_L9wh!w V!@!s VP$ s VP s VPs VPs VPNs $    xqw!w D1N& dA % j555Nffe H6%w w D B5%*s $z|& 4%5oE7o o+ H1e t VP 5t $t VP, t $ t VP s72 y  t VP   xst $w*w D  8$ 3 0 %  %  xst-ww BCE%E % % A @@0%  % A @@55EU%%! ww C%(%  % ww D 5j%e %d ww rDN B @  E U s70 y w<w *CB zju VP _rD  vD _rDt55 9AWpA-2 u VP jft VP%,, &u VP ei.u VP ! % 6u VP Pu VP eieu VP zu VPww CB 05  '4- u $ 4- 54t5 te  ww C B %   1 w^w LB D  %*.L w w  DC B _F_F & 5&  nEe& nEeN (& & nEe& & nEe_F & nEe& nEe5&  nEe& & nEeN (C & & nEe3Nt7- y 5j=>tBe 4%  & F%ww DN& "G u VPNu VP wlw Z @4p5@ *su VP w>w ,B  _8I _8I j5_8I5 E @ 5- dA r 5u-A 5re0= A 5ruA r 5 = wlw Z 5  @ ȋ@wHw 6 %" ] %N* tT AWp eu@ NH tT @  f Y @9 Gfe@&@p&f he  x@Nc tT @@@ @ A@ w2w 3@& T ww  @v73 y 5  f <^ 5 Nf Y @wf <^ 5f ^N tT w ,AWp eu@Ap@A p N _%xNef ^ %( 63= % $ !Ned \5%.@ p@Aedp@=@p @A ehp@=@%  @t5pw~w l 5 N :[ % N _wTw B ]%YAWp eu@  @   x& f ^%@ff ^%@Ap@0 @ @%@  \@ 0 N ]w@ &f ^w ^D m1" a2  ew.eE !݃%%5 |^%Je \w De!c bb! c33ww zf ^%ȍ ^% ^% wJ70 0  ӕ- ӕ0B~0ӕ.  ӕ0~ B~70 0  ӕ-ӕ.0 B~ӕe ӕ- ӕ+ r e0e0Sf@w fw/w/wf@w/wf fw/w/wFf@w/w/w$f//wm/ wt/wmn/fw^/ wwP/ f@w@/w Be0# @dє     BA   W e B@e0dm>- O` deȐ9 ȕ0 dȕ1 ZO- TO`0ddVV'bcc cccc%c,c5c :c Ac Dc JcOcVc[cacjcmcucycccc^h. .PPPPPPPPPPPP0^PPPPPPPP8PPPPPPP8 !ddelb.@ 4 n *8 T ( N intcharfloatdoublestructautoexternstaticregistergotoreturnifwhileelseswitchcasebreakcontinuedodefaultforblissfortranasmsizeofillegal option - %cArg countCan't find %sCan't create temp%.7s startup startup ltorg end %s $main$ %d: initializer too complicatedExpression overflowexpression overflowExpression syntaxBad function$%d equ * dc %dx'00' Bad register %oDeclaration syntax%.8s redeclaredCBEDddddd     B   ,  T,D|||\\PTX`jt|RdvIllegal conditionalCall of non-functionIllegal indirectionIllegal lvalueIllegal structure refIllegal conversionC error-- convertInteger operand requiredExpression overflowLvalue requiredConstant requiredmain entry %.8s @%.7s INT%s %.8s FUNCADDR@%.7s EXT%s %.8s FUNCADDR extrn %.8s main ltorg $main$ equ * subsave runmain subretrn cc=00 ltorg stackdo 0dj,P! (  !P!P!@HL`!!##########$$$$$$$$$$$$$#Too many }'sExternal definition syntax$%d EPILOG %d DC C'END %.8s' %.8s dc %dx'00' Can not initialize externals. cnop 0,%d %.8s equ * dc %dx'00' Too many initializers cnop 0,4 dc a($%d) Inconsistent external initializationUnexpected EOFThe profiler is not supported on IBM C.%.8s PROLOG %d %d args Missing '}'Case not in switchSwitch table overflowDefault not in switchUnknown keyword = %oRedefinitionStatement syntaxUndefined structure: %.8sNot an argument: %.8s%.8s undefinedConflict in storage classType clashBad structure name%.8s redeclared$%d dc d'd'-' P)X)\)d)l)|)`+j+t+~++++Expression too largeStatement syntaxIllegal indirection B $%d $%d equ * Bad structureCompiler error (length)Break/continue error asm delimiter error mainloc codeloc dataloc strgloc tvecloc litloc istrloc Illegal loctr value<<8888888888ABCDEFGHIJQRSTUVWXY_bcdefghijpqrstuvwx    .<(+|&!$*);-/,%_>?`:#@'="abcdefghijklmnopqr~stuvwxyz[]^{ABCDEFGHI}JKLMNOPQR\STUVWXYZ0123456789 *.....nnnnnn    T0"0///T0////T0//T0"0///T0///(o6o@oHoVo   1Z-d-Z1-. /1x- .0D-1P.0R-1f.F1-6-0n1 0bnrt2222223 \222 3Symbol table overflowNonterminated commentillegal floating point constantUnknown character$%d dc x'' dc x'%02x00' dc x'00' Long character constantNonterminated string<=@A>?DEBC=EDCB"*Hghkn555 5*5t55512F::::=*=<*=*=*===ppppp>\9R;=>b:=>:6;<<=>h= ;=L;0>:>b>b>b>b>b>b>0>:>b>b>b>b>b>D>N>X>rcexper register errorIllegal initialization lr 0,%d ltr %d,%d No match for op %dcexpr register errorCompiler ftn call error s SP,=f'%d' lr %d,%d mvi %d(SP),x'80' la 1,%d(SP) la SP,%d(SP) FCALL s SP,=f'%d' la 1,%d sr 1,1 BCALL la SP,%d(SP) compiler error; star without sharp%dcompiler error; bad star entry std %d,%d(R12) temp st %d,%d(R12) temp lr %d,%d lr %d,%d Register overflow: simplify expression%d%d0%x%d(R12) tempasnoxNo match - binary op %dIllegal structure std %d st %d,%d(R12) conv args to ptrs for fortran la %d,%d(R12) std %d,%d(SP) st %d,%d(SP) ^tatetitltptstvtzt}t @@@A@@6A@f@XAT@T@x@xAAA~A~AA*+,HIJ.C$C$C.C$C$CJC c r0,=f'%d' bh $%d lr r1,r0 sll r1,2 b *+4(r1) "56fFEEFbebnebnhblbnlbhblbnhbhbnl$%d=f'%d'=x'%04x%04x'%d+%d(R12)ss$&fnum+%d(R12)%.8s@%.7sIllegal structure reference$0Bad reg. reference%d$%dpname called illegally b $%d s 0,=f'%d' b $%d b $%d c 0,=f'%d' be $%d b $%d * End of switch code. Duplicate case (%d)b $%d %suuuu! HHHFHHHHH?N?W?Tc?v????T???ƀ?Հ????T ?1??T?m??|??T?ˁT???W}???? T?T?C??ۃ?#??PT?T??Ä?LT?}???T?p???? TT%T?TT??Շ? 1?[???ЈTT?)TcT??+?V??ފ?????jv v!v#*w$Hw%w&wevPw(x)x*y+Zy,Zy-$x.Vx/x0x1xF&{G{HyIzJrzKzLzM&{N&{O&{fbv3>|4f|h&vkNv dc f'C1' dc a(A1) dc x'Q' b A1 F01 b #1(R) lfunc 15,A1 F20 lr 15,R sr R,R sr R,R ic R,A1 l R,A1 sdr FR,FR le FR,A1 ld FR,A1 lB1 R,A1 lr 0,R O 0,A2 stB1 0,A1 F01 lB1 0,#1(R) lr 1,0 O 1,A2 stB1 1,#1(R) lr R,0 F01 lr 1,R lB1 R,#1(1) lr 0,R O 0,A2 stB1 0,#1(1) la R,A1 l 1,A1 lB1 R,0(1) H01 sr 1,1 ic 1,~(R) lr R,1 H01 sdr FR,FR le FR,~(R) H01 lBF Z,~(R) F20 lcBFr Z,Z F20 x R,=x'ffffffff' S20 stB1 Z,A1 S20 l 1,A1 stB1 R,0(1) S20 movfi FR,R stB1 R,A1 S00F05 movfi FR,R stB1 R,#1(R1) F03S00 movfi FR,R stB1 R1,#1(R) lr R,R1 F01S04 stB1 R1,#1(R) lr R,R1 F03S20 l 1,T stB1 Z,#1(1) F20F20 srl R,C2 F00S04 srl R,0(R1) S02F20 l 1,T srl R,0(1) F20F20 sll R,C2 F00S04 sll R,0(R1) S02F20 l 1,T sll R,0(1) F00F20 OB2 Z,A2 F00S05 OB2 Z,#2(R1) F00S04 Or R,R1 S02F20 OBF Z,T sr R,R F00 n R,A2 F00S05 n R,#2(R1) F00S04 nr R,R1 S02F00 n R,T F00 m R-,A2 F00S05 m R-,#2(R1) F00S04 mr R-,R1 S02F00 m R-,T F00 mB2 FR,A2 S02F00 md FR,T F00 srda R,32 d R,A2 F00S05 l 0,#2(R1) srda R,32 dr R,0 F00S04 lr 0,R1 srda R,32 dr R,0 S02F00 srda R,32 d R,T F00 dB2 FR,A2 S02F00 dBF FR,T S00 sr 0,0 ic 0,A1 mr R-,0 stc R,A1 S00 m R-,A1 st R,A1 S00F05 sr 0,0 ic 0,#1(R1) mr R-,0 stc R,#1(R1) S00F05 m R-,#1(R1) st R,#1(R1) F03S00 l 1,T sr 0,0 ic 0,#1(1) mr R-,0 stc R,#1(1) F03S00 l 1,T m R-,#1(1) st R,#1(1) F00 mB2 FR,A2 stB1 FR,A1 S02F00 md FR,T stB1 FR,A1 F01 lB1 FR,#1(R) mB2 FR,A2 stB1 FR,#1(R) S02F01 lB1 FR,#1(R) md FR,T stB1 FR,#1(R) S00 lB1 0,A1 srda 0,32 dr 0,R stB1 1,A1 lr R+,1 S00F05 lB1 0,#1(R1) srda 0,32 dr 0,R stB1 1,#1(R1) lr R+,1 S02F01 lB1 0,#1(R) srda 0,32 d 0,T stB1 1,#1(R) lr R+,1 F00 dB2 FR,A2 stB1 FR,A1 S02F00 dd FR,T stB1 FR,A1 F01 lB1 FR,#1(R) mB2 FR,A2 stB1 FR,#1(R) S02F01 lB1 FR,#1(R) dd FR,T stB1 FR,#1(R) S00 lB1 0,A1 srda 0,32 dr 0,R stB1 0,A1 lr R,0 S00F05 lB1 0,#1(R1) srda 0,32 dr 0,R stB1 0,#1(R1) lr R,0 S02F01 lB1 0,#1(R) srda 0,32 d 0,T stB1 0,#1(R) lr R,0 lB1 R,A1 srl R,C2 stB1 R,A1 S00 lB1 1,A1 srl 1,0(R) stB1 1,A1 lr R,1 F01 lB1 1,#1(R) srl 1,C2 stB1 1,#1(R) lr R,1 S00F05 lB1 1,#1(R1) srl 1,0(R) stB1 1,#1(R1) lr R,1 S02F01 lB1 0,#1(R) l 1,T srl 0,0(1) stB1 0,#1(R) lr R,0 lB1 R,A1 sll R,C2 stB1 R,A1 S00 lB1 1,A1 sll 1,0(R) stB1 1,A1 lr R,1 F01 lB1 1,#1(R) sll 1,C2 stB1 1,#1(R) lr R,1 S00F05 lB1 1,#1(R1) sll 1,0(R) stB1 1,#1(R1) lr R,1 S02F01 lB1 0,#1(R) l 1,T sll 0,0(1) stB1 0,#1(R) lr R,0 S20 sr 1,1 ic 1,A1 Or R,1 stc R,A1 S20 OB1 Z,A1 stB1 Z,A1 F01 lB1 0,#1(R) O 0,A2 stB1 0,#1(R) lr R,0 S00F05 sr 0,0 ic 0,#1(R1) Or R,0 stc R,#1(R1) S00F05 O R,#1(R1) st R,#1(R1) F21 lr 1,R lB1 R,#1(1) O R,A2 stB1 R,#1(1) F03S00 l 1,T sr 0,0 ic 0,#1(1) Or R,0 stc R,#1(1) F03S20 l 1,T O R,#1(1) st R,#1(1) F01 l FR,#1(R) aB2 FR,A2 stB1 FR,#1(R) S02F01 l FR,#1(R) ad FR,T stB1 FR,#1(R) S20 sr 1,1 ic 1,A1 Or R,1 lcr R,R stc R,A1 l R,A1 O R,A2 st R,A1 S20 OB1 Z,A1 lcBFr Z,Z stB1 Z,A1 F01 sr 0,0 ic 0,#1(R) O 0,A2 stc 0,#1(R) lr R,0 S00F05 sr 0,0 ic 0,#1(R1) Or 0,R stc 0,#1(R1) lr R,0 F01 l 0,#1(R) O 0,A2 st 0,#1(R) lr R,0 S00F05 O R,#1(R1) lcr R,R st R,#1(R1) F21 lr 1,R sr R,R ic R,#1(1) O R,A2 stc R,#1(1) F03S00 l 1,T sr 0,0 ic 0,#1(1) Or 0,R stc 0,#1(1) lr R,0 F21 lr 1,R l R,#1(1) O R,A2 st R,#1(1) F03S20 l 1,T Or R,#1(1) lcr R,R st R,#1(1) F01 lB1 FR,#1(R) sB2 FR,A2 stB1 FR,#1(R) S02F01 lB1 FR,#1(R) sd FR,T stB1 FR,#1(R) l R,A1 movif FR,R F01 l R,#1(R) movif FR,R F20 movif FR,R F20 movfi FR,R ?????,???;?T?T?TI?^????????n????~<.=.>.?.@.A.B.C.D.E.F20 ltBFr Z,Z F20 cBF Z,A2 F00S05 cBF Z,#2(R1) F00S04 cr R,R1 S02F20 cBF Z,T H00****** %s!"%(!&!QQQQ~QQQQQ~QQhQQQhQQQERROR cputc cputc: %d not opencputc: writing %dftoa Y,Ycgetc cgetc: %d illegal file numbercgetc: %d not open to readcgetc: error on %darwYYYYYYYcopen: bad file %s\[[: argument count wrong That's all, folks  %ewq/dev> cBF Z,A2 F00S05 cBF Z,#2(R1) F00S04 cr R,R1 S02F20 cBF Z,T H00****** %s!"%(!&!QQQQ~QQQQQ~QQhQQQhQQQERROR cputc cputc: %d not opencputc: writing %dftoa Y,Ycgetc cgetc: %d illegal file numbercgetc: %d not open to readcgetc: error on %darwYYYYYYYcopen: bad file %s\[[: argument count wrong That's all, folk@ZJ-! & 6  Ww FX%ssȭ7 ­7 Ҭ@ @mȥ-@ @m5 >b  @@%Rb  Sr@ @m& VR 7@ @m\b   Sew@ @m& VR%77jb  S@@% 쫷 `JԔ %R B&e7 @e&7@ @m5 @ =@ @m|b (I  L  X  'b (I <bbb (I 8  SwVw zV^b (I Nff f fff (Ie  >R ުԫw:Vw (V`Ϊ*@e5@e5x & -_ xp`p "  R#%(  U Z  .] (]  e 5%% %e 5 %N @e5  %+@% %!N@&@&$ e5@ $A1uХ> j  P! F& %&  e! 4b  ' ( '& &  e ?ReL%ȩFb  S _r 7e4 2b  z _ %%" %/#$  #%e Z[d  Z   RitE5% % 2uu- u-1 5Ri+`7[ y %@e@-b  See}_r@5`7n[ y xZ ޣڣwR%_\N _r & & & e?edN %_\_r%_\w `R 5 C% 7%ħ7 R"7Y%% f ff e5` %@wQ "% %Z%7 J7 H%%%  w QC5 % 78 !7 Y%% wzQ T5_F  tAE@P@t55D]ȥ 5 '% % !%% % %% %2! % %2!2 % %  U  _F 3J%  .($  ڥp 5 %& $5 %@eE@5` @eE5 r- >rE%% % % %b % U% > v%7`2B%6W '(W $W c (I  v%c (I  '!%E%5 E%%&c  @ _w O *a7W y tU_b |e~r T\ x` b7 \T & &  e> > %UtU9 %'7V  7  Awp@ 1 -Уʣ ģ X% # tU 2 7Uw N  % w M6c N zwMw MVe Ic  wMw M@0 J xawM w M #( $%  %!%%& &  e?De>@ 5Ri5$  % $3 3 3_%#  %d 5 @ r e^cA! y$& & f e _%c u_4E%c &  &d%#*_E% d & h &$ e_%$  $1% & N &fd 5 @5 5  5_ f `% _& @%F%G,e&  $  %%,d eu@E%N #5f %#  &  e?e( @E%_$ _%52 5 _4N && j Wp eA0ju@E5@E5 @5%P5%V S%%D-I%?5%%:Bd 5%5 55 (%)u 5@%5 5 $ff e5%= %%@c55 f `% A  $ff e&ff e ?e  $f&  e?ewvHw dH@ 5 xc*+N & &  ef& f& e wH34N& f& eUd @w GDCB%#  xc  && %wG 2w GDE%&  &# ewVGw @GCE%%%w&G$e& v & %# w F@%%gd wFw FD%  EewFE%hE! w vFDeex @  wZFw HFTeN d  Sw0Fw FD%%$d wFw ED% % wE@@% xc`?ʖeĖBpC r r@ @Px @ t tw ZE &%d  wBEw 0E 'TE%[%XE%Ed&e  %=e d (I %d:e E%dd&e de E%dd&e d (Ie %e e (I e%t 'wXDw FD 5 @A pe7 0 % 'e (I 'e (I)e (I3e (I=e (IUe (I '7^7XwCw C@5 Au @  =>wCu-<w |C _%_5 7 ޘ7 ܘ35%_5 %/ fe b 5 <7J%  %%z %%_7xJ7 jzL f& fE%8 %, e"_e 5U7 @E% r7I%%N USf  b  US  5 #2E% $% ACpWturfff ne ' % _%%e  e  z whAw VAҖ7 Ԗ– H HN  ff (IeN"f (I 7xwAw @ 'B 7~H% % T_ L_ABpNeEf4f (I%f %Ff %%df (I Npf (I ~%   f% % 7G7G   C- ABpN}f (I %@E% @E%f f (Iw?w ? 7^G%RG# '  '4f (I Q  L%GF < '7 @ 8 x`e|&& & k e & Z$|&& & h%%%)FF N P! @w> w >f w>w > e7K y f w> " .f  'fg (I% ,  %7E  &g  _ xe T  $_ _ h __  _ 5 %a  Z xpe 0 bE \Ef *$% x  $* 2 %;$ 0E *Ef *$%  $  &f *$% l %_ \ %%ґ_D_D D Df *$%   %%D D $ $  $_7hDFDVD7: LD $ BD7(& 8D P& *$%  $ $_ &_@ސ &֐_@ΐ̐CĐ CC CC5 C $  $ $ %_% _f & *$%` $X_ h %_ 6 2gEg _%0CeC C $_C C 5 |f& & n e & Z$ "_ [g  0 %^B7Bx BBp B BF_tqg  < R%:%7 JT g 6B 0B $ 7"B%__7B :& Z$ x %_g  z_w 8:te@7  *Ǝ0 7#3E%%3  v%` pT 6%E% 3EU3% e g  % e g  e%t‚ЍJčw>9w ,9T 5 e g  3U me%tw8w 8D %7p@w8w 8 5 B5 GL I xe g 0 3  N& e% fe b 5wڌ g ƌ  :5%w? w8w 7  % VP J0 % wh 7 ^%  w7%h  ?,7  eEw f%  e h   0w 7  '>7 >+h (I %3h6h (I 9h (Ie` (I ;h (I  'w6w 62 %N %8 $ %@[=⊷ = &5 h%+PJʊ % Xh e,7*CeC= & Z$ %.=w& d &g Ne & Z$ % 7J=%>=  $ w5- wCe-t $f Z$ Nw @5 Z%   H%w*5mh  z w 55 zȩ7p<5 z< $`7 \  RHJB $N $.& = & $"ww4w 4D5~h w|4tEE@Pw V4@tEAE@PUwF4w 44wfffg e & Z$ w4w 4 N *A57 ff "0%w3w 3Nh (I w3w 3Nh (I w3w 3B5w3 #2 $2w t3B  # E%E%%% E ht x>ht  ACp@h  w2w 2N $eEw2w 2 7j:%' N%;"T    :: 4: % %:w2 w l2 +7 -%~wZ2w H2 h wD2w 22 ?>e8$ *& |&& & f |e & Z$ w1w 1 n%;| && & n De & Z$ : $w1w 1 % H*%" h @ >R &*5%"h (I *%) w\1w J1-ކ@  xJhh (Iww*1i ii i*i3i=i w 05 5 JE5`%RA rWpDeT5%E%555 JE@ %RE%wl0 % e%tTe ʋ D e%e4n  S 4 4 4 4 J Se Ҕ%Rw / x7 r7l7%7 r- JD7 @ Ne&&  x,% P 0%(%1e%P<76& & x,% .>?=& & x,% -@A=" * x,% + Jn  " 67  '*m7"C y  &%/_( &%0%9.B5  r e>mA! y,7'_)7 5 %0 5%9dupaewV@0 Z&%0%.%d%D%e%E7 _)@0  &@% @c `n %@0  @% @c @0 7h@0 h_)_) j_)J Vӕ.7 L %_#%R 8% -%{ -%|%R7 h7%%_)ր7Ҁ_)_)5/&_)60|_)n _( -_)w + t $7jw-d@w+7 V@w +ZP37N F3n (I n (I % n (In (I  " `-%n (In (IwT+w B+7   %ta7' `-%n w+w *C # w*$n7@ yn 7\ z#n7j@ y %   F .27 <%*kEw h* @*ljw`*@w H*C V%,R%JM@ 5Ri < 55N& T; 5%dl@o (I 5%@5Nf T; 5%d.@o (I   w)e}-%d?No (I @*%d?$o (I%$N& T; 5E@ %d? Efo (I%e %d?o (I %@"& ; 5%dT?pf ; 51%d0?Fp (I @ 5Ri E@-%@%$%d>N+pe _.w <(%%Up %BCo7> y ~?e%lp cw'%#3%P>| X1% 5Rin N "0%p (I  B?N X1%%|N| X1% %w&p (Ip  _>0w '%%%2t= Np (Iep w&5 Bu5%d@%$2p %|7 {{ m{7{5  H95`%  H95`er{ 7{@ 5Ri %5%6%"8%w4N-5& - ~?eNfs "0%`- $N $Nfs "0%>- :- $@_1%ZO%w_1N&  -5& - ~?eNf "0%5,5 , $N $Nf "0%55 fp (I%uN $_4N < 5 5Nf 8% _4T;Nf .%555  5Ri@5  r eDoA! y$%e^%y7 y%y"yep (I yq (I y!q (I 8q (Iy@q$Rq (I ty jy\q (I sq (IPy Fy{q (I 2y5 %q  < %+%I #%_2 _1@ ȥ1D :_3@ $o79 y < _3d%c >R <5_3@ >R_3 5Ri%$@1D@ ȥ1@q (I _3R _7D@t A@`eP5 5%$q U `< 5*N|& "0%5vev < epvNx8q (I%_3Nfq5@ 55u 5uuN|& "0%55@-_3fr (I%%d7_3r (I_35u_3u-_35uc_3# % Nfr (I%%d`7_3(r < %^72,e x4o@ 5@ u@e5 @ + ݃u -6" " > _>t55 %_>Mt (Iet (I b"5 X"P"5& F"xt (Ie6"5 ,"t (I Nt (I Nt (I t (INt (I Nt (I Nu (I%u (I u (IN%u (I   ~?e& ~?eN $m& & ~?e& & ~?e& ~?eL 5&  ~?e&  & & 5Ri=sBew "0%  & @%ww DN& @ u (INu (I wtw b @n5@ ru (I wFw 4B _B_HB Ri5_HB5 B_B5@_~B *A2 *A25 R:& % rn  & 8: 5  n & 8: 5%d, f&v (Ie r euA! y %d,+v (I 6Nf 8: 2%9(A p Cr%e& FG%w ? ee%u- @w rC 4 *A3,%#&%$%g *A3%$%#%%  xu 4w @  R: 8: 3w `5 5 B <5Nef G% 'C Caeu%% ef FG% %(_E _E %   w% %# A1l %( Ne tECe & 8: 5%*  5 5@ @ae54N R:@-@ %*@ @a% @54 u-%* ?  & 8: 2_NDw @ @me,55 De,_F%*_F%f5 Ce,M I%*E%@Ap,p( *A N *G AA 2r %AA 2r  eC- u-u5ueD- HA1@ @m5DC(@p@0@pAA 2rA1p *A5 _F N&_FwFw 4D eD-w*w D@( 9 xu=a7ADp}1@ =@,=Q)=x@# 9v%+A r=A r@ t@tFv ww BCJ- `f G%f G%_$I *AJ- < E%*%.?%;%(7%1%*AfAVp@1 AADtp0(%( R:5 @ `, R:5!@ `5,@ `,BD " `,wHw 6F@e7dB@e5M% Lwde   % uew~de_~Kf@& %_~K%ue-Fdl :dr0,d0 5 h5.d d T55 5 c5 c r eA! y"N LJ Z@YN K?N L:u6@E?c c-@e5=5e@ffe& Ne 5 @e5 f@& % ?$c@e5N P55 @5@@5rff %@ f@& %@ ff %@ @e7bb b5_I%f& %w w  0Jb Fbw 5  @A tE5 @e0? b bw P @t5E L@E5% e7@e0?a aw w  5 %9AWp Ameua5 za%0 pa@w w \a Xaw w @e5 @ -,a (aA r }eA r 5 @e@-= e 0@?` `@e@-w* w   (INf f fff (Ie  Mh> Sw w f % U %@w AWp e u@ N FM @ f R A@ HA1`@A@,@ & f W%@ @@p&f W% @N FM @@0 w  V % U  %@ 5%f%F5 5 5 - 555 5 B5 @5 5 B5  B5 um555 B5 u-5uWA5 B5@5 % "0 . @@50 u-5 )55e0= @ @-. 5uu5 B5 u- A >E @ 5- dA r 5u-A 5re0= A 5ruA r 5 = w|w j 5  @ ȋ@wXw F ; (QwHw 6 %. U %N6 FM AWp e u@ NT FM @  f R @9 Gfe@&@p&f he  x(@No FM @@@ @ A@ w2w 9@& M ww  @7&: y 5  f W 5 Nf R @wf W 5f VN FM w ,AWp e u@Ap@A p N X%xNef W %( `9= % $ !Ned U5%.@ p@Aedp@=@p @A epp@=@%  @t5pw~w l 5 N T % N WwTw B ]%YAWp e u@  @   x& f W%@ff W%@Ap@0 @ @%@  U@ 0 N Vw@ &f Ww ^D m7" a2  ew.eEΌ !݃%%5 ^W%Je Uw DeΌ!c bb! c33ww zf W%Ԍ W% W% wJ77 7  ӕ- ӕ0B~6ӕ.  ӕ0~ B~76 6  ӕ-ӕ.6 B~ӕe ӕ- ӕ+ r e0e0Sf@w fw6w6wf@w5wf fw5w5 wFf@w5w5w$f55wm5w5wm5fw5wwz5 f@wj5wf5w f@wL5wH5"wf@fA w,"5w"5e"w"5ew"5@lw"5Alew4 &@t`e @& HADCBF7VFf@  x,V "U:@ $$ttyЋ7U@ Uf5w @4 * f5w *4 }7 UUW&fҮ 7 U@% U@A&@73@L> Be0# @Үє     BA   W e B@e0Үmh3 U` ҮeȐ9 ȕ0 Үȕ1 T03 T`0ҮҮVV'aaaaaaaaaa a a a aabb bbb!b%b+b3b7bXNNXXpzz !d\\  \&de \ *f j x intcharfloatdoublestructautoexternstaticregistergotoreturnifwhileelseswitchcasebreakcontinuedodefaultforblissfortranasmsizeofillegal option - %sArg countCan't find %sCan't create temp%.7s startup ltorg end %s $main$ %d: initializer too complicatedExpression overflowexpression overflowExpression syntaxBad function$%d equ * dc %dx'00' Bad register %oDeclaration syntax%.8s redeclaredCBEDhcrczccc    " @  " " F " n RX|Illegal conditionalCall of non-functionIllegal indirectionIllegal lvalueIllegal structure refIllegal conversionC error-- convertInteger operand requiredExpression overflowLvalue requiredConstant requiredmain entry %.8s @%.7s INT%s %.8s FUNCADDR@%.7s EXT%s %.8s FUNCADDR extrn %.8s main ltorg $main$ equ * subsave runmain subretrn cc=00 ltorg stackdo NT@@@@@6`h>dP* * * * P P P P P P P P P P P P P Too many }'sExternal definition syntax$%d EPILOG %d,%d DC C'END %.8s' %.8s dc %dx'00' Can not initialize externals. cnop 0,%d %.8s equ * dc %dx'00' Too many initializers cnop 0,4 dc a($%d) Inconsistent external initializationUnexpected EOFThe profiler is not supported on IBM C.%.8s PROLOG %d Missing '}'Case not in switchSwitch table overflowDefault not in switchUnknown keyword = %oRedefinitionStatement syntaxUndefined structure: %.8sNot an argument: %.8s%.8s undefinedConflict in storage classType clashBad structure name%.8s redeclared$%d dc d'd'-' >%L%>%P%X%h%4'F'L'R'X'^'d'Expression too largeStatement syntaxIllegal indirection B $%d $%d equ * Bad structureCompiler error (length)Break/continue error asm delimiter error mainloc codeloc dataloc strgloc tvecloc litloc istrloc Illegal loctr value<<8888888888ABCDEFGHIJQRSTUVWXY_bcdefghijpqrstuvwx    .<(+|&!$*);-/,%_>?`:#@'="abcdefghijklmnopqr~stuvwxyz[]^{ABCDEFGHI}JKLMNOPQR\STUVWXYZ0123456789 *)*)**JmTm^mdmnmvm    X+B+**+X+***+X+**X+B+**+X+**+mmmmm   n,()R,2).*x*n,))+(n,)+(n,)B,x)(+b, 0bnrt------- \----Symbol table overflowNonterminated commentillegal floating point constantUnknown character$%d dc x'' dc x'%02x00' dc x'00' Long character constantNonterminated string<=@A>?DEBC=EDCB"*Hghkn011`0000112F55J5b577777777NoVo\ohoro845885j8825577@88"85x85888888888888888888d1=%d d2=%d table->tabop=%d opt->tabdeg1=%d p1->op=%d dcalc(p1->tr1)=%d, opt->tabdeg1&077=%d notcompat=%d opt->tabdeg2=%d,p2->op=%d notcompat2=%d rcexper register errorIllegal initialization lr 0,%d ltr %d,%d No match for op %dlowreg=%d, areg=%d cexpr register errorCompiler ftn call error lr %d,%d mvi %d(HGARGP),x'80' la 1,%d(HGARGP) la HGARGP,%d(HGARGP) FCALL s HGARGP,=f'%d' la 1,%d la HGARGP,%d(HGARGP) BCALL s HGARGP,=f'%d' compiler error; star without sharp%dcompiler error; bad star entry std %d,%d(HGAUTOP) temp st %d,%d(HGAUTOP) temp lr %d,%d 010 matches lr %d,%d 020 not recognized Register overflow: simplify expression%d%d0%x%d(HGAUTOP) tempasnoxNo match - binary op %dIllegal structure std %d st %d,%d(HGAUTOP) conv args to ptrs for fortran la %d,%d(HGAUTOP) std %d,%d(HGARGP) st %d,%d(HGARGP) ssssssssss ;::(; ; ;>;::H;::h;n;;h;h;*+,HIJ=<<=<<="56Z@?.@r@bebnebnhblbnlbhblbnhbhbnl$%d=f'%d'%d+%d(HGAUTOP)%.8s@%.7sIllegal structure reference$0Bad reg. reference%d$%dpname called illegally b $%d la 1,4 put 4 into r1 la %d,0 zero r2 $%d c %d,$%d-4(1) is addr 0 ? be $%d+4 c 0,$%d(1) is case satisfied ? be $%d la 1,8(1) bump r1 by 8 b $%d $%d s 1,=f'4' l %d,$%d(1) br %d dataloc cnop 0,4 $%d equ * dc a($%d) dc f'%d' dc f'0' dc a($%d) codeloc * End of switch code. Duplicate case (%d)b $%d %suuuu! BBBLBBBBB,BBLB`BBBVCfCbGGhGGGGGtG~GGoptim(%o), op = %d, d1, d2 = %d, %d degree = %d Divide checkC error: const?????@w???Ow?T?T?T]w?rw????????w????w<Vv=Vv>Vv?Vv@VvAVvBVvCVvDVvEVvF20 ltBFr Z,Z F20 cBF Z,A2 F00S05 cBF Z,#2(R1) F00S04 cr R,R1 S02F20 cBF Z,T H00?r}??}}?}?}?}??}????}?}?}?}?}?}??~T?T?,~?c~??~??~??~??~????~????????(P?7?PT?k?????????3?7?E??Y?s??w?T???????À?Tπ?????T ?!??2?A??P?`?Tw????ف????!T?7T?j??Â? ?L??yT?T??,?Gc???T?T??/k?T??!^?~T?܆??Q??xTTT?T??A?x?Lj?"?? l R,A1 movif FR,R F01 l R,#1(R) movif FR,R F20 movif FR,R F20 movfi FR,R ċ!"%(# #include "../370.h" #include "../param.h" #include "../buf.h" #include "../conf.h" #include "../manifest.h" #include "../proc.h" #include "../systm.h" #include "../tty.h" #include "../user.h" /* States that the terminal driver is in when expecting an interrupt */ #define ENABLING 1 #define WAITING 2 #define READING 3 #define HALTING 4 #define WRITING 5 #define SENSING 6 #define PREPARING 7 #define DISABLING 8 struct tty trms[NTRMS]; extern char revbit[]; /* Inititialize terminals */ trminit() { struct tty *tp; for(tp = trms;tp < &trms[NTRMS]; tp++){ tp->t_state = 0; if(tp->t_devaddr) hdv(tp->t_devaddr); } } /* * Upper level routines: trmopen, trmclose, trmread, trmwrite, * trmsgtty, and trmstart. */ /* * Open a terminal. If already open, do nothing, otherwise set * default values and issue enable for line. */ trmopen(dev,flag) { struct tty *tp; int sps,trmstart(); if(dev.d_minor >= NTRMS){ u->u_error = ENXIO; return; } tp = &trms[dev.d_minor]; if(tp->t_state) return; /* already open */ tp->t_dev = dev; tp->t_devaddr = cdevsw[dev.d_major].d_baseaddr + dev.d_minor; tp->t_addr = &trmstart; tp->t_flags = XTABS|CRMOD; tp->t_erase = CERASE; tp->t_kill = CKILL; /* Get input and output buffers */ tp->t_ibp = getblk(NODEV); tp->t_obp = getblk(NODEV); /* Enable line */ sps = stnsm(~IOINT); enb: switch(trmenbl(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY)goto enb; /* busy */ break; /* shouldn't happen */ case 2: /* channel busy */ goto enb; case 3: /* not operational */ setsm(sps); u->u_error = EIO; brelse(tp->t_ibp); brelse(tp->t_obp); return; } tp->t_state = ENABLING; setsm(sps); if(u->u_procp->p_ttyp == 0) u->u_procp->p_ttyp = tp; } /* * Close a terminal. Flush queues, halt activity, and disable line. */ trmclose(dev) { struct tty *tp; int sps; tp = &trms[dev.d_minor]; wflushtty(tp); /* flush */ sps = stnsm(~IOINT); if(tp->t_state != WRITING){ tp->t_state = DISABLING; /* There should now be a read active, which we halt before disabling */ hlt: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ stosm(IOINT); /* let 'er rip */ stnsm(~IOINT); goto hlt; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto hlt; case 3: /* not operational */ trmerr(tp,"halting for disable"); } } else tp->t_state = DISABLING; /* * During sleep, we get an interrupt indicating * I/O was halted, or write completed. We are * still disabled when we return. */ while(tp->t_state) sleep(&tp->t_state, TTOPRI); tp->t_state = DISABLING; dsbl: switch(trmdsbl(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto dsbl; /* busy */ break; /* shouldn't happen */ case 2: /* channel busy */ goto dsbl; case 3: /* not operational */ trmerr(tp,"disabling"); } setsm(sps); brelse(tp->t_ibp); brelse(tp->t_obp); } trmread(dev) { ttread(&trms[dev.d_minor]); } trmwrite(dev) { ttwrite(&trms[dev.d_minor]); } trmsgtty(dev,v) { ttystty(&trms[dev.d_minor],v); } /* * Start output to a terminal. * If we waiting for something to be typed, * halt read and start writing. */ trmstart(tp) struct tty *tp; { int sps; sps = stnsm(~IOINT); if(tp->t_state == WAITING){ hrd: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ stosm(IOINT); /* let 'er rip */ stnsm(~IOINT); if(tp->t_state == WAITING) goto hrd; break; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto hrd; case 3: /* not operational */ trmdead(tp); } tp->t_state = HALTING; } setsm(sps); } /* * Interrupt level routines: trmintr, trmnorm, trmrd, and trmsens. */ /* * Terminal interrupt routine. Entire routine is run disabled. */ trmintr(dev) { struct tty *tp; int sps,len; char *cp; tp = &trms[dev.d_minor]; /* Huge switch by current state */ switch(tp->t_state){ case 0: /* nothing happening */ return; case ENABLING: /* someone has dialed up */ if(CSW->unitstat == CHEND+DEVEND) trmnorm(tp); else trmdead(tp); /* shouldn't happen */ return; case WAITING: case READING: /* terminal input */ if(CSW->unitstat == 0 && CSW->chanstat&PCI){ /* typing begun, but not finished */ tp->t_state = READING; return; } len = tp->t_il - ((CSW->resid1<<8) | CSW->resid2); cp = tp->t_ibp->b_addr; while(len--) ttyinput(revbit[*cp++],tp); /* process chars */ if(CSW->unitstat == CHEND+DEVEND){ /* normally terminated read */ trmnorm(tp); return; } if(CSW->unitstat == CHEND+DEVEND+UNITCHEK){ /* read terminated by break */ trmsens(tp); return; } trmerr(tp,"reading"); trmnorm(tp); return; case HALTING: /* read was halted to begin write */ trmnorm(tp); return; case WRITING: /* data written */ tp->t_wresid = (CSW->resid1 << 8) | CSW->resid2; if(CSW->unitstat == CHEND+DEVEND){ /* normal write */ trmnorm(tp); return; } if(CSW->unitstat == CHEND+DEVEND+UNITCHEK){ /* write interrupted by break */ trmsens(tp); return; } trmerr(tp,"writing"); trmnorm(tp); return; case SENSING: /* getting sense info */ if(tp->t_sense & INTREQ){ /* * Either break was hit, or the line went dead. * In order to tell the difference, we issue a * prepare command and attempt to halt it. If the * halt works, the line is allright, if not, we'll * get a later interrupt telling us the line is okay, * or the line timed out. */ tp->t_state = PREPARING; prep: switch(trmprep(tp->t_devaddr)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat==STATMOD+CUEND+BUSY)goto prep; break; /* shouldn't happen */ case 2: /* channel busy */ goto prep; case 3: /* not operational */ trmdead(tp); return; } /* now attempt halt */ halt: switch(hdv(tp->t_devaddr)){ case 0: /* interrupt outstanding */ return; case 1: /* CSW stored */ break; /* as expected */ case 2: /* channel busy */ goto halt; case 3: /* not operational */ trmdead(tp); return; } return; } if(tp->t_sense & TIMEOUT){ /* * Line has been dead for 28 seconds, so assume hangup. */ trmhup(tp); return; } printf("dev=%x,sense=%x\n",tp->t_devaddr,tp->t_sense); trmnorm(tp); return; case PREPARING: /* Waiting for line to come to life. */ switch(CSW->unitstat){ case CHEND+DEVEND: case CHEND+DEVEND+UNITEXCP: trmrd(tp); /* * Make state READING rather than WAITING, so * that read will not be halted in trmstart. */ tp->t_state = READING; return; case CHEND+DEVEND+UNITCHEK: trmsens(tp); return; default: trmerr(tp,"preparing"); trmnorm(tp); return; } case DISABLING: tp->t_state = 0; wakeup(&tp->t_state); return; } /* end of state switch */ } /* * Perform normal action for the terminal. * If we need to restart an interrupted write, do so, * if there is anything to write, write it, * else read. */ trmnorm(tp) struct tty *tp; { int i,len; char *cp; if(tp->t_wresid > 0) { /* restart write */ wr: switch(wrterm(tp->t_devaddr,tp->t_obp->b_addr+tp->t_wresid, tp->t_wresid)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto wr; break; /* shouldn't happen */ case 2: /* channel busy */ goto wr; case 3: /* not operational */ trmdead(tp); return; } tp->t_state = WRITING; return; } if(tp->t_outq.c_cc == 0){ /* read */ trmrd(tp); return; } /* write */ cp = tp->t_obp->b_addr; len = min(BLKSIZE, tp->t_outq.c_cc); for(i=0;it_outq)]; tp->t_state = WRITING; w: switch(wrterm(tp->t_devaddr,tp->t_obp->b_addr,len)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto w; break; /* shouldn't happen */ case 2: /* channel busy */ goto w; case 3: /* not operational */ trmdead(tp); return; } wakeup(&tp->t_outq); } /* * Read! */ trmrd(tp) struct tty *tp; { tp->t_state = WAITING; tp->t_il = BLKSIZE; r: switch(rdterm(tp->t_devaddr,tp->t_ibp->b_addr,tp->t_il)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto r; break; /* shouldn't happen */ case 2: /* channel busy */ goto r; case 3: /* not operational */ trmdead(tp); return; } } /* * Get the sense information for a terminal. */ trmsens(tp) struct tty *tp; { tp->t_state = SENSING; s: switch(trmsns(tp->t_devaddr, &tp->t_sense)){ case 0: /* okay */ break; case 1: /* CSW stored */ if(CSW->unitstat == STATMOD+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ trmdead(tp); return; } } /* * Error routines: trmdead, trmhup, and trmerr. */ trmdead(tp) struct tty *tp; { printf("line %x not operational\n",tp->t_devaddr); trmhup(tp); } trmhup(tp) struct tty *tp; { tp->t_state = 0; signal(tp, SIGHUP); } trmerr(tp,s) struct tty *tp; char *s; { printf("term err: dev=%x,csw=%x,sense=%x,id=%s\n", tp->t_devaddr,(CSW+4)->integ,tp->t_sense,s); } tstat == STATMOD+CUEND+BUSY) goto s; break; case 2: /* channel busy */ goto s; case 3: /* not operational */ trmdead(tp); return; } } /* * Error routines: trmdead, trmhup, and trmerr. */ trmdead(tp) struct tty *tp; { printf("line %x not operational\n",tp->t_devaddr); trmhup(tp); } trmhup(tp) struct tty *tp; { tp->t_state = 0; signal(tp, SIGHUP); } trmerr(tp,s) struct tty *tp; char *s; { printf("term err: dev=%x,csw=%x,sense=%x,id=%s\n", DT0:DK2:06:10 PM19-Dec-7406:11 PM19-Dec-7406:14 PM19-Dec-74206:20 PM19-Dec-74 06:34 PM19-Dec-74CE &d t5s (I%essfs (I%es@w8w &u-@w$@w D  w %%# 5Riw C%$ M x\ss (I Fss (I @s7`8 y  se se ss t (I t  't *t.t ww D& " xfs  % %w փt-w BCE%E %