        title term

; edit history
; Fixed destructive tab problem (received from many people)
; Fixed insert/delete line problem when at bottom/top of screen.
; Newlines never scroll in inverse video lines
; Implemented cursor save/restore (from Univ. Md.)
; "Echo" status on mode corrected.
; JD, 6 December 1984
; Commented out equ for lnwrap since it is now in mssdef.h Joe R. Doupnik
; 12 March 1986

        public  term, gss               ; entry points
        include mssdef.h

; some character definitions

chesc   equ     27
bel     equ     7

print_out equ   05h                     ; dos function to print to printer
pbout   equ     02h                     ; dos function to print a character
prscan  equ     72h                     ; print-screen scan code...
upscan  equ     49h                     ; up page
dnscan  equ     51h                     ; down page
ctlup   equ     84h                     ; ctl-up page
ctldn   equ     76h                     ; ctl-down page
homscn  equ     47h                     ; home screen
endscn  equ     4fh                     ; end of screen
screen  equ     10h                     ; bios screen call
kb      equ     16h                     ; keyboard interrupt
alt_shift equ   8H                      ; alt shift key down
ctl_shift equ   4H                      ; ctl key down
left_shift equ  2H                      ; left shift key down
right_shift equ 1H                      ; right shift key down

timer   equ     40h                     ; timer port
bel_prt equ     61h                     ; speaker control

crt_status equ  3dah                    ; crt status port
disp_enb   equ  8                       ; display enable bit

uparr   equ     48h                     ; scan codes for arrow keys
dnarr   equ     50h
lftarr  equ     4bh
rgtarr  equ     4dh


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'
modfrm  ends

datas   segment public 'datas'
waste   db      100h dup (?)            ;*** need this junk because assembler
                                        ;*** generates non-relocatable offsets
                                        ;*** for things like
                                        ;*** "sub di,offset foo"
                                        ;*** if offset foo < 100H
; stuff for screen routines
flags   db      ?                       ; status flags...
flags1  db      0                       ; internal flags.
prtscr  equ     80h                     ; print screen pressed
;;; [jrd] lnwrap  equ     40h                     ; line wrap enabled.
inited  equ     08h                     ; been here before...
cursor  dw      ?
esc_ch  db      ?
argadr  dw      ?                       ; address of arg blk
ckeys   db      0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
        db      uparr,dnarr,lftarr,rgtarr
lckeys  equ     $-ckeys
; ckacts must parallel ckeys above...
ckacts  dw      trnbrk,trnprs,upwpg,dnwpg,endwnd,homwnd,dnwind,upwind
        dw      trnupw,trndnw,trnlfw,trnrgw

uptrn   db      esc,'A'
dntrn   db      esc,'B'
rgtrn   db      esc,'C'
lftrn   db      esc,'D'

spctab  db      chesc,cr,lf,bs,tab,bel
lspctab equ     $-spctab
spcjmp  dw      outesc,outcr,outlf,outbs,outtab,outbel  ; must match spctab
esctab  db      'YABCDEFGHIJKLM'
        db      'NOZ@[pq<vw'
        db      'jk'
lesctab equ     $-esctab
; escjmp must parallel esctab above
escjmp  dw      movcur,curup,curdwn,currt,outbs,clrscr,outign,outign,curhom
        dw      revind,clreow,clreol,inslin,dellin,delchr,noins
        dw      vtident,entins,doansi
        dw      invvid,nrmvid,outign,dowrap,nowrap
        dw      savecur,restcur
vtidstr db      chesc,'/K'
lvtidst equ     $-vtidstr
coord   dw      ?
insmod  db      ?
wcoord  dw      ?
ttstate dw      outtt0
curattr db      ?                       ; current attribute
ansarg  db      ?                       ; ansi argument value
igncnt  db      ?                       ; # of chars to ignore
beldiv  dw      2dch                    ; 550 hz?
crt_mode db     ?
crt_cols db     ?
crt_lins db     ?
low_rgt dw      ?                       ; lower right corner of window
; key redefinitions
ktrntab dw      ?                       ; address of translation table
krpltab dw      ?                       ; address of replacement table
tmptab  db      0eh,3bh                 ; scan code for bs, f1
ktlen   dw      ?
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'
baudnsiz  equ   14                      ; # 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'
; storage for multi-window stuff
swidth  equ     80
slen    equ     24
npgs    equ     5                       ; # of pages on each side
bsize   equ     swidth*slen*npgs*2
scrsav  dw      swidth*slen dup (0700H) ; a blank screen

