;teco subroutines - delphi .insr1 /lib/macro/unix ;.insr1 /lib/macro/t.xx T.EEI=20 T.RAW=40 T.ECHO=10 ;macro to simulate readch for now .macro readch ?a push jsr pc,ttflsh a: clr r0 sys s_read,iotmp,1 bcs a pop movb iotmp,-(sp) .endm .csect stack ;define storage for the tty output buffer .even ttobuf: ttflds: .word 1 ;file descriptor for tty output file ttleft: .word 512. ;remaining slots in buffer ttnext: .word ttobff ;pointer to next free slot ttobff: .blkb 512. ;buffer for actual output ;table of special routines for processing control chars .even ctrtbl: .word ntyi ;^@ .word ntyi ;^a .word ntyi ;^b .word ntyi ;^c .word ntyi ;^d .word ntyi ;^e .word ntyi ;^f .word ntyi ;^g .word ntyi ;^h .word ntyi ;^i .word ctrj ;^j .word ntyi ;^k .word ntyi ;^l .word ctrm ;^m .word ntyi ;^n .word ntyi ;^o .word ntyi ;^p .word ntyi ;^q .word ntyi ;^r .word ntyi ;^s .word ntyi ;^t .word ctru ;^u .word ntyi ;^v .word ntyi ;^w .word ntyi ;^x .word ntyi ;^y .word ntyi ;^z .word ctralt ;^[ (altmode) .word ntyi ;^\ .word ntyi ;^] .word ntyi ;^^ .word ntyi ;^_ ;ttymod command used to read the tty initial status getcmd: gfl: .byte 0 ;low byte of ttflag .byte 4. gfh: .byte 0 ;high byte .byte 5. gf2l: .byte 0 ;low byte of ttflg2 .byte 6. gf2h: .byte 0 ;high byte .byte 7. gf3l: .byte 0 ;low byte of ttflg3 .byte 8. gf3h: .byte 0 ;high byte .byte 9. gll: .byte 0 ;line length .byte 12. gps: .byte 0 ;page size .byte 13. .word -1 ;ttymod command used to replace the tty data setcmd: sfl: .byte 0 .byte 300!4 ;replace ttflag low byte sfh: .byte 0 .byte 300!5 ;replace ttflag high byte sf2l: .byte 0 .byte 300!6 ;replace ttflg2 low byte sf2h: .byte 0 .byte 300!7 ;replace ttflg2 high byte sf3l: .byte 0 .byte 300!8. ;replace ttflg3 low byte sf3h: .byte 0 .byte 300!9. ;replace ttflg3 high byte .word -1 .csect pgm ;routine to initialize the tty intty: clr r0 ;use file 0 sys s_ttymod,getcmd ;put tty data into ttysta jsr pc,copold ;copy the old data bic #T.ECHO,ttflag ;turn of echo bis #T.RAW,ttflag ;turn on raw mode bis #T.EEI,ttflg2 ;turn on EEI mode (enable ^b and ^q interrupts) jsr pc,copnew ;copy the new data to the command clr r0 ;again use file 0 sys s_ttymod,setcmd ;now put in the data the teco wants rts pc ;routine to restore the initial tty status clrtty: jsr pc,copold ;put the old tty data back jsr pc,copnew ;put it in the command to set clr r0 sys s_ttymod,setcmd ;set in in rts pc ;routine to copy the data from ttstatus to command to set tty status copnew: movb ttflag,sfl ;ttflag low byte movb ttflag+1,sfh ;ttflag high byte movb ttflg2,sf2l ;ttflg2 low byte movb ttflg2+1,sf2h ;ttflg2 high byte movb ttflg3,sf3l ;ttflg3 low byte movb ttflg3+1,sf3h ;ttflg3 high byte rts pc ;routine to copy data from get tty status command to save locations copold: movb gfl,ttflag ;copy the data we read in useful places movb gfh,ttflag+1 movb gf2l,ttflg2 movb gf2h,ttflg2+1 movb gf3l,ttflg3 movb gf3h,ttflg3+1 movb gll,llen movb gps,pagsiz rts pc rstty: rts pc ;routine to go into ^r mode rset: bic #T.EEI,ttflg2 ;go out of eei mode jsr pc,copnew ;copy the ttflag words to command clr r0 sys s_ttymod,setcmd ;execute the set command movb #1,echosw ;disable echoing movb #1,graphm ;enable graphics mode rts pc ;routine to go out of ^r mode rclr: mov pagsiz,vpos ;now set cursor to bottom of screen dec vpos ;we want last line clr hpos ;first column jsr pc,setcur ;put the cursor here jsr pc,crlf ;kill the line inc vpos ;move back to previous line inc vpos ;mov back another line so we don't scroll jsr pc,setcur ;put the cursor here bis #T.EEI,ttflg2 ;go back into eei mode jsr pc,copnew ;copy the ttflag words to set command clr r0 sys s_ttymod,setcmd ;execute the set command clrb echosw ;re-enable echoing clrb graphm ;go out of graphics mode rts pc ;routine to read a char from the tty tyi: readch mov (sp)+,r0 bic #177600,r0 cmp r0,#40 ;is it a control char? bhis ntyi ;no asl r0 ;convert to word offset mov ctrtbl(r0),-(sp) ;get the special routine to execute asr r0 ;return r0 to normal jmp @(sp)+ ;now execute special routine and return ntyi: cmp r0,#177 ;is it rubout? beq 2$ ;yes, do nothing tstb ucsw ;should we upper chase the char? beq 1$ ;nope jsr pc,getuc ;change it to upper case 1$: jsr pc,chkrb ;check if we should print \ tstb echosw ;should we echo the char? bne 2$ ;no jsr pc,tyo ;echo it 2$: rts pc ;return char in r0 ;routine to process lf's ctrj: tstb crlfsw ;should we switch lf's and cr's beq 2$ ;no, do normal echo tstb rmode ;are we in ^r mode? bne 1$ ;ignore amsw in ^r mode tstb amsw ;should we further switch to alt beq 1$ ;no, return a cr mov #33,r0 ;else return an altmode br 2$ 1$: mov #15,r0 ;switch to cr 2$: jsr pc,ntyi ;check for rubout and echo rts pc ;routine to process cr's ctrm: tstb crlfsw ;should we switch lf's and cr's beq 1$ ;no mov #12,r0 ;convert to a lf br 2$ 1$: tstb rmode ;are we in ^r mode? bne 2$ ;yes, ignore amsw in ^r mode tstb amsw ;should we switch lf's and alts beq 2$ ;no, leave it alone mov #33,r0 ;else convert to altmode 2$: jsr pc,ntyi ;check for rubout and echo rts pc ;routine to process altmodes ctralt: tstb rmode ;are we in ^r mode? bne 1$ ;yes ignore amsw tstb amsw ;should we swittch lf's and alt's beq 1$ ;no mov #15,r0 ;else convert to cr 1$: jsr pc,ntyi ;check for rubout and echo rts pc ;routine to process ^u's ctru: jsr pc,chkrb ;fix any rubout stuff rts pc ;output a char in r0 tyo: jsr pc,ctyo ;process char and put in buffer jsr pc,ttflsh ;make sure the buffer is empty rts pc ;put char in r0 into the tty output buffer ctyo: cmp r0,#40 ;is it a control char blo 1$ ;yes, special treatment jsr pc,ntyo ;type as normal char rts pc 1$: cmp r0,#33 ;altmode? bne 2$ ;no mov r0,-(sp) ;save the char mov #'$,r0 ;change altmodes to dollar signs jsr pc,ntyo ;type normally mov (sp)+,r0 ;restore the altmode rts pc 2$: cmp r0,#14 ;formf? bne 4$ ;nope tst rmode ;are we in ^r mode? bne 9$ ;yes, type as ^l tstb ffsw ;should we type formfeeds? beq 9$ ;yes, type as ^l jsr pc,ttputc ;put char in buffer rts pc 4$: cmp r0,#12 ;nl? bne 6$ ;no tstb graphm ;do special graphics hack? bne 5$ ;yes push r0 ;save the lf mov #15,r0 ;put a cr into the buffer jsr pc,ttputc pop r0 ;restore the lf and put it in the buffer jsr pc,ttputc rts pc 5$: jsr pc,grcrlf ;special graphic crlf rts pc 6$: cmp r0,#11 ;tab? bne 9$ ;no tstb graphm ;do special graphics hack? bne 7$ ;yes jsr pc,ttputc ;put the char into the buffer rts pc 7$: push ;need some registers mov hpos,r1 ;our current horizontal postion add #8.,r1 bic #7,r1 ;set r1 to next tab stop sub hpos,r1 ;this is how many spaces we must type mov #40,r0 ;a space 8$: jsr pc,ntyo ;type it sob r1,8$ ;type the appropriate number of spaces pop ;clean up rts pc 9$: mov r0,-(sp) ;save the orginal char mov #'^,r0 jsr pc,ntyo ;type an ^ mov (sp),r0 ;now get the original char add #100,r0 ;convert from control to normal jsr pc,ntyo ;now type as normal char mov (sp)+,r0 ;restore the original char rts pc ;routine to type normal chars and hack overflows ntyo: tstb graphm ;special graphic stuff bne 1$ ;yup jsr pc,ttputc ;put char in buffer rts pc 1$: cmp vpos,pagsiz ;are we past the end of screen? bhis 2$ ;yup, ignore anything mov llen,-(sp) ;get the llen of screen dec (sp) ;get number of last column cmp hpos,(sp)+ ;are we there? beq 3$ ;yes, do an overflow jsr pc,ttputc ;put char in buffer inc hpos ;advance to next char position 2$: rts pc 3$: push r0 ;save the char mov #'\,r0 ;backslash marks the overflow jsr pc,ttputc ;put it in the buffer pop r0 ;restore the char jsr pc,grcrlf ;type a newline jsr pc,ntyo ;now type the char normally rts pc .csect stack ;d space storage for indirect execution ttwrit: sys s_write,ttobff ;trap and pointer for tty output write ttwcnt: .word 0 ;count of chars to write .csect pgm ;routine to do buffered output on the tty output buffer ttputc: tst ttleft ;any space left? bgt 1$ ;there are some slots left jsr pc,ttflsh ;otherwise flush the output 1$: movb r0,@ttnext ;put in the char inc ttnext ;advance pointer dec ttleft ;one less space left rts pc ;routine to write out the buffer, and reset count and pointer ttflsh: push r0 ;need a register mov ttflds,r0 ;file descriptor for the tty mov #512.,ttwcnt ;the count for the write sub ttleft,ttwcnt ;subtract number of spaces left beq 1$ ;in case there were none left sys s_indir,ttwrit ;do the write 1$: mov #ttobff,ttnext ;reset the next pointer mov #512.,ttleft ;reset the slots left count pop r0 ;clean up rts pc resetobf: ; reset output buffer (no flushing) mov #ttobff,ttnext ;reset the next pointer mov #512.,ttleft ;reset the slots left count rts pc ;routine to do a special graphics crlf grcrlf: push r0 ;need a register cmp vpos,pagsiz ;have we overflowed screen? bhis 2$ ;yup, don't bother to type anything mov llen,-(sp) ;push the screen width dec (sp) ;find number of last column cmp hpos,(sp)+ ;are we in last column? beq 1$ ;yes, don't bother to kill mov #33,r0 ;kill to the end of this line jsr pc,ttputc mov #'K,r0 jsr pc,ttputc 1$: mov #15,r0 ;now type a carriage return jsr pc,ttputc mov #33,r0 ;finally move cursor down one line jsr pc,ttputc mov #'B,r0 ;we use this cause we dont want scroll jsr pc,ttputc 2$: clr hpos ;go to column 0 inc vpos ;advance to next line pop r0 ;clean up rts pc ;routine to set the cursor to the location specified by hpos and vpos setcur: push r0 ;need a register mov #33,r0 ;type escape char jsr pc,ttputc mov #'Y,r0 ;type y jsr pc,ttputc mov vpos,r0 ;the line address add #40,r0 ;thats how the vt52 likes it jsr pc,ttputc mov hpos,r0 ;the column address add #40,r0 jsr pc,ttputc jsr pc,ttflsh ;make sure the buffer is empty pop r0 ;clean up rts pc ;routine to set nice symnice:jsr pc,setvar ;set the variable sys s_nice mov nice,r0 add #4,sp ;pop arguments jmp eval ;and continue ;routine to kill from here to end of line killln: push r0 mov #33,r0 ;escape char to vt52 jsr pc,ttputc ;send it mov #'K,r0 ;kill to end of line char jsr pc,ttputc ;send it jsr pc,ttflsh ;empty the buffer pop r0 rts pc ;routine to kill from here to end of screen killsc: push r0 mov #33,r0 ;escape char to vt52 jsr pc,ttputc ;send it mov #'J,r0 ;kill to end of screen char jsr pc,ttputc ;send it jsr pc,ttflsh ;empty the buffer pop r0 rts pc ;routine to ring the terminal's bell bell: push r0 mov #7,r0 ;control g jsr pc,ttputc ;send it jsr pc,ttflsh ;empty the buffer pop r0 rts pc ;routine to count screen position offsets from actual chars in ;the buffer. expects starting char pointer in r0, starting screen ;line in r1, starting screen column in r2 and count of chars in r5. ;returns finally line in r1 and final column in r2. count: tst r5 ;any chars to count? beq 10$ ;no, just return mov llen,-(sp) dec (sp) ;find number of last column 1$: cmpb (r0)+,#40 ;is it a control char? bhis 7$ ;no, normal char cmpb -1(r0),#33 ;is it escape? beq 7$ ;prints as "$" cmpb -1(r0),#11 ;tab? bne 4$ ;no mov r2,-(sp) add #8.,(sp) bic #7,(sp) sub r2,(sp) ;find count to next tab stop 2$: cmp r2,2(sp) ;are we in last column blo 3$ ;no inc r1 ;else do crlf clr r2 3$: inc r2 dec (sp) ;one less space in the tab bgt 2$ ;some more left, keep counting tst (sp)+ ;pop the count br 9$ ;that's all for this tab 4$: cmpb -1(r0),#12 ;nl? bne 5$ ;nope inc r1 ;go down one line clr r2 ;back to column 0 br 9$ ;that's all for this nl 5$: cmp r2,(sp) ;assume normal ctrl char, skip two spaces blo 6$ ;no, we're not in last column inc r1 ;else go down one line clr r2 ;and back to column 0 6$: inc r2 ;one space 7$: cmp r2,(sp) ;are we in last column blo 8$ ;no, not in last column inc r1 ;else go down one line clr r2 ;and back to column 0 8$: inc r2 ;one space 9$: sob r5,1$ ;that's all for that char tst (sp)+ ;pop the last column number 10$: rts pc getuc: cmpb r0,#140 blos 1$ cmpb r0,#'Z+40 bhi 1$ sub #40,r0 1$: rts pc chkrb: tstb rbout beq 1$ push r0 mov #'\,r0 jsr pc,ttputc pop r0 clrb rbout 1$: rts r7 tyo.r: jsr r7,chkrb jsr r7,tyo rts r7 ;output a new line crlf: push ;need a register mov #12,r0 ;newline jsr pc,tyo ;type it pop ;clean up rts pc ;output a string between r1 and r2 typer: cmp r1,r2 bhis 1$ jsr pc,checkc movb (r1)+,r0 jsr pc,ctyo ;put a char in the buffer br typer 1$: jsr pc,ttflsh ;empty the buffer rts pc ;decimal convert, expects r1 pointing to string and r2 pointing to max length decin: mov r3,-(sp) ;save it clr r3 ;and clear 1$: cmp r1,r2 ;are we past boundry bhi 2$ ;yes, give up movb (r1)+,r0 ;next char cmp r0,#'0 ;digit? blo 2$ ;no cmp r0,#'9 bhi 2$ mul #10.,r3 sub #'0,r0 add r0,r3 br 1$ 2$: mov r3,r0 mov (sp)+,r3 dec r1 ;adjust rts pc ;decimal output, types the number in r0 decout:push tst r0 ;negative number? bge 1$ ;nope mov #'-,r0 ;else type minus sign jsr pc,tyo ;type it mov 2(sp),r0 ;restore the number neg r0 ;convert to positive, an type as positive 1$: mov r0,r1 ;dividend should be 32 bit number in r0,r1 clr r0 ;zero high order bits of number div #10.,r0 ;put dividend in r0, remainder in r1 tst r0 ;zero remainder? beq 2$ ;yes, now unwind recursion jsr pc,decout ;else type high order digits recursively 2$: mov r1,r0 ;copy remainder add #'0,r0 ;convert to char 0 jsr pc,tyo ;type it pop ;clean up rts pc ;routine to take a number in r0 and convert to a string representation ;which is saved on the stack. When this routine returns, the first ;thing on the stack is a count of chars in the number, then come the ;chars, with most significant digit on top. decoux: mov r1,r2 ;save r1 movb #1,state+1 ;this will be the count 2$: cmp r0,#10. ;less than 10? blo 1$ ;yes, the last digit incb state+1 ;one more char pushed mov r0,r1 ;get ready for the divide clr r0 div #10.,r0 ;get remainder in r1 mov @sp,-(sp) ;move return address down add #60,r1 ;convert to ascii digit mov r1,2(sp) ;copy the digit into stack spot br 2$ ;do another char 1$: mov r2,r1 ;restore r1 mov (sp)+,r2 ;put the return address in r2 add #60,r0 ;convert last digit to ascii digit mov r0,-(sp) ;save the last digit movb state+1,-(sp) ;save the count of chars clrb state+1 ;?? jmp @r2 ;rts sort of ;set of routines for dealing with teco macro files ;routine to open an emfile, returns file handle in r0 emopen: sys s_open,emfil,0 ;open for read rts pc ;routine to read in a block, expects file handle in r0, returns count in r0 emread: sys s_read,embuf,100. ;read 100. bytes into embuf rts pc ;return actual number in r0 ;routine to close any emfiles open, expects file handle in r0 emclose:sys s_close ;close file specified in r0 rts pc ;routines to hack inferior processes runit: push ;need some registers 1$: incb waitlk ;from now on, we will be waiting for inferior sys s_fork ;create an inferior br new ;where the new process should begin bcc 2$ ;if there were no problems clrb waitlk ;re-enable quits for the moment mov #1,r0 ;we want to sleep for 1 sec sys s_sleep ;go away for a while br 1$ ;now try again 2$: mov r0,kidid ;id of our kid's process 3$: sys s_wait ;the father waits for the child here bcs 4$ ;no inferiors left cmp r0,kidid ;is this the kid we were waiting for? bne 3$ ;hmm, son of a bitch 4$: clrb waitlk ;no longer waiting for inferior jsr pc,intty ;reset the tty to teco mode movb r1,kidsta ;status of the child process swab r1 ;put error code in low byte of r1 movb r1,kiderr ;error code of child process pop ;clean up tst kidsta ;did kid finish ok? bne 5$ ;no, must have had an error ccc ;signal no errors rts pc 5$: sec ;signal an error rts pc ;the inferior process starts here new: sys s_signal,2,0 ;quit on command sys s_signal,3,0 ;break on command sys s_signal,16,0 ;restart on command jsr pc,clrtty ;go back to normal tty mode sys s_exec,kidnam,kidcmd ;execute command, should not come back mov #1,r0 ;the error code to return sys s_exit ;in case there was an error with exec .csect stack kidnam: .asciz "/bin/sh" ;execute shell command karg1: .asciz "-c" ;this option says take next arg and ex as cmd .even ;null terminated list of command pointers kidcmd: .word kidnam ;the command we are executing .word karg1 ;option to shell to execute next arg .word exbuf ;the command line to execute, set up by setfnm .word 0 .csect pgm ;routine to read a block of the file into the input buffer pinbuf prinit: push ;need register jsr pc,cloi ;close current input file if any tstb filnam ;are we trying to open null file? beq 1$ ;yes, it would open directory so error sys s_open,filnam,0 ;open file filnam for reading bcs 1$ ;if cannot mov r0,inpfh ;save file handle incb inpflg clr pincnt pop rts pc ;and quit 1$: pop ;clean up sec ;signal error rts pc ;routine to read chars from file and save in input buffer pri: dec pincnt ;is there anything to read? bge 2$ ;no mov #pinbuf,pinptr ;set up input pointer to start mov inpfh,r0 ;input file handle sys s_read,pinbuf,pinmax ;read pinmax bytes into pinbuf bcs 1$ ;if there was some read error mov r0,pincnt ;move count of how much we read bne pri ;got something, so return it 1$: jsr pc,cloi sec rts pc 2$: movb @pinptr,r0 inc pinptr clc rts pc ;routine to create a file for output. it is created with r/w access. ppinit: push ;need a register jsr pc,cloo ;close any currently open output files tstb filnam ;see is he is trying to open directory beq 1$ ;yes, error sys s_open,filnam,1 ;see if there is a file with that name bcc 1$ ;there is!!, better not clobber it with create sys s_creat,filnam,666 ;create filnam with r/w access bcs 1$ ;in case we have problems mov r0,oupfh ;save output file handle incb outflg ;signal that we have output file open pop ;clean up clc ;no errors rts pc 1$: pop ;clean up sec ;signal that we coulnd't create file rts pc ;routine to delete a file delit: sys s_unlink,filnam ;unlink filnam rts pc ;signals c if it can't ;routine to open a file for append appit: push ;need a register jsr pc,cloo ;close any currently open file tstb filnam ;trying to open a null file name? beq 1$ ;yes, it would open directory so error sys s_open,filnam,1 ;open filnam for writing bcs 1$ mov r0,oupfh ;save file handle incb outflg ;signal that we have output file open sys s_seek,0,2 ;seek to end of file to append bcs 1$ ;if something went wrong pop ;clean up ccc ;signal no errors rts pc 1$: pop ;clean up sec ;signal error rts pc ;routine to open a temporary file, for now, just opens file tmpit: jsr pc,ppinit ;open normal file rts pc ;routine to output one char to the output file ppo: movb r0,iotmp ;save the char in io temporary mov oupfh,r0 ;output file handle sys s_write,iotmp,1 ;write this one char bcs 1$ ;if something went wrong movb iotmp,r0 ;restore the char ccc ;no error rts pc 1$: movb iotmp,r0 ;restore the char sec ;signal error rts pc ;routine to output a block of text. uses the indirect write mechanism ;because the start and count will be variables. ppol: push ;need a register mov r1,w_ptr ;pointer to start of transfer mov r2,w_cnt ;pointer to end of block sub r1,w_cnt ;subtract start to get count of chars mov oupfh,r0 ;the output file handle sys s_indir,w_fnc ;do the write function indirectly bcs 1$ ;if something went wrong pop ;clean up ccc ;signal no errors rts pc 1$: pop ;clean up sec ;signal an error rts pc .csect stack .even w_fnc: sys s_write w_ptr: .word 0 w_cnt: .word 0 .csect pgm ;routine to close any open output file. clo: cloo: tstb outflg ;any output files open? beq 1$ ;nothing open, just return push ;need a register clrb outflg ;no longer anything open mov oupfh,r0 ;output file handle sys s_close ;close the file bcs 2$ ;if something went wrong pop ;clean up tstb bakflg ;did we have a backup file? beq 1$ ;no, just return jsr pc,unback ;backup the input file, rn tmp to filnm bcs 3$ ;if we couldn't 1$: ccc ;signal no errors rts pc 2$: pop ;clean up 3$: sec ;signal the error rts pc ;routine to close open input files cloi: tstb inpflg ;any input files open? beq 1$ ;nope just return clrb inpflg ;none open after this.. push ;get a register mov inpfh,r0 ;get the input file handle sys s_close ;close it bcs 2$ ;if something went wrong pop ;clean up 1$: ccc ;signal no errors rts pc 2$: pop ;clean up sec ;signal an error rts pc .csect stack namstr: .asciz "teco" tmpstr: .asciz ".tmp" bakstr: .asciz ".bak" .csect pgm ;routine to open bakfil for input, and teco.tmp for output where teco.tmp ;should be in the same directory as bakfil. backt: push ;need some registers mov #filnam,r0 ;ptr to source field mov #bakfil,r1 ;ptr to result field mov #50.,r2 ;size of result field jsr pc,movstr ;copy it source to result field bcs 1$ ;on overflows jsr pc,prinit ;now open it for input bcs 1$ ;if we couldn't jsr pc,savmod ;save the file mode mov #bakfil,r0 ;ptr to source mov #tmpfil,r1 ;ptr to destination mov #50.,r2 ;size of destination jsr pc,putdir ;copy directory part of source to destination mov #namstr,r0 ;pointer to string "teco" mov #tmpfil,r1 ;ptr to destination field mov #50.,r2 ;length of destination field jsr pc,concat ;concatenate teco.tmp to directory bcs 1$ ;if name overflows mov #tmpfil,r1 ;ptr to destination field mov #50.,r2 ;length of destination jsr pc,apdpid ;append the process id bcs 1$ ;if name overflows mov #tmpstr,r0 ;pointer to string ".tmp" mov #tmpfil,r1 ;pointer to destination mov #50.,r2 ;length of destination jsr pc,concat ;concatenate to name bcs 1$ ;if name overflows mov #tmpfil,r0 ;ptr to source mov #filnam,r1 ;ptr to destination mov #50.,r2 ;length of destination field jsr pc,movstr ;copy source to dest jsr pc,delit ;delete any instances of "teco.tmp" jsr pc,ppinit ;open file for output bcs 1$ ;if problems incb bakflg ;we have backup file pending pop ;clean up ccc ;signal no errors rts pc 1$: jsr pc,cloi ;close the input file pop ;clean up sec ;signal the error rts pc .csect stack .even statbf: .blkb 4 ;storage for the file status oldmod: .blkb 32. ;flag word is first two bytes .csect pgm ;routine to read the mode of a file savmod: sys s_stat,bakfil,statbf ;read the status of bakfil mov oldmod,newmod ;copy the mode (see unback for newmod) bic #170000,newmod ;we only want the relevant bits rts pc ;routine to append the process id as a decimal number to a field. ;expects pointer to field in r1, length of field in r2. sets c ;if the result overflows the field. clears c otherwise. apdpid: tstb (r1)+ ;find zero byte at end of destination beq 1$ ;found it sob r2,apdpid ;can do only this much sec ;signal error rts pc 1$: tstb -(r1) ;back up over zero byte push r5 ;need a register mov r2,r5 ;save r2 here, cause decoux clobbers r2 sys s_getpid ;get the process id into r0 jsr pc,decoux ;push char representation of r0 onto stack mov r5,r2 ;restore r2 movb (sp)+,r0 ;get a count of chars in the number bic #177400,r0 ;since we did a movb -(sp) 2$: dec r2 ;any space left? blt 3$ ;no movb (sp)+,(r1)+ ;copy a char into the field sob r0,2$ ;do it for this many chars pop r5 ;clean up ccc ;signal no errors rts pc 3$: tst r0 ;any chars left on stack? beq 4$ ;no tstb (sp)+ ;pop one sob r0,3$ ;one less char 4$: pop r5 ;clean up sec ;signal an error rts pc .csect stack .even chmod: sys s_chmod,bakfil newmod: .word 0 .csect pgm ;routine to rename the input file to filnam.bak and rename teco.tmp ;to filnam. unback: push ;need some registers mov #bakfil,r0 ;ptr to name of input file mov #filnam,r1 ;ptr to result field mov #50.,r2 ;size of result field jsr pc,putdir ;copy directory name of inp file to result bcs 1$ ;if it overflows cmp r2,#15. ;make sure we have room for file name blo 1$ ;not enough room mov #11.,r2 ;leave room for ".bak" extension mov #bakfil,r0 ;ptr to name of input file again jsr pc,putfil ;now copy the file name, truncate if too long mov #bakstr,r0 ;pointer to extension ".bak" mov #filnam,r1 ;ptr to destination field mov #50.,r2 ;size of desitation field jsr pc,concat ;concatenate to filename to give filnam.bak bcs 1$ ;couldn't... overflow error sys s_unlink,filnam ;delete any old copies of filnam.bak sys s_link,bakfil,filnam ;rename filnam to filnam.bak bcs 1$ ;problems... sys s_unlink,bakfil ;remove the link to filnam sys s_link,tmpfil,bakfil ;rename teco.tmp to filnam bcs 1$ ;problems... sys s_unlink,tmpfil ;remove the link to teco.tmp sys s_indir,chmod ;change mode of bakfil to original mode clrb bakflg ;we no longer have a backup pending pop ;clean up ccc ;signal no errors rts pc 1$: pop ;clean up sec ;signal error rts pc ;subroutines for hacking asciz strings for directories and filenames. ;routine to concatenate one string to another. head goes in r1, tail in r0, ;and length of result field in r2. tail is copied in head field. concat: tstb (r1)+ ;find the end of the head field beq 1$ ;got it sob r2,concat ;can only do this much sec ;signal error rts pc 1$: tstb -(r1) ;back up to zero byte, then fall into movstr ;routine to move an asciz string from one place to another. movstr: movb (r0)+,(r1)+ ;move from (r0) to (r1) beq 1$ ;found the end sob r2,movstr ;r2 has maximum length of result field sec ;signal error 1$: rts pc ;routine to move the directory part of a filename to another field ;as an asciz string. r0 points to filename, r1 to field, and r2 contains ;size of the field. putdir: push ;save ptr to beginning of directory 1$: tstb (r0)+ ;look for the end of the filename bne 1$ ;not there yet.. 2$: cmp r0,(sp) ;are we at the beginning yet? blos 5$ ;yes, must not be any filename cmpb -(r0),#'/ ;look for last / in filename bne 2$ ;not found yet 3$: inc r0 ;move to char after / push ;save this spot mov 2(sp),r0 ;reset pointer to start of filename 4$: movb (r0)+,(r1)+ ;move a char dec r2 ;one less char of room ble 6$ ;no room, overflow error cmp r0,(sp) ;are we done? blo 4$ ;no, still some left pop ;clean up a little 5$: dec r2 ;room for zero? blt 7$ ;no, signal error clrb (r1) ;put a zero at the end pop ;clean up the rest ccc ;signal no errors rts pc 6$: pop ;clean up a little 7$: pop ;clean up the rest sec ;signal error rts pc ;routine to copy just the filename proper from a pathname. putfil: push ;save pointer to pathname 1$: tstb (r0)+ ;look for the end of the pathname bne 1$ ;not there yet 2$: cmp r0,(sp) ;are we at beginning? blos 3$ ;yes cmpb -(r0),#'/ ;else look for last / bne 2$ ;not found yet inc r0 ;skip the / 3$: add #2,sp ;clean up jsr pc,movstr ;put in as much of the name as will fit clrb -(r1) ;make sure there is a zero at the end rts pc ;routines to get a filename from command string, put in qregister ", ;then copy to filnam or emfil. setfnm: mov #filnam,r2 ;put the filename in field filnam br setfn1 setfn: mov #emfil,r2 ;put the filename in field emfil setfn1: mov r2,-(r6) ;pointer to result field movb atsw,-(r6) jsr r7,setlim ;copy name str into qreg ", advance r1 past it movb (r6)+,atsw ;setlim clobbers to delimiter $ in atsw was 0 mov r1,-(r6) ;save pointer to command string jsr r7,qset ;set r0 to start of name string, r2 to end mov qlen,r1 ;length of namestr, set by qset cmp r1,#44. ;was it less than or equal to 44 chars? ble 5$ ;yes, it's ok mov #44.,r1 ;else set it to 44. as maximum 5$: mov 2(r6),r2 ;restore pointer to result field 1$: cmpb @r0,#33 ;$ marks the end of the field, are we there? beq 2$ ;yes, done cmpb (r0),#40 ;shouldn't be any spaces in filenames blos .badf ;found a space or control char, bad file name cmpb (r0),#'Z!40 ;make sure no brackets or other strangos bhi .badf ;found one movb (r0)+,(r2)+ ;move chars to result field sob r1,1$ ;there are r1 of them 2$: clrb (r2)+ ;now put a zero at the end of the field mov (r6)+,r1 ;this puts pointer at end tst (r6)+ ;pop length rts pc .badf: jmp cherwe ;routine to copy a exec string from command string into qregister " and exbuf. ;expects r1 pointing to start of string and returns with r1 pointing after. setexc: movb atsw,-(sp) ;save the atsw jsr pc,setlim ;copy string into qreg " and advance r1 movb (sp)+,atsw ;restore the atsw push ;need a register jsr pc,qset ;set r0 to start of string, r1 to length mov qlen,r1 ;get the length of the string cmp r1,#exblen ;is it too big? bhis 3$ ;yes, error mov #exbuf,r2 ;pointer to destination field 1$: cmpb (r0),#33 ;33 marks the end, are we there? beq 2$ ;yup, stop here movb (r0)+,(r2)+ ;else copy next byte sob r1,1$ ;keep at it 2$: clrb (r2)+ ;put a zero at the end pop ;restore pointer to command string ccc ;signal no errors rts pc 3$: pop ;clean up sec ;signal an error rts pc .csect stack .even ptrace: sys s_ptrace ;ptrace to stop my process ie request 0 .word 0 ;request number ptpid: .word 0 ;process id of my process .word 0 ;address always 0 .csect pgm ;signal handling routines ;handle quits (^q) quit: tst waitlk ;are we waiting for inferior? bne 1$ ;yes, ignore signal completely push r0 ;need a register mov #21,r0 ;^Q jsr pc,tyo ;type it jsr pc,crlf ;make it pretty jsr pc,crlf jsr pc,suspend ;suspend my process pop r0 mov #cont,(sp) ;abort command and restart at command level 1$: sys s_signal,2,quit ;re-establish the handler rtt ;suspend processing for suspend command suspend:push r0 ;need a register jsr pc,clrtty ;reset to normal tty mode sys s_getpid ;get my process into r0 mov r0,ptpid ;save it in the ptrace call mov #1,r0 ;this says actually stop me now sys s_indir,ptrace ;suspend my process ! jsr pc,intty ;go back to teco tty mode pop r0 ;clean up rts pc ;handle breaks (^b) break: tst waitlk ;are we waiting for inferior? bne 1$ ;yes, ignore signal completely push r0 ;need a register jsr pc,resetobf ; make sure buffer is empty mov #2,r0 jsr pc,tyo ;type the ^b pop r0 mov #cont,(sp) ;return here after interrupt 1$: sys s_signal,3,break;re-establish the handler rtt ;handle special interrupts (^a) specl: tst waitlk ;are we waiting for inferior? bne 1$ ;yes, ignore signal completely push r0 tstb debsw ;turning it on? bne 3$ ;if not mov #12,r0 ;queue a NL for output jsr pc,ctyo 3$: mov #1,r0 jsr pc,tyo ;type the ^a comb debsw ;complement the debug switch br 2$ 1$: push r0 ;save r0 2$: sys s_signal,16.,specl;re-establish the handler pop r0 rti ;routine to read system and user times of my process. timer: sys s_times,put ;read times rts pc ;routine to generate a command to em the user's teco.init file from his ;login directory. uses global routine _getcd to get his command directory ;from the usr_info file. .globl _getcd .csect stack inithd: .asciz "em" inittl: .ascii "/.teco.init" .byte 33 .byte 33 .byte 0 .csect pgm getinit:mov #inithd,r0 ;get pointer to head string, dest ptr in r1 mov #50.,r2 ;length of the destination field jsr pc,movstr ;copy in the head string dec r1 ;back up to the zero push ;save this location mov r1,-(sp) ;pointer of where to put login directory jsr pc,_getcd ;copy in the the login directory tst (sp)+ ;pop the argument pop ;restore pointer to start of login directory tstb (r1) ;if zero, then no command directory was found beq 1$ ;zero, so use working directory mov #inittl,r0 ;pointer to tail string mov #50.,r2 ;length of the field jsr pc,concat ;concatentate tail string clz ;signal no error rts pc 1$: sez ;signal an error rts pc .csect stack .even b_fnc: sys s_break ;break system call, for indirect executing b_adr: .word 0 ;the argument to break memmsg: .ascii " bytes]" ;message to type when we get more memory .byte 12 ;finish with crlf memmen: ;just mark end of memmsg kmem: .asciz "/dev/kmem" ;path name of kmem pseudo file .csect pgm ;routine to initialize the buffer and associated variables .globl _end,_etext,_edata bfinit: mov #_end,enbf ;the address of _end should be end of data mov enbf,eoa ;our initial end of all add #memint,enbf ;what our initial break address should be mov enbf,b_adr ;this is what we want for a break sys s_indir,b_fnc ;execute the break call indirectly mov enbf,limbf ;limit of buffer too mov enbf,enbf.4 ;almost end of buffer marker sub #6,enbf.4 ;it's 6 words from real end tst (eoa)+ ;leave a zero at the bottom mov eoa,tbufx ;start of qregister area mov eoa,boq ;end of qregister area tst (eoa)+ ;leave a word to separate qregister from buf mov eoa,bob ;start of buffer mov eoa,cptr ;cursor mov eoa,cend ;command end mov eoa,cmdloc ;location of command mov #memlim,stklim ;stack should not grow into buffer area rts pc ;routine to get more memory for buffer. call with current end of memory ;in r0. return amount additional we got in r0 getmem: add #meminc,r0 ;we want this much more cmp r0,#memlim ;would this put us past end bhis 1$ ;yes, don't do it mov r0,b_adr ;we want the new break here sys s_indir,b_fnc ;execute break call indirectly bcs 1$ ;we couldn't get that much push ;need some registers mov #'[,r0 ;type a [ jsr pc,tyo mov b_adr,r0 ;our current break jsr pc,decout ;type how much core we have now mov #memmsg,r1 ;pointer to our memory message mov #memmen,r2 ;end of our memory message jsr pc,typer ;type it pop ;clean up mov #meminc,r0 ;we got this much more rts pc 1$: clr r0 ;tell caller we couldn't get any more rts pc ;routine to process any command arguments that we were given. ;see writeup in unix manual for exec, for explanation of this. getarg: cmp 2(sp),#1 ;were we given any arguments? beq 1$ ;no, just the name "teco" mov 6(sp),r0 ;this should be pointer to arg #1 string mov #savfil,r1 ;pointer to result field mov #50.,r2 ;size of result field jsr pc,movstr ;copy it 1$: rts pc ;program begins here. set up stack and process arguments start: mov sp,stksv ;put our initial stack pointer here sys s_signal,2,1 ;ignore quits for the moment bit #1,r0 ;was it being ignored? bne 1$ ;yes sys s_signal,2,quit ;establish the quit handler 1$: sys s_signal,3,1 ;ignore breaks for now bit #1,r0 ;was it being ignored? bne 2$ ;yes sys s_signal,3,break;establish the break handler 2$: sys s_signal,16.,1 ;ignore special interrupts bit #1,r0 ;was it being ignored? bne 3$ ;yes sys s_signal,16.,specl;establish the special interrupt handler 3$: clr r0 sys s_nice ;set our priority jsr pc,getarg ;copy the argument string to savfil jsr pc,bfinit ;initialize buffer variables jmp teco ;start up teco proper ;program comes here to leave teco finish: jsr pc,clrtty tstb bakflg ;do we have any tmpfil around? beq 1$ ;no, everything's ok sys s_unlink,tmpfil ;get rid of the tmpfil 1$: clr r0 ;exit code 0 sys s_exit ;exit .end start