        Title   MSYM24
        page 78,132

; edit history
;
; Olivetti M24 VT100 emulation version - A.J. Hunt, 24-Dec-1985.
;
; In order to work like a proper VT100 with EDT, the VT100 keyboard driver -
; KBDVT100.COM (distributed as MSM24KBD.COM) - must be installed, and the
; CTRL/ALT/F2 command must have been issued.  This driver causes the numeric
; keypad and associated keys to return the scan codes for F21 - F38 in the
; FUNCT_LOCK_ON (= NUM_LOCK_OFF) mode.
;
; With the Olivetti type 2 (Deluxe) keyboard, the keypad looks exactly like
; a VT100 keypad in Alternate mode; the '00' key is unused.  With the type 1
; (IBM-PC) keyboard this is, of course, not possible;  in that case the
; following approximation is made:
;
;   o   The IBM-PC ARROWS are accessible in their marked positions by use
;       of the CTRL key.
;
;   o   GOLD is the *-PRTSC key.
;
;   o   HELP is CTRL/*-PRTSC.
;
;   o   FIND/FNDNXT is CTRL/-.
;
;   o   DELL/UNDL is CTRL/+.
;
;   o   DELW/UNDW is -.
;
;   o   DELC/UNDC is +.
;
; F21 - F30 are also obtainable with CTRL/F1 - CTRL/F10, and F31 - F38 with
; ALT/F1 - ALT/F8 on both the type 2 (Olivetti deluxe) and type 1 (IBM-PC)
; keyboards.  Since these involve a non-zero shift state, they may be
; redefined with the DEFINE KEY command without affecting the keypad
; versions.  If they are not redefined, they may be used interchangeably
; with the keypad keys.  The following table lists the codes produced and
; the EDT keyname:
;
;                 Key
;    IBM-PC Type 1  Olivetti Type 2     Function code     EDT name
;
;          1               1                 F21         WORD/CHGCSR
;          2               2                 F22          EOL/DELEOL
;          3               3                 F23         CHAR/SPCINS
;          4               4                 F24          ADV/BOTTOM
;          5               5                 F25          BACK/TOP
;          6               6                 F26          CUT/PASTE
;          7               7                 F27         PAGE/COMMAND
;          8               8                 F28         SECTION/FILL
;          9               9                 F29        APPEND/REPLACE
;          0               0                 F30        LINE/OPEN LINE
;
;       *-PRTSC            /                 F31            GOLD
;    CTRL/*-PRTSC          *                 F32            HELP
;        CTRL/-            -                 F33         FIND/FNDNXT
;        CTRL/+            +                 F34          DELL/UNDL
;          -            SCR PRT              F35          DELW/UNDW
;          +             HELP                F36          DELC/UNDC
;          .               .                 F37         SELECT/RESET
;      CTRL/<cr>         <cr>                F38            ENTER
;
;
; With FUNCT_LOCK_OFF (= NUM_LOCK_ON) the characters marked on the keytops
; are obtainable on both keyboards.  The Keyboard-2 HELP key generates '='
; in numeric mode.
;
; SRC PRT (PRTSC) - print screen - is obtainable with the ALT key on both
; keyboards, and in numeric mode as well with the type 2.  "Copy received
; data to printer" is obtained with ALT/SHIFT/PRTSC on the type 1 keyboard,
; and with CTRL/SCR_PRT on the type 2.
;
; .........................................................AJH 30-Dec-1985.
;
; 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               ; entry points
        include mssdef.h

print_out equ   05h                     ; dos function to print to printer
pbout   equ     02h                     ; dos function to print a character

prscan  equ     72h                     ; copy-print to 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

uparr   equ     48h                     ; scan codes for arrow keys
dnarr   equ     50h
lftarr  equ     4Bh
rgtarr  equ     4Dh

pluscn  equ     4eh                     ; keypad 'CTRL/+' key scan code

f21kpd  equ     5eh                     ; vt100 '1' key
f22kpd  equ     5fh                     ; vt100 '2' key
f23kpd  equ     60h                     ; vt100 '3' key
f24kpd  equ     61h                     ; vt100 '4' key
f25kpd  equ     62h                     ; vt100 '5' key
f26kpd  equ     63h                     ; vt100 '6' key
f27kpd  equ     64h                     ; vt100 '7' key
f28kpd  equ     65h                     ; vt100 '8' key
f29kpd  equ     66h                     ; vt100 '9' key
f30kpd  equ     67h                     ; vt100 '0' key
f31kpd  equ     68h                     ; vt100 'PF1' key (/)
f32kpd  equ     69h                     ; vt100 'PF2' key (*)
f33kpd  equ     6ah                     ; vt100 'PF3' key (-)
f34kpd  equ     6bh                     ; vt100 'PF4' key (+)
f35kpd  equ     6ch                     ; vt100 '-' key   (SCR PRT)
f36kpd  equ     6dh                     ; vt100 ',' key   (HELP)
f37kpd  equ     6eh                     ; vt100 '.' key
f38kpd  equ     6fh                     ; vt100 'ENTER' key

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

sout    equ     016Q                    ; shift out
sin     equ     017Q                    ; shift in

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

rptCPfm struc                           ; format of cursor position report.
        db      esc,'['
line    dw      ?                       ; always allow 2 digits for each
        db      ';'                     ; coordinate.
column  dw      ?
        db      'R'
rptCPfm 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                     ; copy-print screen pressed
;;; [jrd] lnwrap  equ     40h                     ; line wrap enabled.
sftout  equ     20h                     ; shift out character set selected
inited  equ     08h                     ; been here before...
cursor  dw      ?
esc_ch  db      ?
argadr  dw      ?                       ; address of arg blk

G0set   db      'B'                     ; currently selected G0 char set.
G1set   db      '0'                     ; currently selected G1 char set.

; translation table for VT100 special graphics character set.  These
; characters replace '_' <137Q> through '~' <176Q> when the <esc>(0 set is
; selected as the G0 set or <esc>)0 as the G1 set.

vtgraph db       20h, 04h,0b2h,0afh, 17h,0aeh, 19h,0f8h
        db      0f1h, 01h, 12h,0bch,0bbh,0c9h,0c8h,0ceh
        db       1eh, 11h,0cdh, 10h, 1fh,0cch,0b9h,0cah
        db      0cbh,0bah,0f3h,0f2h,0e3h,0f7h,09ch,0f9h

ckeys   db      0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
        db      uparr,dnarr,lftarr,rgtarr,pluscn
        db      f21kpd,f22kpd,f23kpd,f24kpd,f25kpd,f26kpd
        db      f27kpd,f28kpd,f29kpd,f30kpd,f31kpd,f32kpd
        db      f33kpd,f34kpd,f35kpd,f36kpd,f37kpd,f38kpd
lckeys  equ     $-ckeys
; ckacts must parallel ckeys above...
ckacts  dw      trnbrk,trnprs,upwpg,dnwpg,endwnd,homwnd,dnwind,upwind
        dw      trnupw,trndnw,trnlfw,trnrgw,trnmod
        dw      trnf21,trnf22,trnf23,trnf24,trnf25,trnf26
        dw      trnf27,trnf28,trnf29,trnf30,trnf31,trnf32
        dw      trnf33,trnf34,trnf35,trnf36,trnf37,trnf38

uptrn   db      esc,'[A',esc,'A'
dntrn   db      esc,'[B',esc,'B'
rgtrn   db      esc,'[C',esc,'C'
lftrn   db      esc,'[D',esc,'D'

f21seq  db      esc,'Oq',esc,'?q'
f22seq  db      esc,'Or',esc,'?r'
f23seq  db      esc,'Os',esc,'?s'
f24seq  db      esc,'Ot',esc,'?t'
f25seq  db      esc,'Ou',esc,'?u'
f26seq  db      esc,'Ov',esc,'?v'
f27seq  db      esc,'Ow',esc,'?w'
f28seq  db      esc,'Ox',esc,'?x'
f29seq  db      esc,'Oy',esc,'?y'
f30seq  db      esc,'Op',esc,'?p'

f31seq  db      esc,'OP',esc,'P'
f32seq  db      esc,'OQ',esc,'Q'
f33seq  db      esc,'OR',esc,'R'
f34seq  db      esc,'OS',esc,'S'

f35seq  db      esc,'Om',esc,'?m'
f36seq  db      esc,'Ol',esc,'?l'
f37seq  db      esc,'On',esc,'?n'
f38seq  db      esc,'OM',esc,'?M'

spctab  db      esc,cr,lf,bs,tab,bell,sout,sin
lspctab equ     $-spctab
spcjmp  dw      outesc,outcr,outlf,outbs,outtab,outbel  ; must match spctab
        dw      shftout,shftin

esctab  db      'ABCDEFGH'
        db      'IJKLMNO'
        db      'YZ@[#+\'
        db      'jkpqvw'
        db      '78<=>()'
lesctab equ     $-esctab
; escjmp must parallel esctab above
escjmp  dw      curup,curdwn,currt,escD,escE,spcgra,nrmchs,escH
        dw      revind,clreow,clreol,inslin,escM,delchr,noins
        dw      movcur,vtident,entins,doansi,ignor1,outign,outign
        dw      savecur,restcur,invvid,nrmvid,dowrap,nowrap
        dw      savecur,restcur,h19off,outign,outign,G0chset,G1chset

ansitab db      ';?'
        db      'ABCDHJK'
        db      'LM'
        db      'cfhlmnr'
lanstab equ     $-ansitab
ansijmp dw      nxtaarg,ansqmk
        dw      ansup,ansdwn,ansrgt,anslft,ansmvc,ansescr,anselin
        dw      mltinln,mltdeln
        dw      vtident,ansmvc,ansset,ansrset,ansattr,ansrpt,anscrgn

vt100id db      esc,'[?1;0c'            ; VT100 with no options.
lvt100i equ     $-vt100id
vt52id  db      esc,'/Z'                ; VT100 in VT52 mode.
lvt52id equ     $-vt52id
rptOKst db      esc,'[0n'               ; Terminal OK string.
lrptOKs equ     $-rptOKst
rptCPst rptCPfm <>                      ; Cursor position report string.
lrptCPs equ     $-rptCPst

coord   dw      ?
insmod  db      ?
wcoord  dw      ?
ttstate dw      outtt0
curattr db      ?                       ; current attribute
ansarg  db      20 dup (?)              ; allow 20 ANSI arguments
ansarct db      0                       ; ANSI argument count
igncnt  db      ?                       ; # of chars to ignore
beldiv  dw      2dch                    ; 550 hz?
crt_mode db     ?
crt_cols db     ?
crt_lins db     ?
crt_brgt dw     ?                       ; bottom right corner of screen
top_lft dw      ?                       ; top left corner of scroll region
bot_rgt dw      ?                       ; bottom right corner
; 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     crt_brgt,dx             ; save bottom right of screen
        mov     bot_rgt,dx              ; ... = bottom right of window
        mov     top_lft,0
        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,byte ptr bot_rgt+1   ; past logical end of screen?
        jbe     scrin2                  ; no, keep going
        mov     dh,byte ptr bot_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 bot_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                      ; save buffer size
        mov     bh,30h                  ; pale reverse video
        call    filmod                  ; fill mode line with attribute
        pop     cx                      ; restore buffer size
        mov     dx,24 * 100h            ; cursor for start of mode line
        xor     bh,bh                   ; page 0
        mov     ah,2                    ; set cursor position
        int     screen                  ; ...registers are preserved!
;
modl6:  lodsb                           ; get character from buffer
        mov     ah,14                   ; write to terminal
        int     screen                  ; ...registers are preserved!
        loop    modl6                   ; write out entire mode line
;
        mov     dx,cursor               ; fetch real cursor position
        mov     ah,2
        int     screen                  ; put cursor back where it belongs
        ret                             ; and return
modlin  endp

clrmod  proc    near                    ; clear mode line
        mov     bh,07                   ; nice attribute - enitrely blank!
filmod: 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

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                    ; Check to see if there is a character.
        int     kb
        jz      lp                      ; ...nothing available...loop!

        xor     ah,ah                   ; ...yes there is -
        int     kb                      ; get the char from the buffer
        push    ax                      ; save character temporarily
        call    gss                     ; get shift state into 'al'
        mov     bl,al                   ; and put it in 'bl'.
        pop     ax                      ; ...restore scan code and ASCII.

        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     al,00                   ; 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,00                   ; 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 2/3-character escape sequence keys

trnseq: test    flags,emheath           ; emulating Heath H19/VT52?
        jz      trseq3
        mov     cx,2                    ; ...yes, in this case length is 2
        jmp     chgseq

; jump to here for sequences which have 3 characters in both modes:

trseq3: mov     cx,3                    ; ...maybe, but length is 3
        test    flags,emheath           ; emulating Heath H19/VT52?
        jz      sndseq
chgseq: add     si,3                    ; ...yes, use alternate sequence
sndseq: jmp     trnou2                  ; go send definition

; Translate auxiliary keypad and arrow keys to either VT100 or VT52
; sequence.

trnupw: mov     si,offset uptrn
        jmp     trnseq

trndnw: mov     si,offset dntrn
        jmp     trnseq

trnlfw: mov     si,offset lftrn
        jmp     trnseq

trnrgw: mov     si,offset rgtrn
        jmp     trnseq

trnf21: mov     si,offset f21seq
        jmp     trseq3

trnf22: mov     si,offset f22seq
        jmp     trseq3

trnf23: mov     si,offset f23seq
        jmp     trseq3

trnf24: mov     si,offset f24seq
        jmp     trseq3

trnf25: mov     si,offset f25seq
        jmp     trseq3

trnf26: mov     si,offset f26seq
        jmp     trseq3

trnf27: mov     si,offset f27seq
        jmp     trseq3

trnf28: mov     si,offset f28seq
        jmp     trseq3

trnf29: mov     si,offset f29seq
        jmp     trseq3

trnf30: mov     si,offset f30seq
        jmp     trseq3

trnf31: mov     si,offset f31seq
        jmp     trnseq

trnf32: mov     si,offset f32seq
        jmp     trnseq

trnf33: mov     si,offset f33seq
        jmp     trnseq

trnf34: mov     si,offset f34seq
        jmp     trnseq

trnf35: mov     si,offset f35seq
        jmp     trseq3

trnf36: mov     si,offset f36seq
        jmp     trseq3

trnf37: mov     si,offset f37seq
        jmp     trseq3

trnf38: mov     si,offset f38seq
        jmp     trseq3

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 bell
        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,bot_rgt      ; to bottom
        mov     bh,07h          ; standard attributes
        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 bell
        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,crt_brgt     ; to bottom of whole screen
        mov     bh,07h          ; standard attributes
        int     screen          ; scroll it down
        mov     dx,crt_brgt
        xor     dl,dl           ; 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,bot_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: mov     dx,cursor               ; these may need cursor...
        jmp     ttstate                 ; jump according to current state

outtt0: cmp     al,7fh                  ; <DEL>?
        jae     outign                  ; ...yes, ignore
        cmp     al,20h                  ; control character?
        jb      outtt1                  ; ...yes, process specially.
;
        cmp     insmod,0                ; in insert mode?
        je      outnrm                  ; no, output normal
        push    ax                      ; save character
        call    inschr                  ; insert a character
        pop     ax
;
outnrm: call    outrns                  ; translate to appropriate ch set.
        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      updcur                  ; in range, update cursor position
        test    flags1,lnwrap           ; in wrap mode?
        jz      outign                  ; no, just return w/out updating cursor
        call    chkscrl                 ; are we allowed to scroll?
        jc      outign                  ; ...no, don't move cursor.
do_nl:  xor     dl,dl                   ; ...yes, wrap to new line.
do_lf:  inc     dh
        cmp     dh,byte ptr bot_rgt+1
        jbe     updcur                  ; 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
        mov     cx,top_lft              ; from top left of scrolling region
        mov     dx,bot_rgt              ; ...to bottom right.
        mov     bh,07h                  ; nice attribute
        int     screen                  ; do the scroll
        pop     dx
        mov     dh,byte ptr bot_rgt+1   ; go to bottom line again...
updcur: xor     bh,bh                   ; page is 0
        mov     cursor,dx               ; save cursor pos
        mov     ah,2
        int     screen                  ; set cursor
outign: ret                             ; and return

;
; control 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.
        call    typctl                  ; ...yes, type it as ^#
        ret

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

; control char routines.  cursor is in dx, char in al

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      outab2                  ; ...no, go set it
        test    flags1,lnwrap           ; in wrap mode?
        jz      outab1                  ; ...no, just move to end of line
        call    chkscrl                 ; are we allowed to wrap here?
        jnc     do_nl                   ; ...yes, wrap and scroll if needed
outab1: mov     dl,byte ptr bot_rgt     ; ...no, just move to right margin
outab2: jmp     updcur

outnl:  xor     dl,dl
outlf:  call    chkscrl                 ; are we allowed to scroll?
        jc      updcur                  ; ...no just do a <CR> for <NL>
        jmp     do_lf                   ; ...yes, go to it.

outbs:  or      dl,dl
        jle     jstrtn                  ; col 0, can't back up
        dec     dl                      ; back up col
        jmp     updcur                  ; and use if reasonable

outcr:  xor     dl,dl                   ; carriage-return = set col to 0
        jmp     updcur

; special escape dispatch routines requiring short jumps to cursor routines

escD:   test    flags,emheath           ; emulating H19/VT52?
        jz      outlf                   ; ...no, VT100
        jmp     outbs                   ; ...yes

escE:   test    flags,emheath           ; emulating H19/VT52?
        jz      outnl                   ; ...no, VT100
        jmp     clrscr                  ; ...yes

escH:   test    flags,emheath           ; emulating H19/VT52?
        jz      jstrtn                  ; ...no, VT100 - just return.
        jmp     curhom                  ; ...yes

escM:   test    flags,emheath           ; emulating H19/VT52?
        jz      revind                  ; ...no, VT100
        jmp     dellin                  ; ...yes

;
; process a reverse index (reverse-<LF>) request:
;
revind: cmp     dh,byte ptr top_lft+1   ; are we in the top scroll line?
        je      revin1                  ; ...yes, reverse scroll required
        cmp     dh,0                    ; ...no, are we in the screen
        jle     jstrtn                  ; ...no, ignore
        dec     dh                      ; ...yes, back up a row
        jmp     updcur                  ; and go update cursor
revin1: push    dx                      ; save cursor pos
        mov     ax,701h                 ; scroll down one line
        mov     cx,top_lft              ; from top-left
        mov     dx,bot_rgt              ; to bottom-right
        mov     bh,07h                  ; nice attribute!
        int     screen                  ; scroll it down
        pop     dx                      ; restore cursor.
        jmp     updcur
jstrtn: ret                             ; just return - like 'outign'

;
; VT52 cursor UP and DOWN routines.  LEFT = 'outbs', RIGHT is in 'outnrm'.
;
curup:  test    flags,emheath           ; emulating H19/VT52?
        jnz     revind                  ; yes handle as reverse-<LF>
        cmp     dh,0                    ; on screen?
        jle     jstrtn                  ; ...no, skip this
        dec     dh                      ; ...yes, back up one line
        jmp     updcur

curdwn: test    flags,emheath           ; emulating H19/VT52?
        jnz     outlf                   ; yes handle as <LF>
        cmp     dh,byte ptr crt_brgt+1  ; on screen?
        jge     jstrtn                  ; ...no, ignore this one
        inc     dh                      ; ...yes, increment line number.
        jmp     updcur

;
; Subroutine to check if we are in the scrolling region:
;
chkscrl: cmp    dh,byte ptr top_lft+1   ; are we above the scrolling region?
        jl      noscrl                  ; ...if so can't scroll
        cmp     dh,byte ptr bot_rgt+1   ; ...nor if we're below it!
        jg      noscrl
        clc                             ; clear carry to indicate scroll OK.
        ret
noscrl: stc                             ; set carry to indicate not OK
        ret

;
; Subroutine to translate character sets:
;
outrns: test    flags1,sftout           ; are we in shift-out mode?
        jnz     getG1
getG0:  mov     bl,G0set
        jmp     outrn1
getG1:  mov     bl,G1set
;
outrn1: cmp     al,20h                  ; is it <space>?
        je      useUSA                  ; ...yes, we may not translate that
;
tryUK:  cmp     bl,'A'                  ; UK character set?
        jne     tryVTG
        cmp     al,'#'                  ; Pound sign required?
        jne     useUSA                  ; ...no, same as USA set
        mov     al,9ch                  ; ...yes
        jmp     useUSA
;
tryVTG: cmp     bl,'0'                  ; VT100 special graphics set?
        jne     tryROM1
        cmp     al,'_'                  ; code for graphics character?
        jb      useUSA                  ; ...no, same as USA set
        mov     bl,al
        sub     bl,'_'
        xor     bh,bh
        mov     al,vtgraph[bx]
        jmp     useUSA
;
tryROM1: cmp    bl,'1'                  ; ROM character set?
        jne     tryROM2
        cmp     al,'`'
        jb      ROM1a
        add     al,80h
        jmp     useUSA
ROM1a:  cmp     al,'?'
        jb      ROM1b
        add     al,40h
        jmp     useUSA
ROM1b:  cmp     al,':'
        jb      ROM1c
        add     al,71h
        jmp     useUSA
ROM1c:  cmp     al,','
        jae     useUSA
        add     al,7fh
        jmp     useUSA
;
tryROM2: cmp    bl,'2'                  ; ROM special graphics set?
        jne     useUSA                  ; ...no, all others map to US ASCII
        cmp     al,'p'
        jb      ROM2a
        add     al,80h
        jmp     useUSA
ROM2a:  cmp     al,'`'
        jb      ROM2b
        add     al,50h
        jmp     useUSA
ROM2b:  cmp     al,'@'
        jb      ROM2c
        add     al,80h
        jmp     useUSA
ROM2c:  sub     al,20h
useUSA: ret

;
; Subroutine to type a control character (in 'al') as ^#:
;
typctl: mov     cl,flags1               ; save local flags
        mov     ch,G0set                ; ...and G0 character set
        push    cx                      ;    ...on stack
        call    nrmchs                  ; force US ACSII set temporarily
        push    ax                      ; save char
        mov     al,'^'
        call    outtty                  ; type caret (CTRL)
        pop     ax                      ; recover control character
        add     al,'@'                  ; make printable (alphabetic)
        call    outtty                  ; type alphabetic character
        pop     cx
        mov     G0set,ch                ; restore G0 character set
        mov     flags1,cl               ; ...and local flags
        ret

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

; stolen from bios
outbel: mov     al,10110110b            ; timer initialization
        out     timer+3,al
        mov     ax,beldiv               ; bell 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

curhom: xor     dx,dx                   ; move to top left of screen.
        jmp     updcur

ansescr: cmp    ansarg[0],1             ; erase from beginning of screen?
        je      clrbow                  ; ...yes
        cmp     ansarg[0],2             ; ...no, erase entire screen?
        je      clrscr                  ; ...yes
        jmp     clreow                  ; ...no, assume to end of screen.

clrscr: test    flags,emheath           ; emulating H19/VT52?
        jz      clrsc1
        call    curhom                  ; ...yes, home cursor first.
clrsc1: xor     cx,cx
        mov     dx,crt_brgt
        jmp     clwndw

clrbow: cmp     dl,byte ptr crt_brgt    ; at end of line?
        jz      clrbw1                  ; yes, skip this part...
        push    dx
        call    clrbol                  ; clear from beginning of this line
        pop     dx
        dec     dh                      ; back 1 row
        mov     dl,byte ptr crt_brgt    ; end at last column
clrbw1: cmp     dh,00                   ; top of screen?
        jl      clwret                  ; if not in range, forget it
        xor     cx,cx                   ; start at top left of screen
        jmp     clwndw

clreow: cmp     dl,0                    ; at beginning of line?
        jz      clrew1                  ; yes, skip this part...
        push    dx
        call    clreol                  ; clear to end of this line
        pop     dx
        inc     dh                      ; bump row
        xor     dl,dl                   ; start from col 0
clrew1: cmp     dh,byte ptr crt_brgt+1  ; last line on screen
        ja      clwret                  ; if not in range, forget it
        mov     cx,dx                   ; cx points at first complete line
        mov     dx,crt_brgt             ; dx at bottom right corner

clwndw: mov     ax,700h                 ; clear whole window
        mov     bh,07h                  ; default attribute
        int     screen                  ; go clear it
clwret: ret                             ; ...and return

anselin: cmp    ansarg[0],1             ; erase from beginning of line?
        je      clrbol                  ; ...yes
        cmp     ansarg[0],2             ; ...no, erase entire line?
        je      clrlin                  ; ...yes
        jmp     clreol                  ; ...no, assume to end of line.

clrlin: mov     cx,dx
        xor     cl,cl                   ; start at beginning of line
        mov     dl,byte ptr crt_brgt    ; finish at end of line
        jmp     clwndw

clrbol: push    es
        xor     ch,ch
        mov     cl,dl                   ; current col - 1
        inc     cl                      ; this is # of chars to erase
        xor     dl,dl                   ; start from beginning of line
        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,07h                  ; default attribute
        mov     al,' '                  ; fill char
        rep     stosw                   ; fill line section with spaces
        pop     es
        ret                             ; and return

clreol: push    es
        xor     ch,ch
        mov     cl,crt_cols             ; last col + 1
        sub     cl,dl                   ; this is # of chars to move
        jcxz    clrel1
        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,07h                  ; default attribute
        mov     al,' '                  ; fill char
        rep     stosw                   ; fill line with spaces
clrel1: 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,bot_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,bot_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,byte ptr bot_rgt
;        [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,byte ptr bot_rgt
        dec     dl              ; 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

entins: mov     insmod,0ffh             ; enter insert mode...
        ret                             ; and return

ignor1: mov     igncnt,1                ; ignore another 1 character.
        mov     ttstate,offset ignn
        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

movcur: mov     wcoord,2                ; want two coordinates...
        mov     ttstate,offset getcoord
getclx: ret                             ; and return
;
; get a coordinate for VT52 direct cursor addressing.
;
getcoord:
        sub     al,31                   ; -31 gives <1,1> origin to match
        mov     si,wcoord               ; ANSI parameters.
        dec     si
        mov     byte ptr coord[si],al   ; fill in appropriate coordinate
        mov     wcoord,si               ; update flag
        jnz     getclx                  ; more needed, can't do anything yet
        mov     ttstate,offset outtt0   ; reset state
        mov     dx,coord                ; get coordinates
        jmp     vt52mc                  ; and go jump there
;
; process VT100 direct cursor addressing.
;
ansmvc: mov     dh,ansarg[0]            ; get new position from arguments
        mov     dl,ansarg[1]            ; ...column number was second.
vt52mc: cmp     dh,0                    ; ensure that the new position
        jg      ansmc1                  ; is within the screen.
        xor     dh,dh
        jmp     ansmc3
ansmc1: cmp     dh,crt_lins
        jle     ansmc2
        mov     dh,crt_lins
ansmc2: dec     dh
ansmc3: cmp     dl,0
        jg      ansmc4
        xor     dl,dl
        jmp     ansmc6
ansmc4: cmp     dl,crt_cols
        jle     ansmc5
        mov     dl,crt_cols
ansmc5: dec     dl
ansmc6: jmp     updcur                  ; move without allowing scroll!

; save cursor
savecur:
        mov     oldcur,dx
        ret

; restore cursor
restcur:
        mov     dx,oldcur
        jmp     updcur

; Process ANSI (VT100) escape sequences:

doansi: mov     ansarct,0               ; ANSI argument index is 0.
        mov     cx,20                   ; clear all arguments to 0.
        mov     al,0
        mov     di,offset ansarg
        rep     stosb
        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

        push    bx
        xor     bh,bh
        mov     bl,ansarct              ; get index of current argument
        mov     al,ansarg[bx]           ; get previous value of argument
        mov     dh,10
        mul     dh                      ; ...multiply by 10
        add     al,dl                   ; ...add in this digit (ovfl???)
        mov     ansarg[bx],al           ; store new value.
        pop     bx
        ret                             ; and return

getaa1: mov     ttstate,offset outtt0   ; reset state - assuming complete
        mov     di,offset ansitab       ; ANSI control sequence table
        mov     cx,lanstab              ; length of tbl
        repne   scasb                   ; look for it in tbl
        jz      ansisq1                 ; found, go use it
        jmp     ansignr                 ; not there, just print it
ansisq1: sub    di,offset ansitab+1     ; get offset into tbl
        shl     di,1                    ; convert to word offset
        jmp     ansijmp[di]             ; ...and go to it

ansignr: ret                            ; ignore ANSI sequence.

nxtaarg: cmp    ansarct,20              ; have we had 20 arguments?
        jae     ansqmk
        inc     ansarct                 ; ...no, we can use another!
ansqmk: mov     ttstate,offset getaarg  ; still getting arguments
        ret

;
; execute ANSI cursor movements:
;
ansup:  cmp     ansarg[0],0             ; did we get a count?
        jne     ansup1                  ; ...yes
        inc     ansarg[0]               ; ...no, just one line
ansup1: sub     dh,ansarg[0]
        cmp     dh,0                    ; did this move us to high?
        jge     ansup2                  ; ...no
        xor     dh,dh                   ; ...yes, use top line instead
ansup2: jmp     updcur

ansdwn: cmp     ansarg[0],0             ; did we get a count?
        jne     ansdn1                  ; ...yes
        inc     ansarg[0]               ; ...no, just one line
ansdn1: add     dh,ansarg[0]
        cmp     dh,byte ptr crt_brgt+1  ; did this move us to low?
        jle     ansdn2                  ; ...no
        mov     dh,byte ptr crt_brgt+1  ; ...yes, use bottom line instead
ansdn2: jmp     updcur

anslft: cmp     ansarg[0],0             ; did we get a count?
        jne     anslf1                  ; ...yes
        inc     ansarg[0]               ; ...no, just one character
anslf1: sub     dl,ansarg[0]
        cmp     dl,0                    ; did this move us to far left?
        jge     anslf2                  ; ...no
        xor     dl,dl                   ; ...yes, use left end (0) instead
anslf2: jmp     updcur

ansrgt: cmp     ansarg[0],0             ; did we get a count?
        jne     ansrt1                  ; ...yes
        inc     ansarg[0]               ; ...no, just one character
ansrt1: add     dl,ansarg[0]
        cmp     dl,byte ptr bot_rgt     ; did this move us to far right?
        jle     ansrt2                  ; ...no
        mov     dl,byte ptr bot_rgt     ; ...yes, use right end instead
ansrt2: jmp     updcur

;
; execute H19 multiple line inserts and deletes
;
mltinln: mov    al,ansarg[0]            ; multiple? insert line
        jmp     inslin1

mltdeln: mov    al,ansarg[0]            ; multiple? delete line
        jmp     dellin1

;
; execute H19/VT52 and ANSI set and reset commands:
;
ansset: cmp     ansarg[0],7             ; turn wrap off?
        je      nowrap                  ; ...yes
        ret                             ; no other set options supported

ansrset: cmp    ansarg[0],2             ; switch to VT52 mode?
        je      H19on                   ; ...yes, turn on 'emheath'
        cmp     ansarg[0],7             ; turn wrap on?
        je      dowrap                  ; ...yes
        ret                             ; ...no, ignore all others.

H19on:  or      flags,emheath           ; turn on H19/VT52 mode.
        ret

H19off: and     flags,not(emheath)      ; turn off H19/VT52 mode.
        ret

dowrap: or      flags1,lnwrap           ; turn on wrap mode
        ret                             ; and return

nowrap: and     flags1,not (lnwrap)     ; turn off wrap mode
        ret                             ; and return

;
; select shift-in and shift-out character sets:
;
G0chset: mov    ttstate,offset setG0ch  ; next character will specify which
        ret

setG0ch: mov    ttstate,offset outtt0   ; next character will be normal
        mov     G0set,al                ; store G0 character set identifier
        ret

nrmchs: mov     G0set,'B'               ; select normal US ASCII as G0
shftin: and     flags1,not (sftout)     ; switch back to shift-in (G0) set
        ret                             ; and return

G1chset: mov    ttstate,offset setG1ch  ; next character will specify which
        ret

setG1ch: mov    ttstate,offset outtt0   ; next character will be normal
        mov     G1set,al                ; store G1 character set identifier
        ret

spcgra: mov     G1set,'0'               ; select special graphics as G1
shftout: or     flags1,sftout           ; switch to shift-out (G1) char set
        ret                             ; and return

;
; execute ANSI character attribute command:
;
ansattr: mov     cl,ansarct             ; number of arguments - 1
        inc     cl
        xor     ch,ch
        mov     di,offset ansarg        ; argument list
        push    cx                      ; save pointers for second scan
        push    di
        mov     al,0                    ; 0 => turn off all attributes.
        repne   scasb                   ; scan list for a 0
        pop     di                      ; get pointers back
        pop     cx
        jne     ansat1
        call    nrmvid                  ; turn all attributes off
ansat1: push    cx                      ; save pointers for third scan
        push    di
        mov     al,1                    ; 1 => turn BOLD on
        repne   scasb                   ; scan list for a 1
        pop     di                      ; get pointers back
        pop     cx
        jne     ansat4
        call    boldon                  ; turn BOLD on
ansat4: push    cx                      ; save pointers for fourth scan
        push    di
        mov     al,4                    ; 4 => turn Underscore on
        repne   scasb                   ; scan list for a 4
        pop     di                      ; get pointers back
        pop     cx
        jne     ansat5
        call    undscr                  ; turn pseudo-underscore on
ansat5: push    cx                      ; save pointers for fifth scan
        push    di
        mov     al,5                    ; 5 => turn BLINK on
        repne   scasb                   ; scan list for a 5
        pop     di                      ; get pointers back
        pop     cx
        jne     ansat7
        call    blinkon                 ; turn BLINK on
ansat7: mov     al,7                    ; 7 => turn on reverse video
        repne   scasb                   ; scan list for a 7
        jne     ansatx
        call    reverse                 ; turn reverse video on
ansatx: ret                             ; done, exit

;
; process H19/VT52 and ANSI character attributes
;
invvid: mov     curattr,70h             ; attribute for VT52 inverse video
        ret                             ; ...no BOLD or BLINK

nrmvid: mov     curattr,07h             ; attribute for normal video
        ret                             ; ...BOLD and BLINK off

;
;  non-underscored BOLD = xxxx1111,  underscored BOLD = xxxx1001
;
boldon: mov     al,curattr
        and     al,07h
        cmp     al,01h                  ; is underscore on?
        jne     bold1
        or      curattr,08h             ; ...yes, just add bold.
        ret
bold1:  or      curattr,0fh             ; ...no, add BOLD + f'ground white.
        ret

;
; normal underscore = x000x001,  'reverse' underscore = x010x001
;
undscr: and     curattr,0a9h            ; reverse? background to grey,
        or      curattr,01h             ; ...foreground to underline.
        ret

;
; BLINK = 1xxxxxxx
;
blinkon: or     curattr,80h             ; attribute bit for BLINK
        ret

;
; normal reverse = x111x000,       underscored reverse = x010x001
; BOLD reverse   = x1111111,  BOLD underscored reverse = x0101001
;
reverse: mov    al,curattr
        test    al,20h                  ; is background already grey/white?
        jnz     revsok                  ; ...yes, skip this one
        test    al,03h                  ; are we in underscore mode
        jnz     nounds
        or      al,20h                  ; ...yes, just set grey background
        jmp     revs1
nounds: xor     al,77h                  ; ...no, complement f'gnd & b'gnd
        test    al,08h                  ; is BOLD on?
        jz      revs1                   ; ...no, done
        or      al,0fh                  ; ...yes, need super-white f'gnd.
revs1:  mov     curattr,al
revsok: ret

;
; Process the various report requests - ident, OK, and cursor position.
;
vtident: test   flags,emheath           ; emulating H19/VT52?
        jz      idvt100
        mov     si,offset vt52id        ; ...yes
        mov     cx,lvt52id
        jmp     vtid1
idvt100: mov    si,offset vt100id       ; ...no, VT100.
        mov     cx,lvt100i
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

ansrpt: cmp     ansarg[0],5             ; status report requested?
        je      reptOK                  ; ...yes, report OK
        cmp     ansarg[0],6             ; cursor position requested?
        je      reptcp
        ret                             ; ignore anything else

reptOK: mov     si,offset rptOKst       ; get OK report string
        mov     cx,lrptOKs
        jmp     vtid1                   ; go send it like an ID string.

reptcp: mov     al,dh                   ; get line number
        call    twodig                  ; increment & convert to 2 digits
        mov     rptCPst.line,AX         ; place it in report buffer
        mov     al,dl                   ; get column number
        call    twodig                  ; ...and do likewise
        mov     rptCPst.column,AX
        mov     si,offset rptCPst       ; now pick up CP report string
        mov     cx,lrptCPs
        jmp     vtid1                   ; ...and send it like an ID string.
;
; this subroutine divides the number_in_'al'+1 by 10 and then converts its
; quotient and remainder to ACSII characters 'al' and 'ah' respectively.
; It is assumed that the value in 'al' is less than 99.
;
twodig: xor     ah,ah                   ; clear high byte
        inc     al                      ; increment low byte
        mov     bl,10                   ; divide by 10
        idiv    bl
        or      ax,3030h                ; convert to ASCII numerics
        ret

;
; execute the ANSI set scrolling region command:
;
anscrgn: mov    dh,ansarg[0]            ; get new top line number
        mov     dl,ansarg[1]            ; ...and new bottom line
        cmp     dh,0                    ; ensure that both lines
        jg      anscr1                  ; are within the screen.
        xor     dh,dh
        jmp     anscr3
anscr1: cmp     dh,crt_lins
        jle     anscr2
        mov     dh,crt_lins
anscr2: dec     dh                      ; parameter origin is <1,1>
anscr3: cmp     dl,0
        jg      anscr4
        xor     dl,dl
        jmp     anscr6
anscr4: cmp     dl,crt_lins
        jle     anscr5
        mov     dl,crt_lins
anscr5: dec     dl
anscr6: cmp     dh,dl                   ; the top line number must be
        jge     anscr7                  ; less than the second.
        mov     byte ptr top_lft+1,dh
        mov     byte ptr bot_rgt+1,dl
anscr7: xor     dx,dx                   ; move to HOME position
        jmp     updcur

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