; circular buffer.  To work properly, the buffer size should be an exact
; multiple of swidth*2
cbuf    struc
pp      dw      ?                       ; place ptr in buffer
bend    dw      ?                       ; end of buffer
orig    dw      ?                       ; buffer origin
lcnt    dw      0                       ; # of lines in buffer.
cbuf    ends

topbuf  db      bsize dup (?)
botbuf  db      bsize dup (?)           ; top and bottom windows
tlbuf   db      swidth*2 dup (?)
blbuf   db      swidth*2 dup (?)
twnd    cbuf    <topbuf,topbuf+bsize-1,topbuf,0>
bwnd    cbuf    <botbuf,botbuf+bsize-1,botbuf,0>
portno  db      ?
prton   db      'Printer: on'
prtnlen equ     $-prton
prtoff  db      'Printer: off'
prtflen equ     $-prtoff
datas   ends

code    segment public                  ; code segment
        extrn   prtchr:near,outchr:near,sendbr:near
        assume  cs:code,ds:datas,es:datas

scrini  proc    near                    ; init screen stuff
        mov     ah,15                   ; read video state...
        int     screen
        mov     crt_mode,al             ; save crt mode
        cmp     ah,crt_cols             ; is real # of cols < passed?
        jge     scrin1                  ; no
        mov     crt_cols,ah             ; yes, save # of cols
scrin1: mov     dl,crt_cols             ; # of cols again
        mov     dh,crt_lins             ; and # of rows
        dec     dl
        dec     dh
        mov     low_rgt,dx              ; save away window address
        mov     insmod,0                ; not in insert mode
        mov     dx,cursor               ; assume old cursor
        test    flags1,inited           ; have we been here before?
        jnz     scrin4                  ; yes, use old cursor
        mov     curattr,07              ; else set nice screen attribute
        mov     ttstate,offset outtt0   ; normal screen state
        mov     ah,3                    ; figure out where cursor is
        xor     bh,bh                   ; page 0
        int     screen                  ; read cursor position
        cmp     dh,crt_lins             ; past logical end of screen?
        jb      scrin2                  ; no, keep going
        mov     dh,byte ptr low_rgt+1   ; yes, just use lower right corner
scrin2: cmp     dl,crt_cols             ; maybe past right margin
        jb      scrin3                  ; no, use the way it is
        mov     dl,byte ptr low_rgt
scrin3: mov     cursor,dx               ; init cursor
scrin4: mov     ah,2
        xor     bh,bh
        int     screen                  ; set cursor in case it moved
        ret
scrini  endp

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
        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     ax,[bx].belld
        mov     beldiv,ax               ; bell divisor
        mov     ax,[bx].klen
        mov     ktlen,ax                ; length of key redef tbl
        mov     ax,[bx].ktab
        mov     ktrntab,ax              ; save key translation table
        mov     ax,[bx].krpl
        mov     krpltab,ax
        mov     al,[bx].escc
        mov     esc_ch,al
        ret                             ; that's it
argini  endp

modlin  proc    near                    ; turn on mode line
        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     cx,size modfrm          ; this is size of mode line
        mov     si,offset modbuf        ; mode line image
; alternate entry to write an alternate mode line
modwrt: push    cx
        push    si                      ; save mode line and size
        mov     dx,24 * 100h            ; 25th line for mode line
        push    word ptr curattr        ; save current attributes
        mov     curattr,70h             ; want inverse video
        call    clreol                  ; clear to end of line...
        pop     word ptr curattr        ; restore attributes
        mov     dx,24 * 100h
        mov     bh,0
        mov     ah,2                    ; set cursor position
        int     screen
        pop     si
        pop     cx                      ; restore these
modl6:  lodsb                           ; get a byte
        mov     ah,14                   ; write to terminal
        mov     bh,0                    ; page 0
        int     screen
        loop    modl6                   ; write out entire mode line
        mov     dx,cursor
        mov     ah,2
        mov     bh,0
        int     screen                  ; put cursor back where it belongs
        ret                             ; and return
modlin  endp

