;*****************************************************************************
; Filename: TEETO.M68                                       Date: 8/7/90
; Category: UTIL         Hash Code: 447-557-506-065      Version: 1.0(100)
; Initials: ROBB/AM      Name: Erik Petrich
; Company: Microlink Computing Systems, Inc.       Telephone #: 4053218333
; Related Files:
; Min. Op. Sys.: NONE                          Expertise Level: ADV
; Special:
; Description: This program will tee standard output to both a terminal as
; well as a file.
;*****************************************************************************
;PROGRAM        Description
; TeeTo          Tee standard output to a file also
;
; Copyright 1990 Microlink Computing Systems, Inc.
;
;Edit History:
;[100] 16 April 1990 12:13      Written by Erik Petrich
;      07 August 1990 03:20     Uploaded to AMUS network
;       Lots of small bugs suddenly appear after this event. (of course!)
;[101] 09 August 1990 03:46     Edited by Erik Petrich
;       Forced output buffer size to be small, consolidated TRMDEF reset.
;[102] 09 August 1990 04:02     Edited by Erik Petrich
;       Changed to allow abort during sleep.
;[103] 09 August 1990 04:23     Edited by Erik Petrich
;       Undid part of edit #101 -- leave output buffer size alone.
;[104] 09 August 1990 05:07     Edited by Erik Petrich
;       Fixed TEEOUT not to display the TCRT toggle code.
;[105] 10 August 1990 00:02     Edited by Erik Petrich
;       Changed SAVEW D0-D2 to SUB #6,A7 since SUB is faster and
;       was what all I really needing anyway.
;[106] 15 August 1990 09:43     Edited by Erik Petrich
;       Changed RTE to a LSTS/RTN to avoid problems on 68010/20/30
;
;
;
; This was written out of frustration that AMOS/L doesn't support
; redirectable input/output or pipes, like some "other" operating systems
; do. This takes care of standard output, which is what I mostly needed;
; perhaps I'll get around to redirectable input & pipes later.
;
; Some examples:
;
;       TEETO /O DIRECT.LST DIR         ; This is equivilent to DIR =
;
; The same general pattern can be used with programs that don't support
; disk output.
;
;       TEETO /O DIRECT.LST ARCDIR YAZ  ; Saves the directory listing of
;                                       ; archive YAZ into DIRECT.LST
;
; TeeTo is also useful for putting examples of program use into a file
; so that it can be more easily added to the documentation.
;
;       TEETO EXAMPL.TXT PROGRAM        ; output goes both to file & terminal
;
; And when you really get serious about your game play, make a recording of
; it so decide how to do better next time.
;
;       TEETO /T PONG.SCR PONG          ; saves TCRT sequences too
;       TYPE PONG.SCR                   ; instant replay
;


       SEARCH  SYS
       SEARCH  SYSSYM
       SEARCH  TRM

       VMAJOR = 1.
       VMINOR = 0.
       VEDIT  = 106.

       .ofini
       .ofdef  ourdev,2        ; TeeTo file device
       .ofdef  ourdrv,2        ; TeeTo file drive
       .ofdef  ourppn,2        ; TeeTo file ppn
       .ofdef  oldcmz,2        ; Starting command file size
       .ofdef  oldtdv,4        ; Pointer to original terminal driver
       .ofdef  oldobs,4        ; Old output buffer size                [101]
       .ofdef  ourjcb,4        ; Pointer to our JCB
       .ofdef  ourdvt,4        ; Pointer to TeeTo file device table entry
       .ofdef  outfil,d.ddb    ; TeeTo file DDB
       .ofdef  filbuf,512.     ; Buffer for TeeTo file
       .ofdef  datbuf,8192.    ; Buffer that the TEETO.TDV drive writes to
       .ofdef  filler,8.       ; Buffer safety factor
       .ofdef  bufsiz,4        ; Bytes in use in DATBUF
       .ofdef  abortd,2        ; non-zero indicates TEETO.TDV has aborted
                               ; its host job and spliced in
       .ofdef  oldpc,4         ; host job's PC counter
       .ofdef  oldsr,2         ; host job's status register
       .ofdef  notcrt,2        ; no tcrt sequences switch
       .ofdef  prtcrt,2        ; currently processing tcrt
       .ofdef  notty,2         ; no output to screen
       .ofdef  column,2        ; current logical column
       .ofsiz  impsiz

       phdr    -1,0,ph$reu
       byp
       lin
       jne     start
