        NAME    msy55x
; File MSY55X.ASM

; edit history:
; Edit msyibm to produce Sanyo version 8 July 1986 [rwb]
; [2.29] code frozen on 6 May 1986 [jrd]
; Add VT100 terminal emulation from James Harvey at I.U. Purdue
; Move Heath-19 code into that emulation (in module mszibm).
; Revise dynamic memory allocation to obey DOS's rules; use sbrk in mssker.
; Incorporate (limited) support for TopView and Windows in screen handling.
; Revise screen scrolling routines.
; Add functionality for "Del" key to produce ascii Delete code.
; Improve display under debug mode.
; Communicate with emulator module mszibm through global sturcture vtemu.xxx,
; data variables mar_top, mar_bot (screen line numbers for scrolling),
; jwait (wait for screen retrace), scbattr (screen background attributes),
; and several global procedures. Module mszibm is initialized by calling
; vsinit at the completion of initializing this module.
; Do much (but never enough) general cleanup of code.
; Add test for IBM EGA, from C. Kim. If present then don't wait for display
; retrace.
; Add test for running under IBM PC Netbios. If so we lose some info on
; keyboard scan codes (no mode line toggle, Backspace key = backspace).
; Global byte pcnet is non-zero if Netbios is in use. MSXIBM needs pcnet.
; Add dump-screen-to-disk routine for connect mode. Uses Control End.
; Add global entry point TELMSY to get yflags of terminal emulator passed
; in register AL; this is for mode line manipulation.
; Add control of screen refresh via Set Term Color 0 for regular IBM CGA,
; waits for retrace before updating screen, and Set Term Color 10 for
; no waiting. Default is to wait. Controlling variable is refresh defined
; here and set in msxibm under Set Term Color.
; Change names of atscru and atscrd to vtscru and vtscrd, resp.
; Revise memory allocation for screen roll back buffers from fixed size to
;  dynamic size of one line per buffer to npgs*slen (5*24=120) per buffer
;  to accomodate limited free memory systems. Leave space for Command.com
;  before allocating buffer space.  12 April 1986 [jrd]
; Make Dumpscr, the screen dump procedure, available from connect mode drop
;  down menu. This makes Dumpscr a global procedure for all MS Kermits.
; 13 April 1986 [jrd]
; Revise mode (status) line again to show full VT102 name, 5 digit speeds,
;  and so forth.  19 April 1986. [jrd]
; Revise slightly the keypad function key code to accomodate Heath-19.
;  3 May 1986 [jrd]
;
; [2.28 jrd]
; Correct reported bug of Heath emulation ignores CR/LF when cursor is at
; column 80. Done by commenting out parts of outcr and outlf.  [jrd]
;
; [v2.28]
; Don't scroll in inverse video lines (again).
; Backspace in column 0 backs up a line in wrap mode (from Greg Small,
;       UC Berkeley)
; Ansi set graphics rendition support (suggested by Greg Small, UC Berkeley)
; escape sequences of the form esc [ p1;p2;...;pn m where pi is
; one of 0,1,4,5,7 for normal, high-intensity, underline, blink,
; and reverse, respectively.
; JD May 1985

; [v2.27]
; 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

        public  term, gss, lclyini                      ; entry points
        public  prtbout, prtnout, csrtype, scrmod, scrseg, scrsync
        public  scroff, scron, atsclr, vtscru, vtscrd, scrloc, trnmod, dumpscr
        public  modlin, telmsy, scrloc
        public  vtemu, crt_mode, scbattr, pcnet, refresh         ; data [jrd]
        include mssdef.h

; some definitions

; hardware
;;timer   equ     40h                     ; timer port [not in Sanyo, rwb]
;;bel_prt equ     61h                     ; speaker control [not in Sanyo, rwb]
crt_status equ  3dah                    ; crt status port
disp_enb equ    8                       ; display enable bit
crtmset equ     3D8H                    ; CRT mode set port.
crtstat equ     3DAH                    ; CRT status port.
screen  equ     10h                     ; bios screen call
kb      equ     16h                     ; keyboard interrupt
;;;setblk  equ     4ah                  ; DOS Setblock function
;;;(above definition commented out because already supplied in MSSDEF.H)

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
dostty  equ     0eh                     ; dos screen mode write tty [jrd]
                                        ; Keyboard scan codes:
prscan  equ     19h                     ; Alt-P for print-screen [rwb]
upscan  equ     49h                     ; up page
dnscan  equ     51h                     ; down page
ctlup   equ     74h                     ; ctl-rt arrow,  would like to [rwb]
                                        ; use ctl-up page, but Sanyo [rwb]
                                        ; sends same code as pg-up [rwb]
ctldn   equ     76h                     ; ctl-down page
homscn  equ     47h                     ; home screen
modscn  equ     32h                     ; Alt-M [rwb]
endscn  equ     4fh                     ; end of screen
delscan equ     53h                     ; Del key
cendscn equ     75h                     ; ctl-end (dump screen to disk) [jrd]
; no shift state sensing for Sanyo [rwb]
;;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
uparr   equ     48h                     ; scan codes for arrow keys
dnarr   equ     50h
lftarr  equ     4bh
rgtarr  equ     4dh
altmns  equ     130D                    ;[IU2] Alt-"-".
                                        ; Toggles between terminal types
altequ  equ     131D                    ;[IU2] Alt-"=". Reset emualtor

kpf1    equ     59D                     ;[IU2] PF1 - PF4 for VT100 & VT52
kspf1   equ     68h                     ; PF5 - PF10 ctl/PF1 - ctl/PF8 [rwb]
                                        ; for VT100 keypad application mode

modfrm  struc                           ; format of mode (status) line
        db      'Esc-chr: '             ; do not write in last column.
m_echr  db      2 dup (?)
        db      '  help: '
m_hlp   db      2 dup (?)
        db      '? '
        db      ' port:'
m_prt   db      1 dup (?)
        db      ' speed:'
m_baud  db      5 dup (?)
        db      ' parity:'
m_par   db      4 dup (?)
        db      ' echo:'
m_echo  db      3 dup (?)
m_term  db      13 dup (' ')            ; 13 bytes for term type
m_prn   db      3 dup (' ')             ; show PRN when printer is on [jrd]
modfrm  ends



datas   segment public 'datas'
        extrn flags:byte, mar_top:byte, mar_bot:byte, jwait:byte
        extrn filtst:byte, dmpname:byte

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
yflags  db      ?                       ; status flags...
flags1  db      0                       ; internal flags.
prtscr  equ     80h                     ; print screen pressed
inited  equ     08h                     ; been here before...
wrapped equ     04h                     ; on if wrapped on last char...
vtinited db     0                       ; flag for emulator having been inited
cursor  dw      ?
esc_ch  db      ?
argadr  dw      ?                       ; address of arg blk

ega_mode db     0                       ; non-zero if IBM EGA is in use. [jrd]
tvhere  equ     0feh                    ; Topview active query
tvsynch equ     0ffh                    ; Topview resynch request
tv_segs dw      ?                       ; Topview virtual screen, segment
tv_sego dw      ?                       ; and offset
tv_mode db      0                       ; flag, 0 = no Topview.

pcnet           db      0               ; flag, 0 = no PC Network adapter[jrd]
netbios         equ     5ch             ; interrupt for Netbios

; The following are used to turn the display back on (after scrolling etc.)
msets   db      2CH,28H,2DH,29H,2AH,2EH,1EH,29H


vtemu   emulst  <>                      ; emulator flags [jrd]
ansflgs db      0                       ;[IU2] ANSI flags
trmtyp  db      0                       ; most recent terminal type
mtty    db      7 dup (' ')             ; no terminal type (mode line)

; ordered tables of keycodes and corresponding action routine addresses.
ckeys   db      0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
        db      altmns                          ; Alt- toggle terminal types
        db      altequ                          ; Alt= Reset the terminal
lcarr   equ     $-ckeys                 ;[IU2] define base index of arrow keys
        db      uparr,dnarr,rgtarr,lftarr
lcpfk   equ     $-ckeys                 ;[IU2] define base index of pf keys
        db      kpf1,kpf1+1,kpf1+2,kpf1+3
lcxpfk  equ     $-ckeys               ;[IU2] define base index of num key pad
        db      kpf1+4,kpf1+5,kpf1+6,kpf1+7,kpf1+8,kpf1+9    ;[IU2] PF5-PF10
        db      kspf1,kspf1+1,kspf1+2,kspf1+3     ;[rwb] ctl/PF1-ctl/PF4
        db      kspf1+4,kspf1+5,kspf1+6,kspf1+7   ;[rwb] ctl/PF5-ctl/PF8
lckeys  equ     $-ckeys
; ckacts must parallel ckeys above...
ckacts  dw      trnbrk,trnprs,dnwpg,upwpg,endwnd,homwnd,upone,dnone
        dw      vtans52                         ; VT100/VT52/H19 toggle.
        dw      vtinit                          ;[IU2] VT100 "RESET"
        dw      trnarr,trnarr,trnarr,trnarr     ;[IU2] common arrow keys rtn.
        dw      trnpfk,trnpfk,trnpfk,trnpfk     ;[IU2] common pf keys routine
        dw      14 dup (trxpfk)                 ;[IU2] common appl. keys rtn.

arrtab  db      'A','B','C','D'         ; arrows: up, down, right, left

pfktab  db      'P','Q','R','S'         ; PF1, PF2, PF3, PF4

xpfktb  db      'w'                     ;[IU2] PF5 = "7" padkey
        db      'x'                     ;[IU2] PF6 = "8" padkey
        db      'y'                     ;[IU2] PF7 = "9" padkey
        db      'm'                     ;[IU2] PF8 = "-" padkey
        db      't'                     ;[IU2] PF9 = "4" padkey
        db      'u'                     ;[IU2] PF10 = "5" padkey
        db      'v'                     ;[rwb] ctl/PF1 = "6" padkey
        db      'l'                     ;[rwb] ctl/PF2 = "," padkey
        db      'q'                     ;[rwb] ctl/PF3 = "1" padkey
        db      'r'                     ;[rwb] ctl/PF4 = "2" padkey
        db      's'                     ;[rwb] ctl/PF5 = "3" padkey
        db      'M'                     ;[rwb] ctl/PF6 = ENTER padkey
        db      'p'                     ;[rwb] ctl/PF7 = "0" padkey
        db      'n'                     ;[rwb] ctl/PF8 = "." padkey

; key redefinitions
ktrntab dw      ?                       ; address of translation table
krpltab dw      ?                       ; address of replacement table
ktlen   dw      ?

lincur  dw      ?                       ; cursor type save area
scbattr db      ?                       ; Screen background attribute
oldattr db      ?                       ; screen attributes at init time
curattr db      ?                       ; current attribute

modesw  dw      ?
beldiv  dw      2dch                    ; 550 hz?

captrtn dw      ?                       ; routine to call for captured output
dmphand dw      ?                       ; screen dump file handle [jrd]

dumpbuf db      80 dup (?), cr, lf      ; 82 byte dump work buffer
dumpsep db      FF,cr,lf                ; screen image separators
dmperr  db      ' Cannot open file to save screen to disk $'

; some static data for mode line
modbuf  modfrm  <>                      ; mode line buffer
unkbaud db      'unkwn'                 ; must be 5 chars...
baudn   db      ' 45.5',' 50  ',' 75  ',' 110 ','134.5',' 150 ',' 300 ',' 600 '
        db      ' 1200',' 1800',' 2000',' 2400',' 4800',' 9600','19200','38400'
baudnsiz  equ   16                      ; # of baud rates known (tbl size / 4)
parnams db      'even','mark','none','odd ','spc '
lclmsg  db      'loc'
remmsg  db      'rem'
portno  db      ?

; storage for multi-window stuff
crt_mode db     ?
crt_cols db     ?
crt_lins db     ?
low_rgt dw      ?                       ; lower right corner of window
swidth  equ     80                      ; max screen width
slen    equ     24                      ; and length
npgs    equ     5                       ; # of pages of scrolling on each side
scrsav  dw      ?                       ; segment address of save area
refresh db      0                       ; screen refresh (0=wait for retrace)

; circular buffer.  To work properly, the buffer size should be an exact
; multiple of swidth*2 (assured by mem mngmnt code in lclyini, [jrd]).
cbuf    struc
pp      dw      ?                       ; place ptr in buffer
bend    dw      ?                       ; end of buffer
orig    dw      ?                       ; buffer origin
lcnt    dw      0                       ; # of lines in buffer.
lmax    dw      ?                       ; max lines of buffer. [jrd]
cbuf    ends

tlbuf   dw      ?                       ; temp to hold top line of screen
blbuf   dw      ?                       ; temp to hold bottom line of screen
twnd    cbuf    <>                      ; top screen spill-buffer struct
bwnd    cbuf    <>                      ; bottom screen spill buffer struct
topline dw      swidth dup (?)          ; top line screen spill buffer
botline dw      swidth dup (?)          ; bottom line screen spill buffer
datas   ends

code    segment public 'code'                   ; code segment
        extrn   beep:near,prtchr:near,outchr:near,sendbr:near,sbrk:near
        extrn   isfile:near, strlen:near                ; in mssfil
        extrn   anstty:near,ansini:near,ansrei:near     ; in mszibm
        extrn   anstat:near,anskbi:near,ansdsl:near     ; in mszibm
        extrn   ans52t:near, vclick:near, vsinit:near   ; in mszibm
        assume  cs:code,ds:datas,es:datas

; do initialization local to this module...
; Dynamically allocates 4000 bytes for screen save/restore buffer plus
;  320 to 38400 bytes for screen scroll back buffers. Tries to leave space
;  for Command.com before enlarging buffers. [jrd]
lclyini proc    near
        mov     ax,swidth*(slen+1)*2    ; (80 char + 80 attr) * 25 lines [jrd]
        call    sbrk                    ; memory allocation routine (mssker)
        mov     scrsav,ax               ; memory segment for save screens

        mov     tlbuf,offset topline    ; a new buffer in 'datas' space
        mov     blbuf,offset botline    ; ditto                  [jrd]
        mov     ax,swidth*2*2           ; ask for two lines (1 per buffer)
        call    sbrk                    ; allocate mem. Exit Kermit on failure
                                        ;if we get here them we have the lines
        mov     bwnd.orig,ax            ; memory segment, bottom window area
        push    es                      ; save this register.
        mov     es,ax                   ; seg pointer to new memory block
        mov     bx,(swidth*2*slen*2*npgs+24015)/16 ; what we want initially
        mov     ah,setblk               ; DOS Setblock. Ask for that space.
        int     dos                     ; bx has # paragraphs available
        sub     bx,24000D/16            ; deduct space for DOS 3.1 Command.com
        cmp     bx,(swidth*4+15)/16     ; any room left for buffers?
        jge     lclyin2                 ; some space is available for buffers
        mov     bx,(swidth*4+15)/16     ; else use our sbrk allocation
lclyin2:mov     ah,setblk               ; ask for that many paragraphs
        int     dos                     ; Errors here == DOS deceived us.
        pop     es                      ; restore reg.
        mov     ax,bx                   ; bx = # paragraphs allocated by DOS
        shr     ax,1                    ; divide by two (two buffers)
        mov     cx,bwnd.orig            ; segment where allocation begins
        add     cx,ax                   ; segment where next buffer begins
        mov     twnd.orig,cx            ; and store it.
        mov     cl,4                    ; paragraphs to bytes.
        shl     ax,cl                   ; number of bytes per buffer
        mov     cx,swidth*2             ; number of bytes per line in buffer
        mov     dx,0                    ; clear extended numerator
        div     cx                      ; ax = number of lines per buffer
        mov     bwnd.lmax,ax            ; max lines per buffer
        mov     twnd.lmax,ax            ; max lines per buffer
        mov     cx,swidth*2             ; bytes per line (char + attribute)
        mul     cx                      ; ax = effective # bytes per buffer
        dec     ax                      ; adjust for counting from zero
        mov     bwnd.bend,ax            ; offset of last byte in buffer
        mov     twnd.bend,ax            ; offset of last byte in buffer
        mov     bwnd.pp,0               ; offset of first byte in buffer
        mov     twnd.pp,0               ; offset of first byte in buffer
        mov     bwnd.lcnt,0             ; number of lines occupied in buffer
        mov     twnd.lcnt,0             ; number of lines occupied in buffer
        call    scrmod                  ; read video state, get crt_mode.
        mov     ah,8                    ; read current attributes
        xor     bh,bh                   ; page 0
        int     screen
        mov     scbattr,ah              ; save video attributes
        mov     oldattr,ah              ; and here too
        ; test for IBM PC Network adapter, formula in IBM PC Network Tech Ref
        mov     pcnet,0                 ; assume PC Net is inactive
;       push    es                      ; save es around PC Network call
;       mov     ax,netbios              ; interrupt 5cH (Lock/Unlock file)
;       mov     es,ax
;       cmp     word ptr es:[0],0       ; anything there?
;       je      lclyin1                 ; e = no, so no Network
;       mov     ah,0ffh                 ; check further, DOS reserved funct.
;       int     dos
;       cmp     al,03H                  ; adapter present? (3 = magic #)
;       jne     lclyin1                 ; ne = no (not exhaustive test here)
;       mov     pcnet,1                 ; else say Netbios is active.
;lclyin1:pop    es                      ; recover es from network test.
        call    vsinit                  ; init terminal emulator module MSZ
        ret
lclyini endp

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     ah,3                    ; get cursor position and char.
        xor     bh,bh                   ; page 0
        int     screen
        mov     lincur,cx               ; save cursor type (scan line #'s)
        mov     dx,cursor               ; assume old cursor
        mov     ega_mode,0              ; assume no EGA.
        mov     ax,1200H                ; EGA: Bios alternate select
        mov     bl,10H                  ; Ask for EGA info
        mov     bh,0ffH                 ; Bad info, for testing
        mov     cl,0fH                  ; Reserved switch settings
        int     screen                  ; EGA, are you there?
        cmp     cl,0cH                  ; Test reserved switch settings
        jge     scrin6                  ; ge = no EGA in use.
        mov     ega_mode,1              ; yes, set flag to say so.
scrin6: test    flags1,inited           ; have we been here before?
        jnz     scrin4                  ; yes, use old cursor
        mov     ah,oldattr              ; get init time attributes
        mov     curattr,ah              ; and set nice screen attribute
        mov     scbattr,ah
        mov     ah,3                    ; figure out where cursor is
        xor     bh,bh                   ; page 0
        int     screen                  ; read cursor position, in dx
        mov     lincur,cx               ; save cursor type (line #'s)
        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: cmp     flags.vtflg,0           ; doing terminal emulation?
;;;     jne     scrin5                  ; ne = yes
        mov     ah,2                    ; set cursor position
        xor     bh,bh
        int     screen                  ; set cursor in case it moved
scrin5: ret
scrini  endp

; Routine to initialize terminal emulator. Call once.

vtinit  proc    near                    ;[IU2]
        cmp     flags.vtflg,0           ; doing emulation?
        je      vtinix                  ; e = no
        or      vtinited,inited
        call    ansflg                  ; update ansi flags
        mov     al,yflags               ;[IU2] Pass the flags.
        mov     bx,argadr               ;[IU2] Get address of argument block
        mov     dl,[bx].baudb           ;[IU2] Baud rate code in dl
        mov     dh,[bx].parity          ;[IU2] Parity code in bits
        mov     cl,4                    ;[IU2] 0-3 of dh
        shl     dh,cl
        or      dh,07H                  ;[IU2] Just say 7 data bits.
        mov     bx,low_rgt              ;[IU2] Pass screen size in bx.
        call    ansini                  ; call startup routine in mszibm.
vtinix: ret
vtinit  endp


;[IU2] Routine to toggle VT100/VT52/Heath-19 modes in VT100 emulator.

vtans52 proc    near                    ;[IU2]
        cmp     flags.vtflg,0           ; emulating? [jrd]
        je      vtans5                  ; e = no
        call    ans52t                  ;[IU2] Call MSZ toggle-it routine.
        call    ansflg                  ;[IU2] Update flags.
vtans5: ret
vtans52 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+lnwrap
        mov     yflags,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     al,flags.vtflg          ; Terminal type [jrd]
;;      cmp     al,trmtyp               ;[IU2] Same type?
;;      je      argin2                  ;[IU2] Yes, just continue
;;      and     vtinited,not inited     ; No, re-init stuff.
;;      mov     trmtyp,al               ;[IU2] And save new terminal type.
argin2:
        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,5eh       ; caret, note control char
        mov     modbuf.m_hlp,5eh
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,size m_baud          ; each is 5 bytes long
        mul     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    yflags,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,8                    ; blank out terminal id field
        mov     si,offset mtty          ; assume no terminal emulation.
        mov     di,offset modbuf.m_term ; destination
        rep     movsb                   ; copy it in.
        mov     modbuf.m_prn,' '        ; assume not printing the screen [jrd]
        mov     modbuf.m_prn+1,' '
        mov     modbuf.m_prn+2,' '
        test    flags1,prtscr           ; doing a print the screen?
        jz      modl5a                  ; z = no.
        mov     modbuf.m_prn,'P'        ; yes. display PRN at end of line
        mov     modbuf.m_prn+1,'R'
        mov     modbuf.m_prn+2,'N'
modl5a: 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: cmp     flags.vtflg,0   ;;;trmtyp,0             ; emulating?
        jne     modwr1                  ; ne = yes, so having mode line is OK.
        ret                             ; else DOS scrolls 25th line.
modwr1: push    cx
        push    si                      ; save mode line and size
        mov     ah,3                    ; read cursor position
        xor     bx,bx                   ; screen page 0
        int     screen
        mov     cursor,dx               ; save cursor position
        call    trmatt                  ;[IU2] Get terminal attributes
        mov     bh,ah                   ; get video attribute
        mov     cx,1800h                ; line 25...
        mov     dx,184fh
        mov     ax,600h                 ; scroll to clear the line
        int     screen
        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
        cmp     flags.vtflg,0   ;;;trmtyp,0             ; emulating?
        je      modl7                   ; e = no
        mov     al,yflags               ;[IU2] Yes - update flags also
        call    ansdsl                  ; get extras from emulator
;;;     ret                             ;[IU2] ansdsl repositions cursor
modl7:  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
        call    trmatt                  ;[IU2] Get terminal screen attributes
        mov     bh,al                   ;[IU2] Use screen background attribute
        mov     ax,600h                 ; blank window
        mov     cx,24 * 100h            ; beginning of window
        mov     dx,24 * 100h + 79       ; end of window
        int     screen                  ; clear mode line
        ret                             ; and return
clrmod  endp


; Fetch screen attributes from emulator (if emulating). It exists mainly
;[IU2] so that the reverse video will work.   Returns the current mode
;[IU1] line background attribute in ah, the current screen background in al,
;[IU2] and the current "cursor" (foreground) attribute in bl.  (Note: anstat
;[IU2] returns status yflags in bh).

trmatt  proc    near                    ;[IU2] Get attributes
        cmp     flags.vtflg,0           ; emulating? [jrd]
        je      trmat1                  ;[IU2] No, just do simple stuff.
        mov     al,yflags               ;[IU2] anstat expects flags byte in al.
        call    anstat                  ; Fetch emulator status/attributes
        ret

trmat1: mov     al,scbattr              ; Background attributes. [jrd]
        mov     bl,curattr              ;[IU2] And cursor attribute.
        ret
trmatt  endp

; Get byte yflags of terminal emulator passed in AL. Used in mode line
; handling when 25th line is used by the emulator. [jrd]
telmsy  proc    near
        mov     yflags,al               ; get the updated flags
        ret
telmsy  endp


;[IU2] This routine updates the ANSI status flags from the emulator,
; and passes the "yflags" byte to the VT100 emulator also.

ansflg  proc    near
        push    ax                      ;[IU2] Save acs over call
        push    bx
        mov     al,yflags
        call    anstat                  ;[IU2] Get status and attributes
        mov     ansflgs,bh              ;[IU2] Save.
;;;     mov     ah,flags.vtflg
;;;     mov     trmtyp,ah
        pop     bx                      ;[IU2] Restore acs and return.
        pop     ax
        ret
ansflg  endp

term    proc    near                    ; terminal mode 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.
        cmp     flags.vtflg,0           ; emulating a terminal? [jrd]
        jne     term1a                  ; ne = yes, can have mode line [jrd]
;;;     mov     flags.modflg,0          ; disable mode line. [jrd]
        or      yflags,modoff           ; and say want it done. [jrd]
        cmp     trmtyp,0                ; previous terminal type = none?
        jne     term2                   ; ne = no. need to clear mode line.
term1a: cmp     flags.modflg,0          ; is mode line disabled? [jrd]
        jne     term1b                  ; ne = no.
        test    yflags,modoff           ; so, is the mode line really off?
        jnz     term2a                  ; nz = yes, nothing to do
        or      yflags,modoff           ; say want it off and fall thru. [jrd]
term1b: test    yflags,modoff           ; is mode line to be off? [jrd]
        jnz     term2                   ; nz = yes, clear the line. [jrd]
        call    modlin                  ; turn on mode line
        jmp     term2a
term2:  call    clrmod                  ; ensure its off
term2a: mov     al,flags.vtflg          ; current terminal type
        mov     trmtyp,al               ; place to remember it til next time
        or      al,al                   ; terminal type none?
        jz      lp                      ; z = yes, nothing to init.
        mov     al,yflags               ; tell emulator we are back
        cmp     vtinited,inited         ; inited emulator yet?
        je      term3                   ; e - yes
        call    vtinit                  ; init it now
        jmp     lp
term3:  call    ansrei                  ; reinit the emulator
        call    ansflg                  ; and get its flags

lp:     call    portchr                 ; char at port?
         jnc    chkinp                  ; nc = no, keep going
        call    outtty                  ; print on terminal

chkinp: cmp     pcnet,0                 ; IBM PC Netbios active?
        je      chkin2                  ; e = no
        mov     dl,0ffh                 ; Network. keyboard input, via DOS
        mov     ah,dconio               ; get a char into al
        int     dos
        jz      lp                      ; nothing available
        xor     ah,ah                   ; assume no scan code
        cmp     al,0                    ; special char?
        jne     chkin3                  ; ne = no.
        mov     ah,dconio               ; read second byte
        int     dos                     ; get pseudo-scan-code into al
        mov     ah,al                   ; p-scan-code here for following code
        xor     al,al                   ; say special char
        xor     bl,bl                   ; say shift state is none
        jmp     chkin3                  ; and carry on regardless.

chkin2: mov     ah,1                    ; Non-Network. Bios keyboard input.
        int     kb                      ; is anything at keyboard?
        jz      lp                      ; z = no
        xor     ah,ah                   ; yes.
        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
chkin3: cmp     flags.vtflg,0           ; emulating? (0 = no) [jrd]
        je      chkin1                  ; extra clicks not available [jrd]
        test    vtemu.vtflgst,vskeyclick ; flags from SET. [jrd]
        jz      chkin1                  ; extra clicks not wanted [jrd]
        call    vclick                  ; click, what else? [jrd]
chkin1: cmp     al,esc_ch               ; Kermit's escape character?
        je      quit                    ; yes, stop here

        call    trnout                  ; translate if nec., output to prt
        jmp     chkinp                  ; and keep going

quit:   mov     ah,3                    ; get cursor position
        xor     bh,bh                   ; page 0
        int     screen
        mov     cursor,dx               ; save position
        call    savescr                 ; save screen
        cmp     flags.vtflg,0           ; emulating?
        je      quit1                   ; e = no
        call    clrmod                  ; erase mode line
quit1:  mov     ah,oldattr              ; attributes at init time
        mov     scbattr,ah              ; background = original state [jrd]
        mov     cx,lincur               ; cursor type at startup
        mov     ah,1
        int     screen                  ; restore cursor type
        mov     ah,2                    ; Position cursor
        mov     bh,0                    ;[IU3] Page 0
        mov     dx,24 * 100H            ; bottom left
        int     screen                  ;[IU3] Do it.

        mov     al,yflags
        mov     bx,argadr
        mov     [bx].flgs,al            ; update flags in arg block
        pop     es                      ; restore segment register
        ret                             ; and return to caller
term    endp

; put the character in al to the screen
outtty  proc    near
        test    yflags,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: cmp     flags.vtflg,0           ; emulating a terminal?
        jnz     outnop1                 ; nz = yup, go do something smart
        test    yflags,trnctl           ; debug? if so use dos tty mode
        jz      outnp4                  ; z = no
        mov     ah,dostty               ; dostty screen mode
        cmp     al,7fh                  ; Ascii Del char or greater?
        jb      outnp1                  ; b = no
        and     al,3fH                  ; strip high bit (Del --> '?')
        jmp     outnp2                  ; send, preceded by caret
outnp1: cmp     al,' '                  ; control char?
        jae     outnp3                  ; ae = no
        add     al,'A'-1                ; make visible
outnp2: push    ax                      ; save char
        mov     al,5eh                  ; caret
        int     screen                  ; display it
        pop     ax                      ; recover the non-printable char
outnp3: int     screen
        ret
outnp4: cmp     al,bell                 ; bell (Control G)? [jrd]
        jne     outnp5                  ; ne = no
        jmp     beep                    ; use short beep, avoid char loss.
outnp5: mov     dl,al                   ; write without intervention.
        mov     ah,pbout
        int     dos                     ; else let dos display char
        ret                             ; and return

outnop1:jmp     anstty                  ;call terminal emulator routine & ret
outtty  endp

; get shift state into al.  We only care about shift, ctl, and alt keys.
; right shift is collapsed into left shift.
; For Sanyo, indicate no shift keys depressed [rwb]
gss     proc    near
        xor     al,al                   ; [rwb]
        ret
gss     endp

;[IU2] Here to output character to port with no echo (like escape sequences
; sent by PF keys, responses to requests from the host, etc.   It is
; wrong thinking to echo these).

prtbout proc    near                    ;[IU2] Global routine now.
        mov     ah,al                   ; This is where outchr expects it
prtbou1:call    outchr
         nop                            ;[IU2] Ignore skip return.
         nop
         nop
        ret
prtbout endp


;[IU2] Here to output an unsigned 8-bit number (in al) to the port without
; echoing. Used by terminal emulator escape sequence output.

prtnout proc    near
        mov     bl,10                   ;[IU2] Output in base 10.
        jmp     prtno2                  ;[IU2] Ensure at least a zero.

prtno1: cmp     al,0
        jne     prtno2                  ;[IU2] Yes - do more digits
        ret                             ;[IU2] No - return from recursive call.
prtno2: mov     ah,0                    ;[IU2] Clear previous remainder.
        div     bl                      ;[IU2] Divide off a digit
        push    ax                      ;[IU2] Push remainder (in ah) on stack
        call    prtno1                  ;[IU2] Recur.
        pop     ax                      ;[IU2] Pop off a digit
        add     ah,'0'                  ;[IU2] Make it ASCII
        call    prtbou1                 ;[IU2] Output to port
        ret                             ;[IU2] Unwind.
prtnout endp

; send the character in al out to the serial port
; handle echoing also...
outprt  proc    near
        test    yflags,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    yflags,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
        cmp     flags.vtflg,0           ; emulating?
        je      trno2a                  ; e = no
        call    anskbi                  ;[IU2] Yes - say we had keyboard input
trno2a: ret

trnou3: cmp     al,0                    ; is it a special code? [rwb]
        jne     trnou4                  ; no, don't do this
        cmp     ah,modscn               ; Alt-M, mode line toggle? [rwb]
        je      trnmod                  ; e = yes, go toggle mode line [rwb]
        cmp     ah,cendscn              ; Ctrl-End, dump screen to disk? [rwb]
        jne     trno3a                  ; ne = no [rwb]
        call    savescr                 ; save the screen in buffer, [rwb]
        jmp     dumpscr                 ;  go do it and return [rwb]
trno3a: cmp     ah,delscan              ; Del key? [jrd]
        jne     trno3b                  ; ne = no [jrd]
        mov     al,7fH                  ; send ascii Del code [jrd]
        jmp     trnou4                  ; [jrd]
trno3b: 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: cmp     flags.vtflg,0           ; emulating?
        je      trnou5                  ; e = no
        call    anskbi                  ;[IU2] Yes, say we had keyboard input.
        cmp     al,cr                   ;[IU2] A CR?
        jne     trnou5                  ;[IU2] No - just output it and return
        call    ansflg                  ;[IU2] Yes - update VT100 flags
        test    ansflgs,anslnm          ;[IU2] ANSI new-line mode set?
        jz      trnou5                  ;[IU2] No - just send the cr
        call    outprt                  ;[IU2] Yes - output a carriage-return
        mov     al,lf                   ;[IU2] Followed by a line feed.
trnou5: call    outprt
        ret

trnmod: cmp     flags.modflg,1          ; is mode line enabled? [jrd]
        jne     trnm2                   ; ne = no, don't touch it
        test    yflags,modoff           ; mode line already off?
        jnz     trnm1                   ; yes, go turn on
        call    clrmod                  ; no, clear mode line here
        or      yflags,modoff           ; turn on flag
        call    ansflg                  ;[IU2] Update flags all around.
        ret                             ; and return
trnm1:  and     yflags,not modoff       ;[IU2] Clear flag first.
        call    modlin                  ;[IU2] Then turn on mode line.
        call    ansflg                  ;[IU2] Update flags all around.
trnm2:  ret

trnbrk:                                 ; send a Break signal [rwb]
        call    sendbr
        ret
                                        ; toggle ^ PrtSC screen to printer
trnprs: xor     flags1,prtscr           ; flip the flag
        test    yflags,modoff           ; mode line disabled?
        jnz     trnpr1                  ; nz = yes
        call    modlin                  ; else rewrite mode line
trnpr1: ret                             ; and return


;[IU2] Common cursor keys translation.

trnarr: shr     di,1                    ;[IU2] Make offset a byte offset
        sub     di,lcarr                ;[IU2] Make offset into char table
        mov     al,arrtab[di]           ;[IU2] Fetch the character
        push    ax                      ;[IU2] Save it.
        mov     al,chesc                ;[IU2] Output an escape.
        call    outprt                  ; Output, echo permitted
        cmp     flags.vtflg,ttvt100     ; VT100 terminal emulation?
        jne     trnar2                  ;[IU2] No, do VT52/HEATH-19 sequence.
        call    ansflg                  ;[IU2] Update flags all around.
        mov     al,'['                  ;[IU2] Maybe this next?
        test    ansflgs,decckm          ;[IU2] Cursor key mode reset?
        je      trnar1                  ;[IU2] Yes, output the "["
        mov     al,'O'                  ;[IU2] No, set, use the "O".
trnar1: call    outprt                  ; Output it (echo permitted).
trnar2: pop     ax                      ;[IU2] Get the saved char back
        call    outprt                  ; Output to port (echo permitted)
        ret



;[IU2] Common PF key translation: F1-F4 gives VT100 sequences for PF1-PF4
;[IU2] (or VT52 sequences for GOLD, RED and BLUE keys for HEATH-19).

trnpfk: shr     di,1                    ;[IU2] Make offset a byte offset
        sub     di,lcpfk                ;[IU2] Make offset into char table
        mov     al,pfktab[di]           ;[IU2] Fetch the character
        push    ax                      ;[IU2] Save it.
        mov     al,chesc                ;[IU2] Output an escape.
        call    prtbout
        call    ansflg                  ; get emulator flags
        test    ansflgs,decanm          ; ansi mode?
        je      trnpf1                  ; e = no
        mov     al,'O'                  ;[IU2] send an "O".
        call    prtbout                 ;[IU2] Output it.
trnpf1: pop     ax                      ;[IU2] Get the saved char back
        call    prtbout                 ;[IU2] Output to port
        ret


;[rwb] Routine for "application keypad" (F5-F10 and ctl/F1-ctl/F8).
; These translate to 7,8,9,4,5,6,1,2,3,0, period, minus, comma, and ENTER.

trxpfk: call    ansflg                  ;[IU2] Update emulator flags
        test    ansflgs,deckpam         ;[IU2] Application keypad mode?
        jz      trxpfx                  ;[IU2] No, forget this
trxpf0: shr     di,1                    ;[IU2] Make offset a byte offset
        sub     di,lcxpfk               ;[IU2] Make offset into char table
        mov     al,xpfktb[di]           ;[IU2] Fetch the character
        push    ax                      ;[IU2] Save it.
        mov     al,chesc                ;[IU2] Output an escape.
        call    prtbout
        mov     al,'O'                  ;[IU2] Output the "O"
        cmp     flags.vtflg,ttvt100     ; VT100 mode?
        je      trxpf1                  ; e = yes, use "O" code
        test    ansflgs,decanm          ; ANSI (alt application keypad) mode?
        jnz     trxpf1                  ; nz = yes, use "O"
trxpf2: mov     al,'?'                  ; else use "?" instead of "O".
trxpf1: call    prtbout
        pop     ax                      ;[IU2] Retrieve the character
        call    prtbout                 ;[IU2] Output it also
trxpfx: ret

trnout  endp

;;;;; General screen management routines for IBM PC

; computes screen location to ax, given row and col in dx.
; trashes ax
scrloc  proc    near
        push    bx
        mov     al,dh                   ; get row
        mov     bl,swidth               ;** 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
        pop     bx
        ret
scrloc  endp

; wait for retrace so can write to screen memory. Several test conditions
; permit us to skip waiting for all but IBM CGA.
scrwait proc    near
        cmp     refresh,0               ; slow refresh?
        jne     scrwa3                  ; ne = no waiting.
        cmp     tv_mode,0               ; Topview mode?
        jne     scrwa3                  ; ne = yes, no waiting
        cmp     ega_mode,0              ; Extended Graphics Adapter in use?
        jne     scrwa3                  ; ne = yes, no waiting.
        cmp     crt_mode,7              ; bw mode?
        je      scrwa3                  ; e = yes, no waiting
        push    dx
        mov     dx,crt_status
scrwa1: in      al,dx
        test    al,disp_enb             ; display enabled?
        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


; Routine to set cursor type.  Pass cursor type in al: 0 = No cursor,
; 1 = Underline cursor, 2 = Block cursor.   All cursors blink due to hardware.
; Routine frags any ac that video ints frag.

csrtype proc    near
        push    cx                      ; save the reg
        mov     ah,1                    ; Video fxn for set cursor type
        mov     cx,0F00H                ; Assume no cursor
        cmp     al,0                    ; No cursor?
        je      csrty2                  ; Right - set it and be done with it.
        cmp     crt_mode,7              ; B&W card?
        je      csrty3                  ; Yes - different sizes
        mov     cx,0607H                ; No, use CGA underline cursor
        cmp     al,2                    ; Block?
        jne     csrty2                  ; No - set it now.
csrty1: xor     ch,ch                   ; Yes - make it a block
csrty2: int     screen                  ; Set it.
        pop     cx
        ret

csrty3: mov     cx,0B0CH                ; Assume B&W underline cursor
        cmp     al,2                    ; Block?
        jne     csrty2                  ; No - set it now.
        jmp     csrty1                  ; Yes - make it a block
csrtype endp


; save the screen so we can restore it. Saves all 25 lines. [jrd]
savescr proc    near
        push    es
        push    ds
        push    ax
        push    cx
        push    si
        push    di
        call    scrseg                  ; get screen segment in ax and es:di
        push    ax                      ; save screen segment
        mov     si,0
        mov     di,scrsav               ; place to put screen
        mov     es,di                   ; segment of save area
        mov     di,0                    ; no offset
        mov     cx,swidth*(slen+1)      ; # of words on screen
        call    scrwait                 ; wait for screen to be ready
        pop     ds                      ; address screen
        rep     movsw                   ; save the screen
        pop     di
        pop     si
        pop     cx
        pop     ax
        pop     ds                      ; restore this
        pop     es
        ret
savescr endp

; restore screen from scrsav buffer. Restores all 25 lines. [jrd]
restscr proc    near
        push    es
        mov     cx,swidth*(slen+1)      ; the entire screen [jrd]
        call    scrseg                  ; get address of screen data in es:di
        call    scrwait                 ; wait for retrace
        push    ds                      ; save original data segment
        mov     si,scrsav               ; source buffer segment address [jrd]
        mov     ds,si                   ; set buffer segment in ds for si
        mov     si,0                    ; no offset from this
        rep     movsw                   ; restore data to screen
        pop     ds                      ; recover original data segment
        mov     cx,swidth*(slen+1)      ; number of words on the screen
        call    scrsync                 ; synch Topview with new screen
        pop     es
        ret
restscr endp

; Save the screen to a buffer and then append buffer to a disk file. [jrd]
; Default filename is Kermit.scn; actual file can be a device too. Filename
; is determined by mssset and is passed as pointer dmpname.
; Dumpscr reads the screen image saved by savescr so call savescr first.

dumpscr proc    near
        push    ax
        push    bx
        push    cx
        push    dx
        mov     dmphand,-1              ; preset illegal handle
        mov     dx,offset dmpname       ; name of disk file, from mssset
        mov     ax,dx                   ; where isfile wants name ptr
        call    isfile                  ; what kind of file is this?
        jc      dmp5                    ; c = no such file, create it
        test    byte ptr filtst.dta+21,1fh ; file attributes, ok to write?
        jnz     dmp0                    ; nz = no.
        mov     al,1                    ; writing
        mov     ah,open2                ; open existing file
        int     dos
        jc      dmp0                    ; c = failure
        mov     dmphand,ax              ; save file handle
        mov     bx,ax                   ; need handle here
        mov     cx,0ffffh               ; setup file pointer
        mov     dx,-1                   ; and offset
        mov     al,2                    ; move to eof minus one byte
        mov     ah,lseek                ; seek the end
        int     dos
        jmp     dmp1

dmp5:   mov     ah,creat2               ; file did not exist
        mov     cx,20h                  ; attributes, archive bit
        int     dos
        mov     dmphand,ax              ; save file handle
        jnc     dmp1                    ; nc = ok

dmp0:   mov     ah,3                    ; get cursor position
        xor     bx,bx                   ; page 0
        int     screen
        push    dx                      ; save it
        mov     dx,24*100H              ; go to status line
        mov     ah,2                    ; position cursor
        int     screen
        mov     dx,offset dmperr        ; say no can do
        mov     ah,prstr
        int     dos
        pop     dx                      ; get original cursor position
        mov     ah,2                    ; position cursor
        xor     bx,bx                   ; page 0
        int     screen
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

dmp1:   push    di                      ; read screen buffer, write lines
        push    si
        push    es
        mov     cx,slen+1               ; 25 lines
        mov     si,0                    ; offset in memory segment
dmp2:   push    cx                      ; save outer loop counter
        mov     es,scrsav               ; get memory segment
        mov     di,offset dumpbuf       ; data segement memory
        mov     cx,swidth               ; 80 columns
dmp3:   mov     ax,word ptr es:[si]     ; read char + attribute
        mov     byte ptr [di],al        ; store just char, don't use es:
        inc     si                      ; update pointers
        inc     si
        inc     di
        loop    dmp3                    ; do for each column
        std                             ; set scan backward
        mov     cx,swidth               ; 80 columns
        push    es
        mov     ax,ds
        mov     es,ax                   ; set es to data segment for es:di
        mov     di,offset dumpbuf+swidth-1 ; end of line
        mov     al,' '                  ; thing to scan over
        repe    scasb                   ; scan until non-space
        cld                             ; set direction forward
        pop     es
        jz      dmp3a                   ; z = all spaces
        inc     cx
        inc     di
dmp3a:  mov     word ptr [di+1],0A0Dh   ; append cr/lf
        add     cx,2                    ; line count + cr/lf
        mov     dx,offset dumpbuf       ; array to be written
        mov     bx,dmphand              ; need file handle
        mov     ah,write2               ; write the line
        int     dos
        pop     cx                      ; get line counter again
        loop    dmp2                    ; do next line
        mov     dx,offset dumpsep       ; put in formfeed/cr/lf
        mov     cx,3                    ; three bytes overall
        mov     ah,write2               ; write them
        mov     bx,dmphand              ; file handle
        int     dos
        mov     ah,close2               ; close the file now
        int     dos
dmp6:   pop     es
        pop     si
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
dumpscr endp


; Get CRT mode - returns mode in variable crt_mode.

scrmod  proc    near
        mov     ah,15                   ; Get current video state.
        int     screen
        mov     crt_mode,al             ; Store CRT mode value.
        ret                             ; And return.
scrmod  endp


; Get screen segment - returns screen segment in ax, and full address in es:di

scrseg  proc    near
        xor     di,di                   ; start at beginning of screen (0,0)
        mov     ax,0B000H               ; Assume B&W card..
        cmp     crt_mode,7              ; Is it?
        je      scrse1                  ; e = yes
        mov     ax,0B800H               ; No - video memory is here on color
scrse1: mov     es,ax           ; tell Topview our hardware address needs
        mov     tv_segs,es              ; save our hardware screen address
        mov     tv_sego,di              ; segment and offset form
        mov     tv_mode,1               ; assume we're running under Topview
        mov     ah,tvhere               ; query Topview for its presence
        int     screen
        mov     ax,es                   ; get its new segment for screen work
        cmp     ax,tv_segs              ; same as hardware?
        jne     scrse2                  ; ne = no, we are being mapped
        cmp     di,tv_sego              ; check this too.
        jne     scrse2          ; ne = no too. Use TV's work buf as screen.
        mov     tv_mode,0               ; else no Topview or no mapping.
scrse2:ret
scrseg  endp

; Synchronize a Topview provided virtual screen buffer with the image
; seen by the user. Requires cx = number of words written to screen
; (char & attribute bytes) and es:di = ENDING address of screen write.
; Changes ax and di.
scrsync proc    near
        cmp     tv_mode,0               ; Topview mode active?
        je      scrsyn1                 ; e = no, skip DOS call below
        sub     di,cx                   ; backup to start byte (cx = words)
        sub     di,cx                   ;  after storing words to screen
        mov     ah,tvsynch              ; tell Topview we have changed screen
        int     screen                  ;  so user sees updated screen
scrsyn1:ret
scrsync endp

; The following two routines are used to turn off the display while we
; are reading or writing the screen in one of the color card modes.
; Turn screen off for (known) color card modes only. All regs preserved.

scroff  proc    near
        cmp     refresh,0               ; slow refresh?
        jne     scrofx                  ; ne = no wait
        cmp     ega_mode,0              ; Extended Graphics Adapter in use?
        jne     scrofx                  ; ne = yes, no waiting.
        cmp     tv_mode,0               ; Topview mode?
        jne     scrofx                  ; ne = yes, no waiting
        cmp     crt_mode,7              ; B&W card?
        jnb     scrofx                  ; Yes - just return.
        push    ax                      ; Save ax and dx.
        push    dx
        call    scrwait                 ; Wait for retrace.
        cmp     jwait,0                 ; Just wait only?
        jne     scrof1                  ; Right - don't disable screen.
        mov     dx,crtmset              ; Output to CRT mode set port.
        mov     al,25H                  ; This shuts down the display
        out     dx,al                   ; Dumb, but card is too..
scrof1: pop     dx                      ; Restore acs.
        pop     ax
scrofx: ret                             ; And return.
scroff  endp


; Turn screen on for (known) color card modes only
; All registers are preserved.

scron   proc    near
        cmp     refresh,0               ; slow refresh?
        jne     scronx                  ; ne = no wait
        cmp     ega_mode,0              ; Extended Graphics Adapter in use?
        jne     scronx                  ; ne = yes, no waiting.
        cmp     tv_mode,0               ; Topview mode?
        jne     scronx                  ; ne = yes, no waiting
        cmp     crt_mode,7              ; B&W card?
        jnb     scronx                  ; Yes - just return.
        cmp     jwait,0                 ; No - ever turned off?
        jne     scronx                  ; No - just return.
        push    ax                      ; Save ax, dx, and si
        push    dx
        push    si
        mov     al,crt_mode             ; Convert crt_mode to a word
        xor     ah,ah
        mov     si,ax                   ; Get it in a usable register
        mov     al,msets[si]            ; Fetch the modeset byte
        mov     dx,crtmset              ; This port
        out     dx,al                   ; Flash it back on
        pop     si                      ; Restore acs.
        pop     dx
        pop     ax
scronx: ret                             ; And return.
scron   endp


; Screen clearing routine. [IU]
;
; Call:         ax/     coordinates of first screen location to be cleared.
;               bx/     coordinates of last location to be cleared.
; Coord: ah = row [0-24], al = column [0-79]. Preserves all registers. [jrd]

atsclr: push    ax                      ; Save some acs
        push    cx
        push    dx
        mov     dx,bx                   ; Compute last screen offset in ax
        push    ax
        call    scrloc
        mov     cx,ax                   ; Save it in cx for a minute
        pop     dx                      ; Compute first screen offset in ax
        call    scrloc
        sub     cx,ax                   ; Compute number of locs to clear...
        add     cx,2
        sar     cx,1                    ; Make byte count a word count
        jle     atscl2                  ; If nothing to clear, then vamos.
        push    di                      ; Save some more acs
        push    es                      ; save es
        push    ax                      ; save around call
        call    scrseg                  ; Get address of screen in ax, es:di
        pop     ax                      ; recover displacement
        add     di,ax                   ; displacement memory address
        mov     ah,scbattr              ; Use current screen background attr.
        mov     al,' '                  ; Use space for fill
        cmp     cx,swidth               ; Blanking a line or less??
        jg      atscl1                  ; No - make scroff disable display
        mov     jwait,0FFH              ; Yes - wait for retrace only.
atscl1: call    scroff                  ; Turn screen off if color card.
        push    cx                      ; save word count for Topview
        rep     stosw                   ; Blit... (excuse PDP-10ese please)
        pop     cx                      ; recover word count
        call    scrsync                 ; synch Topview
        call    scron                   ; Turn screen back on if color card.
        mov     jwait,0                 ; Reset wait-only flag.
        pop     es                      ; Restore segment register
        pop     di                      ; And destination index
atscl2: pop     dx                      ; restore regs
        pop     cx
        pop     ax
        ret

; Scrolling routines.  vtscru scrolls up one row, vtscrd scrolls down one
; row.  atsprep is called before scrolling up to save the top line in the
; circular buffer. All registers are preserved.

; Screen-roll down. Move text down one line, for terminal emulator only.

vtscrd: push    ax                      ; Upgraded by [jrd]
        push    bx
        push    cx
        push    dx
        mov     ax,701H                 ; scroll down one line
        mov     ch,mar_top              ; top margin line
        mov     cl,0                    ; left most column
        mov     dh,mar_bot              ; bottom margin line
        mov     dl,byte ptr low_rgt     ; right most column
        mov     bh,scbattr              ; attributes
        int     screen                  ; scroll it down
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
                                        ; worker routine for vtscru/d
atsprep:push    es                      ; upgraded from older version  [jrd]
        mov     jwait,0                 ; wait only for retrace
        call    scroff                  ; turn off color screen
        call    scrseg                  ; get display address in es:di
        mov     si,di                   ; si will be source
        mov     di,tlbuf                ; storage for top display line dest
        mov     cx,swidth               ; length of line
        push    ds                      ; swap ds and es for move
        push    es
        pop     ds
        pop     es
        rep     movsw                   ; copy top line from screen to buffer
        push    ds                      ; swap back to normal
        push    es
        pop     ds
        pop     es
        pop     es                      ; and that
        call    scron                   ; turn on screen again
        mov     ax,tlbuf
        mov     bx,offset twnd          ; this is where it goes
        call    putcirc                 ; put into buffer
        ret                             ; and return


; Screen scroll up one line (text moves up) for terminal emulator use.

vtscru: push    ax                      ; Upgraded by  [jrd]
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        cmp     mar_top,0               ; scrolling the top screen line?
        ja      scru1                   ; a = no. don't save anything
        call    atsprep                 ; save top line
scru1:  mov     ax,601H                 ; scroll up one line
        mov     dh,mar_bot              ; bottom row
        mov     dl,byte ptr low_rgt     ; right most column
        mov     ch,mar_top              ; top row of scrolling region
        mov     cl,0                    ; left most column
        mov     bh,scbattr              ; background attributes
        int     screen                  ; scroll up that region
        pop     di                      ; Restore the rest of the regs.
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret                             ; And return

;screen roll up, version for manual scrolling only

mscru:  push    bx                      ; Upgraded by  [jrd]
        push    cx
        push    dx
        push    si
        push    di
        call    atsprep                 ; save top line
mscru1: mov     ax,601H                 ; scroll up one line
        mov     dx,low_rgt              ; lower rignt corner
        xor     cx,cx                   ; top row of scrolling region
        mov     bh,scbattr              ; background attributes
        int     screen                  ; scroll up that region
        mov     ax,blbuf                ; place for an old bottom line
        mov     bx,offset bwnd          ; source of lines
        call    getcirc                 ; try to get a line
        jc      mscru2                  ; c = none, just quit
        cld
        mov     jwait,0                 ; wait for retrace only
        call    scroff
        mov     dx,low_rgt
        mov     dl,0                    ; location is lower left corner
        call    scrloc                  ; get count from display start
        push    es
        push    ax                      ; save count
        call    scrseg                  ; get screen's segment into ax, es:di
        pop     ax                      ; recover count
        add     di,ax                   ; destination memory address (es:di)
        mov     si,blbuf                ; source of line
        mov     cx,swidth
        rep     movsw                   ; copy in the line to the screen
        mov     cx,swidth               ; length for Topview
        call    scrsync                 ; synch Topview
        pop     es                      ; restore es
        call    scron                   ; turn on the screen
mscru2: pop     di                      ; Restore the rest of the regs.
        pop     si
        pop     dx
        pop     cx
        pop     bx
        ret


; prep for screen scroll down. [jrd]
; copies bottom scroll line from screen to buffer  blbuf.
; destroys ax,cx,dx,si,di.
getbot proc near                        ; Upgraded from old version [jrd]
        push    es
        mov     jwait,0                 ; wait only for retrace
        call    scroff                  ; turn off screen
        mov     dx,low_rgt              ; from screen location, row
        mov     dl,0                    ; starting in col 0
        call    scrseg                  ; get adaptor's offset into es:di
        call    scrloc                  ; get offset in display buffer in ax
        add     di,ax                   ; source addr in display buffer es:di
        mov     si,di                   ; screen is source (si)
        mov     di,blbuf                ; destination address
        mov     cx,swidth               ; number of words to copy
        push    ds                      ; swap ds and es for the move
        push    es
        pop     ds                      ; ds <-- org es
        pop     es
        rep     movsw                   ; from screen to buffer
        push    ds                      ; reswap back to normal
        push    es
        pop     ds
        pop     es
        pop     es
        call    scron                   ; turn on display again
        ret
getbot endp

;screen scroll down, for manual mode only
mscrd:  push    bx                      ; Upgraded by [jrd]
        push    cx
        push    dx
        push    si
        push    di
mscrd1: call    getbot                  ; fetch bottom line from screen
        mov     ax,blbuf
        mov     bx,offset bwnd
        call    putcirc                 ; save in circular buffer
mscrd3: mov     ax,701H                 ; scroll down one line
        xor     cx,cx                   ; top left corner
        mov     dx,low_rgt              ; bottom right corner
        mov     bh,scbattr              ; attributes
        int     screen                  ; scroll it down
        mov     ax,tlbuf                ; place to put line temporarily
        mov     bx,offset twnd          ; where to get lines
        call    getcirc                 ; read a line
        jc      mscrd2                  ; c = ok, just no line. quit
        mov     si,tlbuf                ; where to get line
        mov     cx,swidth               ; 80 words.
        mov     jwait,0                 ; wait only for retrace
        call    scroff                  ; turn off display
        cld                             ; Put direction back to "increment".
        push    es
        call    scrseg                  ; get segment address of screen
        rep     movsw                   ; copy line ds:si to es:di
        mov     cx,swidth               ; length for Topview
        call    scrsync                 ; synch Topview
        pop     es
        call    scron                   ; turn on display again
mscrd2: pop     di                      ; Restore the rest of the ACs.
        pop     si
        pop     dx
        pop     cx
        pop     bx
        ret

; move viewing window down as much as possible (text moves up)
endwnd  proc    near                    ; go to end of scrolling text
        mov     cx,slen*npgs            ; as many pages as possible
        jmp     dnwp0                   ; and enter dwnpg
endwnd  endp

dnone   proc    near                    ; move text up one line [jrd]
        mov     cx,1
        jmp     dnwp0
dnone   endp

; scroll viewing window down (text moves up) one page (23 lines, to keep track)
dnwpg   proc    near
        mov     cl,byte ptr low_rgt+1   ; number of rows, excl status
        mov     ch,0
dnwp0:  push    bx
        mov     bx,offset bwnd          ; bottom line buffer
        cmp     [bx].lcnt,cx            ; enough lines?
        jge     dnwp1                   ; ge = we have that many lines stored
        mov     cx,[bx].lcnt            ; do as many as we have
dnwp1:  jcxz    dnwp2                   ; z = nothing to do
        call    mscru                   ; scroll up text one line
        loop    dnwp1
dnwp2:  pop     bx
        ret
dnwpg   endp

; home viewing window
homwnd  proc    near
        mov     cx,slen*npgs            ; large # of lines
        jmp     upwp0                   ; join upwpg
homwnd  endp

upone   proc    near                    ; move text down one line [jrd]
        mov     cx,1
        jmp     upwp0
upone   endp

; scroll viewing window up (text moves down) a page (23 lines, to keep track)
upwpg   proc    near
        mov     cl,byte ptr low_rgt+1   ; number of rows, excl status line
        mov     ch,0
upwp0:  push    bx
        mov     bx,offset twnd          ; how many lines are in top line buf?
        cmp     [bx].lcnt,cx
        jae     upwp1                   ; ae = at least as many as requested
        mov     cx,[bx].lcnt            ; do only as many as are stored.
upwp1:  jcxz    upwp2                   ; z = no lines to scroll
        call    mscrd                   ; roll down text one line
        loop    upwp1
upwp2:  pop     bx
        ret
upwpg   endp


; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax. Revised by [jrd]
putcirc proc    near
        push    es
        push    si
        push    di
        push    bx
        push    cx
        mov     es,[bx].orig            ; get segment of memory area
        mov     di,[bx].pp              ; pick up buffer ptr (offset from es)
        add     di,2*swidth             ; increment to next avail slot
        mov     cx,[bx].bend            ; compare line end with buffer end
        sub     cx,2*swidth-1           ; address of end of this line
        cmp     di,cx                   ; would line extend beyond buffer?
        jbe     putci1                  ; be = not beyond end
        mov     di,0                    ; else start at the beginning
putci1: mov     [bx].pp,di              ; update ptr
        mov     si,ax                   ; this is source
        mov     cx,swidth
        rep     movsw                   ; copy into buffer
        mov     cx,[bx].lmax            ; line capacity of buffer
        cmp     [bx].lcnt,cx            ; can we increment line count?
        jae     putci2                  ; ae = no, keep going
        inc     [bx].lcnt               ; else count this line
putci2: pop     cx
        pop     bx
        pop     di
        pop     si                      ; restore registers
        pop     es
        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. Revised by [jrd]
getcirc proc    near
        push    es
        push    si
        push    di
        push    bx
        push    cx
        mov     cx,ds
        mov     es,cx                   ; ensure proper segment
        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     cx,swidth               ; # of chars to copy
        mov     si,[bx].pp              ; this is source
        mov     di,ax                   ; this is dest
        push    ds                      ; save original ds
        mov     ds,[bx].orig            ; use seg address of buffer for si
        rep     movsw
        pop     ds                      ; recover original data segment
        mov     si,[bx].pp              ; get ptr again
        sub     si,2*swidth             ; move back
        jnc     getcir2                 ; still in buffer, 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     cx
        pop     bx
        pop     di
        pop     si
        pop     es
        ret
getcirc endp

code    ends

if1
        %out [End of pass 1]
else
        %out [End of assembly]
endif

        end