clrmod  proc    near                    ; clear mode line
        mov     ax,600h                 ; blank window
        mov     cx,24 * 100h            ; beginning of window
        mov     dx,24 * 100h + 79       ; end of window
        mov     bh,07                   ; nice attribute
        int     screen                  ; clear mode line
        ret                             ; and return
clrmod  endp

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

        call    scrini                  ; init screen stuff

        test    flags1,inited           ; have we run yet?
        jz      term1                   ; no, forget this part
        call    restscr                 ; restore screen
term1:  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
        call    outtty                  ; print on terminal

chkinp: mov     ah,1
        int     kb
        jz      lp                      ; nothing available...
        xor     ah,ah
        int     kb                      ; get the char from the buffer
        push    ax                      ; save character temporarily
        call    gss                     ; get shift state into al
        mov     bl,al                   ; save shift state
        pop     ax

        cmp     al,esc_ch               ; escape character?
        je      quit                    ; yes, stop here

        call    trnout                  ; translate if nec., output to prt
        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

; get shift state into al.  We only care about shift, ctl, and alt keys.
; right shift is collapsed into left shift.
gss     proc    near
        mov     ah,2
        int     kb                      ; get current shift state
        mov     bl,al                   ; copy for a moment
        and     bl,right_shift          ; mask out all but right shift
        shl     bl,1                    ; move right shift to left shift pos
        or      al,bl                   ; collapse shift bits
        and     al,(left_shift + alt_shift + ctl_shift)
        ret
gss     endp

; save the screen so we can restore it
; maybe save cursor also.
savescr proc    near
        push    ds
        mov     si,0
        mov     di,offset scrsav        ; place to put screen
        mov     cx,80*24                ; # of words on screen
        call    scrseg
        push    ax                      ; save screen segment
        call    scrwait                 ; wait for screen to be ready
        pop     ds                      ; address screen
        rep     movsw                   ; save the screen
        pop     ds                      ; restore this
        ret                             ; and return
savescr endp

; restore screen from scrsav buffer
restscr proc    near
        push    es
        mov     si,offset scrsav        ; source
        mov     di,0
        mov     cx,80*24
        call    scrseg
        mov     es,ax
        call    scrwait
        rep     movsw                   ; restore it
        pop     es
        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

; 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


; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout  proc    near
        test    flags,havtt             ; translate table given?
        jz      trnou3                  ; no, just output character
        push    ax                      ; save original value
        mov     al,ah                   ; put scan code into ah
        mov     ah,bl                   ; shift state into top half.
        mov     di,ktrntab              ; pick up translate tbl
        mov     cx,ktlen                ; length of tbl
        repne   scasw                   ; look for our key
        pop     ax                      ; recover character
        jne     trnou3                  ; not found, forget it
        sub     di,ktrntab              ; get index into tbl
        sub     di,2                    ; (minus 2 for pre-increment)
        mov     bx,krpltab              ; get replacement table
        mov     si,[bx][di]             ; and addr of replacement
        mov     cl,[si]                 ; get first byte (length)
        xor     ch,ch                   ; clear high-order byte
        inc     si                      ; point to translation string
trnou2: lodsb                           ; get a byte
        push    si
        push    cx                      ; save important registers
        call    outprt                  ; send to port
        pop     cx
        pop     si
        loop    trnou2                  ; send all chars
        ret                             ; and return
trnou3: cmp     ah,4eh                  ;*** plus key thing?
        je      trnmod                  ; yes, go toggle mode line
        cmp     al,0                    ; is it a special code?
        jne     trnou4                  ; no, don't do this
        mov     al,ah                   ; get scan code
        mov     cx,lckeys               ; length of table
        mov     di,offset ckeys         ; table address
        repne   scasb
        mov     al,0                    ; ascii code was 0...
        jne     trnou4                  ; not found, keep going
        sub     di,offset ckeys+1       ; get table offset
        shl     di,1                    ; shift for word offset
        jmp     ckacts[di]              ; jump to appropriate routine
trnou4: call    outprt                  ; just output single char
        ret                             ; and return

trnmod: test    flags,modoff            ; mode line already off?
        jnz     trnm1                   ; yes, go turn on
        call    clrmod                  ; no, clear mode line here
        or      flags,modoff            ; turn on flag
        ret                             ; and return
trnm1:  call    modlin                  ; turn on mode line
        and     flags,not modoff        ; clear flag
        ret                             ; and return