usage:  typecr  <Copyright 1990 Microlink Computing Systems, Inc.>
       crlf
       typecr  <TEETO {options} outputfile command {command_parameters}>
       ttyi
       byte    13.
       ascii   /Options:/
       byte    13.
       ascii   %  /T  -  Include TCRT sequences in file%
       byte    13.
       ascii   %  /O  -  Omit output to screen%
       byte    13.,13.,0
       even
       exit

start:  lea     a5,impure
       movw    #<impsiz/2>-1,d7
10$:    clrw    (a5)+
       dbf     d7,10$
       lea     a5,impure

       jobidx
       mov     a6,ourjcb(a5)
       movw    jobdev(a6),ourdev(a5)
       movw    jobdrv(a6),ourdrv(a5)
       movw    jobusr(a6),ourppn(a5)
       movw    jobcmz(a6),oldcmz(a5)
       mov     jobtrm(a6),a6
       mov     t.tdv(a6),oldtdv(a5)
       mov     t.obs(a6),oldobs(a5)            ;[101]
       call    getopt
       fspec   outfil(a5),TXT
       lea     a1,filbuf(a5)
       mov     a1,d.buf+outfil(a5)
       orb     #d$ini,d.flg+outfil(a5)
       mov     #512.,d.siz+outfil(a5)
       call    chkdev
       call    fnddvt
       lookup  outfil(a5)
       bne     20$
       dskdel  outfil(a5)
20$:    openo   outfil(a5)
       type    <(TeeTo >
       pfile   outfil(a5)
       typecr  < open)>
       byp
       lin
       beq     30$
       save    a2
       orb     #d$erc!d$byp,d.flg+outfil(a5)
       call    watoip
       call    chgtdv
       rest    a2
25$:    save    a5
       amos
       rest    a5
       mov     jobcur,a6
       tstb    @a6
       bmi     30$
       movw    oldcmz(a5),d7
       cmpw    d7,jobcmz(a6)
       bhis    30$

       mov     jobcur,a6
       movb    jobcms(a6),d7
       andb    #c.trc,d7
       beq     27$
       ttyl    jobprm(a6)

27$:    mov     jobcur,a6
       orb     #c.mon,jobcms(a6)
       mov     jobtrm(a6),a6
       andw    #^C<t$imi>,@a6
       kbd     30$
       mov     jobcur,a6
       andb    #^C<C.mon>,jobcms(a6)
       br      25$

30$:    call    watoip
;       mov     jobcur,a6               ;[101]
;       mov     jobtrm(a6),a6           ;[101]
;       mov     oldtdv(a5),t.tdv(a6)    ;[101]
       call    rsttdv                  ;[101]
       incw    abortd(a5)
35$:    call    wrtbuf
       tstw    bufsiz(a5)
       bne     35$
       andb    #^C<d$byp>,d.flg+outfil(a5)
       close   outfil(a5)
       type    <(TeeTo >
       pfile   outfil(a5)
       typecr  < closed)>
       exit

watoip:
30$:    mov     jobcur,a6
       mov     jobtrm(a6),a6
35$:    movw    @a6,d7
       andw    #t$oip,d7
       bne     35$
       rtn

chkdev: movw    d.dev+outfil(a5),d7
       cmpw    d7,#[MEM]
       jeq     baddev
       cmpw    d7,#[RES]
       jeq     baddev
       cmpw    d7,#[TRM]
       beq     10$
       rtn
10$:    movw    d.fil+outfil(a5),d7
       mov     jobcur,a6
       mov     jobtrm(a6),a6
       cmp     d6,-8.(a7)
       beq     20$
       rtn
20$:    typecr  <%Can't TeeTo your own terminal>
       exit
baddev: type    <%Can't TeeTo MEM: or RES:>
       exit

fnddvt: jobidx
       movw    d.dev+outfil(a5),d1
       bne     10$
       movw    jobdev(a6),d1
10$:    movw    d.drv+outfil(a5),d2
       cmpw    d2,#-1
       bne     20$
       movw    jobdrv(a6),d2
20$:    mov     devtbl,a0
30$:    cmpw    d1,dv.dev(a0)
       bne     40$
       cmpw    d2,dv.unt(a0)
       bne     40$
       mov     @a0,a0
       mov     a0,d7
       bne     30$
       typecr  <?Device not found>
       exit
40$:    mov     a0,ourdvt(a5)
       rtn

getopt: setw    notcrt(a5)
5$:     byp
       movb    @a2,d1
       cmpb    d1,#'/
       beq     10$
       rtn
10$:    inc     a2
       movb    (a2)+,d1
       cmpb    d1,#'T
       bne     20$
       clrw    notcrt(a5)
       br      5$
