        title term

; edit history:
;     7-DEC-85  ORIGINAL        BGP
;    20-FEB-86  CORRECTED FOR ERROR IN PUTMOD IN MSXV9000     (BGP)
;    22-FEB-86  ADDED CODE TO SWALLOW ANSI ESCAPE SEQUENCES   (BGP)
;

        public  term, lclyini           ; entry points
        include mssdef.h

SEG_CRTC EQU    0E800H                  ; segment for crt controller
OFF_CRTC EQU    0                       ; offset for crt controller

modfrm  struc                           ; format of mode line
        db      ' Esc chr: '
m_echr  db      2 dup (?)
        db      ', Port: '
m_prt   db      1 dup (?)
        db      ', Speed: '
m_baud  db      4 dup (?)
        db      ', Parity: '
m_par   db      4 dup (?)
        db      ', Echo: '
m_echo  db      3 dup (?)
        db      ', Type '
m_hlp   db      2 dup (?)
        db      '? for Help'
        db      ' $'                    ; must be dollar terminated
modfrm  ends

DATAS   segment public 'datas'

; stuff for screen routines
flags   db      ?                       ; status flags...
flags1  db      0                       ; internal flags.
prtscr  equ     80h                     ; print screen pressed
inited  equ     08h                     ; been here before...
esc_ch  db      ?
argadr  dw      ?                       ; address of arg blk
crt_cols db     ?
crt_lins db     ?
modbuf  modfrm  <>                      ; mode line buffer
; routine to call for captured output
captrtn dw      ?
oldcur  dw      0                       ; save'd cursor position
; some static data for mode line
unkbaud db      'Unk '                  ; must be 4 chars...
baudn   db      '45.5'
        db      '  50'
        db      '  75'
        db      ' 110'
        db      ' 135'
        db      ' 150'
        db      ' 300'
        db      ' 600'
        db      '1200'
        db      '1800'
        db      '2000'
        db      '2400'
        db      '4800'
        db      '9600'
        db      '19.2'
        db      '38.4'
baudnsiz  equ   16                      ; # of baud rates known (tbl size / 4)
parnams db      'Even'
        db      'Mark'
        db      'None'
        db      'Odd '                  ; must be 4 chars
        db      'Spc '
offmsg  db      'Off'
onmsg   db      'On '
lclmsg  db      'Lcl'
remmsg  db      'Rem'

portno  db      ?