trnbrk: mov     ah,dconio
        mov     dl,0ffH
        int     dos                     ; read the bogus ^C DOS gets.
        call    sendbr
        ret
trnprs: xor     flags1,prtscr           ; flip the flag
        and     flags,not modoff        ; turn on mode line
        mov     si,offset prton
        mov     cx,prtnlen
        test    flags1,prtscr           ; did it go on?
        jnz     trnpr1                  ; yes, say so
        mov     si,offset prtoff
        mov     cx,prtflen
trnpr1: call    modwrt                  ; write into mode line
        ret                             ; and return

; common entry for arrow keys
trnarr: mov     cx,2                    ; length is always 2
        jmp     trnou2                  ; go send definition

trnupw: mov     si,offset uptrn
        jmp     trnarr

trndnw: mov     si,offset dntrn
        jmp     trnarr

trnlfw: mov     si,offset lftrn
        jmp     trnarr

trnrgw: mov     si,offset rgtrn
        jmp     trnarr

trnout  endp

; move viewing window up (screen moves down).
; alternate entry upwin2 doesn't beep if invalid.
upwind  proc    near
        mov     ax,offset tlbuf ; place to put line temporarily
        mov     bx,offset twnd ; where to get lines from
        call    getcirc         ; try to get a line
        jnc     upwin3          ; have a line, go show it
        call    outbel          ; else ring bel
        ret                     ; and return
upwin2: mov     ax,offset tlbuf
        mov     bx,offset twnd
        call    getcirc
        jnc     upwin3
        ret                     ; this just rets if no line avail.
upwin3: mov     ax,offset blbuf ; place for bottom line
        call    getbot          ; fetch bottom line
        mov     ax,offset blbuf
        mov     bx,offset bwnd
        call    putcirc         ; save in circular buffer
        mov     ax,701h         ; scroll down one line
        xor     cx,cx           ; from top
        mov     dx,low_rgt      ; to bottom
        mov     bh,curattr
        int     screen          ; scroll it down
        mov     di,0            ; offset for destination
        mov     si,offset tlbuf ; where to get line from
        mov     cx,swidth       ; length of line
        push    es
        call    scrseg
        push    ax
        call    scrwait
        pop     es
        rep     movsw           ; copy the line in
        pop     es              ; restore this
        ret                     ; and return
upwind  endp


; move viewing window down a line (screen scrolls up)
; entry dwin2 does same w/out checking to see if scroll is legal
dnwind  proc    near
        mov     ax,offset blbuf ; place to put line temporarily
        mov     bx,offset bwnd ; where to get lines from
        call    getcirc         ; try to get a line
        jnc     dnwin3          ; have a line, go show it
        call    outbel          ; else ring bel
        ret                     ; and return
dnwin2: mov     ax,offset blbuf
        mov     bx,offset bwnd
        call    getcirc
        jnc     dnwin3
        ret                     ; this just rets if no line avail.
dnwin3: call    scrprep         ; save top line
        mov     ax,601h         ; scroll up one line
        xor     cx,cx           ; from top
        mov     dx,low_rgt      ; to bottom
        mov     bh,curattr
        int     screen          ; scroll it down
        mov     dx,low_rgt
        mov     dl,0            ; get addr of last line
        call    scrloc
        mov     di,ax           ; this is offset in dest
        mov     si,offset blbuf ; where to get line from
        mov     cx,swidth       ; length of line
        push    es
        call    scrseg
        push    ax
        call    scrwait
        pop     es
        rep     movsw           ; copy the line in
        pop     es              ; restore this
        ret                     ; and return
dnwind  endp

; move viewing window down as much as possible...
endwnd  proc    near
        mov     cx,1000                 ; large number of lines
        jmp     dnwp1                   ; and enter dwnpg
endwnd  endp

; scroll viewing window down (contents move up) crt_lins times...
dnwpg   proc    near
        mov     cl,crt_lins
        mov     ch,0
dnwp1:  push    cx                      ; save this
        call    dnwin2
        pop     cx
        loop    dnwp1
        ret                             ; and return
dnwpg   endp

; home viewing window
homwnd  proc    near
        mov     cx,1000                 ; large # of lines
        jmp     upwp1                   ; join upwpg
homwnd  endp

; scroll viewing window up (screen moves down) a page
upwpg   proc    near
        mov     cl,crt_lins
        mov     ch,0