20$:    cmpb    d1,#'O
       bne     30$
       setw    notty(a5)
       br      5$
30$:    typecr  <Invalid option>
       exit


define  fixjmp  addr,ofs
       lea     a3,addr
       lea     a6,ofs(a1)
       movw    #^h4ef9,(a3)+   ; jmp long absolute opcode
       mov     a6,(a3)+        ; operand
       endm

chgtdv: save    a0-a3
       mov     oldtdv(a5),a1
       lea     a0,newtdv
       mov     -8.(a1),-8.(a0)         ; copy link to next TDV
       mov     -4(a1),-4(a0)           ; copy name
       movw    td.typ(a1),td.typ(a0)
       movw    td.imp(a1),td.imp(a0)
       movw    td.row(a1),td.row(a0)
       mov     td.flg(a1),td.flg(a0)
       fixjmp  jmpinp,td.inp
       fixjmp  jmpech,td.ech
       fixjmp  jmpout,td.otp
       fixjmp  jmpcrt,td.crt
       fixjmp  jmpini,td.ini
       fixjmp  jmptch,td.tch
       jobidx
       mov     jobtrm(a6),a6
10$:    tst     t.obx(a6)                       ;[101]
       beq     20$                             ;[101]
       save    a6                              ;[101]
       sleep   #100.                           ;[101]
       rest    a6                              ;[101]
       br      10$                             ;[101]
20$:
;       mov     #2,t.obs(a6)                    ;[101] [103]
       mov     a0,t.tdv(a6)
       rest    a0-a3
       rtn

;
; Starting here is the terminal driver that gets attached to the current
; job. It intercepts all output, deals with it, and then passes control back
; to the original terminal driver.
;
       lword   0
tdvnam: rad50   /TEETO /
newtdv: word    0
       br      jmpinp
       br      teeout
       br      jmpech
       br      teecrt
       br      jmpini
       word    0
       byte    0
       byte    0
       lword   0
       br      teetch

jmpinp: blkw    3
jmpech: blkw    3
jmpcrt: blkw    3
jmpini: blkw    3
teetch: lea     a2,impure
       mov     oldtdv(a2),a2
jmptch: blkw    3


teecrt: call    tcrtgl
       call    jmpcrt
       call    tcrtgl
       rtn

teeout: save    a0-a6,d0-d7
       lea     a5,impure
       tstw    notcrt(a5)
       beq     3$
       cmpb    d1,#255.
       bne     2$
       movw    prtcrt(a5),d7   ; toggle TCRT processing status
       xorw    #1,d7
       movw    d7,prtcrt(a5)
;       jmp     90$             ;                                       [104]
       jmp     igntty          ; don't display the TCRT toggle code    [104]
2$:     tstw    prtcrt(a5)
       jne     90$
3$:     lea     a1,datbuf(a5)
       mov     bufsiz(a5),d0
       cmp     d0,#8191.
       jhis    90$             ; no more room in buffer... we have failed.
                               ; bummer dude!

       incw    column(a5)
       cmpb    d1,#13.
       bne     10$
       clrw    column(a5)
10$:
       cmpb    d1,#9.          ; convert tabs to spaces
       bne     50$             ; because TRMSER will do it also
       movb    #32.,d1
       tstw    notty(a5)
       beq     50$             ; nothing special... TRMSER will take care
                               ; of the rest of the spacing
       cmp     d0,#8191.-10.
       bhis    50$             ; can't tab-- no buffer space
       add     d0,a1
20$:    movb    d1,(a1)+
       inc     d0
       movw    column(a5),d7
       andw    #7,d7
       cmpw    d7,#1
       beq     80$
       incw    column(a5)
       br      20$

50$:    add     d0,a1
       movb    d1,@a1
       inc     d0
80$:    mov     d0,bufsiz(a5)
       cmp     d0,#512.
       jlos    90$             ; nothing of interest yet...
       call    tryabt
90$:    tstw    notty(a5)
       bne     igntty
       lcc     #ps.n
       rest    a0-a6,d0-d7
jmpout: blkw    3

igntty: mov     bufsiz(a5),d7
       cmp     d7,#512.
       bhi     10$
       lcc     #ps.z
       rest    a0-a6,d0-d7
       rtn
10$:    rest    a0-a6,d0-d7
       clrb    d1              ; output a null in an attempt to
       lcc     #ps.n           ; suspend the host job in tow
       br      jmpout

