       page    60,132
       title MSYIBM screen and keyboard handlers
; edit history:
;
; [v2.29]
; Ansi set graphics rendition support extended to handle color attributes
;   in addition to monochrome
; Direct moves to and from the video buffer modified using TOPVIEW
;   interrupt codes, so that KERMIT can run in the background under
;   TOPVIEW and take advantage of TOPVIEW windowing support.
;
;
; [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
       include msdefs.h

; some character definitions

chesc  equ     27
bel    equ     7

print_out equ  05h                     ; dos function to print to printer
pbout  equ     02h                     ; dos function to print a character
prscan equ     72h                     ; print-screen scan code...
upscan equ     49h                     ; up page
dnscan equ     51h                     ; down page
ctlup  equ     84h                     ; ctl-up page
ctldn  equ     76h                     ; ctl-down page
homscn equ     47h                     ; home screen
endscn equ     4fh                     ; end of screen
screen equ     10h                     ; bios screen call
kb     equ     16h                     ; keyboard interrupt
alt_shift equ  8H                      ; alt shift key down
ctl_shift equ  4H                      ; ctl key down
left_shift equ 2H                      ; left shift key down
right_shift equ 1H                     ; right shift key down

timer  equ     40h                     ; timer port
bel_prt equ    61h                     ; speaker control

crt_status equ 3dah                    ; crt status port
disp_enb   equ 8                       ; display enable bit

uparr  equ     48h                     ; scan codes for arrow keys
dnarr  equ     50h
lftarr equ     4bh
rgtarr equ     4dh


modfrm struc                           ; format of mode line
       db      'Esc chr: '
m_echr db      2 dup (?)
       db      ', Port: '
m_prt  db      1 dup (?)
       db      ', Speed: '
m_baud db      4 dup (?)
       db      ', Parity: '
m_par  db      4 dup (?)
       db      ', Echo: '
m_echo db      3 dup (?)
       db      ', Type '
m_hlp  db      2 dup (?)
       db      '? for Help'
modfrm ends

datas  segment public 'datas'
waste  db      100h dup (?)            ;*** need this junk because assembler
                                       ;*** generates non-relocatable offsets
                                       ;*** for things like
                                       ;*** "sub di,offset foo"
                                       ;*** if offset foo < 100H
; stuff for screen routines
flags  db      ?                       ; status flags...
flags1 db      0                       ; internal flags.
prtscr equ     80h                     ; print screen pressed
inited equ     08h                     ; been here before...
wrapped equ    04h                     ; on if wrapped on last char...
cursor dw      ?
esc_ch db      ?
argadr dw      ?                       ; address of arg blk
ckeys  db      0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
       db      uparr,dnarr,lftarr,rgtarr
lckeys equ     $-ckeys
; ckacts must parallel ckeys above...
ckacts dw      trnbrk,trnprs,dnwpg,upwpg,endwnd,homwnd,upwind,dnwind
       dw      trnupw,trndnw,trnlfw,trnrgw

uptrn  db      esc,'A'
dntrn  db      esc,'B'
rgtrn  db      esc,'C'
lftrn  db      esc,'D'

spctab db      chesc,cr,lf,bs,tab,bel
lspctab equ    $-spctab
spcjmp dw      outesc,outcr,outlf,outbs,outtab,outbel  ; must match spctab



esctab db      'YABCDEFGHIJKLM'
       db      'NOZ@[pq<vw'
       db      'jkxy'
lesctab equ    $-esctab
; escjmp must parallel esctab above
escjmp dw      movcur,curup,curdwn,currt,outbs,clrscr,outign,outign,curhom
       dw      revind,clreow,clreol,inslin,dellin,delchr,noins
       dw      vtident,entins,doansi
       dw      invvid,nrmvid,outign,dowrap,nowrap
       dw      savecur,restcur,setmode,clrmode
vtidstr db     chesc,'/K'
lvtidst equ    $-vtidstr
coord  dw      ?
insmod db      ?
wcoord dw      ?
ttstate dw     outtt0
curattr db     ?                       ; current attribute
ansarg db      10 dup (?)              ; ansi argument values
ansargl equ    $-ansarg                ; length of table
ansidx dw      ?                       ; index into table
grtab  db      0,1,4,5,7               ; supported graphics renditions
       db      8,30,31,32,33,34,35,36,37  ;color foreground [v2.29]
       db      40,41,42,43,44,45,46,47    ;color background [v2.29]
grtlen equ     $-grtab
; the following two tables must parallel grtab
attatab db     07h,0ffh,0f8h,0ffh,088h ; attribute bits to leave on
       db      00h,0f8h,0f8h,0f8h,0f8h,0f8h,0f8h,0f8h,0f8h ;[v2.29]
       db      8fh,8fh,8fh,8fh,8fh,8fh,8fh,8fh             ;[v2.29]
attotab db     07h,08h,01h,080h,070h   ; attributes to set
       db      00h,00h,04h,02h,0eh,01h,05h,03h,07h         ;[v2.29]
       db      00h,40h,20h,0e0h,10h,50h,30h,70h            ;[v2.29]
modesw dw      ?
igncnt db      ?                       ; # of chars to ignore
beldiv dw      2dch                    ; 550 hz?
tv_segs dw     ?            ; topview screen segment            [v2.29]
tv_sego dw     ?            ; topview screen offset             [v2.29]
tv_mode db     0            ; topview environment flag          [v2.29]
crt_mode db    ?
crt_cols db    ?
crt_lins db    ?
low_rgt dw     ?                       ; lower right corner of window
; key redefinitions
ktrntab dw     ?                       ; address of translation table
krpltab dw     ?                       ; address of replacement table
tmptab db      0eh,3bh                 ; scan code for bs, f1
ktlen  dw      ?
modbuf modfrm  <>                      ; mode line buffer
; routine to call for captured output
captrtn dw     ?
oldcur dw      0                       ; save'd cursor position
; some static data for mode line
unkbaud db     'Unk '                  ; must be 4 chars...
baudn  db      '45.5'
       db      '  50'
       db      '  75'
       db      ' 110'
       db      ' 135'
       db      ' 150'
       db      ' 300'
       db      ' 600'
       db      '1200'
       db      '1800'
       db      '2000'
       db      '2400'
       db      '4800'
       db      '9600'
       db      '19.2'
       db      '38.4'
baudnsiz  equ  16                      ; # of baud rates known (tbl size / 4)
parnams db     'Even'
       db      'Mark'
       db      'None'
       db      'Odd '                  ; must be 4 chars
       db      'Spc '
offmsg db      'Off'
onmsg  db      'On '
lclmsg db      'Lcl'
remmsg db      'Rem'
; 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      ?


; 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

tlbuf  dw      ?                       ; db    swidth*2 dup (?)
blbuf  dw      ?                       ; db    swidth*2 dup (?)
twnd   cbuf    <>
bwnd   cbuf    <>
portno db      ?
prton  db      'Printer: on'
prtnlen equ    $-prton
prtoff db      'Printer: off'
prtflen equ    $-prtoff
lincur dw      ?
datas  ends

code   segment public                  ; code segment
       extrn   prtchr:near,outchr:near,sendbr:near,malloc:near
       assume  cs:code,ds:datas,es:datas

; do initialization local to this module...
lclyini proc   near
       mov     ax,swidth*slen*2
       call    malloc
       mov     scrsav,ax               ; place to save screens
       mov     ax,swidth*2
       call    malloc
       mov     tlbuf,ax
       mov     ax,swidth*2
       call    malloc
       mov     blbuf,ax                ; top and bottom temp line buffers
       mov     ax,bsize
       call    malloc
       mov     twnd.pp,ax
       mov     twnd.orig,ax
       add     ax,bsize-1
       mov     twnd.bend,ax
       mov     ax,bsize
       call    malloc
       mov     bwnd.pp,ax
       mov     bwnd.orig,ax
       add     ax,bsize-1
       mov     bwnd.bend,ax
       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     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
       mov     lincur,cx               ; save line (?) cursor value
       cmp     dh,crt_lins             ; past logical end of screen?
       jb      scrin2                  ; no, keep going
       mov     dh,byte ptr low_rgt+1   ; yes, just use lower right corner
scrin2: cmp    dl,crt_cols             ; maybe past right margin
       jb      scrin3                  ; no, use the way it is
       mov     dl,byte ptr low_rgt
scrin3: mov    cursor,dx               ; init cursor
scrin4: mov    ah,2
       xor     bh,bh
       int     screen                  ; set cursor in case it moved
       ret
scrini endp

argini proc    near                    ; read passed arguments
       mov     bx,argadr               ; base of argument block
       mov     al,[bx].flgs            ; get flags
       and     al,capt+emheath+havtt+trnctl+lclecho+modoff+lnwrap
       mov     flags,al                ; mask for allowable and save
       and     flags1,not (prtscr)     ; these are allowable
                                       ; (others remain).
       mov     al,[bx].prt
       cmp     al,portno               ; using same port?
       je      argin1                  ; yes, go on
       and     flags1,not inited       ; else re-init stuff
argin1: mov    portno,al               ; update port number
       mov     al,[bx].cols
       mov     crt_cols,al
       mov     al,[bx].rows
       mov     crt_lins,al             ; init # of rows and cols
       mov     ax,[bx].captr
       mov     captrtn,ax              ; buffer capture routine
       mov     ax,[bx].belld
       mov     beldiv,ax               ; bell divisor
       mov     ax,[bx].klen
       mov     ktlen,ax                ; length of key redef tbl
       mov     ax,[bx].ktab
       mov     ktrntab,ax              ; save key translation table
       mov     ax,[bx].krpl
       mov     krpltab,ax
       mov     al,[bx].escc
       mov     esc_ch,al
       ret                             ; that's it
argini endp

modlin proc    near                    ; turn on mode line
       mov     al,esc_ch
       mov     modbuf.m_echr,' '       ; first char is initial space
       mov     modbuf.m_hlp,' '        ; goes here too.
       cmp     al,32                   ; printable?
       jnb     modl1                   ; yes, keep going
       add     al,40h                  ; made printable
       mov     modbuf.m_echr,'^'       ; note control char
       mov     modbuf.m_hlp,'^'
modl1: mov     modbuf.m_echr+1,al      ; fill in character
       mov     modbuf.m_hlp+1,al
       mov     bx,argadr               ; get argument block
       mov     al,[bx].baudb           ; get baud bits
       mov     si,offset unkbaud       ; assume unknown baud
       cmp     al,baudnsiz             ; too big?
       jnb     modl2                   ; yes, use default
       mov     cl,2                    ; each is 4 bytes long
       shl     al,cl
       mov     ah,0
       add     ax,offset baudn
       mov     si,ax
modl2: mov     cx,size m_baud          ; length of baud space
       mov     di,offset modbuf.m_baud
       rep     movsb                   ; copy in baud rate
       mov     al,[bx].parity          ; get parity code
       mov     cl,2                    ; each is 4 bytes long...
       shl     al,cl
       mov     ah,0
       add     ax,offset parnams       ; names of parity settings
       mov     si,ax
       mov     cx,4                    ; each is 4 long
       mov     di,offset modbuf.m_par
       rep     movsb
       mov     si,offset remmsg        ; Assume remote echoing.
       test    flags,lclecho           ; Is remote side echoing?
       jz      modl4                   ; Yes, keep going
       mov     si,offset lclmsg        ; Else it's local echoing.
modl4: mov     cx,3                    ; size of on/off
       mov     di,offset modbuf.m_echo
       rep     movsb
       mov     al,'1'
       cmp     portno,1                ; port 1?
       je      modl5                   ; yes, keep going
       mov     al,'2'
modl5: mov     modbuf.m_prt,al         ; fill in port number
       mov     cx,size modfrm          ; this is size of mode line
       mov     si,offset modbuf        ; mode line image
; alternate entry to write an alternate mode line
modwrt: push   cx
       push    si                      ; save mode line and size
       mov     cx,1800h                ; line 25...
       mov     dx,184fh
       mov     ax,600h                 ; scroll to clear the line
       mov     bh,70h                  ; inverse video
       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
       mov     dx,cursor
       mov     ah,2
       mov     bh,0
       int     screen                  ; put cursor back where it belongs
       ret                             ; and return
modlin endp

clrmod proc    near                    ; clear mode line
       mov     ax,600h                 ; blank window
       mov     cx,24 * 100h            ; beginning of window
       mov     dx,24 * 100h + 79       ; end of window
       mov     bh,07                   ; nice attribute
       int     screen                  ; clear mode line
       ret                             ; and return
clrmod endp

term   proc    near                    ; terminal emulator entry point

       mov     argadr,ax               ; save argument ptr
       push    es                      ; save caller's extra segment address
       mov     ax,seg datas
       mov     es,ax

       call    argini                  ; init options from arg address

       call    scrini                  ; init screen stuff

       test    flags1,inited           ; have we run yet?
       jz      term1                   ; no, forget this part
       call    restscr                 ; restore screen
term1: or      flags1,inited           ; remember we've run already.

       call    clrmod                  ; empty mode line
       test    flags,modoff            ; is mode line disabled?
       jnz     lp                      ; yes, skip it
       call    modlin                  ; turn on mode line

lp:    call    portchr                 ; char at port?
        jnc    chkinp                  ; no, keep going
       call    outtty                  ; print on terminal

chkinp: mov    ah,1
       int     kb
       jz      lp                      ; nothing available...
       xor     ah,ah
       int     kb                      ; get the char from the buffer
       push    ax                      ; save character temporarily
       call    gss                     ; get shift state into al
       mov     bl,al                   ; save shift state
       pop     ax

       cmp     al,esc_ch               ; escape character?
       je      quit                    ; yes, stop here

       call    trnout                  ; translate if nec., output to prt
       jmp     chkinp                  ; and keep going

quit:  call    clrmod                  ; erase mode line
       call    savescr                 ; save screen

       mov     al,flags
       mov     bx,argadr
       mov     [bx].flgs,al            ; update flags in arg block
       pop     es                      ; restore segment register
       ret                             ; and return to caller

term   endp

; get shift state into al.  We only care about shift, ctl, and alt keys.
; right shift is collapsed into left shift.
gss    proc    near
       mov     ah,2
       int     kb                      ; get current shift state
       mov     bl,al                   ; copy for a moment
       and     bl,right_shift          ; mask out all but right shift
       shl     bl,1                    ; move right shift to left shift pos
       or      al,bl                   ; collapse shift bits
       and     al,(left_shift + alt_shift + ctl_shift)
       ret
gss    endp

; save the screen so we can restore it
; maybe save cursor also.
savescr proc   near
       push    ds
       push    es                      ;save across scrseg call [v2.29]
       call    scrseg                  ; get tv screen buffer   [v2.29]
       mov     si,di                   ; set source offset      [v2.29]
       mov     di,scrsav               ; place to put screen
       mov     cx,80*24                ; # of words on screen
       call    scrwait                 ; wait for screen to be ready
       push    es                      ; transfer es to ds      [v2.29]
       pop     ds                      ; address screen
       pop     es                      ; restore entry es       [v2.29]
       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,scrsav               ; source
       mov     cx,80*24
       call    scrseg                  ; get tv screen buffer   [v2.29]
       call    scrwait
       rep     movsw                   ; restore it
       mov     cx,80*24                ; set length for scrsync [v2.29]
       call    scrsync                 ; synchronize topview    [v2.29]
       pop     es
       ret
restscr endp

; send the character in al out to the serial port
; handle echoing also...
outprt proc    near
       test    flags,lclecho           ; echoing?
       jz      outpr1                  ; no, forget it
       push    ax                      ; save char
       call    outtty                  ; print it
       pop     ax                      ; restore
outpr1: mov    ah,al                   ; this is where outchr expects it
       call    outchr                  ; output to the port
        nop
        nop
        nop                            ; skip returns...
       ret
outprt endp

; returns with carry on if a character is available

portchr proc   near
       call    prtchr                  ; character at port?
        jmp    short portc1            ; yes, go handle
       nop                             ; skip return is stupid...
       clc                             ; no carry -> no character
       ret                             ; and return...
portc1: and    al,7fh                  ; we don't worry about parity here
       stc                             ; have a character
       ret                             ; and return
portchr endp


; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout proc    near
       test    flags,havtt             ; translate table given?
       jz      trnou3                  ; no, just output character
       push    ax                      ; save original value
       mov     al,ah                   ; put scan code into ah
       mov     ah,bl                   ; shift state into top half.
       mov     di,ktrntab              ; pick up translate tbl
       mov     cx,ktlen                ; length of tbl
       repne   scasw                   ; look for our key
       pop     ax                      ; recover character
       jne     trnou3                  ; not found, forget it
       sub     di,ktrntab              ; get index into tbl
       sub     di,2                    ; (minus 2 for pre-increment)
       mov     bx,krpltab              ; get replacement table
       mov     si,[bx][di]             ; and addr of replacement
       mov     cl,[si]                 ; get first byte (length)
       xor     ch,ch                   ; clear high-order byte
       inc     si                      ; point to translation string
trnou2: lodsb                          ; get a byte
       push    si
       push    cx                      ; save important registers
       call    outprt                  ; send to port
       pop     cx
       pop     si
       loop    trnou2                  ; send all chars
       ret                             ; and return
trnou3: cmp    ah,4eh                  ;*** plus key thing?
       je      trnmod                  ; yes, go toggle mode line
       cmp     al,0                    ; is it a special code?
       jne     trnou4                  ; no, don't do this
       mov     al,ah                   ; get scan code
       mov     cx,lckeys               ; length of table
       mov     di,offset ckeys         ; table address
       repne   scasb
       mov     al,0                    ; ascii code was 0...
       jne     trnou4                  ; not found, keep going
       sub     di,offset ckeys+1       ; get table offset
       shl     di,1                    ; shift for word offset
       jmp     ckacts[di]              ; jump to appropriate routine
trnou4: call   outprt                  ; just output single char
       ret                             ; and return

trnmod: test   flags,modoff            ; mode line already off?
       jnz     trnm1                   ; yes, go turn on
       call    clrmod                  ; no, clear mode line here
       or      flags,modoff            ; turn on flag
       ret                             ; and return
trnm1: call    modlin                  ; turn on mode line
       and     flags,not modoff        ; clear flag
       ret                             ; and return

trnbrk: mov    ah,dconio
       mov     dl,0ffH
       int     dos                     ; read the bogus ^C DOS gets.
       call    sendbr
       ret
trnprs: xor    flags1,prtscr           ; flip the flag
       and     flags,not modoff        ; turn on mode line
       mov     si,offset prton
       mov     cx,prtnlen
       test    flags1,prtscr           ; did it go on?
       jnz     trnpr1                  ; yes, say so
       mov     si,offset prtoff
       mov     cx,prtflen
trnpr1: call   modwrt                  ; write into mode line
       ret                             ; and return

; common entry for arrow keys
trnarr: mov    cx,2                    ; length is always 2
       jmp     trnou2                  ; go send definition

trnupw: mov    si,offset uptrn
       jmp     trnarr

trndnw: mov    si,offset dntrn
       jmp     trnarr

trnlfw: mov    si,offset lftrn
       jmp     trnarr

trnrgw: mov    si,offset rgtrn
       jmp     trnarr

trnout endp

; move viewing window up (screen moves down).
; alternate entry upwin2 doesn't beep if invalid.
upwind proc    near
       mov     ax,tlbuf        ; place to put line temporarily
       mov     bx,offset twnd ; where to get lines from
       call    getcirc         ; try to get a line
       jnc     upwin3          ; have a line, go show it
       call    outbel          ; else ring bel
       ret                     ; and return
upwin2: mov    ax,tlbuf
       mov     bx,offset twnd
       call    getcirc
       jnc     upwin3
       ret                     ; this just rets if no line avail.
upwin3: mov    ax,blbuf        ; place for bottom line
       call    getbot          ; fetch bottom line
       mov     ax,blbuf
       mov     bx,offset bwnd
       call    putcirc         ; save in circular buffer
       mov     ax,701h         ; scroll down one line
       xor     cx,cx           ; from top
       mov     dx,low_rgt      ; to bottom
       mov     bh,07           ; normal attributes
       int     screen          ; scroll it down
       mov     si,tlbuf        ; where to get line from
       mov     cx,swidth       ; length of line
       push    es
       call    scrseg          ; get tv screen buffer           [v2.29]
       call    scrwait
       rep     movsw           ; copy the line in
       mov     cx,swidth              ; set length for scrsync  [v2.29]
       call    scrsync                 ; synchronize topview    [v2.29]
       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,blbuf        ; place to put line temporarily
       mov     bx,offset bwnd ; where to get lines from
       call    getcirc         ; try to get a line
       jnc     dnwin3          ; have a line, go show it
       call    outbel          ; else ring bel
       ret                     ; and return
dnwin2: mov    ax,blbuf
       mov     bx,offset bwnd
       call    getcirc
       jnc     dnwin3
       ret                     ; this just rets if no line avail.
dnwin3: call   scrprep         ; save top line
       mov     ax,601h         ; scroll up one line
       xor     cx,cx           ; from top
       mov     dx,low_rgt      ; to bottom
       mov     bh,07           ; normal attribute
       int     screen          ; scroll it down
       mov     dx,low_rgt
       mov     dl,0            ; get addr of last line
       call    scrloc
       mov     di,ax           ; this is offset in dest
       mov     si,blbuf        ; where to get line from
       mov     cx,swidth       ; length of line
       push    es
       push    di              ; save offset in dest            [v2.29]
       call    scrseg          ; get tv screen buffer           [v2.29]
       pop     ax              ; restore offset in dest         [v2.29]
       add     di,ax           ; offset to last line            [v2.29]
       call    scrwait
       rep     movsw           ; copy the line in
       mov     cx,swidth              ; set length for scrsync  [v2.29]
       call    scrsync                 ; synchronize topview    [v2.29]
       pop     es              ; restore this
       ret                     ; and return
dnwind endp

; move viewing window down as much as possible...
endwnd proc    near
       mov     cx,1000                 ; large number of lines
       jmp     dnwp1                   ; and enter dwnpg
endwnd endp

; scroll viewing window down (contents move up) crt_lins times...
dnwpg  proc    near
       mov     cl,crt_lins
       mov     ch,0
dnwp1: push    cx                      ; save this
       call    dnwin2
       pop     cx
       loop    dnwp1
       ret                             ; and return
dnwpg  endp

; home viewing window
homwnd proc    near
       mov     cx,1000                 ; large # of lines
       jmp     upwp1                   ; join upwpg
homwnd endp

; scroll viewing window up (screen moves down) a page
upwpg  proc    near
       mov     cl,crt_lins
       mov     ch,0
upwp1: push    cx
       call    upwin2
       pop     cx
       loop    upwp1
       ret                             ; and return
upwpg  endp

; get the bottom line into the buffer pointed to by ax.
getbot proc    near
       push    ds
       mov     di,ax                   ; save dest
       mov     cx,swidth
       mov     dx,low_rgt
       mov     dl,0
       call    scrloc
       mov     si,ax
       push    es                      ; save es across scrseg  [v2.29]
       push    si                      ; save screen location   [v2.29]
       call    scrseg                  ; get tv screen buffer   [v2.29]
       mov     si,di                   ; set scrn buff offset   [v2.29]
       pop     ax                      ; restore screen loc     [v2.29]
       add     si,ax                   ;   relative to scrn buf [v2.29]
       call    scrwait
       push    es                      ; transfer es to ds      [v2.29]
       pop     ds
       pop     es                      ; set dest segment       [v2.29]
       rep     movsw
       pop     ds
       ret
getbot endp

; get 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
       push    es                      ; save across scrseg     [v2.29]
       mov     cx,swidth               ; length of line
       call    scrseg                  ; get tv screen buffer   [v2.29]
       mov     si,di                   ; set source offset      [v2.29]
       mov     di,tlbuf                ; place to put line temporarily
       call    scrwait
       push    es                      ; transfer es            [v2.29]
       pop     ds
       pop     es                      ; restore entry es       [v2.29]
       rep     movsw                   ; copy the line
       pop     ds                      ; restore this
       mov     ax,tlbuf
       mov     bx,offset twnd          ; this is where it goes
       call    putcirc                 ; put into buffer
       ret                             ; and return
scrprep endp


; put the character in al to the screen
outtty proc    near
       test    flags,capt              ; capturing output?
       jz      outnoc                  ; no, forget this part
       push    ax                      ; save char
       call    captrtn                 ; give it captured character
       pop     ax                      ; restore character and keep going

outnoc: test   flags1,prtscr           ; should we be printing?
       jz      outnop                  ; no, keep going
       push    ax
       mov     ah,print_out
       mov     dl,al                   ; put character here for dos...
       int     dos
       pop     ax

outnop: test   flags,emheath           ; emulating heath?
       jnz     outnop1                 ; yup, go do something smart
       mov     dl,al
       mov     ah,pbout
       int     dos                     ; else let dos print char
       ret                             ; and return

outnop1:mov    dx,cursor               ; these may need cursor...
       jmp     ttstate                 ; jump according to current state

outtt0:
       cmp     al,32                   ; special character?
       jb      outtt1                  ; yes, handle specially...

       cmp     insmod,0                ; in insert mode?
       je      outnrm                  ; no, output normal
       push    ax                      ; save character
       call    inschr                  ; insert a character
       pop     ax
outnrm: xor    bh,bh                   ; current page
       mov     cx,1                    ; only one char
       mov     bl,curattr              ; with current attribute
       mov     ah,9
       int     screen                  ; put onto screen
       mov     dx,cursor               ; get cursor pos
currt: inc     dl                      ; bump col
       cmp     dl,crt_cols             ; see if in range
       jb      setcur                  ; in range, go set cursor
       test    flags,lnwrap            ; in wrap mode?
       jz      outign                  ; no, just return w/out updating cursor
wrap:  xor     dl,dl
       inc     dh                      ; handle wrap
       or      flags1,wrapped          ; remember we wrapped
       jmp     short setc0             ; and don't turn off flag
setcur: and    flags1,not wrapped      ; if moving cursor, turn off wrap flag
setc0: cmp     dh,crt_lins
       jb      setc1                   ; not off end, keep going
       push    dx                      ; save row/col
       call    scrprep                 ; save top line in window buf
       mov     ax,0601h                ; scroll up one line
       xor     cx,cx                   ; from 0,0
       mov     dx,low_rgt              ; to 24,80
       mov     bh,7                    ; nice attribute
       int     screen                  ; do the scroll
       pop     dx
       mov     dh,crt_lins             ; go to bottom line again...
       dec     dh
setc1: xor     bh,bh                   ; page is 0
       mov     cursor,dx               ; save cursor pos
       mov     ah,2
       int     screen                  ; set cursor
outign: ret                            ; and return
; special character (in al)
outtt1: mov    di,offset spctab        ; special char table
       mov     cx,lspctab              ; length of tbl
       repne   scasb                   ; look for char in tbl
       jz      outtt2                  ; found, go do something with it
       test    flags,trnctl            ; are we allowed to print carets?
       jz      outign                  ; no, just ignore it.
       push    ax                      ; save char
       mov     al,'^'
       call    outtty                  ; print caret
       pop     ax
       add     al,'A'-1                ; make printable
       jmp     outtty                  ; print, then return

outtt2: mov    dx,cursor               ; might need cursor pos
       sub     di,offset spctab+1      ; get index of char
       shl     di,1                    ; double for word offset
       jmp     spcjmp[di]              ; and go handle

; special char routines.  cursor is in dx, char in al

outlf:;;;[jrd] test    flags1,wrapped          ; just wrapped?
      ;;;[jrd] jz      outlf1                  ; no, proceed normally
      ;;;[jrd] and     flags1,not wrapped      ; else turn off wrap flag
      ;;;[jrd] ret                             ; and skip this lf

outlf1: inc    dh                      ; bump row
       jmp     setcur

outcr:;;;[jrd] test    flags1,wrapped  ; just wrapped?
      ;;;[jrd] jz      outcr1          ; no, continue
      ;;;[jrd] ret                     ; yes, already in col 0
outcr1: xor    dl,dl                   ; set col to 0
       jmp     setcur

outbs: or      dl,dl
       jg      outbs1                  ; > col 0, just back up
       test    flags,lnwrap            ; auto wrap on?
       jz      outbs2                  ; no, stay at left margin
       or      dh,dh                   ; at top of screen?
       jz      outbs2                  ; yes, stay where we are
       mov     dl,crt_cols             ; get rightmost column
       dec     dh                      ; go up a row
outbs1: dec    dl                      ; back up a column
outbs2: jmp    setcur                  ; go set cursor

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?
       jae     outta1                  ; yes, skip it
       jmp     setcur                  ; else set it
outta1: test   flags,lnwrap            ; in wrap mode?
       jnz     outta2                  ; yes, wrap to next line
       mov     dl,byte ptr low_rgt     ; else just move to right margin
       jmp     setcur
outta2: jmp    wrap

; stolen from bios
outbel: mov    al,10110110b            ; timer initialization
       out     timer+3,al
       mov     ax,beldiv               ; bel divisor
       out     timer+2,al
       mov     al,ah
       out     timer+2,al              ; output divisor
       in      al,bel_prt
       mov     ah,al                   ; remember original value
       or      al,3                    ; turn speaker on
       out     bel_prt,al
       mov     cx,8888h
outbe1: loop   outbe1                  ; wait a while
       mov     al,ah
       out     bel_prt,al              ; turn bell off
       ret                             ; and return

outesc: mov    ttstate,offset escseq   ; expect escape sequence.
       ret                             ; and return

; escape-char handling routines
escseq: mov    ttstate,offset outtt0   ; put state back to normal
       mov     di,offset esctab        ; escape char tbl
       mov     cx,lesctab              ; length of tbl
       repne   scasb                   ; look for it in tbl
       jz      escsq1                  ; found, go use it
       jmp     outnoc                  ; not there, print it (already logged)
escsq1: sub    di,offset esctab+1      ; get offset into tbl
       shl     di,1                    ; convert to word offset
       jmp     escjmp[di]              ; and go dispatch on it

; escape dispatch routines
revind: cmp    dh,0
       jle     revin1
       dec     dh                      ; back up a row
       jmp     setcur                  ; and go set cursor
revin1: push   dx                      ; save cursor pos
       mov     ax,701h                 ; scroll down one line
       xor     cx,cx                   ; from top
       mov     dx,low_rgt              ; to bottom
       mov     bh,07                   ; normal attributes
       int     screen                  ; scroll it down
       pop     dx                      ; restore cursor.
       mov     dh,0                    ; set row back to 0
       jmp     setcur

curup: cmp     dh,0                    ; w/in range?
       jle     curu1                   ; no, skip this
       dec     dh                      ; else back up
curu1: jmp     setcur                  ; and go set position

curdwn: inc    dh
       jmp     setcur                  ; increment row (setcur can scroll!)

; currt is above

clrscr: call   curhom                  ; go home cursor
       jmp     clreow                  ; then clear to end of window

curhom: xor    dx,dx                   ; move to 0,0
       jmp     setcur

clreow: cmp    dl,0                    ; at beginning of line?
       jz      clrw1                   ; yes, skip this part...
       push    dx                      ; remember cursor pos
       call    clreol                  ; clear to end of this line
       pop     dx
       inc     dh                      ; bump row
       xor     dl,dl                   ; start from col 0
clrw1: cmp     dh,crt_lins             ; last line on screen
       jnb     clrw2                   ; if not in range, forget it
       mov     ax,700h                 ; clear whole window
       mov     cx,dx                   ; this is beginning
       mov     dx,low_rgt
       mov     bh,07                   ; normal attribute
       int     screen                  ; go clear it
clrw2: ret                             ; and return

clreol: push   es
       mov     cl,crt_cols             ; last col + 1
       sub     cl,dl                   ; this is # of chars to move
       xor     ch,ch
       jcxz    clrl1
       call    scrloc                  ; compute screen location (to ax)
       mov     di,ax
       push    di                      ; hold screen disp.      [v2.29]
       call    scrseg                  ; get tv screen buffer   [v2.29]
       pop     ax                      ; restore screen disp.   [v2.29]
       add     di,ax                   ; relative to di         [v2.29]
       call    scrwait                 ; wait for retrace
       mov     ah,07                   ; current attribute
       mov     al,' '                  ; fill char
       push    cx                      ; save length for scrsync[v2.29]
       rep     stosw                   ; fill line with spaces
       pop     cx                      ; set length for scrsync [v2.29]
       call    scrsync                  ; synchronize topview   [v2.29]
clrl1: pop     es
       ret                             ; and return

inslin: mov    al,1                    ; scroll one line
; alternate entry if inserting more then one line
inslin1:mov    ch,dh                   ; start at current row
       xor     cl,cl                   ; column 0
       mov     dx,low_rgt
       mov     ah,7h                   ; scroll down.
       mov     bh,07                   ; normal attribute
       cmp     ch,dh                   ; moving last line down?
       jne     insli2                  ; no, keep going
       mov     al,0                    ; yes, just clear it
insli2: int    screen
       ret

dellin: mov    al,1                    ; scroll 1 line
; alternate entry if deleting more than one line
dellin1:mov    ch,dh                   ; start at current row
       xor     cl,cl                   ; column 0
       mov     dx,low_rgt
       mov     ah,6h                   ; scroll up.
       mov     bh,07                   ; normal 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
       push    di              ; save screen disp.              [v2.29]
       call    scrseg          ; get tv screen buffer           [v2.29]
       pop     ax                      ; restore screen disp.   [v2.29]
       add     ax,di                   ; relative to di         [v2.29]
       mov     di,ax                   ; put it in di and si    [v2.29]
       mov     si,ax
       add     si,2            ; source is next position over
       call    scrwait         ; wait for retrace
       push    ds              ; save ds                        [v2.29]
       push    es              ; copy es to ds                  [v2.29]
       pop     ds              ; address screen segment
       push    cx              ; save length for scrsync        [v2.29]
       rep     movsw           ; delete it
       mov     byte ptr [di],' ' ; kill char at end of line
       pop     cx                      ; set length for scrsync [v2.29]
       pop     ds                      ; restore data segment   [v2.29]
       call    scrsync                  ; synchronize topview   [v2.29]
delch1: popf
       pop     es
       pop     ds
       ret

inschr: push   ds
       push    es              ; save these as well
       pushf                   ; might as well save flags...
       mov     dx,cursor       ; this is place to do it
       mov     cl,crt_cols
       dec     cl
;      mov     cl,79           ; this is last col to move, +1 for length
       sub     cl,dl           ; compute distance to end
       xor     ch,ch           ; clear top half of offset
       jcxz    insch1          ; nothing to move...
       mov     dl,crt_cols
       sub     dl,2            ; last col to move
;      mov     dl,78           ; this is address of last col to move
       call    scrloc          ; compute pos
       push    ax              ; save screen disp.              [v2.29]
       call    scrseg          ; get tv screen buffer           [v2.29]
       mov     si,di           ; to si                          [v2.29]
       pop     ax                      ; restore screen disp.   [v2.29]
       add     ax,si                   ; relative to di         [v2.29]
       mov     si,ax
       mov     di,ax
       add     di,2            ; destination is one byte over...
       std                     ; remember to move us backwards
       call    scrwait         ; wait until save to write
       push    ds              ; save data segment ptr          [v2.29]
       push    es              ; copy es to ds                  [v2.29]
       pop     ds              ; address screen segment
       push    cx              ; save length for scrsync        [v2.29]
       rep     movsw           ; move each char and attribute
       pop     cx                      ; set length for scrsync [v2.29]
       sub     di,2            ; destination is one byte over...[v2.29]
       add     di,cx                   ; .. accounting for the  [v2.29]
       add     di,cx                   ; .. backwards move      [v2.29]
       add     di,cx                   ; .. accounting for the  [v2.29]
       add     di,cx                   ; .. backwards move      [v2.29]
       pop     ds              ; restore data segment pointer   [v2.29]
       call    scrsync                  ; synchronize topview   [v2.29]
insch1: popf
       pop     es
       pop     ds
       ret                     ; and return

noins: mov     insmod,0                ; turn off insert mode
       ret                             ; and return

movcur: mov    wcoord,2                ; want two coordinates...
       mov     ttstate,offset getcoord
       ret                             ; and return

vtident: mov   si,offset vtidstr
       mov     cx,lvtidst
vtid1: lodsb                           ; get a byte from the string
       push    si                      ; have to save from outprt
       push    cx
       call    outprt                  ; send to serial port
       pop     cx
       pop     si
       loop    vtid1                   ; go thru all chars
       ret                             ; and return

entins: mov    insmod,0ffh             ; enter insert mode...
       ret                             ; and return

doansi: mov    cx,ansargl              ; # of arg bytes
       mov     al,0
       mov     di,offset ansarg        ; point to arg list
       rep     stosb                   ; clear it
       mov     ansidx,0                ; none seen yet
       mov     ttstate,offset getaarg  ; state is get ansi argument
       ret

getaarg:cmp    al,'0'
       jb      getaa1                  ; in range for digit?
       cmp     al,'9'
       ja      getaa1
       sub     al,'0'                  ; convert to binary
       mov     dl,al                   ; tuck away
       mov     bx,ansidx
       mov     al,ansarg[bx]
       mov     dh,10
       mul     dh                      ; shift sum
       add     al,dl                   ; add in this digit (what about ovfl?)
       mov     ansarg[bx],al
       ret                             ; and return

getaa1: cmp    al,'?'                  ; the dreaded question mark?
       jne     getaa2
       mov     ttstate,offset ignn     ; we ignore these...
       mov     igncnt,2                ; this is how many chars come after him
       ret

getaa2: cmp    al,';'                  ; argument separator?
       jne     getaa5                  ; no, keep going
       inc     ansidx                  ; yes, increment index
       cmp     ansidx,ansargl          ; is it too big?
       jb      getaa6                  ; no, keep going
;### this isn't good, but it beats having these parameters written
; all over our data segment...
       mov     ansidx,1                ; yes, throw some away
getaa6: ret                            ; and return

getaa5: mov    ttstate,offset outtt0   ; reset state
       mov     dx,cursor               ; this needs cursor position
       mov     bl,ansarg
       xchg    al,bl                   ; put argument in nice place
       cmp     bl,'L'                  ; insert line?
       jne     getaa3
       jmp     inslin1                 ; and go do it

getaa3: cmp    bl,'M'                  ; maybe delete line?
       jne     getaa4
       jmp     dellin1

getaa4: cmp    bl,'m'                  ; maybe set graphic rendition
       jne     getaa7                  ; no, keep going
       mov     cx,ansidx               ; argument index
       inc     cx                      ; allow for 0 index origin...
       mov     si,offset ansarg
       call    setgrph                 ; go set it all up
getaa7: ret                            ; ignore.


; set graphic rendition... enter with si/ pointer to argument table,
; cx/ length of table
setgrph:
       mov     ah,curattr              ; get current attributes
       jcxz    setgr3                  ; empty table, stop here
setgr1: lodsb                          ; get a byte from the table
       push    cx
       mov     di,offset grtab         ; table of recognizable attributes
       mov     cx,grtlen               ; length of table
       repne   scasb                   ; look for our char
       jne     setgr2                  ; not found, keep going
       sub     di,offset grtab+1       ; get table index
       and     ah,attatab[di]          ; keep correct bits
       or      ah,attotab[di]          ; and turn on appropriate new bits
setgr2: pop    cx                      ; restore counter
       loop    setgr1                  ; loop thru table
setgr3: mov    curattr,ah              ; restore attributes
       ret                             ; and return

invvid: mov    curattr,70h             ; attribute for inverse video
       ret

nrmvid: mov    curattr,07h             ; attribute for normal video
       ret

dowrap: or     flags,lnwrap            ; turn on wrap mode
       ret                             ; and return

nowrap: and    flags,not lnwrap        ; turn off wrap mode
       ret                             ; and return

; get a coordinate.
getcoord:
       sub     al,32                   ; coordinates offset by 32
       mov     si,wcoord
       dec     si
       mov     byte ptr coord[si],al   ; fill in appropriate coordinate
       mov     wcoord,si               ; update flag
       jnz     getco1                  ; more needed, can't do anything yet
       mov     ttstate,offset outtt0   ; reset state
       mov     dx,coord                ; get coordinates
       jmp     setcur                  ; and go jump there
getco1: ret

; ignore following igncnt characters
ignn:  dec     igncnt                  ; decrement count
       jnz     ignn1
       mov     ttstate,offset outtt0   ; put state back to normal if done
ignn1: ret

; save cursor
savecur:
       mov     oldcur,dx
       ret

; restore cursor
restcur:
       mov     dx,oldcur
       jmp     setcur

setmode:
       mov     modesw,offset setmo1
       mov     ttstate,offset getmod
       ret
; al contains argument digit...
setmo1:
       cmp     al,4                    ; set block mode?
       jne     setmo2                  ; no, go on
       mov     cx,10ch                 ; block cursor
       mov     ah,1                    ; set cursor...
       int     screen
setmo2: ret

clrmode:
       mov     modesw,offset clrmo1
       mov     ttstate,offset getmod
       ret
; al contains argument digit
clrmo1: cmp    al,4                    ; is it set block mode?
       jne     clrmo2
       mov     cx,lincur
       mov     ah,1
       int     screen
clrmo2: ret

; get a single-digit mode argument
getmod: mov    ttstate,offset outtt0   ; reset state
       cmp     al,'0'
       jb      getmo1                  ; out of range
       cmp     al,'9'
       ja      getmo1
       sub     al,'0'                  ; convert to numeric
       jmp     modesw                  ; return to whoever wanted argument
getmo1: ret                            ; just ignore if out of range

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                             [v2.29]
scrseg proc    near         ;                                   [v2.29]
       xor     di,di        ; screen location 0                 [v2.29]
       mov     ax,0b000h    ; assume bw for now                 [v2.29]
       cmp     crt_mode,7   ; 7 is bw (***)                     [v2.29]
       je      scrseg1      ;                                   [v2.29]
       mov     ax,0b800h    ; color card                        [v2.29]
scrseg1: mov    es,ax       ;                                   [v2.29]
       mov     tv_segs,es   ; save hardware                     [v2.29]
       mov     tv_sego,di   ;   screen address                  [v2.29]
       mov     tv_mode,1    ; assume running under topview      [v2.29]
       mov     ah,0feh      ; topview query                     [v2.29]
       int     screen       ; if topview is there               [v2.29]
       mov     ax,es        ;                                   [v2.29]
       cmp     ax,tv_segs   ; ... he will update es:di          [v2.29]
       jne     scrseg2      ; ... to point to a virtual         [v2.29]
       cmp     di,tv_sego   ; ... screen buffer which we        [v2.29]
       jne     scrseg2      ; ... can update while running      [v2.29]
       mov     tv_mode,0    ; ..  in background mode            [v2.29]
scrseg2: ret                ;                                   [v2.29]
scrseg  endp                ;                                   [v2.29]
                            ;                                   [v2.29]
; synchronize a topview provided virtual screen buffer          [v2.29]
;   with the image the user sees                                [v2.29]
scrsync  proc   near        ;                                   [v2.29]
       cmp     tv_mode,0               ; topview mode?          [v2.29]
       je      tvsyn1                  ; no, skip dos call      [v2.29]
       sub     di,cx                   ; back up to start byte  [v2.29]
       sub     di,cx                   ; .. after word moves    [v2.29]
       mov     ah,0ffh                 ; topview synch request  [v2.29]
       int     screen                  ; call him               [v2.29]
tvsyn1: ret                            ;                        [v2.29]
scrsync  endp                          ;                        [v2.29]

; wait for retrace so can write to screen memory
scrwait proc   near
       cmp     tv_mode,1               ; topview mode?          [v2.29]
       je      scrwa3                  ; yes, no waiting        [v2.29]
       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