upwp1:  push    cx
        call    upwin2
        pop     cx
        loop    upwp1
        ret                             ; and return
upwpg   endp

; get the bottom line into the buffer pointed to by ax.
getbot  proc    near
        push    ds
        mov     di,ax                   ; save dest
        mov     cx,swidth
        mov     dx,low_rgt
        mov     dl,0
        call    scrloc
        mov     si,ax
        call    scrseg
        push    ax
        call    scrwait
        pop     ds
        rep     movsw
        pop     ds
        ret
getbot  endp

; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax.
putcirc proc    near
        push    si
        push    di
        push    cx
        push    dx
        mov     di,[bx].pp              ; pick up buffer ptr
        add     di,2*swidth             ; increment to next avail slot
        cmp     di,[bx].bend            ; past end?
        jb      putci1                  ; no, leave alone
        mov     di,[bx].orig            ; else start at beginning
putci1: mov     [bx].pp,di              ; update ptr
        mov     si,ax                   ; this is source
        mov     cx,swidth
        rep     movsw                   ; copy into buffer
        cmp     [bx].lcnt,npgs*slen     ; can we increment it?
        jae     putci2                  ; no, keep going
        inc     [bx].lcnt               ; else count this line
putci2: pop     dx
        pop     cx
        pop     di
        pop     si                      ; restore registers
        ret
putcirc endp

; get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; pass the buffer structure in bx, the buffer to copy the line into
; in ax.
getcirc proc    near
        push    si
        push    di
        push    cx
        push    dx
        cmp     [bx].lcnt,0             ; any lines in buffer?
        jne     getci1                  ; yes, ok to take one out.
        stc                             ; else set carry
        jmp     short getcir3           ; and return
getci1: mov     si,[bx].pp              ; this is source
        mov     di,ax                   ; this is dest
        mov     cx,swidth               ; # of chars to copy
        rep     movsw
        mov     si,[bx].pp              ; get ptr again
        sub     si,2*swidth             ; move back
        cmp     si,[bx].orig            ; compare to origin
        jae     getcir2                 ; still in range, continue
        mov     si,[bx].bend            ; else use end of buffer
        sub     si,2*swidth-1           ; minus length of a piece
getcir2:mov     [bx].pp,si              ; update ptr
        dec     [bx].lcnt               ; decrement # of lines in buffer
        clc                             ; make sure no carry
getcir3:pop     dx
        pop     cx
        pop     di
        pop     si
        ret
getcirc endp

; call before scrolling to save top line...
scrprep proc    near
        push    ds
        mov     si,0                    ; offset of top line
        mov     cx,swidth               ; length of line
        mov     di,offset tlbuf         ; place to put line temporarily
        call    scrseg
        push    ax
        call    scrwait
        pop     ds
        rep     movsw                   ; copy the line
        pop     ds                      ; restore this
        mov     ax,offset tlbuf
        mov     bx,offset twnd          ; this is where it goes
        call    putcirc                 ; put into buffer
        ret                             ; and return
scrprep 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,print_out
        mov     dl,al                   ; put character here for dos...
        int     dos
        pop     ax

outnop: test    flags,emheath           ; emulating heath?
        jnz     outnop1                 ; yup, go do something smart
        mov     dl,al
        mov     ah,pbout
        int     dos                     ; else let dos print char
        ret                             ; and return

outnop1:mov     dx,cursor               ; these may need cursor...
        jmp     ttstate                 ; jump according to current state

outtt0:
        cmp     al,32                   ; special character?
        jb      outtt1                  ; yes, handle specially...

        cmp     insmod,0                ; in insert mode?
        je      outnrm                  ; no, output normal
        push    ax                      ; save character
        call    inschr                  ; insert a character
        pop     ax
outnrm: xor     bh,bh                   ; current page
        mov     cx,1                    ; only one char
        mov     bl,curattr              ; with current attribute
        mov     ah,9
        int     screen                  ; put onto screen
        mov     dx,cursor               ; get cursor pos
currt:  inc     dl                      ; bump col
        cmp     dl,crt_cols             ; see if in range
        jb      setcur                  ; in range, go set cursor
        test    flags1,lnwrap           ; in wrap mode?
        jz      outign                  ; no, just return w/out updating cursor
wrap:   xor     dl,dl
        inc     dh                      ; handle wrap