; try to abort the job that originally ran TeeTo, so we can steal a valid
; context to do file operations with. (can't do disk i/o in a terminal
; driver, normally)
tryabt: tstw    abortd(a5)
       jne     90$             ; already aborted, just have to wait
       mov     ourjcb(a5),a4
       movw    jobsts(a4),d7
       andw    #j.iow!j.exw!j.smw!j.msg!j.fil!j.lok!j.sus,d7
       jne     90$             ; don't abort during these states
       movw    jobsts(a4),d7
       andw    #j.tow!j.tiw!j.slp,d7   ;[102]
       jeq     90$             ; need to be in tow, tiw, or slp state
       tst     ddbchn
       bne     90$             ; DONT'T abort in the middle of disk i/o
       tst     loksem
       bmi     90$             ; don't abort in LOKSER
       mov     ourdvt(a5),a6
       tst     dv.sem(a6)
       bmi     90$             ; make sure device would be available
       mov     dv.bmp(a6),a6
       movw    bm.sts(a6),d7
       andw    #bm$lok,d7
       bne     90$             ; make sure bitmap is not locked
       mov     jobcur,a3
       cmp     a3,a4
       beq     90$             ; can't abort if currently running

       mov     jobrnq+10(a4),a1        ; index saved system stack pointer
       mov     76(a1),oldpc(a5)        ; save the job's program counter
       movw    74(a1),oldsr(a5)        ; and status register, so the running
                                       ; process can be resumed after we're
                                       ; done with it.
       lea     a6,iwrtbf
       MOV     A6,76(A1)       ; chain to our buffer write routine
       andw    #^h7fff,74(a1)  ; disable trace
       orw     #^h2000,74(a1)  ; set supervisor bit
       incw    abortd(a5)
90$:    rtn

iwrtbf: sub     #4,a7           ; reserve space for RTN [106]
;       sub     #6,a7           ; reserve space for RTE info    [105]
;       savew   d0-d2           ; [105]
       save    a0-a6,d0-d7
; reverify that it's really ok to go ahead with this.
; (paranoid mode on)
       lea     a5,impure
       tst     ddbchn
       bne     10$
       tst     loksem
       bmi     10$
       mov     ourdvt(a5),a6
       tst     dv.sem(a6)
       bmi     10$             ; make sure device would be available
       mov     dv.bmp(a6),a6
       movw    bm.sts(a6),d7
       andw    #bm$lok,d7
       bne     10$             ; make sure bitmap is not locked
; (paranoid mode off)

       call    wrtbuf          ; save that buffer!
10$:
;       movw    oldsr(a5),60.(a7)       ; [106]
;       mov     oldpc(a5),62.(a7)       ; [106]
       mov     oldpc(a5),60.(a7)       ; [106]
       lsts    oldsr(a5)               ; [106]
       decw    abortd(a5)
       rest    a0-a6,d0-d7
;       rte                             ; [106]
       rtn                             ; [106]

wrtbuf: save    d0,d1,a1
       cmpw    abortd(a5),#1
       bne     20$
       clr     d0
       tst     bufsiz(a5)
       beq     20$
       mov     jobcur,a6
       pushw   jobdev(a6)
       pushw   jobdrv(a6)
       pushw   jobusr(a6)
       movw    ourdev(a5),jobdev(a6)
       movw    ourdrv(a5),jobdrv(a6)
       movw    ourppn(a5),jobusr(a6)
       lea     a1,datbuf(a5)
10$:    movb    (a1)+,d1
       filotb  outfil(a5)
       jne     30$
       inc     d0
       cmp     d0,bufsiz(a5)
       blo     10$
15$:    clr     bufsiz(a5)
       mov     jobcur,a6
       popw    jobusr(a6)
       popw    jobdrv(a6)
       popw    jobdev(a6)
20$:    rest    d0,d1,a1
       rtn

30$:
;       mov     jobcur,a6               ;[101]
;       mov     jobtrm(a6),a6           ;[101]
;       mov     oldtdv(a5),t.tdv(a6)    ;[101]
       call    rsttdv                  ;[101]
       typecr  <(device error, TeeTo halted)>
       jmp     15$

; output the TCRT toggle code
;
tcrtgl: save    d1,a5
       lea     a5,impure
       tstw    notcrt(a5)
       beq     10$
       movb    #255.,d1
       tty
10$:    rest    d1,a5
       rtn

rsttdv: mov     jobcur,a6               ;[101]
       mov     jobtrm(a6),a6           ;[101]
       mov     oldtdv(a5),t.tdv(a6)    ;[101]
       mov     oldobs(a5),t.obs(a6)    ;[101]
       rtn

impure: blkb    impsiz
       even

       end