diff --git a/Makefile b/Makefile index 033f3881..8654bc97 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SRC = system syseng sysen1 sysnet midas _teco_ +SRC = system syseng sysen1 sysnet kshack midas _teco_ MINSYS = _ sys sys3 RAM = bin/boot/ram.262 NSALV = bin/boot/salv.rp06 diff --git a/src/kshack/ksfedr.146 b/src/kshack/ksfedr.146 new file mode 100755 index 00000000..fffc1d16 --- /dev/null +++ b/src/kshack/ksfedr.146 @@ -0,0 +1,1062 @@ +;;;-*-Midas-*- + +title KSFEDR - Manipulate KS-10 front end filesystem under timesharing + +nfifls==:0 ; Number of FI files. + ; (Feature doesn't work) + +a=:1 +b=:2 +c=:3 +d=:4 +e=:5 +t=:6 +tt=:7 + +p=:17 + +ttyich==:1 +ttyoch==:2 +echoch==:3 +dskich==:4 +dskoch==:5 +errich==:7 +fsch==:8 + +call=:pushj p, +return=:popj p, +save=:push p, +rest=:pop p, +tyi=:.iot ttyich, +tyo=:.iot ttyoch, +pause=:.break 16,100000 + +define syscall name,args + .call [setz ? sixbit /name/ ? args(400000)] +termin + +define conc foo,bar +foo!bar!termin + +;;; Grok the KS10: +.insrt dsk:system;ksdefs + +;;; Grok the ITS filesystem: +.insrt dsk:system;fsdefs + +;;; The FE filesystem under ITS lives in a single file named something like +;;; DSK:.;.FEFS. PK0001, where PK0001 is the pack number that contains the +;;; file. The format of the file is set forth at the end of this file. + +;;; To create a new FE filesystem, give the CREATE command to KSFEDR. +;;; KSFEDR will then search the pack on the drive you specify for a +;;; suitable location. (If the pack is mostly empty, this shouldn't take +;;; long.) +;;; +;;; When it has created the file, KSFEDR will tell you the "Directory +;;; address" of the new FE filesystem. This is the absolute disk address +;;; that the 8080 front end needs in order to find the FE filesystem. +;;; KSFEDR gives you this address as a 36-bit quantity in the format the +;;; 8080 uses. There is a routine in the salvager called FESET that knows +;;; how to install this address on the disk where the 8080 will find it. +;;; +;;; Before taking ITS down to run FESET, you will probably want to use the +;;; WRITE command in KSFEDR to put a microcode into the FE file named "RAM" +;;; and a DSKDMP bootstrap into the FE file named "BT". It is also +;;; recommended that a suitably nasty message be placed in the FE file +;;; named "README", so that people will know that playing with the FE +;;; filesystem file under timesharing is a no-no. + +popj1: aos (p) +cpopj: return + +rfn"$$rfn==:1 +rfn"$$pfn==:1 +.insrt dsk:syseng;rfn + +rsixtp==:cpopj +psixtp==:cpopj + +;;;RFN"RFN +;;; A (val): terminating character +;;; B (arg, val): filename block +;;; D (arg, val): byte pointer +;;; E (val): 3.4 - 3.1 indicate names specified + +format"$$pfn==:1 +format"pfn=:rfn"pfn +format"$$pcode==:1 +format"$$errs==:1 +format"erri==:errich +.insrt dsk:syseng;format + +outstr: syscall siot,[movei ttyoch ? a ? b] + .lose %lssys + return + +define format &string&,[args] + pushj p,[ + pushj p,fmtin +zzz==-1 +irp arg,,args + push p,arg +zzz==.irpcnt +termin + hrroi a,[ascii string] + movei b,.length string + movni c,zzz+1 + jrst format"format] +termin + +fmtin: push p,a + push p,b + push p,c + push p,[fmtout] + jrst @-4(p) + +fmtout: pop p,c + pop p,b + pop p,a + pop p,(p) + popj p, + +lpdl==:100. +.vector pdl(lpdl) + +go: .core fspage + .lose + move p,[-lpdl,,pdl-1] + .open ttyich,[.uai,,'tty ? setz ? setz] + .lose + .open ttyoch,[.uao+%tjdis,,'tty ? setz ? setz] + .lose + .open echoch,[.uao,,'tty ? setz ? setz] + .lose +irps sym,,[secblk,nblks,nblksc,nsecs]param,,[sec%bl,bl%dsk,bl%cyl,sec%tr] + move tt,[squoze 0,sym] + .eval tt, + .lose + movem tt,param +termin + movei tt,2000 + idiv tt,sec%bl + movem tt,wd%sec + move tt,[squoze 0,QPKID] + .eval tt, + .lose + hrlzi tt,(tt) + hrri tt,fspak ; Pack number of pack on unit #0 + .getloc tt, + call setpak + jrst check ; Do automatic check-out first thing. + +abort: move p,[-lpdl,,pdl-1] + format "~&(Abort)" + jrst cmdlp + +eh: format "~&?Eh" +cmdlp: movei a,[ + tyo [^P] + tyo ["A] + tyo ["!] + return] + call token + jrst eh + jrst docmd + +.vector fname(4) +.scalar arg + +cmds: check,,[asciz "Check correctness of FE filesystem"] + create,,[asciz "Create a new FE filesystem from scratch"] + help,,[asciz "List commands"] + pack,,[asciz "Select pack"] + quit,,[asciz "Return to superior"] + read,,[asciz "Read a file from FE filesystem"] + write,,[asciz "Write a file into the FE filesystem"] +ncmds==:.-cmds + +cmdnam: sixbit /check/ + sixbit /create/ + sixbit /help/ + sixbit /pack/ + sixbit /quit/ + sixbit /read/ + sixbit /write/ +ifn .-cmdnam-ncmds, .err CMDNAM wrong length. + +docmd: movsi b,-ncmds + came a,cmdnam(b) + aobjn b,.-1 + jumpge b,eh + hlrz t,cmds(b) + jrst (t) + +quit: .logout 1, + +help: format "~&KSFEDR commands:" + movsi a,-ncmds +helplp: hrro t,cmds(a) + format "~& ~S ~A",[cmdnam(a),t] + aobjn a,helplp + jrst cmdlp + +pack: move a,[format "~&Select which pack? "] + call number + jrst pack + cail a,10000 + jrst pack + movem a,fspak + call setpak + jrst check ; Check it out + +create: call mapin + jrst [ cain e,%ensfl + jrst creat1 + syscall lose,[movei %lssys(e) ? movei create] + .lose %lssys ] + .close fsch, + format "~&ERROR: File already exists." + jrst cmdlp + +creat1: setzm bigbuf + move tt,[bigbuf,,bigbuf+1] + blt tt,bigbuf+1777 +creat4: syscall open,[[.bio,,fsch] ? fsdev ? fsfn1 ? fsfn2 ? fsdir] + .lose %lsfil + movei t,fslen +creat2: move tt,[-2000,,bigbuf] + .iot fsch,tt + sojg t,creat2 + syscall finish,[movei fsch] + .lose %lssys + call mapin + .lose %lssys(e) + call proper + jrst [ syscall delewo,[movei fsch] + .lose %lssys + jrst creat4 ] + call herald + movsi e,-nptrs +creat5: hrrz a,ptrs(e) + subi a,fsbase + call qaddr + hlrz b,ptrs(e) + movem a,(b) + aobjn e,creat5 + call finish + jrst cmdlp + +herald: save a + movei tt,trunam + format "~&FE filesystem ~F on pack #~D.",[tt,trupak] + movei a,dir-fsbase + call qaddr + format "~&Directory address: ~O",a + rest a + return + +finish: syscall dskupd,[movei fsch] + .lose %lssys + syscall sreapb,[movei fsch ? movei 1] + .lose %lssys + move t,trulen + jrst finsh1 + +finsh2: syscall pgwrit,[movei fspage(t)] + .lose %lssys +finsh1: sojge t,finsh2 + return + +.scalar fspak ; Intended pack number + +; CALL SETPAK: Set pack from FSPAK +setpak: save a + save b + move a,fspak + move b,[sixbit /PK0000/] + call devnum + movem b,fsdev + movem b,fsfn2 + rest b + rest a + return + +.vector ufd(2000) + +lblknum==:100. +.vector blknum(lblknum) + +.vector trunam(4) +.scalar trupak +.scalar trulen ; In blocks. + +;;;CALL MAPIN: Map in the FE filesystem. +;;; Skips if everything OK, else returns error code in E. +;;; Sets up BLKNUM, TRUNAM, TRUPAK, and TRULEN. +;;; Leaves FSCH open. +mapin: syscall open,[[.bii,,fsch] ? moves e ? fsdev ? fsfn1 ? fsfn2 ? fsdir] + return + syscall open,[[.bii,,dskich] ? fsdev ? [sixbit /.file./] + [sixbit /(dir)/] ? fsdir] + .lose %lsfil + move t,[-2000,,ufd] + .iot dskich,t + .close dskich, + syscall fillen,[movei fsch ? movem t] + .lose %lssys + addi t,1777 + lsh t,-12 + cail t,fslen + caile t,lblknum + jrst [ movei e,%ebdfl ? return ] + movem t,trulen + movn t,t + hrlz t,t + hrri t,fspage + syscall corblk,[movei %cbndr+%cbndw ? movei %jself ? t ? movei fsch] + .lose %lssys + movei tt,trunam + syscall rfname,[movei fsch ? movem 0(tt) ? movem 1(tt) + movem 2(tt) ? movem 3(tt)] + .lose %lssys + syscall filblk,[movei fsch ? movem t ? movem t ? movem t] + .lose %lssys + ldb tt,[unpkn t] + movem tt,trupak + + aos (p) ; Everything is cool from here on in. + save b + save c + save d + ldb b,[undscp t] +ifn ufdbyt-6, .err Somebody changed UFDBYT! + idivi b,6 + add b,(c)[ ; B: descriptor byte pointer + 440600,,ufd+uddesc + 360600,,ufd+uddesc + 300600,,ufd+uddesc + 220600,,ufd+uddesc + 140600,,ufd+uddesc + 060600,,ufd+uddesc] + setoi c, ; C: last block number + setzi d, ; D: index into BLKNUM +nxtblk: ildb t,b + jumpe t,mapinx + caig t,udtkmx + jrst taket + caige t,udwph + jrst skipt + caig t,udwph + jrst nxtblk + setzi c, + ;; 8/19/90 DM "funny" bit officially flushed. We now take 5 bits + ;; from the first byte. + dpb t,[<<060000*nxlbyt>+000500>,,c] +repeat nxlbyt,[ + ildb t,b + dpb t,[<<060000*>+000600>,,c] + ] +take1: movem c,blknum(d) + aos c + aoja d,nxtblk + +taket: skipge c + .lose ; Shouldn't happen +taket0: movem c,blknum(d) + aos c + aos d + sojg t,taket0 + jrst nxtblk + +skipt: skipge c + .lose ; Shouldn't happen + addi c,-udtkmx(t) + jrst take1 + +mapinx: rest d + rest c + rest b + return + +;;;CALL PROPER: Skips if microcode blocks are proper. +proper: move t,blknum+ramblk + move tt,[1-ramlen,,ramblk+1] +propr1: aos t + camn t,blknum(tt) + aobjn tt,propr1 + jumpl tt,cpopj + save a + save b + move a,blknum+ramblk + call qaddr1 + ldb b,[$88cyl a] + move a,blknum+ramblk+ramlen-1 + call qaddr1 + ldb a,[$88cyl a] + camn a,b + aos -2(p) + rest b + rest a + return + +check: call mapin + jrst [ movei tt,fsblk + format "~&ERROR: ~:@E: ~F",[e,tt] + jrst cmdlp ] + call herald + move tt,fspak + came tt,trupak + format "~&WARNING: This filesystem lives on pack #~D, not pack #~D as expected.",[trupak,fspak] + call proper + format "~&WARNING: Microcode blocks are not properly positioned." + movsi e,-nptrs +check5: hrrz a,ptrs(e) + subi a,fsbase + call qaddr + move c,a + hlrz b,ptrs(e) + hrro a,ptrtxt(e) + call corect + aobjn e,check5 + jrst cmdlp + +ptrs: ramptr,,ram + btptr,,bt + bt1ptr,,bt1 + b2ptr,,b2 +repeat nfifls, conc fi,\.rpcnt,ptr,,conc fi,\.rpcnt +nptrs==:.-ptrs + +ptrtxt: [asciz "Bad microcode pointer"] + [asciz "Bad bootstrap (BT) pointer"] + [asciz "Bad bootstrap (BT1) pointer"] + [asciz "Bad bootstrap (B2) pointer"] +repeat nfifls,[ + [conc asciz "Bad indirect file (FI,\.rpcnt,) pointer"] + ] +ifn .-ptrtxt-nptrs, .err PTRTXT wrong length. + +corect: camn c,(b) + return + format "~&WARNING: ~A.",a + move a,[format "~& Clobber it? "] + call yesnop + return + movem c,(b) + jrst finish + +.scalar sec%bl ; # sectors per block +.scalar wd%sec ; # words per sector (2000/SEC%BL) +.scalar bl%dsk ; # blocks per disk +.scalar bl%cyl ; # blocks per cylinder +.scalar sec%tr ; # sectors per track + +;;;QADDR: Compute physical disk address from file address +;;; A (arg): file address +;;; A (val): FE format disk address of that sector +qaddr: save b + save c + idivi a,2000 ; A: file block # + idiv b,wd%sec ; B: block sector # + skipe c + .lose ; Not a sector address? + move t,blknum(a) ; T: disk block # + caml t,bl%dsk + .lose ; Block # too big? + setzi a, + idiv t,bl%cyl + dpb t,[$88cyl a] + imul tt,sec%bl + add b,tt ; B: cylinder sector # + idiv b,sec%tr + dpb b,[$88trk a] + dpb c,[$88sec a] + rest c + rest b + return + +;;;QADDR1: Compute physical disk address from disk block # +;;; A (arg): disk block # +;;; A (val): FE format disk address of first sector of that block +qaddr1: move t,a + caml t,bl%dsk + .lose ; Block # too big? + setzi a, + idiv t,bl%cyl + dpb t,[$88cyl a] + move t,tt + imul t,sec%bl + idiv t,sec%tr + dpb t,[$88trk a] + dpb tt,[$88sec a] + return + +files: sixbit /readme/ + sixbit /ram/ + sixbit /bt/ + sixbit /bt1/ + sixbit /b2/ +repeat nfifls, conc sixbit /fi,\.rpcnt,/ +nfiles==:.-files + +listem: format "~&Existing files are:" + movsi e,-nfiles + format " ~S",files(e) + aobjn e,.-1 +filidx: move a,[format "~&Which file? "] + call token + jrst listem + movsi e,-nfiles + came a,files(e) + aobjn e,.-1 + jumpge e,listem + return + +.vector iofile(4) + +itstyp: sixbit />/ + sixbit /RAM/ + sixbit /BIN/ + sixbit /BIN/ + sixbit /BIN/ +repeat nfifls, sixbit />/ +ifn .-itstyp-nfiles, .err ITSTYP wrong length. + +gfname: movsi tt,(sixbit /DSK/) + movem tt,iofile+0 + movem a,iofile+1 + move tt,itstyp(e) + movem tt,iofile+2 + .suset [.rsname,,iofile+3] + move a,b + movei b,iofile + jrst readfn + +define dfhckr loc,type + [movei d,loc ? jrst rd!type],,[movei d,loc ? jrst wr!type] +termin + +hacker: +dfhckr readme,txt +dfhckr ram,ram +dfhckr bt,bt +dfhckr bt1,bt +dfhckr b2,bt +repeat nfifls, conc dfhckr fi,\.rpcnt,,fi +ifn .-hacker-nfiles, .err HACKER wrong length. + +read: call filidx + move b,[format "~&Output to"] + call gfname + hlrz t,hacker(e) + jrst (t) + +write: move a,[ + format "~&Are you sure you want to scribble in the FE filesystem? "] + call yesnop + jrst cmdlp + call filidx + move b,[format "~&Input from"] + call gfname + hrrz t,hacker(e) + jrst (t) + +rdram: movei c, ? jrst rdimag +wrram: movei c, ? jrst wrimag +rdtxt: +rdbt: movei c,1000 ? jrst rdimag + +rdimag: syscall open,[[.bio,,dskoch] ? 0(b) ? [sixbit /_FEDR_/] + [sixbit /OUTPUT/] ? 3(b)] + .lose %lsfil + movn t,c + hrl d,t + .iot dskoch,d +oclose: syscall renmwo,[movei dskoch ? 1(b) ? 2(b)] + .lose %lssys + .close dskoch, + jrst cmdlp + + ramlen==:6 ; Duplicate definition (2 passes suck) +.vector bigbuf(ramlen_12) ; Microcode files are biggest + +wrimag: syscall open,[[.bii,,dskich] ? 0(b) ? 1(b) ? 2(b) ? 3(b)] + .lose %lsfil + caile c,ramlen_12 + .lose ; BIGBUF too small? + movn t,c + hrlz t,t + hrri t,bigbuf + .iot dskich,t + jumpl t,[ + format "~&ERROR: File too small. Nothing was written." + .close dskich, + jrst cmdlp] + move t,[-1,,tt] + .iot dskich,t + jumpge t,[ + format "~&ERROR: File too large. Nothing was written." + .close dskich, + jrst cmdlp] + hrlzi t,bigbuf + hrri t,(d) + addi d,-1(c) + blt t,(d) + .close dskich, + call finish + jrst cmdlp + +wrbt: syscall open,[[.uii,,dskich] ? 0(b) ? 1(b) ? 2(b) ? 3(b)] + .lose %lsfil +wrbt1: .iot dskich,a + came a,[jrst 1] + jrst wrbt1 + setzm bigbuf + move tt,[bigbuf,,bigbuf+1] + blt tt,bigbuf+777 + move a,[ksrim,,bigbuf] + blt a,bigbuf+lksrim-1 + hrli a,lksrim-1000 +wrbt2: .iot dskich,(a) + aobjp a,wrbtov + skipl b,-1(a) + jrst wrbtex + move c,b +wrbt3: .iot dskich,(a) + rot c,1 + add c,(a) + aobjp a,wrbtov + aobjn b,wrbt3 + .iot dskich,(a) + came c,(a) + jrst [ + format "~&ERROR: Checksum error. Nothing was written." + .close dskich, + jrst cmdlp] + aobjn a,wrbt2 +wrbtov: format "~&ERROR: SBLK file too long. Nothing was written." + .close dskich, + jrst cmdlp + +wrbtex: movem b,(a) +bltblk: hrli d,bigbuf + movei tt,777(d) + blt d,(tt) + .close dskich, + call finish + jrst cmdlp + +ksrim: ; Think of this as read-in mode for the KS-10. +offset 1000-. ; 8080 plunks BT file down in location + ; 1000 and starts it there. + setzb 17,2000 ; Clear core to flush all that bad parity. + dmove 1,rimblt ; 1: Below & 2: Above + blt 1,777 ; Zero the 8080 area. + blt 2,777677 ; But don't clobber DSKDMP bootstrap. +rimacl::wrubr rimacs ; Now clear all ACs: Select a block. + movsi 16,-1100 ; Decrement WRUBR argument for next time + addm 16,rimacs + setzi 0, ; Zero this block + movei 17,1 + blt 17,17 + skipge rimacs ; All done? (Leave block 0 selected.) + jrst rimacl + movsi a,lksrim-1000 ; A: aobjn into sblks +rimblk::skipl b,sblks(a) ; B: aobjn into memory (if not start address) + jrst (b) + aobjp a,rimerr + move c,b ; C: checksum so far +rimwrd::move d,sblks(a) + aobjp a,rimerr + movem d,(b) + rot c,1 + add c,d + aobjn b,rimwrd + camn c,sblks(a) + aobjn a,rimblk +rimerr::jrst 4,. + +rimblt::17,,20 + 2000,,2001 + +rimacs::407700,,0 + + jrst 1 ; Look like ITS SBLK file if dumped out... + +sblks:: + +offset 0 +lksrim==:.-ksrim + +rdfi: syscall open,[[.uao,,dskoch] ? 0(b) ? [sixbit /_FEDR_/] + [sixbit /OUTPUT/] ? 3(b)] + .lose %lsfil + hrli d,-1000 + movei c,3 + jrst rdfi0 + +rdfilp: .iot dskoch,a + cain a,^M + .iot dskoch,[^J] +rdfi0: ldb a,(c)[301000,,(d) ? 201000,,(d) ? 101000,,(d) ? 001000,,(d)] + jumpe a,oclose + sojge c,rdfilp + movei c,3 + aobjn d,rdfilp + format "~&ERROR: Indirect file not terminated by a null. ~ + Output file was not renamed." + .close dskoch, + jrst cmdlp + +wrfi: syscall open,[[.uai,,dskich] ? 0(b) ? 1(b) ? 2(b) ? 3(b)] + .lose %lsfil + setzm bigbuf + move tt,[bigbuf,,bigbuf+1] + blt tt,bigbuf+777 + move e,[-1000,,bigbuf] + movei c,3 + jrst wrfi0 + +wrfilp: cain a,177 ; Kludge to experiment with... + movei a,377 + caie a,^J + jsp t,wrfidp +wrfi0: .iot dskich,a + hrrz a,a + caie a,^C + jrst wrfilp + movei a,0 + jsp t,wrfidp + hrlzi t,bigbuf + hrri t,(d) + blt t,777(d) + .close dskich, + call finish + jrst cmdlp + +wrfidp: dpb a,(c)[301000,,(e) ? 201000,,(e) ? 101000,,(e) ? 001000,,(e)] + sojge c,(t) + movei c,3 + aobjn e,(t) + format "~&ERROR: Indirect file too large. Nothing was written." + .close dskich, + jrst cmdlp + +wrtxt: move tt,[.byte 7 ? 3 ? 3 ? 3 ? 3 ? 3] + movem tt,bigbuf + move tt,[bigbuf,,bigbuf+1] + blt tt,bigbuf+777 + syscall open,[[.bai,,dskich] ? 0(b) ? 1(b) ? 2(b) ? 3(b)] + .lose %lsfil + move tt,[-1001,,bigbuf] + .iot dskich,tt + .close dskich, + jumpl tt,bltblk + format "~&ERROR: File too long. Nothing was written." + jrst cmdlp + +;;;CALL YESNOP: Ask the user a yes or no question. +;;; A (arg): prompt routine +;;; +0: no +;;; +1: yes +yesnop: save a +ysnop1: call token + jrst insist + camn a,[sixbit /YES/] + jrst yes + camn a,[sixbit /NO/] + jrst no +insist: format "~&YES or NO" + move a,(p) + jrst ysnop1 + +yes: aos -1(p) +no: rest a + return + +.scalar rfnprm ; filename prompt routine +.scalar rfnblk ; filename default + +;;;CALL READFN: Read filename +;;; A (arg): prompt routine +;;; B (arg, val): filename block +readfn: save c + save d + save e + save b + movem a,rfnprm + movem b,rfnblk + move a,[440700,,linbuf] + movei b,5*llinbuf + movei c,[ + call @rfnprm + format " (Default ~F): ",rfnblk + return] + call (c) + call readln + rest b + move d,[440700,,linbuf] + call rfn"rfn + rest e + rest d + rest c + return + +;;;CALL NUMP: Skip if token is all digits. +;;; A (arg, val): sixbit token +nump: move tt,a +nump1: setzi t, + lshc t,6 + cail t,'0 + caile t,'9 + return + jumpn tt,nump1 + aos (p) + return + +; CALL DEVNUM: Create numerical device name +; A (arg): number +; B (arg): right justified device name with 0's (like PK0000). +; B (arg): right justified device name (like PK0017). +devnum: move t,b + idivi a,8 + add b,t + jumpe a,cpopj + rot b,-6 + call devnum + rot b,6 + return + +;;;CALL NUMBER: Return number (decimal) typed all on a single line. +;;; A (arg): prompt routine +;;; A (val): number +;;; +0: user typed something suprising +;;; +1: user typed a number +number: save b + save c + movem a,prompt +numbr0: setzb c,linct +numbr1: jsp b,class + jrst numbr1 + jrst numbrx + jrst numbr2 + jrst numbrx + jrst numbr0 + +numbr2: imuli c,10. + addi c,-"0(a) + jsp b,class + jrst numbr9 + jrst numbrx + jrst numbr2 + jrst numbrx + aos -2(p) + jrst numbrx + +numbr9: jsp b,class + jrst numbr9 + jrst numbrx + jrst numbrx + jrst numbrx + aos -2(p) +numbrx: move a,c + rest c + rest b + return + +;;;CALL TOKEN: Return sixbit token typed all on a single line. +;;; A (arg): prompt routine +;;; A (val): word of SIXBIT +;;; +0: user typed something suprising +;;; +1: user typed a token +token: save b + save c + save d + movem a,prompt +token0: setzm linct + move c,[440600,,d] + setzi d, +wscan: jsp b,class + jrst wscan + jrst gotcha + jrst gotcha + jrst tokenx + jrst token0 + +gotcha: subi a,40 + tlne c,770000 + idpb a,c + jsp b,class + jrst xscan + jrst gotcha + jrst gotcha + jrst tokenx + aos -3(p) + jrst tokenx + +xscan: jsp b,class + jrst xscan + jrst tokenx + jrst tokenx + jrst tokenx + aos -3(p) +tokenx: move a,d + rest d + rest c + rest b + return + +;;;JSP B,CLASS: Return and classify next character: +;;; A (val): character +;;; +0: whitespace +;;; +1: letter +;;; +2: digit +;;; +3: other +;;; +4: terminator +class: call input + caie a,40 + cain a,^I + jrst 0(b) + cain a,^M + jrst 4(b) + cail a,"A + caile a,"Z + skipa + jrst 1(b) + cail a,"0 + caile a,"9 + skipa + jrst 2(b) + cail a,"a + caile a,"z + jrst 3(b) + subi a,"a-"A + jrst 1(b) + +llinbuf==:100. +.vector linbuf(llinbuf) +.scalar linbp +.scalar linct +.scalar prompt + +input: sosge linct + jrst input1 + ildb a,linbp + return + +input1: save b + save c + move a,[440700,,linbuf] + movei b,5*llinbuf + move c,prompt + call (c) + call readln + movem a,linbp + movem b,linct + rest c + rest b + jrst input + +;;;READLN: Read a line with rubout handling. +;;; A (arg, val): byte pointer for text +;;; B (arg): room for text +;;; B (val): length of text +;;; C (arg): reprompt routine +readln: save d + save e + hrrzi c,(c) ; C: flags,,prompt + move d,a ; D: bp + move e,b ; E: room +readlp: tyi t + cain t,177 + jrst rubout + cain t,^U + jrst flush + cain t,^G + jrst abort + caie t,^L + cain t,^R + jrst replay + cain t,^M + jrst readex + sosg e + .lose + idpb t,d + jrst readlp + +flush: move e,b +replay: tyo [^P] + tyo ["A] + call (c) + move d,a + move tt,b + sub tt,e + syscall siot,[movei echoch ? d ? tt] + .lose %lssys + jrst readlp + +rubout: aos e + camle e,b + jrst abort + add d,[070000,,] + skipge d + sub d,[430000,,1] + tyo [^P] + tyo ["X] + jrst readlp + +readex: idpb t,d + subi b,-1(e) + rest e + rest d + return + +siot: setz + sixbit /siot/ + movei ttyoch + move t + setz tt + +variables + +fsblk:: +fsdev: -1 +fsfn1: sixbit /.FEFS./ +fsfn2: -1 +fsdir: sixbit /./ + +cnstnts: +constants + +fspage==:<.+1777>_-12 +fsbase==:fspage_12 + +;;; Here are the file addresses of various interesting things in the file: + +readme=:fsbase+0 ; Start of first block is ASCII explanation. + +dir=:fsbase+1000 ; Directory is halfway through first block +ramptr=:dir+88ram +btptr=:dir+88bt +bt1ptr=:dir+88bt1 +b2ptr=:dir+88b2 +repeat nfifls, conc fi,\.rpcnt,ptr=:dir+88fi0+.rpcnt + +ram=:fsbase+2000 ; These blocks must be contiguous. +ramblk==:_-12 ; First such block +ramlen==:6 ; Number of such blocks + +bt=:ram+ + +bt1=:bt+1000 + +b2=:bt1+1000 + +repeat nfifls, conc fi,\.rpcnt,=:b2+1000+<1000*.rpcnt> + +fsmax=:b2+1000+<1000*nfifls> + +fslen==:<_-12>-fspage ; Minimum number of blocks. + +end go diff --git a/src/syseng/format.305 b/src/syseng/format.305 new file mode 100755 index 00000000..307d8a63 --- /dev/null +++ b/src/syseng/format.305 @@ -0,0 +1,1471 @@ +;;;-*-Midas-*- + +subttl FORMAT + +.begin format + +;;;The master copy of this library lives in AI:SYSENG;FORMAT >. + +;;;Modification history: +;;; Alan 12/2/87 Added ~U. +;;; Alan 10/10/87 Added ~:H. $$ERRS defaults to 0 since nobody seems +;;; to want it usually. ~Q and ~F should now work +;;; inside justification. +;;; Alan 5/16/87 Fixed ~E to understand multi-line error messages. +;;; Alan 11/23/85 Added : flag to ~S (and ~F?). +;;; Alan 5/2/85 Added ~| and ~Q and $$TIME. +;;; Alan 1/26/85 New switch: +;;; $$IERR = 1, allow user supplied error macro +;;; CStacy ??? New switches: +;;; $$ITS = 0, not running under ITS +;;; $$ERRS = 0, ~E not enabled +;;; Alan 7/17/83 Installed FORMAT on SYSENG; + +comment  + +File: FORMAT, Node: Top, Up: (LIB), Next: Requirements + +FORMAT is a .INSRTable MIDAS library patterned after Lisp's FORMAT +function. The Lisp FORMAT function traces its ancestry back to the +FORTRAN FORMAT statement and to the ioa_ routine on Multics. This is +simply the latest entry in a long tradition. + +* Menu: + +* Introduction:: Introduction to FORMAT for people who have never + seen anything like it before. + +* Requirements:: Basic requirements and calling convention. + +* Operators:: Table of all operators. + +* Switches:: Varying the behavior of FORMAT to suit your + application. + +File: FORMAT, Node: Switches, Previous: Operators, Up: Top, Next: PCode + +For the most part FORMAT can function quite well without having \any/ +switches set, most all operators are always assembled. (The single +exception is the ~T operator currently.) + +Some switches allow the user to choose between different techniques FORMAT +can use to accomplish the same ends. For example, the $$ITAB and $$UTAB +switches allow the user to choose how the ~T operator keeps track of +horizontal position. + +In some cases FORMAT can do a better job with some hints and assistance +from the user. For example, the $$PCODE switch tells FORMAT that it is +outputting to a display mode TTY channel so that it can use -codes to +perform cursor positioning. + +Anyone reading this documentation for the first time should probably glance +briefly at each node in the following menu, just to learn what the options +are. + +* Menu: + +* PCode:: The $$PCODE switch, for when FORMAT is outputting + to a display mode TTY. + +* ITab:: The $$ITAB switch, for when you can track + horizontal position yourself. + +* UTab:: The $$UTAB switch, for when you want FORMAT to + track horizontal position for you. + +* PFN:: The $$PFN switch allows FORMAT to call the RFN"PFN + routine from the RFN library to print filenames. + *Note RFN:(LIB)RFN. + +* Time:: The $$TIME switch allows FORMAT to call routines + from the DATIME library to print dates and times. + *Note DATIME:(LIB)DATIME. + +* Engl:: The $$ENGL switch can be used to disable the + english number printer to conserve space. + +* IErr:: The $$IERR switch controls the way FORMAT + signals errors. + +* Errs:: The $$ERRS switch controls the ~E operator. + +* ITS:: The $$ITS switch says FORMAT is running under the + ITS operating syste. + +File: FORMAT, Node: Introduction, Previous: Top, Up: Top, Next: Requirements + +If anyone needs this introduction, I refer them to the Lisp Machine +manual's documentation for the FORMAT function. Perhaps someday I will +write something here myself. + +File: FORMAT, Node: Operators, Previous: Requirements, Up: Top, Next: Switches + +FORMAT operators are introduced by the escape character tilde (~), as with +the Lisp FORMAT function. Each operator is identified by a single +character following the tilde, for example the two character sequence ~D is +the format operator for printing a number in decimal. Additionally, +various "infix" arguments are allowed between the tilde and the +identifying character. A sequence of digits is a numeric "parameter"; it +is interpreted in decimal. (Unlike the Lisp FORMAT, only a single +parameter is permitted.) Also the characters colon (:) and atsign (@) may +appear in the infix argument, they are simply "flags" that modify the +behavior of the operator in some binary way. + +Instead of a sequence of digits the character "v" may appear as an infix +argument. In this case the next FORMAT argument is gobbled and it is used +as the parameter. This is mostly useful with the ~R, ~T and ~< operators. + +Here is a table of all currently defined FORMAT operators. + +~% Outputs a carrage return. ~n% outputs n carrage returns. No + argument is gobbled. + +~& Advances to a fresh line. ~n& advances to a fresh line and outputs + n-1 carrage returns. No argument is gobbled. FORMAT's ability to + bring you to a fresh line without extraneous carrage returns + depends on the settings of the $$PCODE, $$ITAB and $$UTAB switches. + if none of those switches is set, then ~& behaves exactly like ~%. + +~A One argument is gobbled. It should be a byte pointer to an ASCIZ + string which is to be output. -1 and 0 in the left half are both + equivalent to 440700. + +~B One argument is gobbled. It is output as a number in binary. + +~C One argument is gobbled. It is output as a single character. + Control characters are output as a two character sequence starting + with ^. ~:C just outputs the character, even if it is a control + character. + +~D One argument is gobbled. It is output as a number in decimal. + +~E The error message associated with the channel currently in .BCHN is + output (in all lower case). Normally this is the error message + associated with the most recent error (.CALL that failed to skip, + or whatever). ~:E gobbles one argument, which should be an ITS + error code, and prints the associated error message. The @ flag + causes the first character of the error message to be capitalized. + This operator is not assembled unless the $$ERRS flag is set. + See *Note Errs:Errs. + +~F One argument is gobbled. It should be a pointer to a four-word + block containing an ITS filename. (In the usual order: device, + first filename, second filename, directory.) The filename is + printed in the standard way. If the $$PFN flag is set, then FORMAT + will call the RFN"PFN routine from the RFN library. See + *Note PFN:PFN. + +~H One argument is gobbled. It is printed in octal halfword format + similar to H mode in DDT (In fact the algorithm is borrowed from + DDT). -105 is printed as "-1,,-105" etc. ~:H is the same except + the left half is omitted when it would be redundant. (That is, + 777 is printed as "777" rather than "0,,777" and -1 is printed as + "-1" rather than "-1,,-1".) + +~O One argument is gobbled. It is printed in octal. + +~P The previously gobbled argument is re-examined. If it is 1, + nothing is output, otherwise "s" is output. No new arguments are + gobbled. ~:P is the same except instead of re-examining the + previous argument, a new argument is gobbled. (Note that the sense + of the colon flag is reversed from the Lisp FORMAT function's ~P + operator.) ~@P and ~:@P are similar except if the argument is 1 + then "y" is output, otherwise "ies" is output. + +~Q One argument is gobbled. It is interpreted as a date and time in + ITS disk format and output. ~:Q outputs just a time. ~@Q outputs + just a date. This operator is not assembled unless the $$TIME + switch is set. See *Note Time:Time. + +~R ~nR gobbles one argument and outputs it in base n. + ~R (no parameter) gobbles one argument and outputs it in english + ("one", "two", "three", etc.). ~:R outputs an ordinal number + ("first", "second", "third", etc.). The @ flag causes the first + character of the first word output to be capitalized. FORMAT's + english number printing facilities can be disabled by use of the + $$ENGL switch to save space, see *Note Engl:Engl. + +~S One argument is gobbled. It is interpreted as a word of SIXBIT and + output. ~:S prints in lower case. + +~T ~nT outputs enought tabs and spaces to advance the output to a + horizontal position of n. If the output is already beyond that + column, nothing is output. This operator is not assembled unless + one of the switches $$ITAB or $$UTAB is set. No arguments are + gobbled. (See *Note ITab:ITab, and its Next for details.) + +~U One argument is gobbled. It is interpreted as a word of SQUOZE and + output. + +~X ~X gobbles one argument. It is output in hexidecimal. + +~| If $$PCODE is set, ~| clears the screen. If $$PCODE is not set, ~| + outputs a formfeed. In both cases ~n| then outputs n-1 carrage + returns. + +~~ Outputs a tilde. No argument is gobbled. + +~ Occasionally it may be necessary to insert a carrage return in a + long FORMAT control string to improve readability. The ~ + operator allow this to be done gracefully. ~ simply discards + the carrage return and any leading whitespace on the next line and + then continues processing the control string. ~@ outputs the + carrage return and discards the whitespace. ~: discards the + carrage return and outputs the whitespace. ~:@ outputs both, + and is therefore a no-op. No argument is gobbled. + +~< These three operators provide a justification feature. +~; ~n processes the two strings "ll...ll" and +~> "rr...rr" and outputs them with as many copies of the character "c" + between them as is necessary to fill up exactly n characters. So + for example, to print a decimal number in a 6 column field padded + on the left with zeros, one could write: "~6<~;0~D~>". If the + output cannot possibly fit, no copies of the padding character will + be output and the resulting text will simply be too long. + + By special dispensation, if the lefthand string is empty, and the + padding character is a space, then the "~; " may be omitted. Thus to + output a decimal number in 6 columns padded on the left with + spaces, one could simply write: "~6<~D~>". + + A few operators are illegal within the strings "ll...ll" and + "rr...rr", mostly those having to do with knowing horizontal + position. They are: ~%, ~&, ~T and ~<. The last means that + recursive justifications are not currently supported. + + Note that unlike ~T, the justification feature counts characters, + rather than computing horizontal position. + +File: FORMAT, Node: Requirements, Previous: Top, Up: Top, Next: Operators + +FORMAT requires no static storage, it keeps all of its state on the stack +(including its small output buffer). It is completely pure, and completely +reentrant. + +FORMAT stack frames are relatively large, about 20. to 30. words. Keep +this in mind when you are allocating your PDL! + +The following accumulators must be defined: A, B, C, D, E, and P. C must +be B+1. P must be the pdl-pointer. + +OUTSTR must be defined. It should be a routine for printing an ASCII +string. This is what FORMAT calls to output characters. + +FORMAT does not define any symbols outside of its own symbol block, named +FORMAT. (See *Note Blocks:(Midas)Blocks, for a review of Midas block +structure.) Thus, for example, FORMAT's entrypoint is usually written +FORMAT"FORMAT by the caller. + +Calling convention: + +When FORMAT"FORMAT is called it expects A to contain a byte pointer to an +ASCII string. B should contain the length of that string in characters. C +should contain minus the number of arguments being passed to FORMAT. Those +arguments should have been pushed on the PDL (in order) before FORMAT was +called. The return address will be found below those arguments. FORMAT +returns by popping the arguments off the stack and then doing a POPJ P,. A +typical call to FORMAT might look like: + + push p,[foo69] + push p,errors + push p,trials + hrroi a,[ascii "~&~D error~P in ~D trial~P."] + movei b,.length "~&~D error~P in ~D trial~P." + movni c,2 + jrst format"format +foo69: + +Obviously a little macrology can sugar this up to be not quite so +cumbersome. See *Note Macro:Macro, for a possible candidate. + +Notice, by the way, that a -1 or a 0 in the left half of the byte pointer +passed to FORMAT in A will be treated as if it were 440700. + +FORMAT calls the routine OUTSTR to do output with a byte pointer in A, +character count in B. That routine mustn't clobber D or E, but C is +fair game. It is called by PUSHJ P,OUTSTR. A likely OUTSTR might be: + +outstr: .call [setz ? sixbit /siot/ + movei ttyo + move a + setz b] + .lose %lssys + popj p, + +File: FORMAT, Node: PFN, Previous: UTab, Up: Switches, Next: Time + +By default $$PFN==0. + +If $$PFN==0 then ~F will simply format a filename like: "~S: ~S; ~S ~S" + +If $$PFN==1 then the ~F operator will work by calling the routine PFN, +which you must supply somehow. It will be invoked as if it is the RFN"PFN +routine from the RFN library. (*Note RFN:(LIB)RFN.) Typically setting +$$PFN==1 will look something like: + +rfn"$$pfn==:1 +.insrt dsk:syseng;rfn > + +format"$$pfn==:1 +format"pfn==:rfn"pfn +.insrt dsk:syseng;format > + +FORMAT assumes that no filename can require more than 54 characters to +print (including quotes, and including a potential ^@ after the last +character deposited). + +Of course it needn't really be RFN"PFN that format calls in this case, the +routines RFN"PFNMCH and RFN"PFNBRF also have the same calling convention +and can be used instead. Indeed, any routine can be used as long as it has +the same calling convention and doesn't deposit more than 54 characters! + +File: FORMAT, Node: Time, Previous: PFN, Up: Switches, Next: Engl + +By default $$TIME==0. + +If $$TIME==1 then the ~Q operator is enabled. It will format dates and +times by calling the routines FORMAT"DATIME, FORMAT"TIME, and FORMAT"DATE +in order to implement ~Q, ~:Q, and ~@Q respectively. These routines are +all invoked as if they were output routines from the DATIME library. +(*Note DATIME:(LIB)DATIME.) Typically setting $$TIME==1 will look +something like: + +datime"$$out==:1 +.insrt dsk:syseng;datime > + +format"$$time==:1 +format"datime==:datime"twdasc +format"time==:datime"timasc +format"date==:datime"datasc +.insrt dsk:syseng;format > + +FORMAT assumes that no date or time requires more than 35 characters to +print. (This is easily true of all the routines in DATIME.) + +File: FORMAT, Node: Engl, Previous: Time, Up: Switches, Next: IErr + +By default $$ENGL==1. + +If $$ENGL==1, then FORMAT's english number printing routines are assembled. +This enables ~R, ~:R, ~@R and ~:@R. + +If you are tight for space, you can set $$ENGL==0 and save about 250 words. + +File: FORMAT, Node: ITS, Previous: Errs, Up: Switches + +By default $$ITS==1. + +If $$ITS==0, then FORMAT will not assume that it is running under ITS +timesharing. + +File: FORMAT, Node: Errs, Previous: IErr, Up: Switches, Next: ITS + +By default $$ERRS==0. + +If $$ERRS==1, then the ~E operator is enabled and ERRI must be defined. It +should be a channel on which FORMAT can open the ERR device if need be. + +File: FORMAT, Node: IErr, Previous: Engl, Up: Switches, Next: Errs + +By default $$IERR==0. + +If $$IERR==1 then FORMAT will expect the user to define a macro named +FMTERR that FORMAT will use to signal errors. It should expect a single +macro argument of a string of text surrounded by doublequotes. + +If $$IERR==0 then format uses the following macro: + +define fmterr *text* + .value .+2 + jrst .-1 + asciz ":text +" +termin + +File: FORMAT, Node: PCode, Previous: Switches, Up: Switches, Next: ITab + +By default $$PCODE==0. + +If $$PCODE==1 then FORMAT is allowed to output ^P codes. This is different +from allowing the caller to include ^P codes in his FORMAT control string. +The only restriction on the latter is that if $$UTAB==1 (*Note UTab:UTab.), +then FORMAT will be confused by ^P codes not produced by FORMAT itself. + +File: FORMAT, Node: ITab, Previous: PCode, Up: Switches, Next: UTab + +By default $$ITAB==0. + +If $$ITAB==1 then the ~T operator is enabled. The user should supply a +routine named GETPOS to return the horizontal cursor position. The routine +will be called using PUSHJ P,GETPOS. It should return the current +horizontal cursor position in A. It should return -1 if the horizontal +position is unknown. It mustn't clobber D or E, but B and C are fair game. +A likely GETPOS might be: + +getpos: .call [setz ? sixbit /rcpos/ + movei ttyo + setzm a] + skipa a,[-1] + hrrz a,a + popj p, + +File: FORMAT, Node: UTab, Previous: ITab, Up: Switches, Next: PFN + +By default $$UTAB==0. + +If $$UTAB==1 then the ~T operator is enabled. FORMAT will keep track +of horizontal position itself in this case. This requires that D should +contain the starting horizontal position whenever FORMAT"FORMAT is called. +When FORMAT returns, D will contain the updated horizontal position. A +GETPOS routine is NOT required. + +The algorithm FORMAT uses to compute horizontal position is the same as +that employed by EMACS when it displays a file without SAIL characters +enabled. That is, most control characters are two characters wide, +including ^H (backspace) and isolated ^M's (carrage return) and ^J's (line +feed). ^M immediately followed by ^J resets horizontal position to 0. +^I (tab) characters are understood to advance the horizontal position to +the next multiple of 8 (and at least to advance it by 1). + +File: FORMAT, Node: Macro + +Here is a simple macrology for calling FORMAT: + +The user writes: + + format "~&~D error~P in ~D trial~P.",[errors,trials] + +This macro expands into a single instruction, so that it can be skipped +over. All accumulators are saved and restored. The arguments written in +the IRP list after the control string can refer to any location and will +find the expected value there, EXCEPT for P. + +define format &string&,args + pushj p,[ + pushj p,fmtin +zzz==-1 +irp arg,,[args] + push p,arg +zzz==.irpcnt +termin + hrroi a,[ascii string] + movei b,.length string + movni c,zzz+1 + jrst format"format] +termin + +fmtin: push p,a + push p,b + push p,c + push p,[fmtout] + jrst @-4(p) + +fmtout: pop p,c + pop p,b + pop p,a + pop p,(p) + popj p, + + ;end comment +.auxil ;Don't cref me please. + +.tyo6 .ifnm1 +.tyo 40 +.tyo6 .ifnm2 +fmtvrs==:.ifvrs +printx / included in this assembly. +/ + +ifn b+1-c, .err FORMAT requires C=B+1 + +ifndef $$pcode, $$pcode==0 ;can use ^P codes +ifndef $$itab, $$itab==0 ;~T and ~& can call GETPOS to do their job. +ifndef $$utab, $$utab==0 ;format itself is tracking the hpos. +ifndef $$pfn, $$pfn==0 ;PFN routine prints filenames. +ifndef $$engl, $$engl==1 ;English number printing is enabled. +ifndef $$time, $$time==0 ;Date and time printing is enabled. +ifndef $$ierr, $$ierr==0 ;User has defined a fmterr macro for us. +ifndef $$its, $$its==1 ;Running under ITS. +ifndef $$errs, $$errs==0 ;Error code/message printing is enabled. + +ifn $$utab, ifn $$itab, .err $$ITAB and $$UTAB simultaneously non-zero. + +ife $$its,[ +ifn $$errs, .err ~E feature only available under ITS +ife $$ierr, .value==jrst 4, +];ife $$its + +ife $$ierr,[ +define fmterr *text* + .value .+2 + jrst .-1 + asciz ":text +" +termin +] ;ife $$ierr + +call==pushj p, +return==popj p, +jcall==jrst + +;;;Flags are kept in left half of E. +%fmcol==1_17. ;Colon flag, sign bit of E +%fmats==1_16. ;Atsign flag +%fmnum==1_15. ;Numeric argument seen. +%fmv==1_14. ;~vX type numeric argument seen. +%fmjst==1_13. ;Set during a justification. +%fmbuf==1_12. ;Indicates that a justification is still + ;possible on characters still in the + ;buffer. If $$UTAB==1 then the characters + ;in the buffer have not been counted into + ;HPOS yet. Cleared whenever the buffer is + ;dumped. +%fmcrl==1_11. ;If $$UTAB==1, this bit remembers that a ^M + ;was the last character out in case the + ;next one is ^J. +%fmeng==1_10. ;Internal to the english number printer. + +ifn $$utab,[ +define tyo x +ifn c-x, move c,x + format"call format"%tyo +termin +] ;end ifn $$utab + +ife $$utab,[ +define tyo x + idpb x,format"obp(e) + sosg format"bufct(e) + format"call format"dump +termin +] ;end ife $$utab + +define nojust + tlne e,format"%fmjst + format"call format"bdjust +termin + +define nextarg x,inst=move + move x,format"argptr(e) + aobjp x,format"nxarg + movem x,format"argptr(e) + inst x,(x) +termin + +define getarg x,inst=move + move x,format"argptr(e) + inst x,(x) +termin + +format: push p,d + save.d=400000 ;saved contents of D + hpos==save.d ;If $$UTAB==1, this is hpos. + movei d,-1(p) + add d,c + push p,d + save.p=400001 ;saved pdl height + hrli d,-1(c) + push p,d + argptr=400002 ;AOBJP Argument pointer. + push p,e + save.e=400003 ;saved contents of E + hrrzi e,-save.e(p) ;Flags all clear initially. + hlrz c,a + caie c,-1 ;0 or -1 in left half acts like 440700 + skipn c + hrli a,440700 + push p,a + bp=400004 ;Control string byte pointer. + push p,[0] + numarg=400005 ;numeric argument to operator. + movei a,buffer(e) + hrli a,440700 + push p,a + obp=400006 ;Output byte pointer. + ibfsiz==10. ;50. character buffer initially. + push p,[ibfsiz*5] + bufct=400007 ;Output buffer count. + push p,[0] + just=400010 ;For justification + push p,[ibfsiz*5] + bufsiz=400011 ;Size of buffer is variable. +repeat ibfsiz, push p,[ascii "_____"] + buffer=400012 ;Buffer must be last. + move d,b ;D: length of string +loop: sojl d,done ;nothing left? +scan: ildb c,bp(e) +xloop: cain c,"~ + jrst escape + tyo c + sojge d,scan +done: call dump + tlne e,%fmjst + call eof + hrrz c,p + sub c,save.p(e) + move d,save.d(e) + move e,save.e(e) + hrl c,c + sub p,c + return + +escape: movei c,1 + movem c,numarg(e) ;default arg is 1 + tlz e,%fmats\%fmcol\%fmnum\%fmv\%fmeng ;clear flags +escp1: sojl d,eof + ildb c,bp(e) + jrst @esctbl(c) + +esctbl: +repeat 200, nxop + +define defop char,handlr +zzz==. +loc format"esctbl+char + handlr +ifge char-"A,[ +ifle char-"Z,[ +loc format"esctbl+char+"a-"A + handlr +]] +loc zzz +termin + +defop ":,opcolon +opcolo: tloe e,%fmcol + call bdop + jrst escp1 + +defop "@,opatsign +opatsi: tloe e,%fmats + call bdop + jrst escp1 + +defop "V,op.V +op.V: tloe e,%fmnum\%fmv + call bdop + nextarg c + movem c,numarg(e) + jrst escp1 + +repeat 10., defop "0+.rpcnt,opdigit +opdigi: tlne e,%fmv + call bdop + move a,numarg(e) + tlon e,%fmnum + setzi a, + imuli a,10. + subi c,"0 + add a,c + movem a,numarg(e) + jrst escp1 + +defop "~,optilde +optild: tyo c +cloop: jrst loop + +defop "R,op.R +defop "X,op.X +op.R: tlnn e,%fmnum + jrst englsh + skipa a,numarg(e) +op.X: movei a,16. + jrst op.num + +defop "B,op.B +op.B: movei a,2 + jrst op.num + +defop "D,op.D +defop "O,op.O +op.D: skipa a,[10.] +op.O: movei a,8 +op.num: nextarg b + call ntype + jrst loop + +defop "H,op.H +op.H: nextarg b + caml b,[-4000] + cail b,774000 + skipa + jumpl e,op.H1 + hlrz b,b + cail b,774000 + hrre b,b + movei a,8 + call ntype + movei c,", + tyo c + movei c,", + tyo c + getarg b,hrrz + cail b,774000 + hrre b,b +op.H1: movei a,8 + call ntype + jrst loop + +ifn $$engl,[ + +englsh: nextarg b + push p,cloop + jumpe b,eng0 + jumpg b,eng1 + movei a,$minus + call prinz + movn b,b + jumpl b,ensetz +eng1: cail b,10000. + jrst eng1E9 + move a,b + idivi b,100. + idivi b,10. + move b,a + jumpe c,eng1E3 + tlo e,%fmeng + jcall engb + +eng0: movei a,$0 + tlne e,%fmcol + movei a,$0th + jcall prinz + +;;;An inordinate number of instructions have been written in this world to +;;;compensate for the fact that in two's-compliment binary there is an +;;;extra negative number: +ensetz: idiv b,[1000000000.] + movn b,b + movn c,c + jrst eng1E8 + +eng1E9: idiv b,[1000000000.] +eng1E8: move a,[$billion,,eng1E6] + jrst engil + +eng1E6: idiv b,[1000000.] + move a,[$million,,eng1E3] + jrst engil + +eng1E3: idivi b,1000. +eng1E2: move a,[$thousand,,eng1E0] + jrst engil + +eng1E0: tlo e,%fmeng + jcall engb + +engil: exch b,c + jumpe c,(a) + push p,a + push p,b + call engc + pop p,b + hlr a,(p) + call prinz + jumpn b,sppopj + pop p,(p) + jumpge e,cpopj +thpopj: movei a,[asciz "th"] + jcall prinz + +sppopj: movei c,40 + tyo c + return + +;;;Still within IFN $$ENGL: + +;;;Subroutine. Outputs C(B) in english. 0 < C(B) < 10000., but not +;;;1000.|C(B). ENGC prints C(C), ENGB prints C(B). If %FMENG and %FMCOL are +;;;set, then we output an ordinal number. +engc: move b,c +engb: idivi b,100. + jumpe b,engx2 + push p,c + tlze e,%fmeng + tlnn e,%fmcol + jrst engx1 + call eng100 + movei a,$hundred + call prinz + pop p,b + jumpe b,thpopj + movei a,$and + call prinz + tlo e,%fmeng + jcall eng100 + +engx1: call eng100 + movei a,$hundred + call prinz + pop p,b + jumpn b,eng140 + return + +engx2: move b,c + jcall eng100 + +;;;Subroutine. Outputs C(B) in english. 0 < C(B) < 100.. +;;;ENG140 prints a space first, ENG100 does not. If %FMENG is set, we +;;;output an ordinal number. +eng140: movei c,40 + tyo c +eng100: caige b,20. + jrst eng102 + idivi b,10. + move a,C$20-2(b) + move b,c + call prinz + jumpe b,eng103 + movei a,[asciz "y-"] + call prinz +eng102: move a,C$1-1(b) + tlne e,%fmeng + hlr a,a +prinz: hrli a,440700 + tlzn e,%fmats ;Atsign flag causes capitalization + jrst prinz0 + ildb c,a + subi c,"a-"A +prinz1: tyo c +prinz0: ildb c,a + jumpn c,prinz1 + return + +eng103: tlne e,%fmeng + jrst eng104 + movei c,"y + tyo c + return + +eng104: movei a,[asciz "ieth"] + jcall prinz + +;;;Still within IFN $$ENGL: + +C$1: $1th,,$1 ? $2th,,$2 ? $3th,,$3 ? $4th,,$4 ? $5th,,$5 ? $6th,,$6 + $7th,,$7 ? $8th,,$8 ? $9th,,$9 ? $10th,,$10 ? $11th,,$11 + $12th,,$12 ? $13th,,$13 ? $14th,,$14 ? $15th,,$15 ? $16th,,$16 + $17th,,$17 ? $18th,,$18 ? $19th,,$19 + +C$20: $20 ? $30 ? $40 ? $50 ? $60 ? $70 ? $80 ? $90 + +$minus: asciz "minus " +$and: asciz " and " +$0: asciz "zero" +$0th: asciz "zeroth" + +$1: asciz "one" +$1th: asciz "first" +$2: asciz "two" +$2th: asciz "second" +$3: asciz "three" +$3th: asciz "third" +$4: asciz "four" +$4th: asciz "fourth" +$5: asciz "five" +$5th: asciz "fifth" +$6: asciz "six" +$6th: asciz "sixth" +$7: asciz "seven" +$7th: asciz "seventh" +$8: asciz "eight" +$8th: asciz "eighth" +$9: asciz "nine" +$9th: asciz "ninth" +$10: asciz "ten" +$10th: asciz "tenth" +$11: asciz "eleven" +$11th: asciz "eleventh" +$12: asciz "twelve" +$12th: asciz "twelfth" +$13: asciz "thirteen" +$13th: asciz "thirteenth" +$14: asciz "fourteen" +$14th: asciz "fourteenth" +$15: asciz "fifteen" +$15th: asciz "fifteenth" +$16: asciz "sixteen" +$16th: asciz "sixteenth" +$17: asciz "seventeen" +$17th: asciz "seventeenth" +$18: asciz "eighteen" +$18th: asciz "eighteenth" +$19: asciz "nineteen" +$19th: asciz "nineteenth" + +$20: asciz "twent" +$30: asciz "thirt" +$40: asciz "fort" +$50: asciz "fift" +$60: asciz "sixt" +$70: asciz "sevent" +$80: asciz "eight" +$90: asciz "ninet" + +$hundr: asciz " hundred" +$thous: asciz " thousand" +$milli: asciz " million" +$billi: asciz " billion" + +] ;end ifn $$engl + +defop "A,op.A +op.A: nextarg a + hlrz c,a + caie c,-1 ;0 or -1 in left half acts like 440700 + skipn c + hrli a,440700 + jrst op.A1 + +op.A2: tyo c +op.A1: ildb c,a + jumpn c,op.A2 + jrst loop + +defop "S,op.S +op.S: nextarg a + call 6type + jrst loop + +defop "C,op.C +op.C: nextarg a + jumpl e,op.C1 ;If colon set, just tyo it. + cail a,40 ;Normal printing characters just tyo'd + cail a,177 + jrst op.C2 +op.C1: tyo a + jrst loop + +op.C2: cain a,33 ;altmode just gets tyo'd + jrst op.C1 + movei c,"^ + tyo c + trc a,100 + jrst op.C1 + +defop "P,op.P +op.P: jumpl e,op.P1 ;Colon flag reversed from Lisp version. + getarg c + tlne e,%fmats + jrst op.P2 +op.P3: cain c,1 + jrst loop +op.Ps: movei c,"s + tyo c + jrst loop + +op.P1: nextarg c + tlnn e,%fmats + jrst op.P3 +op.P2: cain c,1 + jrst op.P4 + movei c,"i + tyo c + movei c,"e + tyo c + jrst op.Ps + +op.P4: movei c,"y + tyo c + jrst loop + +; SQUOZE ASCII +; 0 "/" 57 +; 1 - 12 "0" - "9" 60 - 71 +; 13 - 44 "A" - "Z" 101 - 132 +; 45 "." 56 +; 46 "$" 44 +; 47 "%" 45 + +defop "U,op.U +op.U: nextarg b + tlz b,740000 +op.U1: idiv b,[50*50*50*50*50] + addi b,"A-13 + caige b,"A + subi b,<"A-1>-"9 + caile b,"Z + subi b,<"Z+2>-"$ + cain b,"$-1 + movei b,". + exch b,c + tyo c + imuli b,50 + jumpn b,op.U1 + jrst loop + +ifn $$errs,[ +defop "E,op.E +op.E: jumpl e,op.E4 ;If colon flag is set, arg is error code. + movei a,1 +op.E1: .call op.Ecl + .lose %lssys + tlnn e,%fmats ;If atsign flag is set, Capitalize first word. + jrst op.E3 + .iot erri,c + jrst op.E2 + +op.E5: movei c,", + tyo c + movei c,40 + tyo c +op.E6: movei c,"a-"A(a) + cail c,"a + caile c,"z + subi c,"a-"A +op.E2: tyo c +op.E3: .iot erri,a + cail a,40 + jrst op.E6 + caie a,^M + jrst op.E9 + .iot erri,a + caie a,^J + jrst op.E9 + .iot erri,a + cail a,40 + jrst op.E5 +op.E9: .close erri, + jrst loop + +op.E4: nextarg b + movei a,4 + jrst op.E1 + +op.Ecl: setz + sixbit /open/ + [.uai,,erri] + [sixbit /err/] + move a + setz b +];$$errs + +ife $$pfn,[ +defop "F,op.F +op.F: nextarg a + push p,2(a) ;fn2 + push p,1(a) ;fn1 + push p,3(a) ;dir + move a,0(a) ;dev + call 6type + movei c,": + tyo c + movei c,40 + tyo c + pop p,a + call 6type + movei c,"; + tyo c + movei c,40 + tyo c + pop p,a + call 6type + movei c,40 + tyo c + pop p,a + call 6type + jrst loop +] ;end ife $$pfn + +ifn $$pfn,[ + +defop "F,op.F +op.F: movei b,54. ;maximum size of filename including + jsp a,grow ; the ^@ at the end. + push p,d + move d,obp(e) + nextarg b + call pfn + move a,d + pop p,d + call nstr + jrst loop + +] ;end ifn $$pfn + +ifn $$time,[ + +defop "Q,op.Q +op.Q: movei b,35. + jsp a,grow + push p,d + move d,obp(e) + nextarg a + tlnn e,%fmcol\%fmats + call datime + tlne e,%fmcol + call time + tlne e,%fmats + call date + move a,d + pop p,d + call nstr + jrst loop + +] ;end ifn $$time + +defop ^M,opcrlf +opcrlf: tlne e,%fmats + call crlf + sojl d,done + ildb c,bp(e) + caie c,^J ;flush linefeed if it is there + jrst nolf + jumpl e,loop ;If colon set, we are done. +skpws1: sojl d,done + ildb c,bp(e) +skipws: caie c,40 + cain c,^I + jrst skpws1 + jrst xloop + +nolf: jumpge e,skipws ;If colon not set, skip white space. + jrst xloop + +ife $$pcode,[ +defop "|,opvbar +opvbar: movei c,^L + tyo c + sosg a,numarg(e) + jrst loop + jrst op.%1 +] ;end ife $$pcode + +;;;~& is the same as ~% if you can't orient yourself: +ife $$pcode\$$itab\$$utab, defop "&,op.% + +defop "%,op.% +op.%: skipg a,numarg(e) + jrst loop +op.%1: call crlf + sojg a,op.%1 + jrst loop + +ifn $$pcode,[ +defop "|,opvbar +defop "&,opamper +opvbar: skipa a,["C] +opampe: movei a,"A +opamp1: nojust + movei c,^P + tyo c + tyo a +ifn $$utab, setzm hpos(e) + sosg a,numarg(e) + jrst loop + jrst op.%1 +] ;end ifn $$pcode + +defop "<,oples +oples: call dump + tloe e,%fmjst\%fmbuf + call bdjust + tlnn e,%fmnum + call bdop + move b,numarg(e) + jsp a,grow ;Make sure buffer is big enough. +oples3: movei c,40 + hrl c,numarg(e) + movem c,just(e) + jrst loop + +defop 73,opsemi ;"; (Emacs and Midas both give you grief if + ; you actually write a semicolon here...) +opsemi: tlnn e,%fmjst + call bdop + sojl d,eof + ildb a,bp(e) + tlnn e,%fmbuf ;buffer overflew, no justification. + jrst loop + move c,bufsiz(e) + sub c,bufct(e) + jumpe c,opsmi1 + hlrz b,just(e) + sub b,c + jumple b,loop ;already no more room, no padding needed. + hrl a,b + movem a,just(e) + call dump + tlo e,%fmbuf + jrst loop + +opsmi1: hrrm a,just(e) + jrst loop + +defop ">,opgrt +opgrt: tlzn e,%fmjst + call bdop + tlnn e,%fmbuf ;buffer overflew, no justification. + jrst loop + move c,bufsiz(e) + sub c,bufct(e) + hlrz b,just(e) + sub b,c + jumple b,opgrt7 ;no padding needed. + move a,obp(e) + push p,a + push p,b + hrrz c,just(e) +opgrt9: idpb c,a +ifn $$utab, call pos + sojg b,opgrt9 + pop p,b + pop p,a + call outstr +opgrt7: call dump + jrst loop + +ifn $$itab,[ + +ife $$pcode,[ +defop "&,opamper +opampe: nojust + call dump + call getpos + jumpn a,op.% + sose numarg(e) + jrst op.% + jrst loop +] ;end ife $$pcode + +defop "T,op.T +op.T: nojust + tlnn e,%fmnum + call bdop + call dump + call getpos + jumpl a,loop + move b,numarg(e) + camg b,a + jrst loop + rot b,-3 ;Only works for 8 character wide tabs. + rot a,-3 + sub b,a + hrrz a,b + lsh b,-33. + jumpe a,op.T2 + movei b,^I +op.T1: tyo b + sojg a,op.T1 + ldb b,[000300,,numarg(e)] + jumpe b,loop +op.T2: movei a,40 +op.T3: tyo a + sojg b,op.T3 + jrst loop + +] ;end ifn $$itab + +ifn $$utab,[ + +%tyo: idpb c,obp(e) + sosg bufct(e) + jrst tyodmp +%tyo1: tlne e,%fmbuf + return +pos: tlze e,%fmcrl + jrst poscrl +posfoo: cail c,40 + cain c,177 + jrst poscc + aos hpos(e) + return + +tyodmp: push p,c + call dump + pop p,c + jrst %tyo1 + +poscrl: caie c,^J + jrst posfoo + setzm hpos(e) + return + +poscc: cain c,^I + jrst postab + cain c,^M + tloa e,%fmcrl + caie c,33 + aos hpos(e) + aos hpos(e) + return + +postab: exch c,hpos(e) + addi c,10 ;Only works for 8 character wide tabs. + andcmi c,7 + exch c,hpos(e) + return + +;;; still in ifn $$utab + +ife $$pcode,[ +defop "&,opamper +opampe: nojust + skipn hpos(e) + sose numarg(e) + jrst op.% + jrst loop +] ;end ife $$pcode + +defop "T,op.T +op.T: nojust + tlnn e,%fmnum + call bdop + move b,numarg(e) + move a,hpos(e) + camg b,a + jrst loop + rot b,-3 + rot a,-3 + sub b,a + hrrz a,b + lsh b,-33. + jumpe a,op.T2 +op.T1: movei c,^I + tyo c + sojg a,op.T1 + ldb b,[000300,,numarg(e)] + jumpe b,loop +op.T2: movei c,40 + tyo c + sojg b,op.T2 + jrst loop + +] ;end ifn $$utab + +;;;Make room in the buffer for B more characters. Called by JSP A,GROW: +grow: sub b,bufct(e) + jumple b,(a) + addi b,4 + idivi b,5 ;B: how many words we need. + movei c,5 + imul c,b ;C: how many characters that will add to + addm c,bufsiz(e) ; the buffer. + addm c,bufct(e) + push p,[ascii "_____"] + sojg b,.-1 + jrst (a) + +;;;Empty the buffer. +dump: push p,b + move b,bufsiz(e) + move c,b + exch c,bufct(e) + sub b,c + jumpe b,dump3 + push p,a + movei a,buffer(e) + hrli a,440700 + movem a,obp(e) +ifn $$utab,[ + tlnn e,%fmbuf + jrst dump1 + push p,a + push p,b +dump2: ildb c,a + call pos + sojg b,dump2 + pop p,b + pop p,a +] ;end ifn $$utab +dump1: call outstr + pop p,a +dump3: tlz e,%fmbuf + pop p,b + return + +;;;Outputs a crlf. +crlf: movei c,^M + tyo c + movei c,^J + tyo c + return + +;;;Types number in B in base A: +ntype: jumpge b,ntype1 + movei c,"- + tyo c +ntype1: idiv b,a + movm b,b + movm c,c +ntype2: addi c,"0 + caile c,"9 + addi c,<"A-10.-"0> + jumpe b,ntype3 + hrlm c,(p) + idiv b,a + call ntype2 + hlrz c,(p) +ntype3: tyo c + return + +;;;Types the word in A in SIXBIT. In lowercase if colon flag is set. +6type: jumpe a,cpopj +6type1: ldb c,[360600,,a] + addi c,40 + tlne e,%fmcol + jrst 6type2 +6type3: tyo c + lsh a,6 + jumpn a,6type1 +cpopj: return + +6type2: cail c,"A + caile c,"Z + jrst 6type3 + addi c,"a-"A + jrst 6type3 + +ifn $$pfn\$$time,[ +;;;Cleanup for the case where a string has been deposited in the buffer by +;;;someone other than ourselves. New byte pointer is found in A. +nstr: move c,a + exch a,obp(e) + skipge a + sub a,[430000,,1] ;In case A is 440700,,1 and B is 010700,,0 + sub c,a + jumpe c,cpopj + ldb b,[360600,,c] + imuli c,5 + imuli b,55. + lsh b,30. + ash b,-30. + subi b,(c) + addm b,bufct(e) +ifn $$utab,[ ;If we are tabbing, then we must update the HPOS + tlne e,%fmbuf + return +nstr1: ildb c,a + call pos + aojl b,nstr1 +] ;end ifn $$utab + return +] ;end ifn $$pfn\$$time + +;;;Under construction: +;host printing +;flonum printing +;defop "?,opques ;Funcall escape. + +nxop: fmterr "Undefined format operator." + +bdop: fmterr "Bad call to format operator." +ife $$engl, englsh==:bdop + +bdjust: fmterr "Illegal format operation during justification." + +eof: fmterr "Format string terminated unexpectedly." + +nxarg: fmterr "Format ran out of arguments." + +;define sizhac size +;printx / (size!. words) +;/ +;termin +; +;oradix==10 +;radix 10. +;sizhac \.-format +;radix oradix + +.end format diff --git a/src/syseng/rfn.13 b/src/syseng/rfn.13 new file mode 100755 index 00000000..42449276 --- /dev/null +++ b/src/syseng/rfn.13 @@ -0,0 +1,401 @@ +.BEGIN RFN ;-*-MIDAS-*- + +SUBTTL Routines for parsing and printing filenames + +;Basic conventions: + +;We assume that there are accumulators A, B, C, D and E, not necessarily +;consecutive, and that the stack is in P. +;No routine clobbers ACs other than the +;ones it is documented to clobber, and none touches even temporarily +;any AC other than A, B, C, D, E and P. +;All code generated is pure. +;The main routines, RFN, PFN, PFNBRF and PFNMCH, never skip. + +;This file contains two routines, RFN to read filenames and PFN to print. +;Both expect a b.p. for ILDB'ing or IDPB'ing the text, in D. +;Both expect a pointer to a filename block in B. +;A filename block consists of four words, which hold the +;left-justified sixbit DEVICE, FN1, FN2 and SNAME in that order. + +;The RFN routine assumes that the user has defined the label RSIXTP +;(RSIX-Terminator-P) which should expect a character in A +;and skip if it should terminate a filename, or start a switch. +;It will not be called with control characters. +;Thus, you might want it to skip when given ",", "_" or "/". +;PFN similarly assumes that there is a routine PSIXTP will will +;skip for a character in A that needs a ^Q printed in front of it. +;Normally, RSIXTP and PSIXTP can be the same routine. + +;If you want switches to be processed in filenames, +;set $$SWITCH to 1 and define the label SWITCH as a routine to read a switch. +;It will be called with the first character of the switch in A. +;It can read more characters off D, or by calling RFN"RUC +;If it skips, RFN assumes that the character in A should be reprocessed. +;A slash is followed by a single switch, while parentheses enclose +;any number of switches. However, neither slash nor "(" will be +;recognized unless RSIXTP skips for it. This gives the caller +;run-time control of whether switches are to be processed. + +;If $$MNAME is set, the user must define MNAME to point to +;a word holding this machine's name in SIXBIT. RFN"RMNAME initializes it. +;If $$PFNBRF is set, the user must define MSNAME to point to +;a word holding the "default" SNAME (the one not to be mentioned). + +;These symbols should be defined by the user to select parts of this file: +IFNDEF $$RFN,$$RFN==0 ;Include RFN, the routine for reading + ; filenames. +IFNDEF $$SWITCH,$$SWITCH==0 ;Include routines for processing "/" and + ; "(-)" switches. +IFNDEF $$PFN,$$PFN==0 ;Include PFN, the routine for printing + ; filenames. +IFNDEF $$PFND,$$PFND==0 ;Include PFND, like PFN but device of "DSK" + ; or fn2 of ">" is suppressed. +IFNDEF $$PFNBRF,$$PFNBRF==0 ;Include routine to print filenames briefly. +IFNDEF $$RUC,$$RUC==0 ;1 => don't define RUC; use a + ; user-supplied definition. +IFNDEF $$MNAME,$$MNAME==0 ;1 => assume MNAME is defined and holds + ; this machine's name. + +IFN $$SWITCH,$$RFN==1 +IFN $$PFNBRF,$$PFNBRF==1 + +DEV==0 ;Indices into the filename block. +FN1==1 +FN2==2 +SNM==3 + +.AUXIL ;Don't mention all our internal symbols in crefs. + +;PRINT VERSION NUMBER +.TYO6 .IFNM1 +.TYO 40 +.TYO6 .IFNM2 +PRINTX/ included in this assembly. +/ + +DEFINE SYSCAL NAME,ARGS +.CALL [SETZ ? SIXBIT/NAME/ ? ARGS ((SETZ))] +TERMIN + +IFN $$RFN,[ ;Routines for reading filenames. + +;Read a file spec off the b.p. in D into the file block that B points at. +;A filename block has four words, which get the device name, sname, fn1 and fn2. +;The terminating character is left in A. +;In E's left half we return flags indicating which of the four names were +;specified: 1,, for the device; 2,, for the FN1; 4,, for the FN2; 10,, for the SNAME. + +RFN: PUSH P,B + PUSH P,C + MOVE C,B ;Put the filename block addr in C since RSIXG returns in B. + PUSH P,FN1(C) ;Save default FN1, FN2 for ^X, ^Y. + PUSH P,FN2(C) + SETZ E, ;Number of names stored yet is 0. +RFN0: PUSHJ P,RUC +RFN2: PUSHJ P,RSIXG ;Read one filename. + JRST RFN1 + PUSHJ P,RFNN ;If it's not null, store it. +RFN1: CAIE A,": ;Check char that ended name, + CAIN A,"; + JRST RFN0 + CAIN A,40 ;If was space, : or ;, read another name. + JRST RFN0 +IFN $$SWITCH,[ + CAIN A,"/ ;If stopped on "/" or "(", call switch rtn. + JRST RFNSL ;Read 1 switch. + CAIN A,"( + JRST RFNPAR ;Read many switches until ). +];IFN $$SWITCH + CAIE A,^X + CAIN A,^Y ;If was ^X or ^Y, + JRST RFNX ;use the default FN1 or FN2 as next name. + SUB P,[2,,2] + POP P,C + POP P,B + POPJ P, + +;STORE THE NAME IN B. +RFNN: CAIN A,": + JRST RFNC ;: => USE AS DEVICE. + CAIN A,"; + JRST RFNS ;; => USE AS SNAME. + AOJA E,RFNT(E) ;ELSE USE AS NEXT NAME IN NORMAL SEQUENCE. + +;Table for storing a name normally. +;N'th entry used for N'th name. +RFNT: JRST RFNF1 ;1st name, set FN1 + JRST RFNF2 ;Second, set FN2. + JRST RFNC ;Third, set device. + JRST RFNS ;Fourth, set sname. + SOS E ;Fifth one, ignore, and don't let count advance past 5. + POPJ P, ;So this word isn't part of the dispatch. + +IFN $$SWITCH,[ ;Code for processing switches, when a "/" or "(" is seen. + +RFNPAR: PUSHJ P,RUC ;Get next char. Is it a ")"? +RFNPA1: CAIN A,") + JRST RFN0 ;Paren ends switches. + CAIE A,0 + CAIN A,^M + JRST RFN1 ;CR ends spec even in switch list. + PUSHJ P,SWITCH ;Try to gobble the switch. + JRST RFNPAR ;Char in A used up, get another. + JRST RFNPA1 ;Char in A not part of switch; is it ")"? + +RFNSL: PUSHJ P,RUC + CAIE A,0 + CAIN A,^M ;/ ENDS SPEC. + JRST RFN1 + PUSHJ P,SWITCH ;Otherwise, process it as switch. + JRST RFN0 ;No skip => char in A was gobbled by switch. + JRST RFN2 ;Skip => let next RSIXG gobble the char now in A. + +];IFN $$SWITCH + +;Here to store the word in B as the SNAME. +RFNS: MOVEM B,SNM(C) ;Set the sname. + TLO E,1_SNM ;Say the sname has been specified in this filespec. + MOVSI B,'DSK ;Consider setting the device to DSK: + TLNN E,1_DEV ;If dev was just spec'd, don't override the spec'd one. + MOVEM B,DEV(C) ;It is a feature that this doesn't set 1_DEV, + POPJ P, ; it lets the user tell exactly what was given. + +;Here to store the word in B as the device name. +RFNC: MOVEM B,DEV(C) ;Set the dev name, + TLO E,1_DEV ;and say the device name was explicitly specified. + POPJ P, + +;Here to store the word in B as the FN1. +RFNF1: MOVEM B,FN1(C) ;Set the FN1, say was spec'd. + TLO E,1_FN1 + POPJ P, + +;Here to store the word in B as the FN2. +RFNF2: MOVEM B,FN2(C) ;Set the FN2, say was spec'd. + TLO E,1_FN2 + POPJ P, + +;Here to process a ^X or ^Y, by taking the stacked default FN1 or FN2 and using it as input. +RFNX: ADDI A,(P) ;POINT TO THE STACKED DEFAULT FN1 OR FN2 + MOVE B,-^Y(A) ;AND GET IT. + PUSHJ P,RFNN ;STORE IT NORMALLY. + JRST RFN0 ;GET NEXT NAME. + +;Subroutines for filename reading. + +IFE $$RUC,[ +;Read char into A from b.p. in D and convert to upper case. +;Filename reading does all its input via RUC. +;If $$RUC is set, we assume that the user has defined RUC, +;and call the user's definition. +RUC: ILDB A,D + CAIL A,140 + SUBI A,40 + POPJ P, +];IFE $$RUC + +;Read SIXBIT word into B from b.p. in D, leaving terminating char in A. +;Expects first character in A already. +;Terminates on a Space or control character or Rubout. +;Skips if the word was non-null. +RSIX: PUSH P,C + MOVE C,[440640,,B] ;Extra bit in b.p. set says RSIX, not RSIXG. + JRST RSIXG1 + +;Similar but stop on :, ;, and any characters which RSIXTP skips for. +;We assume that the user has defined RSIXTP to accept a character in A +;and skip or not, clobbering no ACs. +RSIXG: PUSH P,C + MOVE C,[440600,,B] ;BP FOR STORING THE SIXBIT. +RSIXG1: SETZ B, ;NO CHARS SO . +RSIX0: CAIN A,^Q + JRST RSIX2 ;^Q QUOTES A CHAR WHICH WOULD TERMINATE. + CAIE A,177 + CAIG A,40 + JRST RSIX1 ;SPACE OR CTL CHAR => STOP. + TLNE C,40 + JRST RSIX3 ;RSIX WAS CALLED, ALL OTHER CHARS NORMAL. + CAIE A,": + CAIN A,"; ;RSIXG, STOP ON : AND ;. + JRST RSIX1 + PUSHJ P,RSIXTP + JRST RSIX3 +RSIX1: SKIPL C ;TERMINATE: SKIP IF GOT >= 1 CHAR. + AOS -1(P) + POP P,C + POPJ P, + +RSIX2: PUSHJ P,RUC ;^Q => READ THE QUOTED CHAR. +RSIX3: SUBI A,40 ;INSERT NORMAL CHAR IN THE 6BIT. + TLNE C,77^4 ;IGNORE CHARS AFTER THE FIRST 6. + IDPB A,C + PUSHJ P,RUC + JRST RSIX0 + +];IFN $$RFN + +IFN $$PFN,[ ;Routines for printing filenames. + +;Convert the filenames in the filename block which B points at +;to ASCII, depositing it down the b.p. in D, followed by a ^@ +;which D is not advanced over. +;A filename block is four words containing the device, fn1, fn2 and sname. + +;It is assumed that PSIXTP (PSIX-Terminator-P) is defined as a +;routine which, given a character in B, skips if a ^Q should be +;printed before that character. Space, colon and semicolon get ^Q'd +;in any case. Thus, you can usually make PSIXTP and RSIXTP the same. + +;PFNMCH is the same as PFN except that if the device is DSK +;it prints the name of this machine (from MNAME) instead. + +IFN $$MNAME,[ + +PFNMCH: PUSH P,A + PUSH P,C + MOVE C,DEV(B) + CAMN C,[SIXBIT/DSK/] + MOVE C,MNAME + JRST PFN1 +] + +PFN: PUSH P,A + PUSH P,C + MOVE C,DEV(B) ;Print device name, colon, and space. +PFN1: PUSHJ P,PSIXF + MOVEI A,": + IDPB A,D + MOVEI A,40 + IDPB A,D + MOVE C,SNM(B) ;Print sname, semicolon, and space. + PUSHJ P,PSIXF + MOVEI A,"; + IDPB A,D + MOVEI A,40 + IDPB A,D + MOVE C,FN1(B) ;Print fn1 and space. + PUSHJ P,PSIXF + MOVEI A,40 + IDPB A,D + MOVE C,FN2(B) ;Print fn2. + PUSHJ P,PSIXF + SETZ A, ;Store terminating ^@ but don't advance D over it. + MOVE C,D + IDPB A,C + POP P,C + POP P,A + POPJ P, + +;Print SIXBIT word in C down b.p. in D, +;putting ^Q's before appropriate characters. +;Clobbers C. +PSIXF: LDB A,[360600,,C] ;Extract first character + LSH C,6 ;and flush it. + ADDI A,40 + CAIE A,": + CAIN A,"; + JRST PSIXF1 + CAIN A,40 + JRST PSIXF1 + PUSHJ P,PSIXTP + JRST PSIXF2 +PSIXF1: PUSH P,A + MOVEI A,^Q + IDPB A,D + POP P,A +PSIXF2: IDPB A,D + JUMPN C,PSIXF ;ALL THE REST BLANK => DONE. + POPJ P, + +];IFN $$PFN + +IFN $$PFNBRF,[ ;Routine to print filenames, as briefly as possible. + +;PFNBRF is called like PFN, but it omits names which have their default values. +;It is assumed that MSNAME is defined to be the address of a word +;holding the "default" SNAME. +PFNBRF: PUSH P,A + PUSH P,C + MOVE C,DEV(B) ;Print device name, colon, and space. +IFN $$MNAME,CAME C,MNAME + CAMN C,[SIXBIT /DSK/] + JRST PFNBR1 + JUMPE C,PFNBR1 + PUSHJ P,PSIXF + MOVEI A,": + IDPB A,D + MOVEI A,40 + IDPB A,D +PFNBR1: SKIPE C,SNM(B) ;Print sname, semicolon, and space. + CAMN C,MSNAME + JRST PFNBR2 + PUSHJ P,PSIXF + MOVEI A,"; + IDPB A,D + MOVEI A,40 + IDPB A,D +PFNBR2: MOVE C,FN1(B) ;Print fn1 and space. + PUSHJ P,PSIXF + SKIPN FN2(B) + JRST PFNBR3 + MOVEI A,40 + IDPB A,D + MOVE C,FN2(B) ;Print fn2. + PUSHJ P,PSIXF +PFNBR3: SETZ A, ;Store terminating ^@ but don't advance D over it. + MOVE C,D + IDPB A,C + POP P,C + POP P,A + POPJ P, + +];IFN $$PFNBRF + +IFN $$PFND,[ ;Routine to print filenames, omitting true defaults. + +;PFND is called like PFN, but it omits the device if it is "DSK" and the +; fn2 if it is ">". +PFND: PUSH P,A + PUSH P,C + SKIPE C,DEV(B) ;Print device name, colon, and space. + CAMN C,[SIXBIT /DSK/] + JRST PFND1 + PUSHJ P,PSIXF + MOVEI A,": + IDPB A,D + MOVEI A,40 + IDPB A,D +PFND1: SKIPN C,SNM(B) ;Print sname, semicolon, and space. + JRST PFND2 + PUSHJ P,PSIXF + MOVEI A,"; + IDPB A,D + MOVEI A,40 + IDPB A,D +PFND2: MOVE C,FN1(B) ;Print fn1. + PUSHJ P,PSIXF + SKIPE C,FN2(B) ;Print space, fn2. + CAMN C,[SIXBIT />/] + JRST PFND3 + MOVEI A,40 + IDPB A,D + PUSHJ P,PSIXF +PFND3: SETZ A, ;Store terminating ^@ but don't advance D over it. + MOVE C,D + IDPB A,C + POP P,C + POP P,A + POPJ P, + +];IFN $$PFND + +IFN $$MNAME,[ ;Routine to initialize MNAME. + +RMNAME: SYSCAL SSTATU,[REPEAT 6,[? %CLOUT,,MNAME]] + .LOSE %LSSYS + POPJ P, + +];IFN $$MNAME + +.END RFN