setcur: cmp     dh,crt_lins
        jb      setc1                   ; not off end, keep going
        push    dx                      ; save row/col
        call    scrprep                 ; save top line in window buf
        mov     ax,0601h                ; scroll up one line
        xor     cx,cx                   ; from 0,0
        mov     dx,low_rgt              ; to 24,80
        mov     bh,7                    ; nice attribute
        int     screen                  ; do the scroll
        pop     dx
        mov     dh,crt_lins             ; go to bottom line again...
        dec     dh
setc1:  xor     bh,bh                   ; page is 0
        mov     cursor,dx               ; save cursor pos
        mov     ah,2
        int     screen                  ; set cursor
outign: ret                             ; and return
; special character (in al)
outtt1: mov     di,offset spctab        ; special char table
        mov     cx,lspctab              ; length of tbl
        repne   scasb                   ; look for char in tbl
        jz      outtt2                  ; found, go do something with it
        test    flags,trnctl            ; are we allowed to print carets?
        jz      outign                  ; no, just ignore it.
        push    ax                      ; save char
        mov     al,'^'
        call    outtty                  ; print caret
        pop     ax
        add     al,'A'-1                ; make printable
        jmp     outtty                  ; print, then return

outtt2: mov     dx,cursor               ; might need cursor pos
        sub     di,offset spctab+1      ; get index of char
        shl     di,1                    ; double for word offset
        jmp     spcjmp[di]              ; and go handle

; special char routines.  cursor is in dx, char in al

outlf:  inc     dh                      ; bump row
        jmp     setcur

outcr:  xor     dl,dl                   ; set col to 0
        jmp     setcur

outbs:  or      dl,dl
        jle     setcur                  ; col 0, can't back up
        dec     dl                      ; back up col
        jmp     setcur                  ; and use if reasonable

outtab: mov     dl,byte ptr cursor      ; get initial column
        add     dl,8                    ; tab is at most 8 columns
        and     dl,not 111b             ; round down to a multiple of 8
        cmp     dl,crt_cols             ; out of range?
        jb      setcur                  ; no, go set it
        test    flags1,lnwrap           ; in wrap mode?
        jnz     outta1                  ; yes, wrap to next line
        mov     dl,byte ptr low_rgt     ; else just move to right margin
        jmp     setcur
outta1: jmp     wrap

; stolen from bios
outbel: mov     al,10110110b            ; timer initialization
        out     timer+3,al
        mov     ax,beldiv               ; bel divisor
        out     timer+2,al
        mov     al,ah
        out     timer+2,al              ; output divisor
        in      al,bel_prt
        mov     ah,al                   ; remember original value
        or      al,3                    ; turn speaker on
        out     bel_prt,al
        mov     cx,8888h
outbe1: loop    outbe1                  ; wait a while
        mov     al,ah
        out     bel_prt,al              ; turn bell off
        ret                             ; and return

outesc: mov     ttstate,offset escseq   ; expect escape sequence.
        ret                             ; and return

; escape-char handling routines
escseq: mov     ttstate,offset outtt0   ; put state back to normal
        mov     di,offset esctab        ; escape char tbl
        mov     cx,lesctab              ; length of tbl
        repne   scasb                   ; look for it in tbl
        jz      escsq1                  ; found, go use it
        jmp     outtty                  ; not there, just print it
escsq1: sub     di,offset esctab+1      ; get offset into tbl
        shl     di,1                    ; convert to word offset
        jmp     escjmp[di]              ; and go dispatch on it

; escape dispatch routines
revind: cmp     dh,0
        jle     revin1
        dec     dh                      ; back up a row
        jmp     setcur                  ; and go set cursor
revin1: push    dx                      ; save cursor pos
        mov     ax,701h                 ; scroll down one line
        xor     cx,cx                   ; from top
        mov     dx,low_rgt              ; to bottom
        mov     bh,curattr
        int     screen                  ; scroll it down
        pop     dx                      ; restore cursor.
        mov     dh,0                    ; set row back to 0
        jmp     setcur

curup:  cmp     dh,0                    ; w/in range?
        jle     curu1                   ; no, skip this
        dec     dh                      ; else back up
curu1:  jmp     setcur                  ; and go set position

curdwn: inc     dh
        jmp     setcur                  ; increment row (setcur can scroll!)

; currt is above