screen  dw      1920 dup (?)
curloc  db      2 dup (?)               ; column,row
scrseg  dw      0F000H
rptcur  db      27,'n$'                 ; request cursor location
escseq  db      0                       ; ANSI escape sequence indicator
                                        ; 0=none, 1=escape, 2=[

DATAS   ends

CODE    segment public                  ; code segment
        extrn   prtchr:near,outchr:near,sendbr:near
        extrn   putmod:near,clrmod:near,cmblnk:near,poscur:near
        assume  cs:code,ds:datas,es:datas

; do initialization local to this module...

LCLYINI proc    near
        ret
LCLYINI endp

; We need to save the arguments to TERM where they are a little more
; accessible than in the way they were passed

ARGINI  proc    near                    ; read passed arguments
        mov     bx,argadr               ; base of argument block
        mov     al,[bx].flgs            ; get flags
        and     al,capt+emheath+havtt+trnctl+lclecho+modoff+lnwrap
        mov     flags,al                ; mask for allowable and save
        and     flags1,not (prtscr)     ; these are allowable
                                        ; (others remain).
        mov     al,[bx].prt
        cmp     al,portno               ; using same port?
        je      argin1                  ; yes, go on
        and     flags1,not inited       ; else re-init stuff
argin1:
        mov     portno,al               ; update port number
        mov     al,[bx].cols
        mov     crt_cols,al
        mov     al,[bx].rows
        mov     crt_lins,al             ; init # of rows and cols
        mov     ax,[bx].captr
        mov     captrtn,ax              ; buffer capture routine
        mov     al,[bx].escc
        mov     esc_ch,al
        ret                             ; that's it
ARGINI  endp

; We need to generate the mode line and output it

MODLIN  proc    near                    ; turn on mode line
        push    ds
        pop     es                      ; make sure es is correct
        mov     al,esc_ch
        mov     modbuf.m_echr,' '       ; first char is initial space
        mov     modbuf.m_hlp,' '        ; goes here too.
        cmp     al,32                   ; printable?
        jnb     modl1                   ; yes, keep going
        add     al,40h                  ; made printable
        mov     modbuf.m_echr,'^'       ; note control char
        mov     modbuf.m_hlp,'^'
modl1:
        mov     modbuf.m_echr+1,al      ; fill in character
        mov     modbuf.m_hlp+1,al
        mov     bx,argadr               ; get argument block
        mov     al,[bx].baudb           ; get baud bits
        mov     si,offset unkbaud       ; assume unknown baud
        cmp     al,baudnsiz             ; too big?
        jnb     modl2                   ; yes, use default
        mov     cl,2                    ; each is 4 bytes long
        shl     al,cl
        mov     ah,0
        add     ax,offset baudn
        mov     si,ax
modl2:
        mov     cx,size m_baud          ; length of baud space
        mov     di,offset modbuf.m_baud
        rep     movsb                   ; copy in baud rate
        mov     al,[bx].parity          ; get parity code
        mov     cl,2                    ; each is 4 bytes long...
        shl     al,cl
        mov     ah,0
        add     ax,offset parnams       ; names of parity settings
        mov     si,ax
        mov     cx,4                    ; each is 4 long
        mov     di,offset modbuf.m_par
        rep     movsb
        mov     si,offset remmsg        ; Assume remote echoing.
        test    flags,lclecho           ; Is remote side echoing?
        jz      modl4                   ; Yes, keep going
        mov     si,offset lclmsg        ; Else it's local echoing.
modl4:
        mov     cx,3                    ; size of on/off
        mov     di,offset modbuf.m_echo
        rep     movsb
        mov     al,'1'
        cmp     portno,1                ; port 1?
        je      modl5                   ; yes, keep going
        mov     al,'2'
modl5:
        mov     modbuf.m_prt,al         ; fill in port number
        mov     dx,offset modbuf        ; where it is
        call    putmod
        ret
MODLIN  endp

; This is the entry point for terminal emulation

TERM    proc    near                    ; terminal emulator entry point
        mov     argadr,ax               ; save argument ptr
        push    es                      ; save caller's extra segment address
        mov     ax,seg datas
        mov     es,ax
        call    argini                  ; init options from arg address
        test    flags1,inited           ; have we run yet?
        jz      term1                   ; no, forget this part
        call    restscr                 ; restore screen
        jmp     term2
term1:
        call    cmblnk                  ; clear it off
term2:
        or      flags1,inited           ; remember we've run already.
        call    clrmod                  ; empty mode line
        test    flags,modoff            ; is mode line disabled?
        jnz     lp                      ; yes, skip it
        call    modlin                  ; turn on mode line
lp:
        call    portchr                 ; char at port?
         jnc    chkinp                  ; no, keep going
; we want to swallow any ANSI escapes that come for now
;    they are all of the form ESC [ nn ; nn ; nn ; nn a
;    where n is a numeric character, and a is a non-numeric character
        cmp     escseq,0                ; escape sequence in progress?
        jne     eat_esc_seq             ; yes
        cmp     al,ESC                  ; got an escape?
        jne     no_esc                  ; no
        mov     escseq,1                ; yes, flag it
        jmp     chkinp
eat_esc_seq:
        cmp     escseq,1                ; got [?
        jne     eat_esc_seq2            ; yes
        cmp     al,'['
        je      eat_esc_seq1
        push    ax                      ; not [, print esc and char
        mov     al,ESC
        call    outtty                  ; send the escape
        pop     ax                      ; and the following character
        mov     escseq,0                ; no escape sequence
        jmp     no_esc
eat_esc_seq1:
        mov     escseq,2                ; flag [
        jmp     chkinp
eat_esc_seq2:
        cmp     al,';'                  ; check for terminator
        je      chkinp
        cmp     al,'0'
        jl      end_esc
        cmp     al,'9'
        jg      end_esc
        jmp     chkinp                  ; no terminator, keep eating
end_esc:
        mov     escseq,0                ; end of sequence
        jmp     chkinp
no_esc:
        call    outtty                  ; print on terminal
chkinp:
        mov     ah,DCONIO               ; Get it with no checking
        mov     dl,0FFH
        int     DOS
        cmp     al,0                    ; anything there?
        je      lp                      ; no...
        cmp     al,esc_ch               ; escape character?
        je      quit                    ; yes, stop here
        call    outprt
        jmp     chkinp                  ; and keep going
quit:
        call    clrmod                  ; erase mode line
        call    savescr                 ; save screen
        mov     al,flags
        mov     bx,argadr
        mov     [bx].flgs,al            ; update flags in arg block
        pop     es                      ; restore segment register
        ret                             ; and return to caller
TERM    endp

; Save the screen so we can restore it

SAVESCR proc    near
        mov     dx,offset rptcur
        mov     ah,PRSTR
        int     DOS
        mov     ah,CONINQ               ; input no check
        int     DOS                     ; gets the ESC
        int     DOS                     ; gets the Y
        int     DOS
        sub     al,32
        mov     curloc+1,al             ; this is line number
        int     DOS
        sub     al,32
        mov     curloc,al               ; this is column number
        mov     bx,SEG_CRTC             ; where crt controller is
        mov     es,bx
        mov     bx,OFF_CRTC
        mov     byte ptr es:[bx],12
        mov     ah,es:1[bx]
        and     ah,7                    ; only want bottom 3 bits
        mov     byte ptr es:[bx],13
        mov     al,es:1[bx]
        shl     ax,1                    ; multiply by 2 (was word address)
        mov     si,ax
        push    ds
        pop     es
        mov     ds,scrseg
        mov     di,offset screen
        mov     cx,1920
        rep     movsw
        push    es
        pop     ds
        call    cmblnk                  ; let them start with a blank one
        ret                             ; and return
SAVESCR endp

; Restore screen from scrsav buffer

RESTSCR proc    near
        call    cmblnk                  ; start with a clear screen
        mov     si,offset screen
        mov     es,scrseg
        xor     di,di                   ; start at start
        mov     cx,1920                 ; 1920 words to go
        rep     movsw
        mov     dx,word ptr curloc      ; get cursor location
        call    poscur
        ret
RESTSCR endp

; Send the character in al out to the serial port
; Handle echoing also...

OUTPRT  proc    near
        test    flags,lclecho           ; echoing?
        jz      outpr1                  ; no, forget it
        push    ax                      ; save char
        call    outtty                  ; print it
        pop     ax                      ; restore
outpr1:
        mov     ah,al                   ; this is where outchr expects it
        call    outchr                  ; output to the port
         nop
         nop
         nop                            ; skip returns...
        ret
OUTPRT  endp

; Get a character from the serial port in al
; returns with carry on if a character is available

PORTCHR proc    near
        call    prtchr                  ; character at port?
         jmp    short portc1            ; yes, go handle
        nop                             ; skip return is stupid...
        clc                             ; no carry -> no character
        ret                             ; and return...
portc1:
        and     al,7fh                  ; we don't worry about parity here
        stc                             ; have a character
        ret                             ; and return
PORTCHR endp

; Put the character in al to the screen

OUTTTY  proc    near
        test    flags,capt              ; capturing output?
        jz      outnoc                  ; no, forget this part
        push    ax                      ; save char
        call    captrtn                 ; give it captured character
        pop     ax                      ; restore character and keep going
outnoc:
        test    flags1,prtscr           ; should we be printing?
        jz      outnop                  ; no, keep going
        push    ax
        mov     ah,LSTOUT
        mov     dl,al                   ; put character here for dos...
        int     DOS
        pop     ax
outnop:
        mov     dl,al
        mov     ah,DCONIO               ; no checking!!
        int     DOS                     ; else let dos print char
        ret                             ; and return
OUTTTY  endp

CODE    ends

        end