clrscr: call    curhom                  ; go home cursor
        jmp     clreow                  ; then clear to end of window

curhom: xor     dx,dx                   ; move to 0,0
        jmp     setcur

clreow: cmp     dl,0                    ; at beginning of line?
        jz      clrw1                   ; yes, skip this part...
        push    dx                      ; remember cursor pos
        call    clreol                  ; clear to end of this line
        pop     dx
        inc     dh                      ; bump row
        xor     dl,dl                   ; start from col 0
clrw1:  cmp     dh,crt_lins             ; last line on screen
        jnb     clrw2                   ; if not in range, forget it
        mov     ax,700h                 ; clear whole window
        mov     cx,dx                   ; this is beginning
        mov     dx,low_rgt
;       mov     dx,174fh                ; this is lower right corner
        mov     bh,curattr              ; default attribute
        int     screen                  ; go clear it
clrw2:  ret                             ; and return

clreol: push    es
        mov     cl,crt_cols             ; last col + 1
        sub     cl,dl                   ; this is # of chars to move
        xor     ch,ch
        jcxz    clrl1
        call    scrloc                  ; compute screen location (to ax)
        mov     di,ax
        call    scrseg
        mov     es,ax                   ; address screen segment
        call    scrwait                 ; wait for retrace
        mov     ah,curattr              ; current attribute
        mov     al,' '                  ; fill char
        rep     stosw                   ; fill line with spaces
clrl1:  pop     es
        ret                             ; and return

inslin: mov     al,1                    ; scroll one line
; alternate entry if inserting more then one line
inslin1:mov     ch,dh                   ; start at current row
        xor     cl,cl                   ; column 0
        mov     dx,low_rgt
        mov     ah,7h                   ; scroll down.
        mov     bh,curattr              ; attribute
        cmp     ch,dh                   ; moving last line down?
        jne     insli2                  ; no, keep going
        mov     al,0                    ; yes, just clear it
insli2: int     screen
        ret

dellin: mov     al,1                    ; scroll 1 line
; alternate entry if deleting more than one line
dellin1:mov     ch,dh                   ; start at current row
        xor     cl,cl                   ; column 0
        mov     dx,low_rgt
;       mov     dx,174fh                ; to bottom of screen
        mov     ah,6h                   ; scroll up.
        mov     bh,curattr              ; attribute
        cmp     ch,dh                   ; deleting last line?
        jne     delli2                  ; no, go on
        mov     al,0                    ; yes, just blank it
delli2: int     screen
        ret

delchr: push    ds
        push    es
        pushf                   ; these may get changed...
        mov     cl,crt_cols
        dec     cl
        sub     cl,dl           ; from what we're fiddling)
        xor     ch,ch
        jcxz    delch1          ; none to move, forget it
        call    scrloc          ; compute location
        mov     di,ax
        mov     si,ax
        add     si,2            ; source is next position over
        call    scrseg          ; pick up screen segment
        push    ax              ; put screen segment onto stack
        mov     es,ax           ; and in destination segment
        call    scrwait         ; wait for retrace
        pop     ds              ; address screen segment
        rep     movsw           ; delete it
        mov     byte ptr [di],' ' ; kill char at end of line
delch1: popf
        pop     es
        pop     ds
        ret

inschr: push    ds
        push    es              ; save these as well
        pushf                   ; might as well save flags...
        mov     dx,cursor       ; this is place to do it
        mov     cl,crt_cols
        dec     cl
;       mov     cl,79           ; this is last col to move, +1 for length
        sub     cl,dl           ; compute distance to end
        xor     ch,ch           ; clear top half of offset
        jcxz    insch1          ; nothing to move...
        mov     dl,crt_cols
        sub     dl,2            ; last col to move
;       mov     dl,78           ; this is address of last col to move
        call    scrloc          ; compute pos
        mov     si,ax
        mov     di,ax
        add     di,2            ; destination is one byte over...
        std                     ; remember to move us backwards
        call    scrseg          ; find screen segment
        mov     es,ax
        push    ax              ; save screen seg on stack
        call    scrwait         ; wait until save to write
        pop     ds              ; address screen segment
        rep     movsw           ; move each char and attribute
insch1: popf
        pop     es
        pop     ds
        ret                     ; and return

noins:  mov     insmod,0                ; turn off insert mode
        ret                             ; and return

movcur: mov     wcoord,2                ; want two coordinates...
        mov     ttstate,offset getcoord
        ret                             ; and return

vtident: mov    si,offset vtidstr
        mov     cx,lvtidst
vtid1:  lodsb                           ; get a byte from the string
        push    si                      ; have to save from outprt
        push    cx
        call    outprt                  ; send to serial port
        pop     cx
        pop     si
        loop    vtid1                   ; go thru all chars
        ret                             ; and return

entins: mov     insmod,0ffh             ; enter insert mode...
        ret                             ; and return

doansi: mov     ansarg,0                ; ansi argument is 0 (default)
        mov     ttstate,offset getaarg  ; state is get ansi argument
        ret

getaarg:cmp     al,'0'
        jb      getaa1                  ; in range for digit?
        cmp     al,'9'
        ja      getaa1
        sub     al,'0'                  ; convert to binary
        mov     dl,al                   ; tuck away
        mov     al,ansarg
        mov     dh,10
        mul     dh                      ; shift sum
        add     al,dl                   ; add in this digit (what about ovfl?)
        mov     ansarg,al
        ret                             ; and return

getaa1: cmp     al,'?'                  ; the dreaded question mark?
        jne     getaa2
        mov     ttstate,offset ignn     ; we ignore these...
        mov     igncnt,2                ; this is how many chars come after him
        ret

getaa2: mov     ttstate,offset outtt0   ; reset state
        mov     dx,cursor               ; this needs cursor position
        mov     bl,ansarg
        xchg    al,bl                   ; put argument in nice place
        cmp     bl,'L'                  ; insert line?
        jne     getaa3
        jmp     inslin1                 ; and go do it

getaa3: cmp     bl,'M'                  ; maybe delete line?
        jne     getaa4
        jmp     dellin1

getaa4: ret                             ; ignore.

invvid: mov     curattr,70h             ; attribute for inverse video
        ret

nrmvid: mov     curattr,07h             ; attribute for normal video
        ret

dowrap: or      flags1,lnwrap           ; turn on wrap mode
        ret                             ; and return

nowrap: and     flags1,not lnwrap       ; turn off wrap mode
        ret                             ; and return

; get a coordinate.
getcoord:
        sub     al,32                   ; coordinates offset by 32
        mov     si,wcoord
        dec     si
        mov     byte ptr coord[si],al   ; fill in appropriate coordinate
        mov     wcoord,si               ; update flag
        jnz     getco1                  ; more needed, can't do anything yet
        mov     ttstate,offset outtt0   ; reset state
        mov     dx,coord                ; get coordinates
        jmp     setcur                  ; and go jump there
getco1: ret

; ignore following igncnt characters
ignn:   dec     igncnt                  ; decrement count
        jnz     ignn1
        mov     ttstate,offset outtt0   ; put state back to normal if done
ignn1:  ret

; save cursor
savecur:
        mov     oldcur,dx
        ret

; restore cursor
restcur:
        mov     dx,oldcur
        jmp     setcur

outtty  endp

; computes screen location to ax, given row and col in dx.
; trashes ax,bx
scrloc  proc    near
        mov     al,dh           ; get row
        mov     bl,crt_cols     ;** row size
        mul     bl              ; multiply by row size
        xor     dh,dh           ; clear col
        add     ax,dx           ; this is current position
        sal     ax,1            ; double for attributes
        ret
scrloc  endp

; puts current screen segment in ax
scrseg  proc    near
        mov     ax,0b000h               ; assume bw for now
        cmp     crt_mode,7              ; 7 is bw (***)
        je      scrse1
        mov     ax,0b800h               ; color card
scrse1: ret
scrseg  endp

; wait for retrace so can write to screen memory
scrwait proc    near
        cmp     crt_mode,7              ; bw mode?
        je      scrwa3                  ; yes, no waiting
        push    dx
        mov     dx,crt_status
scrwa1: in      al,dx
        test    al,disp_enb             ; display enable?
        jnz     scrwa1                  ; yes, keep waiting
scrwa2: in      al,dx
        test    al,disp_enb             ; now wait for it to go off
        jz      scrwa2                  ; so can have whole cycle
        pop     dx
scrwa3: ret                             ; that was easy...
scrwait endp
code    ends

if1
        %out [End of pass 1]
else
        %out [End of assembly]
endif

        end
