    NAME    msy55x
; File MSY55X.ASM
; Sanyo version by Robert W. Babcock and Joe H. White
; For version which replaces the BIOS keycode translation table,
;  use -dMODIFIED flag for MASM
; Last edit 11 May 1988
; edit history:
; 15 Sept 1987 version which runs with or without optional video board [rwb]
; 1 Jan 1988 version 2.30
; Add mods to msyibm to produce Sanyo version 16 Aug, 1987 [rwb]
; 24 Dec 1987 Restore startup screen attributes at Kermit prompts. [jrd]
; 21 Dec 1987 Fix memory size sign problem for >640K systems. From Edgar Butt
; 4 Dec 1987 cleanup mode switching, add more Video 7 material. [jrd]
; 8 Nov 1987 Add EGA mode switching, from Terry Kennedy.
; 1 Nov 1987 Add support for Tektronix, based on work by Brian Holley. [jrd]
; 13 Oct 1987 Revise memory allocation sequence to avoid small holes. [jrd]
; 2 Oct 1987 Make log file character width match Set Display but 8 bits when
;  debugging. [jrd]
; 12 Sept 1987 clarify sequence of translation and 8 bit display. [jrd]
; 27 Aug 1987 Do Translation before logging. [jrd]
; 18 Aug 1987 Change ESC to escape for MASM 4.5+ [jrd]
; 28 July 1987 Fix scron problem in screen save. [jrd]
; 16 June 1987 Remove global byte jwait (wait for vertical retrace), embedd
;  code for proc scrwait in proc scroff. Replace calls for scrwait with
;  calls to scron/scroff. Thanks to Dave Tweten (dt). [jrd]
; 11 June 1987 Add control of automatic screen roll back when new characters
;  are to be displayed; default is off (no roll back). [jrd]
; 8 June 1987 Add keypad application mode tests to emit single chars. [jrd]
; 10 May 1987 Move input translation into terminal emulator, leave copy
;  here for terminal type None, use mszibm byte anspflg to sense print
;  screen is active, print translated characters but don't translate if
;  debugging is active. [jrd]
; 28 March 1987 Make low_rgt screen coord word a global parameter.
;  Add support for variable length screens and cursor size changes with
;  EGA boards. Tests ok with 25, 35, 43, 50 lines with &/without MS Windows.
;  EGA Memory locations 40:84H and 40:87H are used in this process.
;  Use savadr dw as place to save screen: usually screen page 1 if screen
;  dimensions are non-standard (80x25), else memory buffer scrsav. [jrd]
; 21 March 1987 Translate arriving Connect mode chars via table rxtable. [jrd]
;  Add 132 Column support for Tseng Labs EVA board via procedure chgdsp,
;  add restore scrolled screen before writing to it. From David L. Knoell [dlk]
; Modify msy and msz to use variable screen length and widths. [dlk] and [jrd]
; 17 March 1987 Reduce screen roll back buffer to half the memory to do the
;  same number of screens. [jrd]
; 12 Jan 1987 Add keyboard translator, remove older code. [jrd]
; 1 Oct 1986 Version 2.29a

    public    term, lclyini                ; entry points
    public    prtbout, prtnout, csrtype, scrmod, scrseg, scrsync
    public    scroff, scron, atsclr, vtscru, vtscrd, scrloc, trnmod, telmsy
    public    chgdsp, vtroll, crt_lins, crt_cols, getflgs, tv_mode
            ; action verb procedures for keyboard translator
    public    uparrw, dnarrw, rtarr, lfarr, pf1, pf2, pf3, pf4
    public    kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9
    public    kpminus, kpcoma, kpenter, kpdot, chrout, cstatus, cquit
    public    cquery, dmpscn, vtans52, vtinit, dnwpg, upwpg, endwnd, homwnd
    public    upone, dnone, trnprs, dumpscr, modlin, modwrt, snull
    public    klogon, klogof, cdos, chang
ifdef    MODIFIED                ; [jhw]
    public    ktest,dkeytab,skeyptr        ; for diagnostics [jhw]
endif                        ; [jhw]

    public    vtemu, crt_mode, scbattr, refresh, low_rgt  ; data
    public    savescr, restscr
    public    usevb                ; [rwb]
ifdef    MODIFIED
    public    rbtabl, sbtabl            ; bios key set/reset [jhw]
endif

    include mssdef.h

; some definitions
; hardware
crt_status equ    3dah            ; CGA crt status port
disp_enb equ    8            ; CGA display enable bit
crtmset equ    3D8H            ; CGA CRT mode set port.
screen    equ    10h            ; bios screen interrupt
biostty equ    0eh            ; Bios screen tty write mode
cmd8251 equ    2ah            ; i/o address of 8251 command port [jhw]
rxrdy    equ    02h            ; 8251 receiver ready bit [jhw]

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


datas    segment para public 'datas'
    extrn flags:byte, mar_top:byte, mar_bot:byte, portval:word
    extrn filtst:byte, dmpname:byte, kbdflg:byte, rxtable:byte
    extrn anspflg:byte, tekflg:byte

;                                [begin jhw]
ifdef    MODIFIED
;
; stuff for replacement key definition table for the Sanyo BIOS
;
dkeytab dw    00000h,04F00h,05100h,04603h,04900h,00F00h,00000h,05200h
    dw    00E08h,00F09h,01C0Ah,04700h,00000h,01C0Dh,00000h,00000h
    dw    03B00h,03C00h,03D00h,03E00h,03F00h,04000h,04100h,04200h
    dw    04300h,04400h,00000h,0011Bh,04B00h,04D00h,04800h,05000h
    dw    03920h,00221h,02822h,00423h,00524h,00625h,00826h,02827h
    dw    00A28h,00B29h,0372Ah,00D2Bh,0332Ch,00C2Dh,0342Eh,0352Fh
    dw    00B30h,00231h,00332h,00433h,00534h,00635h,00736h,00837h
    dw    00938h,00A39h,0273Ah,0273Bh,0333Ch,00D3Dh,0343Eh,0353Fh
    dw    00340h,01E41h,03042h,02E43h,02044h,01245h,02146h,02247h
    dw    02348h,01749h,0244Ah,0254Bh,0264Ch,0324Dh,0314Eh,0184Fh
    dw    01950h,01051h,01352h,01F53h,01454h,01655h,02F56h,01157h
    dw    02D58h,01559h,02C5Ah,01A5Bh,02B5Ch,01B5Dh,0075Eh,00C5Fh
    dw    02960h,01E61h,03062h,02E63h,02064h,01265h,02166h,02267h
    dw    02368h,01769h,0246Ah,0256Bh,0266Ch,0326Dh,0316Eh,0186Fh
    dw    01970h,01071h,01372h,01F73h,01474h,01675h,02F76h,01177h
    dw    02D78h,01579h,02C7Ah,01A7Bh,02B7Ch,01B7Dh,0297Eh,05300h
    dw    00080h,00081h,00082h,00083h,00084h,00085h,00086h,00087h
    dw    00088h,00089h,0008Ah,0008Bh,0008Ch,0008Dh,0008Eh,0008Fh
    dw    00090h,00091h,00092h,00093h,00094h,00095h,00096h,00097h
    dw    00098h,00099h,0009Ah,0009Bh,0009Ch,0009Dh,0009Eh,0009Fh
    dw    000A0h,000A1h,000A2h,000A3h,000A4h,000A5h,000A6h,000A7h
    dw    000A8h,000A9h,000AAh,000ABh,000ACh,000ADh,000AEh,000AFh
    dw    000B0h,000B1h,000B2h,000B3h,000B4h,000B5h,000B6h,000B7h
    dw    000B8h,000B9h,000BAh,000BBh,000BCh,000BDh,000BEh,000BFh
    dw    000C0h,000C1h,000C2h,000C3h,000C4h,000C5h,000C6h,000C7h
    dw    000C8h,000C9h,000CAh,000CBh,000CCh,000CDh,000CEh,000CFh
    dw    000D0h,000D1h,000D2h,000D3h,000D4h,000D5h,000D6h,000D7h
    dw    000D8h,000D9h,000DAh,000DBh,000DCh,000DDh,000DEh,000DFh
    dw    000E0h,000E1h,000E2h,000E3h,000E4h,000E5h,000E6h,000E7h
    dw    000E8h,000E9h,000EAh,000EBh,000ECh,000EDh,000EEh,000EFh
    dw    000F0h,000F1h,000F2h,000F3h,000F4h,000F5h,000F6h,000F7h
    dw    000F8h,000F9h,000FAh,000FBh,000FCh,000FDh,000FEh,000FFh
    dw    00000h,08400h,07600h,08500h,08600h,08700h,00000h,08800h
    dw    00E7Fh,00009h,07500h,07700h,00000h,08900h,00000h,00000h
    dw    06800h,06900h,06A00h,06B00h,06C00h,06D00h,06E00h,06F00h
    dw    07000h,07100h,00000h,08A1Bh,07300h,07400h,08B00h,08C00h
    dw    08D20h,07800h,06200h,07A00h,07B00h,07C00h,07E00h,06400h
    dw    08000h,08100h,07F00h,08300h,06500h,09300h,06600h,06700h
    dw    05D00h,05400h,05500h,05600h,05700h,05800h,05900h,05A00h
    dw    05B00h,05C00h,06100h,06300h,08E00h,05E00h,08F00h,09000h
    dw    07900h,01E00h,03000h,02E00h,02000h,01200h,02100h,02200h
    dw    02300h,01700h,02400h,02500h,02600h,03200h,03100h,01800h
    dw    01900h,01000h,01300h,01F00h,01400h,01600h,02F00h,01100h
    dw    02D00h,01500h,02C00h,01A1Bh,02B1Ch,01B1Dh,07D00h,08200h
    dw    00300h,01E01h,03002h,02E03h,02004h,01205h,02106h,02207h
    dw    02308h,01709h,0240Ah,0250Bh,0260Ch,0320Dh,0310Eh,0180Fh
    dw    01910h,01011h,01312h,01F13h,01414h,01615h,02F16h,01117h
    dw    02D18h,01519h,02C1Ah,05F00h,0911Ch,06000h,0071Eh,09200h
    dw    00080h,00081h,00082h,00083h,00084h,00085h,00086h,00087h
    dw    00088h,00089h,0008Ah,0008Bh,0008Ch,0008Dh,0008Eh,0008Fh
    dw    00090h,00091h,00092h,00093h,00094h,00095h,00096h,00097h
    dw    00098h,00099h,0009Ah,0009Bh,0009Ch,0009Dh,0009Eh,0009Fh
    dw    000A0h,000A1h,000A2h,000A3h,000A4h,000A5h,000A6h,000A7h
    dw    000A8h,000A9h,000AAh,000ABh,000ACh,000ADh,000AEh,000AFh
    dw    000B0h,000B1h,000B2h,000B3h,000B4h,000B5h,000B6h,000B7h
    dw    000B8h,000B9h,000BAh,000BBh,000BCh,000BDh,000BEh,000BFh
    dw    000C0h,000C1h,000C2h,000C3h,000C4h,000C5h,000C6h,000C7h
    dw    000C8h,000C9h,000CAh,000CBh,000CCh,000CDh,000CEh,000CFh
    dw    000D0h,000D1h,000D2h,000D3h,000D4h,000D5h,000D6h,000D7h
    dw    000D8h,000D9h,000DAh,000DBh,000DCh,000DDh,000DEh,000DFh
    dw    000E0h,000E1h,000E2h,000E3h,000E4h,000E5h,000E6h,000E7h
    dw    000E8h,000E9h,000EAh,000EBh,000ECh,000EDh,000EEh,000EFh
    dw    000F0h,000F1h,000F2h,000F3h,000F4h,000F5h,000F6h,000F7h
    dw    000F8h,000F9h,000FAh,000FBh,000FCh,000FDh,000FEh,000FFh

skeyptr dw    0,0            ; for address of pointer in bios to key
                    ;    scan code/char translation table
ckeyptr dw    0,0            ; same for control keys (seg:offset)
kinitf    db    0            ; flag to indicate pointers were found
keyng    db    "Unable to Find BIOS Key Translation Table Pointers"
    db    cr,lf,'$'
;                                [end jhw]

endif
; stuff for screen routines
yflags    db    ?            ; status flags...
flags1    db    0            ; internal flags (but used in mszibm).
prtscr    equ    1            ; print screen pressed
inited    equ    08h            ; been here before...
vtinited db    0            ; flag for emulator having been inited
cursor    dw    ?
esc_ch    db    ?
parmsk    db    ?            ; 8/7 bit parity mask, for reception
argadr    dw    ?            ; address of arg blk

ega_mode db    0            ; non-zero if IBM EGA is in use.
tvhere    equ    0feh            ; Topview active query
tvsynch equ    0ffh            ; Topview resynch request
tv_segs dw    ?            ; Topview virtual screen, segment
tv_sego dw    ?            ; and offset
tv_mode db    0            ; flag, 0 = no Topview.
savadr    dw    2 dup (?)        ; offset then segment of saved screen
savflg    dw    ?            ; low_rgt at time of screen save
;
swid2m    db    swidth * 2    ; used as divisor in getcirc      [jhw]
curpost    dw    0        ; temp storage for cursor position[jhw]
usevb    db    ?        ; 1 if video board, 0 if Sanyo mode [rwb]
vbmsg    db    CR,'Video board in use',CR,LF,'$'
novbmsg    db    CR,'Sanyo mode screen',CR,LF,'$'


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


vtemu    emulst    <>            ; emulator flags
ansflgs db    0            ; ANSI flags
trmtyp    db    0            ; most recent terminal type
mtty    db    '  TTY   '        ; no terminal type (mode line)

lincur    dw    ?            ; cursor type save area
scbattr db    ?            ; Screen background attribute
oldattr db    ?            ; screen attributes at init time
curattr db    ?            ; current attribute
temp    dw    ?            ; scratch storage
modtemp db    0            ; temp to hold Kermit modeline status
captrtn dw    ?            ; routine to call for captured output
dmphand dw    ?            ; screen dump file handle
dumpbuf db    132 dup (?), cr, lf    ; 134 byte dump work buffer
dumpsep db    FF,cr,lf        ; screen image separators
dmperr    db    ' Cannot open file to save screen to disk $'
crlf    db    cr,lf,'$'

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

; storage for multi-window stuff
swidth    equ    80            ; max screen width
slen    equ    24            ; and length of text
npages    equ    10            ; # of pages of scrolling on each side
crt_norm db    ?            ; video mode for normal screen
crt_mode db    ?            ; video mode (typ 3, must be text)
crt_cols db    ?            ; number of screen columns (typ 80)
crt_lins db    24            ; number of screen rows - 1 (typ 24)
low_rgt dw    ?            ; lower right corner of text window
                    ; high = row address (typ 23)
                    ; low = column address (typ 79)
inipara dw    ?            ; initial paragraphs of scroll memory
scrsav    dw    ?            ; segment address of save area
refresh db    0            ; screen refresh (0=wait for retrace)
vtroll    db    0            ; auto roll back allowed (0 = no).

; circular buffer for screen roll back.
cbuf    struc
pp    dw    ?            ; place ptr in buffer
bend    dw    ?            ; end of buffer
orig    dw    ?            ; buffer origin
lcnt    dw    0            ; # of lines in buffer.
lmax    dw    ?            ; max lines of buffer.
cbuf    ends

twnd    cbuf    <>            ; top screen spill-buffer struct
bwnd    cbuf    <>            ; bottom screen spill buffer struct

datas    ends

code    segment public 'code'
    extrn    beep:near, prtchr:near, outchr:near, sbrk:near, pcwait:near
    extrn    isfile:near, strlen:near, strcpy:near    ; in mssfil
    extrn    anstty:near,ansini:near,ansrei:near    ; in mszibm
    extrn    anstat:near,anskbi:near,ansdsl:near    ; in mszibm
    extrn    ans52t:near, vsinit:near        ; in mszibm
    extrn    msuinit:near, keybd:near        ; in msuibm
    extrn    tekini:near,tekcls:near,tekemu:near,tekend:near ;in msgibm

    assume    cs:code, ds:datas, es:datas

; do initialization local to this module...
; Dynamically allocates 4000 bytes for screen save/restore buffer plus
;  320 to 38400 bytes for screen scroll back buffers. Tries to leave space
;  for Command.com before enlarging buffers. [jrd]
lclyini proc    near
; Figure out whether a Video board is in use by writing 2 different [rwb]
; characters to the same screen location with a DOS call and reading
; back from the correspnding video board memory location.  Cursor is known
; to be in the first column before doing this, but may not be in the last row
; if the screen was recently cleared.

    mov    ax,0b800h        ; VB segment
    mov    es,ax
    mov    ah,3
    mov    bh,0
    int    screen            ; read cursor position
    mov    ax,0a0h            ; length of row (2 bytes/char)
    mul    dh            ; offset of current row, col 0
    push    ax            ; save offset
    mov    ax,0a00h+'i'
    mov    cx,1
    int    SCREEN            ; display 'i'
    pop    si            ; recover offset
    cmp    byte ptr es:[si],'i'    ; read character back (maybe)
    jne    lclynovb        ; ne = no video board
    push    si            ; save offset again
    mov    ax,0a00h+'V'
    int    SCREEN            ; display 'V'
    pop    si            ; recover offset
    cmp    byte ptr es:[si],'V'    ; read it back
    jne    lclynovb        ; ne = no video board
    mov    usevb,1            ; say video board
    mov    dx,offset vbmsg
    jmp short lclyvb
lclynovb:
    mov    usevb,0            ; say no video board
    mov    dx,offset novbmsg
lclyvb:    mov    ah,9
    int    dos            ; display msg with video board status
    call    msuinit            ; initialize keyboard module msuxxx

    mov    ax,swidth*(slen+1)*2    ; (80 char + 80 attr) * 25 lines
    call    sbrk            ; memory allocation routine (mssker)
                    ;if we get here them we have the lines
    mov    scrsav,ax        ; memory segment for save screens
                    ; screen roll back buffers
    mov    bx,0ffffh        ; ask for all of memory, to get size
    mov    ah,alloc        ; allocate all of memory (must fail)
    int    dos            ; bx has # free paragraphs
    mov    ax,bx            ; ax has copy of number free paragraphs
    sub    ax,24000D/16        ; space for Command.com copy #2
    jbe    lclyin1            ; be = not enough for it. [ebb]
    cmp    ax,(swidth*slen+15)/16    ; minimum roll back space left over?
    jbe    lclyin1            ; be = not even that much
    cmp    ax,(swidth*slen*npages+7)/8 ; paragraphs wanted for roll back
    jbe    lclyin2            ; be = enough but not more than needed
    mov    ax,(swidth*slen*npages+7)/8 ; limit to our actual needs
    jmp    short lclyin2        ; ask for all we really want
lclyin1:mov    ax,(4*swidth+15)/16    ; use minimum needed paragraphs
lclyin2:mov    inipara,ax        ; save for later resizing of buffers
    mov    cl,4            ; convert paragraphs to bytes
    shl    ax,cl            ;  for sbrk
    call    sbrk            ; ask for that many bytes
                    ;if we get here them we have the space
    mov    bwnd.orig,ax        ; memory segment, bottom window area
    mov    twnd.orig,ax        ; top. same place for both buffers!
    mov    ax,inipara        ; # paragraphs allocated by DOS
    mov    cl,3            ; 2**3 = 8
    shl    ax,cl            ; paragraphs to words (char + attrib)
    xor    dx,dx            ; clear extended size
    mov    cx,swidth        ; number of chars per line in buffer
    div    cx            ; ax = number of lines in buffer
    mov    bwnd.lmax,ax        ; max lines per buffer (quotient)
    mov    twnd.lmax,ax        ; max lines per buffer
    add    cx,cx            ; count char and attribute per item
    xor    dx,dx            ; clear extended numerator
    mul    cx            ; ax = effective # bytes per buffer
    dec    ax            ; adjust for counting from zero
    mov    bwnd.bend,ax        ; offset of last byte in buffer
    mov    twnd.bend,ax        ; offset of last byte in buffer
    mov    bwnd.pp,0        ; offset of first byte in buffer
    mov    twnd.pp,0        ; offset of first byte in buffer
    mov    bwnd.lcnt,0        ; number of lines occupied in buffer
    mov    twnd.lcnt,0        ; number of lines occupied in buffer
    call    scrseg            ; test running in an Environment
    call    scrmod            ; read video state, get crt_mode.
    mov    al,crt_mode
    mov    crt_norm,al        ; save as normal mode
    mov    ah,8            ; read current attributes
    xor    bh,bh            ; page 0
    int    screen
    mov    scbattr,ah        ; save video attributes
    mov    oldattr,ah        ; and here too
    test    usevb,1            ; video board in use? [rwb]
    jnz    lcliniv            ; nz = yes [rwb]
;                                [begin jhw]
                    ; store ank segment:offset in topview
                    ;   pointers left over from IBM vers.
                    ;   (the ank buffer, sometimes called
                    ;   shadow buffer is an array of 4000
                    ;   bytes containing the char/attrib
                    ;   of all characters currently on
                    ;   the screen.
    call    scrseg            ; get ank ram seg:offset in es:di
    mov    tv_segs,es        ; store segment
    mov    tv_sego,di        ; store offset
lcliniv:
ifdef    MODIFIED
; find ptr to keyboard translation table (pointer is in BIOS keyboard
;   interrupt service routine
ktest:    xor    ax,ax            ; set segment register to
    mov    es,ax            ; point to interrupt vector table
    mov    si,es:[3ech]        ; get offset of int 0fb ser.rtn
    mov    es,es:[3eeh]        ;   and segment
    mov    cx,30h            ; set safety counter
lcini3: cmp    word ptr es:[si],1ec5h    ; look for "lds bx"
    je    lcini4            ; found? yes, jump out of loop
    inc    si            ; increment and try again
    loop    lcini3
    jmp    lclng            ; not found, go tattle
lcini4: inc    si
    inc    si            ; point to arg of lds bx
    mov    di,es:[si]        ; es:di pts at pointer to std key table
    mov    word ptr skeyptr,di    ; store address of pointer
    mov    word ptr skeyptr+2,es    ;
    mov    cx,30h            ; set another safety counter
lcini5: cmp    word ptr es:[si],1ec5h    ; look for lds bx again
    je    lcini6            ; jmp out of loop if found
    inc    si            ; point to next location
    loop    lcini5            ;   and try again
lcini6: inc    si            ; point to arg of lds bx
    inc    si
    mov    di,es:[si]        ; get pointer to control key table ptr
    mov    word ptr ckeyptr,di    ; store address of pointer
    mov    word ptr ckeyptr+2,es
    les    si,dword ptr skeyptr
    cmp    word ptr es:[si],0    ; check present value in pointer
    jne    lclng            ; should be fc00:0000 (0000 first)
    cmp    word ptr es:[si+2],0fc00h
    jne    lclng
    les    si,dword ptr ckeyptr    ; check pointer for control key table
    cmp    word ptr es:[si],200h    ; should be fc00:0200 (200 first)
    jne    lclng
    cmp    word ptr es:[si+2],0fc00h
    jne    lclng
    mov    kinitf,1        ; success, remember that
    jmp    lcini7            ; and go on
lclng:    mov    kinitf,0        ; failure, remember that too
    mov    ax,0900h        ; and notify someone
    mov    dx,offset keyng
    int    21h
    mov    word ptr skeyptr,0    ; zero pointers to pointers
    mov    word ptr skeyptr+2,0
    mov    word ptr ckeyptr,0
    mov    word ptr ckeyptr+2,0
endif
lcini7:

;                                [end jhw]
    call    vsinit            ; init terminal emulator module MSZ
    ret
lclyini endp
;                    ;            [begin jhw]
ifdef    MODIFIED
sbtabl    proc    near            ; set ptr (in bios) to new key table
    cmp    kinitf,1        ; did we find the pointer?
    jne    sbtab9            ; no, skip this stuff
    push    di
    push    es
    push    ax
    mov    ax,offset dkeytab
    shr    ax,1
    shr    ax,1
    shr    ax,1
    shr    ax,1
    add    ax,seg dkeytab        ; get segment of new table
    les    di,dword ptr skeyptr    ; get address of standard key tab. ptr
    mov    word ptr es:[di+2],ax    ; put seg. in -- offset doesn't change
    les    di,dword ptr ckeyptr    ; get address of control key tab. ptr
    mov    word ptr es:[di+2],ax    ; put segment in like before
    pop    ax            ; that was it!
    pop    es
    pop    di
sbtab9: ret
sbtabl    endp
;
rbtabl    proc    near            ; set ptr (in bios) to new key table
    cmp    kinitf,1        ; did we find the pointer?
    jne    rbtab9            ; no, skip this stuff
    push    di
    push    es
    push    ax
    mov    ax,0fc00h        ; get segment of original table
    les    di,dword ptr skeyptr    ; get address of standard key tab. ptr
    mov    word ptr es:[di+2],ax    ; put seg. in -- offset doesn't change
    les    di,dword ptr ckeyptr    ; get address of control key tab. ptr
    mov    word ptr es:[di+2],ax    ; put segment in like before
    pop    ax            ; that was it!
    pop    es
    pop    di
rbtab9: ret
rbtabl    endp
endif
;                                [end jhw]

scrini    proc    near            ; init screen stuff
    call    scrmod            ; get screen mode, low_rgt
    mov    ah,3            ; get cursor position and char.
    xor    bh,bh            ; page 0
    int    screen
    mov    lincur,cx        ; save cursor type (scan line #'s)
    mov    dx,cursor        ; assume old cursor
scrin6: test    flags1,inited        ; have we been here before?
    jnz    scrin4            ; nz = yes, use old cursor
    mov    ah,oldattr        ; get init time attributes
    mov    curattr,ah        ; and set nice screen attribute
    mov    scbattr,ah
    mov    ah,3            ; figure out where cursor is
    xor    bh,bh            ; page 0
    int    screen            ; read cursor position, in dx
scrin4: cmp    dh,byte ptr low_rgt+1    ; past logical end of screen?
    jb    scrin2            ; b = no, keep going
    mov    dh,byte ptr low_rgt+1    ; yes, just use lower right corner
scrin2: cmp    dl,byte ptr low_rgt    ; maybe past right margin
    jb    scrin3            ; b = no, use the way it is
    mov    dl,byte ptr low_rgt
scrin3: mov    cursor,dx        ; init cursor
    mov    ah,2            ; set cursor position
    xor    bh,bh            ; page zero
    int    screen            ; set cursor in case it moved
    ret
scrini    endp

; Routine to initialize VT102/52/Heath-19 terminal emulator.

vtinit    proc    near
    cmp    flags.vtflg,0        ; doing emulation?
    je    vtinix            ; e = no
    cmp    tekflg,0        ; Tek mode active?
    jne    vtini2            ; ne = yes, do it's reinit
    or    vtinited,inited
    call    ansflg            ; update ansi flags
    mov    al,yflags        ; Pass the flags.
    mov    bx,argadr        ; Get address of argument block
    mov    dl,[bx].baudb        ; Baud rate code in dl
    mov    dh,[bx].parity        ; Parity code in bits
    mov    cl,4            ; 0-3 of dh
    shl    dh,cl
    test    flags.remflg,d8bit    ; eight bit display?
    jnz    vtini1            ; nz = yes
    or    dh,07H            ; Just say 7 data bits.
    call    ansini            ; call startup routine in mszibm.
    ret
vtini1: or    dh,8            ; say 8 bits
    call    ansini
vtinix: clc
    ret
vtini2: call    tekcls            ; clear Tek screen
    clc
    ret
vtinit    endp


argini    proc    near            ; read passed arguments
    mov    bx,argadr        ; base of argument block
    mov    al,[bx].flgs        ; get flags
    and    al,capt+emheath+havtt+trnctl+lclecho+modoff+lnwrap
    mov    yflags,al        ; mask for allowable and save
    mov    al,[bx].prt
    mov    portno,al        ; update port number
    mov    al,[bx].rows
    mov    crt_lins,al        ; init # of rows and cols
    mov    ax,[bx].captr
    mov    captrtn,ax        ; buffer capture routine
    mov    al,[bx].escc
    mov    esc_ch,al
    mov    parmsk,0ffh        ; parity mask, assume parity = None
    cmp    [bx].parity,parnon    ; is parity None?
    je    argini1            ; e = yes, keep all 8 bits
    mov    parmsk,07fh        ; else keep lower 7 bits
argini1:ret                ; that's it
argini    endp

modlin    proc    near            ; turn on mode line
    mov    al,esc_ch
    mov    modbuf.m_echr,' '    ; first char is initial space
    mov    modbuf.m_hlp,' '    ; goes here too.
    cmp    al,32            ; printable?
    jnb    modl1            ; yes, keep going
    add    al,40h            ; made printable
    mov    modbuf.m_echr,5eh    ; caret, note control char
    mov    modbuf.m_hlp,5eh
modl1:    mov    modbuf.m_echr+1,al    ; fill in character
    mov    modbuf.m_hlp+1,al
    mov    bx,argadr        ; get argument block
    mov    al,[bx].baudb        ; get baud bits
    mov    si,offset unkbaud    ; assume unknown baud
    cmp    al,baudnsiz        ; too big?
    jnb    modl2            ; nb = yes, use default
    mov    cl,size m_baud        ; each is 5 bytes long
    mul    cl
    mov    ah,0
    add    ax,offset baudn
    mov    si,ax
modl2:    mov    cx,size m_baud        ; length of baud space
    mov    di,offset modbuf.m_baud
    push    es            ; save es
    push    ds
    pop    es            ; set es to datas segment
    cld
    rep    movsb            ; copy in baud rate
    mov    al,[bx].parity        ; get parity code
    mov    cl,2            ; each is 4 bytes long...
    shl    al,cl
    mov    ah,0
    add    ax,offset parnams    ; names of parity settings
    mov    si,ax
    mov    cx,4            ; each is 4 long
    mov    di,offset modbuf.m_par
    rep    movsb
    mov    si,offset remmsg    ; Assume remote echoing.
    test    yflags,lclecho        ; Is remote side echoing?
    jz    modl4            ; Yes, keep going
    mov    si,offset lclmsg    ; Else it's local echoing.
modl4:    mov    cx,3            ; size of on/off
    mov    di,offset modbuf.m_echo
    rep    movsb
    mov    al,'1'            ; only port one possible on Sanyo [rwb]
modl5:    mov    modbuf.m_prt,al        ; fill in port number
    mov    cx,8            ; blank out terminal id field
    mov    si,offset mtty        ; assume no terminal emulation.
    mov    di,offset modbuf.m_term ; destination
    rep    movsb            ; copy it in.
    mov    modbuf.m_prn,' '    ; assume not printing the screen
    mov    modbuf.m_prn+1,' '
    mov    modbuf.m_prn+2,' '
    test    anspflg,prtscr        ; doing a print the screen?
    jz    modl5a            ; z = no.
    mov    modbuf.m_prn,'P'    ; yes. display PRN at end of line
    mov    modbuf.m_prn+1,'R'
    mov    modbuf.m_prn+2,'N'
modl5a: mov    cx,size modfrm        ; this is size of mode line
    mov    si,offset modbuf    ; mode line image
    pop    es
            ; alternate entry to write an alternate mode line
modwrt: push    cx
    push    si            ; save mode line and size
    mov    ah,3            ; read cursor position
    xor    bx,bx            ; screen page 0
    int    screen
    mov    cursor,dx        ; save cursor position
    call    trmatt            ; Get terminal attributes
    and    ah,77h            ; omit blinking/bold attributes
    mov    bh,ah            ; get video attribute
    mov    dx,low_rgt        ; right most column
    inc    dh            ; refer to status line
    mov    ch,dh            ; bottom line [dlk]
    mov    cl,0            ; left col = 0 (first) [dlk]
    mov    ax,600h            ; scroll to clear the line
    int    screen
    mov    dh,byte ptr low_rgt+1    ; refer to status line
    inc    dh
    xor    dl,dl            ; left most column
    mov    bh,0
    mov    ah,2            ; set cursor position
    int    screen
    pop    si
    pop    cx            ; restore these
    cmp    cl,crt_cols        ; mode line longer than screen?
    jbe    modl6            ; le = no
    mov    cl,crt_cols        ; else do just one line's worth
    dec    cx            ; don't let screen scroll
modl6:    cld
    lodsb                ; get a byte
    mov    ah,14            ; write to terminal
    mov    bh,0            ; page 0
    int    screen
    loop    modl6            ; write out entire mode line
    cmp    flags.vtflg,0        ; emulating?
    je    modl7            ; e = no
    and    yflags,not modoff    ; update local flags (mode line on)
    mov    al,yflags        ; Yes - update flags also
    call    ansdsl            ; get extras from emulator
modl7:    mov    dx,cursor
    mov    ah,2
    mov    bh,0
    int    screen            ; put cursor back where it belongs
    ret                ; and return
modlin    endp

clrmod    proc    near            ; clear mode line
    call    trmatt            ; Get terminal screen attributes
    mov    bh,al            ; Use screen background attribute
    mov    ax,600h            ; blank window
    mov    dx,low_rgt        ; right most column
    inc    dh            ; refer to status line
    mov    cx,dx            ; bottom line [dlk]
    xor    cl,cl            ; left most column
    int    screen            ; clear mode line
    ret                ; and return
clrmod    endp


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

trmatt    proc    near            ; Get attributes
    cmp    flags.vtflg,0        ; emulating?
    je    trmat1            ; No, just do simple stuff.
    mov    al,yflags        ; anstat expects flags byte in al.
    call    anstat            ; Fetch emulator status/attributes
    ret
trmat1: mov    al,scbattr        ; Background attributes.
    mov    bl,curattr        ; And cursor attribute.
    mov    ah,al            ; where modlin needs them
    and    ah,77h            ; get colors part, no blink/bold
    rol    ah,1            ; reverse them
    rol    ah,1
    rol    ah,1
    rol    ah,1
    ret
trmatt    endp

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


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

ansflg    proc    near
    push    ax            ; Save acs over call
    push    bx
    mov    al,yflags
    call    anstat            ; Get status and attributes
    mov    ansflgs,bh        ; Save.
    pop    bx
    pop    ax
    ret
ansflg    endp

getflgs proc    near            ; supply yflags for terminal emulators
    mov    al,yflags
    ret
getflgs endp

term    proc    near            ; terminal mode entry point
    mov    argadr,ax        ; save argument ptr
    call    argini            ; init options from arg address
    call    scrini            ; init screen stuff
ifdef    MODIFIED
    call    sbtabl            ; reset kbd table [jhw]
endif

    test    flags1,inited        ; have we run yet?
    jz    term1            ; z = no, so no saved screen yet
    call    restscr            ; restore screen
term1:    or    flags1,inited        ; remember we've run already.
    cmp    flags.vtflg,0        ; current terminal type = None?
    je    term3a            ; e = yes, nothing to init.
    mov    al,yflags        ; tell emulator we are back
    cmp    vtinited,inited        ; inited emulator yet?
    je    term3            ; e = yes
    cmp    tekflg,0        ; Tek mode still active?
    jne    term3a            ; ne = yes, no re-init here
    call    vtinit            ; init it now
    jmp    term3a
term3:    call    ansrei            ; reinit the emulator
    call    ansflg            ; and get its flags
term3a: cmp    flags.modflg,0        ; is mode line disabled?
    je    term2a            ; e = yes, disabled
    cmp    flags.vtflg,0        ; emulating a terminal?
    jne    term1a            ; ne = yes, can have mode line
    cmp    trmtyp,0        ; previous terminal type = none?
    jne    term2            ; ne = no. need to clear mode line.
    jmp    term2a            ; yes, let 25th line be intact
term1a: test    yflags,modoff        ; is mode line toggled off?
    jnz    term2            ; nz = yes, clear the line.
    cmp    flags.vtflg,tttek    ; going to be a Tek terminal?
    je    term2a            ; e = yes, no mode line
    call    modlin            ; turn on mode line
    jmp    term2a
term2:    call    clrmod            ; ensure its off
term2a: mov    al,flags.vtflg        ; current terminal type
    mov    trmtyp,al        ; place to remember it til next time
    cmp    flags.vtflg,tttek    ; Tek mode?
    je    term4            ; e = yes
    cmp    tekflg,0        ; Tek mode active within DEC stuff?
    je    lp            ; e = no
term4:    call    tekini            ; reinit to get graphics screen

lp:    call    portchr            ; char at port?
    jc    lp1            ; yes, go display it      [begin jhw]
                ; check for receipt of character which
                ; came in without generating interrupt
    in    al,cmd8251        ; get port status
    test    al,rxrdy        ; is character in 8251?
    jz    chkinp            ; no, go on
    cli                ; yes, hold interrupts and
    int    0fah            ; call the interrupt service routine
    sti                ; re-enable interrupts
    call    portchr            ; get the character
    jnc    chkinp            ; if there was no character, go on
lp1:    call    outtty            ; else display it     [end jhw]
chkinp: call    keybd            ; call keyboard translator in msu
    jnc    lp            ; nc = no char or have processed it
                    ; carry set = quit Connect mode.

quit:    call    tekend            ; [bjh]
    mov    ah,3            ; get cursor position
    xor    bh,bh            ; page 0
    int    screen
    mov    cursor,dx        ; save position
    call    savescr            ; save screen
    cmp    flags.vtflg,0        ; emulating?
    je    quit1            ; e = no
    mov    ax,0600h        ; clear mode line with old attributes
    mov    bh,oldattr        ; attributes
    mov    dx,low_rgt        ; right most column
    inc    dh            ; refer to status line
    mov    cx,dx            ; bottom line [dlk]
    xor    cl,cl            ; left most column
    int    screen            ; clear the mode line
quit1:    mov    ah,oldattr        ; attributes at init time
    mov    scbattr,ah        ; background = original state
    mov    cx,lincur        ; cursor type at startup
    mov    ah,1
    int    screen            ; restore cursor type
quit3:    mov    ah,2            ; Position cursor
    mov    bh,0            ; Page 0
    mov    dx,low_rgt        ; bottom line
    inc    dh            ; status line position
    xor    dl,dl            ; left most column
    int    screen            ; Do it.
    mov    al,yflags
    mov    bx,argadr
    mov    [bx].flgs,al        ; update flags in arg block
ifdef    MODIFIED
    call    rbtabl            ; reset key trans table ptr [jhw]
endif
    ret                ; and return to caller
term    endp

; put the character in al to the screen
outtty    proc    near
    cmp    flags.vtflg,0        ; emulating a terminal?
    jne    outnoc            ; ne = yes, emulator handles printing
    test    flags.remflg,d8bit    ; keep 8 bits for displays?
    jnz    outnp9            ; nz = yes, 8 bits if possible
    and    al,7fh            ; remove high bit
outnp9: cmp    rxtable+256,0        ; translation turned off?
    je    outnp7            ; e = yes, no translation
    push    bx
    mov    bx,offset rxtable    ; address of translate table
    xlatb                ; new char is in al
    pop    bx
outnp7: test    anspflg,prtscr        ; should we be printing?
    jz    outnop            ; no, keep going
    push    ax
    mov    ah,lstout        ; write to system printer device
    mov    dl,al
    int    dos
    pop    ax
    jnc    outnop            ; nc = successful print
    push    ax
    call    beep            ; else make a noise and
    call    trnprs            ;  turn off printing
    pop    ax
outnop: test    yflags,capt        ; capturing output?
    jz    outnoc            ; no, forget this part
    push    ax            ; save char
    call    captrtn            ; give it captured character
    pop    ax            ; restore character and keep going
outnoc: cmp    tekflg,0        ; Tek mode active?
    jne    outnp6            ; ne = yes, skip screen rolling
    cmp    vtroll,0        ; auto roll back allowed?
    jz    outnp6            ; z = no, leave screen as is.
    cmp    bwnd.lcnt,0        ; is screen rolled back? [dlk]
    je    outnp6            ; e = no
    call    endwnd            ; restore screen before writing [dlk]
outnp6: cmp    flags.vtflg,0        ; emulating a terminal?
    jnz    outnop1            ; nz = yup, go do something smart
    test    yflags,trnctl        ; debug? if so use Bios tty mode
    jz    outnp4            ; z = no
    mov    ah,biostty        ; Bios tty screen write
    cmp    al,7fh            ; Ascii Del char or greater?
    jb    outnp1            ; b = no
    je    outnp0            ; e = Del char
    push    ax            ; save the char
    mov    al,7eh            ; output a tilde for 8th bit
    int    screen
    pop    ax            ; restore char
    and    al,7fh            ; strip high bit
outnp0: cmp    al,7fh            ; is char now a DEL?
    jne    outnp1            ; ne = no
    and    al,3fH            ; strip next highest bit (Del --> '?')
    jmp    outnp2            ; send, preceded by caret
outnp1: cmp    al,' '            ; control char?
    jae    outnp3            ; ae = no
    add    al,'A'-1        ; make visible
outnp2: push    ax            ; save char
    mov    al,5eh            ; caret
    int    screen            ; display it
    pop    ax            ; recover the non-printable char
outnp3: int    screen
    ret
outnp4: cmp    al,bell            ; bell (Control G)?
    jne    outnp5            ; ne = no
    jmp    beep            ; use short beep, avoid char loss.
outnp5: mov    dl,al            ; write without intervention.
    mov    ah,conout
    int    dos            ; else let dos display char
    ret                ; and return

outnop1:cmp    flags.vtflg,tttek    ; doing Tektronix emulation?
    je    outnop2            ; e = yes, use Tek emulator
    cmp    tekflg,0        ; Tek submode active?
    jne    outnop2            ; ne = yes, use Tek emulator
    jmp    anstty            ; call terminal emulator routine & ret
outnop2:jmp    tekemu            ; use Tek emulator and return

outtty    endp

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

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


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

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

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

; send the character in al out to the serial port; handle echoing.
; Can send an 8 bit char while displaying only 7 bits locally.
outprt    proc    near
    test    yflags,lclecho        ; echoing?
    jz    outpr1            ; z = 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,parmsk        ; apply 8/7 bit parity mask
    stc                ; have a character
    ret                ; and return
portchr endp

;;; Action routines (verbs) for keyboard translator KEYBD in msuibm.
; These are invoked by a jump instruction. Return carry clear for normal
; processing, return carry set for invoking Quit (kbdflg has transfer char).
uparrw: mov    al,'A'            ; cursor keys
    jmp    short comarr
dnarrw: mov    al,'B'
    jmp    short comarr
rtarr:    mov    al,'C'
    jmp    short comarr
lfarr:    mov    al,'D'
comarr: push    ax            ; save final char
    mov    al,escape        ; Output an escape.
    call    outprt            ; Output, echo permitted
    cmp    flags.vtflg,tttek    ; Tek terminal?
    jne    comar0            ; ne = yes, use VT100 codes
    cmp    flags.vtflg,ttvt100    ; VT100 terminal emulation?
    jne    comar2            ; No, do VT52/HEATH-19 sequence.
comar0: call    ansflg            ; Update flags all around.
    mov    al,'['            ; Maybe this next?
    test    ansflgs,decckm        ; Cursor key mode reset?
    je    comar1            ; Yes, output the "["
    mov    al,'O'            ; No, set, use the "O".
comar1: call    outprt            ; Output it (echo permitted).
comar2: pop    ax            ; recover final char
    call    outprt            ; Output to port (echo permitted)
    clc
    ret

pf1:    mov    al,'P'            ; keypad function keys 1-4
    jmp    short compf
pf2:    mov    al,'Q'
    jmp    short compf
pf3:    mov    al,'R'
    jmp    short compf
pf4:    mov    al,'S'
compf:    push    ax            ; save final char
    mov    al,escape        ; Output an escape.
    call    prtbout
    call    ansflg            ; get emulator flags
    test    ansflgs,decanm        ; ansi mode?
    jz    short compf1        ; z = no
    mov    al,'O'            ; send an "O".
    call    prtbout            ; Output it.
compf1: pop    ax            ; Get the saved char back
    call    prtbout            ; Output to port
    clc
    ret

kp0:    mov    al,'p'            ; keypad numeric keys
    jmp    short comkp
kp1:    mov    al,'q'
    jmp    short comkp
kp2:    mov    al,'r'
    jmp    short comkp
kp3:    mov    al,'s'
    jmp    short comkp
kp4:    mov    al,'t'
    jmp    short comkp
kp5:    mov    al,'u'
    jmp    short comkp
kp6:    mov    al,'v'
    jmp    short comkp
kp7:    mov    al,'w'
    jmp    short comkp
kp8:    mov    al,'x'
    jmp    short comkp
kp9:    mov    al,'y'
    jmp    short comkp
kpminus:mov    al,'m'
    jmp    short comkp
kpcoma: mov    al,'l'
    jmp    short comkp
kpenter:mov    al,'M'
    jmp    short comkp
kpdot:    mov    al,'n'
comkp:    test    ansflgs,deckpam        ; keypad application mode active?
    jnz    comkp3            ; nz = yes, use escape sequences
    sub    al,40h            ; deduct offset to numeric symbols
    jmp    comkp0            ; and send that single char
comkp3: push    ax            ; save final char
    mov    al,escape        ; Output an escape.
    call    prtbout
    mov    al,'O'            ; Output the "O"
    cmp    flags.vtflg,ttvt100    ; VT100 mode?
    je    comkp1            ; e = yes, use "O" code
    cmp    flags.vtflg,tttek    ; Tek terminal
    je    comkp1            ; e = yes, use VT100 codes
    test    ansflgs,decanm        ; ANSI (alt application keypad) mode?
    jnz    comkp1            ; nz = yes, use "O"
comkp2: mov    al,'?'            ; else use "?" instead of "O".
comkp1: call    prtbout
    pop    ax            ; recover final char
comkp0: call    prtbout            ; send it
    clc
    ret
klogon    proc    near            ; resume logging (if any)
    test    flags.capflg,logses    ; session logging enabled?
    jz    klogn            ; z = no, forget it
    or    argadr.flgs,capt    ; turn on capture flag
    or    yflags,capt        ; set local msy flag as well
    call    ansflg            ; tell emulator
klogn:    clc
    ret
klogon    endp

klogof    proc    near            ; suspend logging (if any)
    and    argadr.flgs,not capt    ; stop capturing
    and    yflags,not capt        ; reset local msy flag as well
    call    ansflg            ; tell emulator
klogo:    clc
    ret
klogof    endp

snull    proc    near            ; send a null byte
    mov    al,0            ; the null
    call    prtbout            ; send without logging and local echo
    clc
    ret
snull    endp
                    ; general character out for emulator
chrout: cmp    flags.vtflg,0        ; emulating?
    je    chrou5            ; e = no
    call    anskbi            ; Yes, say we had keyboard input.
    cmp    al,cr            ; A CR?
    jne    chrou5            ; No - just output it and return
    call    ansflg            ; Yes - update VT100 flags
    test    ansflgs,anslnm        ; ANSI new-line mode set?
    jz    chrou5            ; No - just send the cr
    call    outprt            ; Yes - output a carriage-return
    mov    al,lf            ; Followed by a line feed.
chrou5: call    outprt
    clc
    ret

                    ; these commands invoke Quit
cdos:    mov    al,'P'            ; Push to DOS
    jmp    short cmdcom
cstatus:mov    al,'S'            ; Status
    jmp    short cmdcom
cquit:    mov    al,'C'            ; Exit Connect mode
    jmp    short cmdcom
cquery: mov    al,'?'            ; Help
    jmp    short cmdcom
chang:    mov    al,'H'            ; Hangup, drop DTR & RTS
    jmp    short cmdcom
cmdcom: mov    kbdflg,al        ; pass char to msster.asm via kbdflg
    stc                ; signal that Quit is needed
    ret

dmpscn    proc    near            ; dump screen to file
    call    savescr            ; save screen to buffer
    call    dumpscr            ; do buffer to file
    clc                ; do not exit Connect mode
    ret
dmpscn    endp


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

vtans52 proc    near
    cmp    flags.vtflg,0        ; emulating?
    je    vtans5            ; e = no
    call    ans52t            ; Call MSZ toggle-it routine.
    call    ansflg            ; Update flags.
    clc                ; clear c bit so don't exit Connect.
vtans5: ret
vtans52 endp
                    ; Toggle Mode Line
trnmod: cmp    flags.modflg,0        ; is mode line enabled?
    je    trnm2            ; e = no, don't touch it
    cmp    flags.vtflg,tttek    ; Tek mode?
    je    trnm2            ; yes
    cmp    tekflg,0        ; Tek submode?
    jne    trnm2            ; ne = yes, no mode line changes
    test    yflags,modoff        ; mode line already off?
    jnz    trnm1            ; yes, go turn on
    call    clrmod            ; no, clear mode line here
    or    yflags,modoff        ; turn on flag
    call    ansflg            ; Update flags all around.
    clc                ; clear c bit so don't exit Connect
    ret                ; and return
trnm1:    and    yflags,not modoff    ; Clear flag first.
    call    modlin            ; Then turn on mode line.
    call    ansflg            ; Update flags all around.
trnm2:    clc
    ret

trnprs: push    ax            ; toggle ^ PrtSc screen to printer
    test    anspflg,prtscr        ; are we currently printing?
    jnz    trnpr2            ; nz = yes, its on and going off
    mov    ah,ioctl
    mov    al,7            ; get output status of printer
    push    bx
    mov    bx,4            ; file handle for system printer
    int    dos
    pop    bx
    jc    trnpr1            ; c = printer not ready
    cmp    al,0ffh            ; Ready status?
    je    trnpr2            ; e = Ready
trnpr1: call    beep            ; Not Ready, complain
    jmp    trnpr3            ; and ignore request
trnpr2: xor    anspflg,prtscr        ; flip the flag
    test    yflags,modoff        ; mode line off?
    jnz    trnpr3            ; nz = yes
    call    modlin            ; else rewrite mode line
trnpr3: pop    ax
    clc                ; return carry clear (don't quit)
    ret

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

; computes screen location to ax, given row and col in [dh,dl], resp.
; trashes dx
scrloc    proc    near
    mov    al,dh            ; get row
    xor    ah,ah            ; clear ah
    mul    crt_cols        ; multiply by number of columns
    xor    dh,dh            ; clear row
    add    ax,dx            ; this is current position
    shl    ax,1            ; double for attributes
    ret
scrloc    endp

; Routine to set cursor type.  Pass cursor type in al: 0 = No cursor,
; 1 = Underline cursor, 2 = Block cursor.   All cursors blink due to hardware.
; Routine frags any ac that video ints frag.
; For EGA boards running in non-25 line mode the cursor emulation is turned
; off during cursor shape changing and restored afterward. It's another
; ega Feature. [jrd]
csrtype proc    near
    push    cx            ; save the reg
    test    usevb,1            ; video board present?
    jz    sancsr            ; z = no
    mov    ah,1            ; Video fxn for set cursor type
    mov    cx,0F00H        ; Assume no cursor
    cmp    al,0            ; No cursor?
    je    csrty2            ; Right - set it and be done with it.
    mov    cx,0607H        ; No, use CGA underline cursor
    cmp    al,2            ; Block?
    jne    csrty2            ; No - set it now.
csrty1: xor    ch,ch            ; Yes - make it a block
csrty2:
csrty4: int    screen            ; regular cursor shape setting
    pop    cx
    ret
; [begin jhw]
; Sanyo does not support small/large cursor changes without patching the
; BIOS.     Since there are so many BIOS versions around, I considered it to be
; too risky to try to implement that here.  So the following routine modifies
; the flash rate instead of the cursor size -- 0 for no cursor, 1 for slow
; (normal) flash, and 2 for rapid flash.
sancsr:    push    ax            ; save cursor type
    mov    ax,0100h        ; Sanyo bios is sequence sensitive on
    xor    cx,cx            ; cursor type calls.  Can't set fast
    int    SCREEN            ; cursor from no cursor -- so set to
    pop    ax            ; normal, then set to desired type
    cmp    al,0
    jne    csr1
    mov    cx,2600h        ; set for no cursor
    jmp    csr3
csr1:    cmp    al,1
    jne    csr2
    xor    cx,cx            ; set for slow cursor (normal)
    jmp    csr3
csr2:    mov    cx,107h            ; set for quick flash
csr3:    mov    ah,1            ; ah=1 for cursor set bios call
    int    SCREEN
    pop    cx
    ret                ; [end jhw]
csrtype endp


; Save the entire screen in a buffer so we can restore and/or dump it.
; Saves regular (80x25) screens to memory buffer scrsav and other sized
; screens to video memory page 1. Resultant save place put into savadr
; (offset then segment) and current low_rgt size info in savflg. Note,
; some Environments (TopView/Windows etc) may not permit use of page 1. [jrd]
savescr proc    near
    push    es
    push    ds
    push    ax
    push    cx
    push    si
    push    di
    call    scrseg            ; get screen segment in ax and es:di
    push    ax            ; save screen segment
    mov    si,0
    test    usevb,1            ; video board in use?
    jnz    savscv1            ; nz = yes
    mov    si,di
savscv1:
    mov    di,scrsav        ; place to put screen (memory buff)
    mov    savadr+2,di        ; working seg address for restore
    mov    savadr,0        ; and no offset for memory buffer

    call    scrmod            ; ascertain video mode and screen
    mov    ax,low_rgt        ; text screen lower right (typ 23,79)
    mov    savflg,ax        ; save it for screen restore
    inc    al            ; number of columns
    add    ah,2            ;  plus status line = number of rows
    cmp    al,swidth        ; same as preset screen space (80)?
    ja    savsc1            ; a = no, use screen video page 1
    cmp    ah,slen+1        ; same as preset screen length (24)?
    je    savsc3            ; e = yes, use our memory buffer
savsc1: mul    ah            ; times rows = characters on screen
    shl    ax,1            ; times two for attributes = page 1
    mov    cx,ax            ; cx = working copy of screen size
    and    cx,000fh        ; get lower four bits for offset part
    mov    savadr,cx        ; save offset in this word
    mov    cl,4
    shr    ax,cl            ; compute number of paragraphs
    pop    di            ; source screen address
    push    di            ; restore again
    add    di,ax            ; add paragraphs, point di to page 1
    mov    savadr+2,di        ; and save segment in this word
savsc3:
    mov    es,savadr+2        ; segment of storage area
    mov    di,savadr        ;  offset of same
    mov    ax,low_rgt        ; lower right of text screen
    inc    al            ; number of columns on screen
    add    ah,2            ; number of rows on screen
    mul    ah            ; number of characters on the screen
    mov    cx,ax            ; save this in counter cx
    call    scroff            ; turn off screen [dt]
    pop    ds            ; address screen
    cld
    rep    movsw            ; save the screen
    pop    di
    pop    si
    pop    cx
    pop    ax
    pop    ds            ; restore this
    call    scron            ; turn on screen [dt]
    pop    es
    ret
savescr endp

; restore screen from buffer (offset and seg in savadr, text coord in savflg).
; Restores all screen lines. [jrd]
restscr proc    near
    push    es
    mov    ax,savflg        ; saved low_rgt text screen coord
    add    ah,2            ; number of screen lines
    inc    al            ; number of screen columns
    mul    ah            ; columns time lines = # characters
    mov    cx,ax            ; save this in counter cx
    push    cx            ; save count
    call    scrseg            ; get address of screen in es:di
    call    scroff            ; turn off screen [dt]
    test    usevb,1            ; video board in use?
    jz    rstscv1            ; z = no
    push    ds            ; save original data segment
    mov    si,savadr        ; offset of storage area
    push    savadr+2        ; segment of same
    pop    ds            ; put storage segment into ds
    cld
    rep    movsw            ; restore data to screen
    pop    ds            ; recover original data segment
    jmp short rstscv2
rstscv1:mov    si,savadr
    push    savadr+2
    pop    es
    dec    cx
    add    si,cx        ; move to end, reload backwards
    add    si,cx
    xor    ax,ax
    mov    bx,low_rgt
    inc    bh        ; also do mode line
    call    atsclr        ; clear screen before restoring
    mov    al,' '
    mov    ah,scbattr
    mov    temp,ax        ; blank with background attribute
    mov    dx,low_rgt    ; lower right corner
    inc    dh        ; including mode line
rstsc1:    mov    ax,es:[si]
    cmp    ax,temp
    je    rstsc2        ; skip over blanks
    mov    ah,2        ; set cursor function
    int    SCREEN        ; position cursor
    mov    ax,es:[si]    ; get char and attribute
    mov    bl,ah
    mov    cx,1        ; number of chars to display
    mov    ah,9        ; write attribute and char function
    int    SCREEN        ; display char
rstsc2:    dec    si
    dec    si
    dec    dl        ; back one column
    jge    rstsc1        ; jump unless row done
    mov    dl,byte ptr low_rgt    ; end of row
    dec    dh        ; on to previous column
    jge    rstsc1        ; jump unless all done
;
rstscv2:
    mov    ah,2        ; set cursor position
    mov    bh,0        ; page 0
    mov    dx,cursor    ; saved cursor position
    int    SCREEN        ; position cursor
    call    scron            ; turn on screen [dt]
    pop    cx            ; recover count
    call    scrsync            ; synch Topview with new screen
    pop    es
    ret
restscr endp

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

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

dmp5:    test    filtst.fstat,80h    ; access problem?
    jnz    dmp0            ; nz = yes
    mov    ah,creat2        ; file did not exist
    mov    cx,20h            ; attributes, archive bit
    int    dos
    mov    dmphand,ax        ; save file handle
    jnc    dmp1            ; nc = ok

dmp0:    mov    ah,3            ; get cursor position
    xor    bx,bx            ; page 0
    int    screen
    push    dx            ; save it
    mov    dh,byte ptr low_rgt+1    ; go to status line
    inc    dh
    xor    dl,dl            ; left most column
    mov    ah,2            ; position cursor
    int    screen
    mov    dx,offset dmperr    ; say no can do
    mov    ah,prstr
    int    dos
    pop    dx            ; get original cursor position
    mov    ah,2            ; position cursor
    xor    bx,bx            ; page 0
    int    screen
    pop    dx
    pop    cx
    pop    bx
    pop    ax
    clc
    ret

dmp1:    mov    ah,ioctl        ; is destination ready for output?
    mov    al,7            ; test output status
    mov    bx,dmphand        ; handle
    int    dos
    jc    dmp0            ; c = error
    cmp    al,0ffh            ; ready?
    jne    dmp0            ; ne = not ready.
    push    di            ; read screen buffer, write lines
    push    si
    push    es
    mov    cl,byte ptr low_rgt+1    ; number of lines - 2
    add    cl,2            ; number of line on screen
    xor    ch,ch
    mov    si,savadr        ; offset in storage area
dmp2:    push    cx            ; save outer loop counter
    mov    es,savadr+2        ; get storage segment
    mov    di,offset dumpbuf    ; data segment memory
    mov    cl,byte ptr savflg    ; number of columns on screen - 1
    inc    cl            ; number of columns on screen
    xor    ch,ch
dmp3:    mov    ax,word ptr es:[si]    ; read char + attribute
    mov    byte ptr [di],al    ; store just char, don't use es:
    inc    si            ; update pointers
    inc    si
    inc    di
    loop    dmp3            ; do for each column
    std                ; set scan backward
    mov    cl,byte ptr savflg    ; number of columns on screen - 1
    inc    cl            ; number of columns on screen
    xor    ch,ch
    push    es
    mov    ax,ds
    mov    es,ax            ; set es to data segment for es:di
    mov    di,offset dumpbuf    ; start of line
    add    di,cx            ; plus length of line
    dec    di            ; minus 1 equals end of line
    mov    al,' '            ; thing to scan over
    repe    scasb            ; scan until non-space
    cld                ; set direction forward
    pop    es
    jz    dmp3a            ; z = all spaces
    inc    cx
    inc    di
dmp3a:    mov    word ptr [di+1],0A0Dh    ; append cr/lf
    add    cx,2            ; line count + cr/lf
    mov    dx,offset dumpbuf    ; array to be written
    mov    bx,dmphand        ; need file handle
    mov    ah,write2        ; write the line
    int    dos
    pop    cx            ; get line counter again
    jc    dmp3b            ; c = error
    loop    dmp2            ; do next line
    mov    dx,offset dumpsep    ; put in formfeed/cr/lf
    mov    cx,3            ; three bytes overall
    mov    ah,write2        ; write them
dmp3b:    mov    bx,dmphand        ; file handle
    int    dos
    mov    ah,close2        ; close the file now
    int    dos
dmp6:    pop    es
    pop    si
    pop    di
    pop    dx
    pop    cx
    pop    bx
    pop    ax
    clc
    ret
dumpscr endp


; Get CRT mode - returns mode in variable crt_mode,
; updates crt_cols and low_rgt.
; For EGA active it looks in Bios work memory 40:84H for number of rows. [jrd]
scrmod    proc    near
    push    ax
    push    dx
    mov    ah,15            ; Get current video state.
    int    screen
    mov    crt_mode,al        ; Store CRT mode value.
    mov    crt_cols,ah        ; store # of cols
    mov    dl,ah            ; # of cols again
    mov    dh,crt_lins        ; and # of rows (constant from msster)
scrmod4:
    dec    dl            ; max text column, count from zero
    dec    dh            ; max text row, count from zero
    mov    low_rgt,dx        ; save away window address
    pop    dx
    pop    ax
    ret                ; And return.
scrmod    endp


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

scrseg    proc    near
    test    usevb,1        ; video board in use?
    jz    scrsegv        ; z = no
    xor    di,di            ; start at beginning of screen (0,0)
    mov    ax,0B800H        ; No - video memory is here on color
    jmp short scrse1
scrsegv:push    bx
    push    cx
    mov    ah,71h        ; find shadow screen
    int    SCREEN
    push    ds
    mov    ds,ax
    mov    ax,[bx]     ; ax:bx points to variable with segment
    mov    bx,cx        ; can't do mov from [cx]
    mov    di,[bx]     ; ax:cx points to variable with offset
    mov    es,ax
    pop    ds
    pop    cx
    pop    bx
    cmp    crt_mode,12        ; graphics set?
    jb    scrse1            ; b = no
    mov    ax,0A000H        ; graphics
scrse1: mov    es,ax        ; tell Topview our hardware address needs
    mov    tv_segs,es        ; save our hardware screen address
    mov    tv_sego,di        ; segment and offset form
scrse2: ret
scrseg    endp

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

; The following two routines are used to turn off the display while we
; are reading or writing the screen in one of the color card modes.
; Turn screen off for (known) color card modes only. All regs preserved.
; Includes code for old procedure scrwait. 16 June 1987 [jrd]
scroff    proc    near
    test    usevb,1            ; video board in use?
    jz    scrofx            ; z = no
    cmp    refresh,0        ; slow refresh?
    jne    scrofx            ; ne = no wait
    cmp    ega_mode,0        ; Extended Graphics Adapter in use?
    jne    scrofx            ; ne = yes, no waiting.
    cmp    tv_mode,0        ; Topview mode?
    jne    scrofx            ; ne = yes, no waiting
    cmp    crt_mode,7        ; B&W card?
    jnb    scrofx            ; Yes - just return.
    push    ax            ; Save ax and dx.
    push    dx
    mov    dx,crt_status        ; CGA: Wait for vertical retrace
scrof1: in    al,dx
    test    al,disp_enb        ; display enabled?
    jnz    scrof1            ; yes, keep waiting
scrof2: in    al,dx
    test    al,disp_enb        ; now wait for it to go off
    jz    scrof2            ; so can have whole cycle
    mov    dx,crtmset        ; Output to CRT mode set port.
    mov    al,25H            ; This shuts down the display
    out    dx,al            ; Dumb, but card is too..
    pop    dx            ; Restore acs.
    pop    ax
scrofx: ret                ; And return.
scroff    endp


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

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


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

atsclr: push    ax            ; Save some acs
    push    cx
    push    dx
    test    usevb,1            ; video board in use?
    jz    atsclv            ; z = no
    mov    dx,bx            ; Compute last screen offset in ax
    push    ax
    call    scrmod            ; update column length
    pop    ax            ; scrmod zaps ax
    push    ax
    call    scrloc            ; get screen start address in ax
    mov    cx,ax            ; Save it in cx for a minute
    pop    dx            ; Compute first screen offset in ax
    call    scrloc
    sub    cx,ax            ; Compute number of locs to clear...
    add    cx,2
    sar    cx,1            ; Make byte count a word count
    jle    atscl2            ; If nothing to clear, then vamos.
    push    di            ; Save some more acs
    push    es            ; save es
    push    ax            ; save around call
    call    scrseg            ; Get address of screen in ax, es:di
    pop    ax            ; recover displacement
    add    di,ax            ; displacement memory address
    mov    ah,scbattr        ; Use current screen background attr.
    mov    al,' '            ; Use space for fill
    mov    dl,byte ptr low_rgt    ; line length - 1
    inc    dl            ; line length
    xor    dh,dh
    cmp    cx,dx            ; Blanking a line or less??
    jg    atscl1            ; No - make scroff disable display
atscl1: call    scroff            ; Turn screen off if color card.
    push    cx            ; save word count for Topview
    cld
    rep    stosw            ; Blit... (excuse PDP-10ese please)
    pop    cx            ; recover word count
    call    scrsync            ; synch Topview
    call    scron            ; Turn screen back on if color card.
    pop    es            ; Restore segment register
    pop    di            ; And destination index
    jmp short atscl2
atsclv:    push    bx
    push    di
    push    si
    mov    cx,ax
    mov    dx,bx
    mov    ax,600h
    mov    bh,scbattr
    int    SCREEN
    pop    si
    pop    di
    pop    bx
atscl2: pop    dx            ; restore regs
    pop    cx
    pop    ax
    ret

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

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

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


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

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

;screen text roll up, version for manual scrolling only

mscru:    push    ax            ; Upgraded by  [jrd]
    push    bx
    push    cx
    push    dx
    push    si
    push    di
    cmp    bwnd.lcnt,0        ; any lines in bottom window?
    je    mscru2            ; e = no, so ignore request
    call    atsprep            ; save top line
mscru1: mov    ax,601H            ; scroll up one line
    mov    dx,low_rgt        ; lower right corner
    xor    cx,cx            ; top row of scrolling region
    mov    bh,scbattr        ; background attributes
    int    screen            ; scroll up that region
    call    scroff
    mov    dx,low_rgt
    mov    dl,0            ; location is lower left corner
    call    scrloc            ; get count from display start
    push    es
    push    ax            ; save count
    call    scrseg            ; get screen's segment into ax, es:di
    pop    ax            ; recover count
    add    di,ax            ; destination memory address (es:di)
    mov    bx,offset bwnd        ; source of lines
    call    getcirc            ; get line from circ buf to screen
    pop    es            ; restore es
    call    scron            ; turn on the screen
mscru2: pop    di            ; Restore the rest of the regs.
    pop    si
    pop    dx
    pop    cx
    pop    bx
    pop    ax
    ret


; prep for screen scroll down.
; copies bottom scroll line from screen to bottom window buffer.
; destroys ax,cx,dx,si,di.
getbot proc near            ; Upgraded from old version [jrd]
    push    es
    call    scroff            ; turn off screen
    mov    dx,low_rgt        ; from screen location, row
    mov    dl,0            ; starting in col 0
    call    scrseg            ; get adaptor's offset into es:di
    call    scrloc            ; get offset in display buffer in ax
    add    di,ax            ; source addr in display buffer es:di
    mov    si,di            ; screen is source (si)
    mov    bx,offset bwnd        ; buffer to use (bottom window)
    call    putcirc            ; copy bottom screen line to circ buf
    pop    es
    call    scron            ; turn on display again
    ret
getbot endp

;screen text scroll down, for manual mode only
mscrd:    push    ax            ; Upgraded by [jrd]
    push    bx
    push    cx
    push    dx
    push    si
    push    di
    cmp    twnd.lcnt,0        ; any lines left in top window?
    je    mscrd1            ; e = no, ingore request
    call    getbot            ; fetch bottom line from screen
    mov    ax,701H            ; scroll down one line
    xor    cx,cx            ; top left corner
    mov    dx,low_rgt        ; bottom right corner
    mov    bh,scbattr        ; attributes
    int    screen            ; scroll it down
    call    scroff            ; turn off display
    push    es
    call    scrseg            ; get segment address of screen
    mov    bx,offset twnd        ; buffer to use (top window)
    call    getcirc            ; copy from circ buf to screen
    pop    es
    call    scron            ; turn on display again
mscrd1: pop    di            ; Restore the rest of the ACs.
    pop    si
    pop    dx
    pop    cx
    pop    bx
    pop    ax
    ret

; move viewing window down as much as possible (text moves up)
endwnd    proc    near            ; go to end of scrolling text
    push    cx
    mov    cx,bwnd.lcnt        ; all bottom window lines [dlk]
    jmp    dnwp0            ; and enter dwnpg
endwnd    endp

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

; scroll viewing window down (text moves up) one page (24 lines)
dnwpg    proc    near
    push    cx
    mov    cl,byte ptr low_rgt+1    ; number of rows, excl status
    inc    cl            ; count from 1, not 0
    mov    ch,0
dnwp0:                    ; additional entry point
    cmp    bwnd.lcnt,cx        ; enough lines in bottom line buffer?
    jge    dnwp1            ; ge = we have that many lines stored
    mov    cx,bwnd.lcnt        ; do as many as we have
dnwp1:    jcxz    dnwp2            ; z = nothing to do
    cmp    tekflg,0        ; Tek mode active?
    jne    dnwp2            ; ne = yes, no scrolling
    call    mscru            ; scroll up text one line
    loop    dnwp1
dnwp2:    pop    cx
    clc
    ret
dnwpg    endp

; home viewing window
homwnd    proc    near
    push    cx
    mov    cx,twnd.lcnt        ; all top window lines [dlk]
    jmp    upwp0            ; join upwpg
homwnd    endp

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

; scroll viewing window up (text moves down) a page (24 lines)
upwpg    proc    near
    push    cx
    mov    cl,byte ptr low_rgt+1    ; number of rows, excl status line
    inc    cl            ; count from 1, not 0
    mov    ch,0
upwp0:                    ; additional entry point
    cmp    twnd.lcnt,cx        ; enough lines in top line buffer?
    jae    upwp1            ; ae = at least as many as requested
    mov    cx,twnd.lcnt        ; do only as many as are stored.
upwp1:    jcxz    upwp2            ; z = no lines to scroll
    cmp    tekflg,0        ; Tek mode active?
    jne    upwp2            ; ne = yes, no scrolling
    call    mscrd            ; roll down text one line
    loop    upwp1
upwp2:    pop    cx
    clc
    ret
upwpg    endp


; Put a line into the circular buffer.    Pass the buffer structure in bx.
; Source is tv_segs:si which is the current screen address.
; Rewritten by [jrd]
putcirc proc    near
    push    es
    mov    cl,crt_cols        ; number of columns
    xor    ch,ch
    mov    es,[bx].orig        ; get segment of memory area
    cmp    bx,offset bwnd        ; bottom buffer?
    je    putci6            ; e = yes
    mov    di,twnd.pp        ; pick up buffer ptr (offset from es)
    add    di,cx            ; increment to next available slot
    add    di,cx            ; char and attribute
    cmp    di,twnd.bend        ; would line extend beyond buffer?
    jb    putci1            ; b = not beyond end
    mov    di,0            ; else start at the beginning
putci1: mov    twnd.pp,di        ; update ptr
    cld                ; set direction to forward
    push    ds            ; save regular datas seg reg
    mov    ds,tv_segs        ; use screen segment for ds:si
    rep    movsw            ; copy into buffer
    pop    ds            ; restore regular datas segment
    mov    cx,twnd.lmax        ; line capacity of buffer
    dec    cx            ; minus one work space line
    cmp    twnd.lcnt,cx        ; can we increment line count?
    jae    putci1b            ; ae = no, keep going
    inc    twnd.lcnt        ; else count this line
putci1b:cmp    bwnd.lcnt,0        ; any lines in bottom buffer?
    je    putci2            ; e = no
    mov    cx,bwnd.pp        ; see if we overlap bot buf
    cmp    cx,twnd.pp        ; is this line in bot buf area?
    jne    putci2            ; ne = no
    add    cl,crt_cols        ; move bottom pointer one slot earlier
    adc    ch,0
    add    cl,crt_cols        ; words
    adc    ch,0
    cmp    cx,bwnd.bend        ; beyond end of buffer?
    jb    putci1a            ; b = no
    mov    cx,0            ; yes, start at beginning of buffer
putci1a:mov    bwnd.pp,cx        ; new bottom pointer
    dec    bwnd.lcnt        ; one less line in bottom buffer
putci2: pop    es
    ret
putci6:                    ; bottom buffer
    add    cx,cx            ; words worth
    cmp    bwnd.lcnt,0        ; any lines in the buffer yet?
    jne    putci7            ; ne = yes
    mov    di,twnd.pp        ; get latest used slot of top buff
    add    di,cx            ; where first free (?) slot starts
    cmp    di,bwnd.bend        ; are we now beyond the buffer?
    jb    putci6a            ; b = no
    mov    di,0            ; yes, start at beginning of buffer
putci6a:add    di,cx            ; start of second free (?) slot
    cmp    di,bwnd.bend        ; are we now beyond the buffer?
    jb    putci6b            ; b = no
    mov    di,0            ; yes, start at beginning of buffer
putci6b:mov    cx,twnd.lmax        ; buffer line capacity
    sub    cx,twnd.lcnt        ; minus number used by top buffer
    sub    cx,2            ; minus one work slot and one we need
    cmp    cx,0            ; overused some slots?
    jge    putci8            ; ge = enough to share
    add    twnd.lcnt,cx        ; steal these from top window beginning
    jmp    short putci8

putci7: mov    es,bwnd.orig        ; get segment of memory area
    mov    di,bwnd.pp        ; pick up buffer ptr (offset from es)
    cmp    di,0            ; would line start before buffer?
    jne    putci7a            ; ne = after start of buffer
    mov    di,bwnd.bend        ; else start at the end minus one slot
    inc    di
putci7a:sub    di,cx
putci8: mov    bwnd.pp,di        ; update ptr (this is latest used slot)
    mov    cl,crt_cols
    xor    ch,ch
    cld                ; set direction to forward
    push    ds            ; save regular datas seg reg
    mov    ds,tv_segs        ; use screen segment for ds:si
    rep    movsw            ; copy into buffer
    pop    ds            ; restore regular datas segment
    mov    cx,bwnd.lmax        ; line capacity of buffer
    cmp    bwnd.lcnt,cx        ; can we increment line count?
    jae    putci8b            ; ae = no, keep going
    inc    bwnd.lcnt        ; else count this line
putci8b:cmp    twnd.lcnt,0        ; any lines in top line buf?
    je    putci9            ; e = no
    mov    cx,twnd.pp        ; yes, see if we used last top line
    cmp    cx,bwnd.pp        ; where we just wrote
    jne    putci9            ; not same place, so all is well
    dec    twnd.lcnt        ; one less line in top window
    cmp    cx,0            ; currently at start of buffer?
    jne    putci8a            ; ne = no
    mov    cx,twnd.bend        ; yes
    inc    cx
putci8a:sub    cl,crt_cols        ; back up top window
    sbb    ch,0
    sub    cl,crt_cols        ; by one line
    sbb    ch,0
    mov    twnd.pp,cx        ; next place to read
putci9: pop    es
    ret

putcirc endp

; Get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; Pass the buffer structure in bx.
; Destination preset in es:di which is the current screen address.
; Rewritten by [jrd]
getcirc proc    near
    cmp    [bx].lcnt,0        ; any lines in buffer?
    jne    getci1            ; ne = yes, ok to take one out.
    stc                ; else set carry
    ret                ; and return
getci1:                    ; top and bottom window common code
    test    usevb,1            ; video board in use?
    jz    getci1v            ; z = no
    mov    cl,crt_cols        ; # of chars to copy
    xor    ch,ch
    push    cx            ; save around calls
    mov    si,[bx].pp        ; this is source
    cld                ; set direction to forward
    push    ds            ; save original ds
    mov    ds,[bx].orig        ; use seg address of buffer for si
    rep    movsw
    pop    ds            ; recover original data segment
    pop    cx            ; length for Topview
    push    cx            ; save again
    jmp getci2v
getci1v:mov    si,[bx].pp        ; this is source        [begin jhw]
    push    es            ; calculate screen location
    push    di
    call    scrseg            ; get start of ank seg in es:di
    pop    ax            ; restore current curs posit. in ank seg
    sub    ax,di            ; compute cursor offset from above
    pop    es
    div    swid2m            ; row in al,col in ah
    xchg    ah,al            ; ax now contains row/col
    push    ax

    mov    al,' '            ; [rwb]
    mov    ah,scbattr        ; [rwb]
    mov    temp,ax        ; blank with background attribute [rwb]

    mov    ax,0300h        ; save present cursor position
    int    SCREEN
    mov    curpost,dx
    pop    dx            ; retrieve row/col (was in ax)

                    ; write line to screen
    push    bx            ; save bx before reusing in bios call
    push    es            ; save es
    mov    es,[bx].orig        ; get segment for circular buffer
    mov    cx,swidth        ; # of characters to copy per line
getci1a:
    mov    bx,es:[si]        ; get character and attribute [rwb]
    cmp    bx,temp            ; is it a blank with proper attr [rwb]
    je    getci1x            ; yes, no need to display [rwb]
    push    si            ; potentially zapped by int 10h [rwb]
    push    cx            ; save character count for loop counter
    mov    ax,0200h
    int    SCREEN            ; position the cursor
    mov    ax,bx            ; get attr/char in ah,al [rwb]
    mov    bl,ah
    mov    cx,1
    mov    ah,09
    int    SCREEN            ; display character with attribute
    pop    cx            ; restore character count
    pop    si            ; [rwb]
getci1x:
    inc    si
    inc    si
    cmp    dl,79            ; are we at end of line?
    jae    getci1b
    inc    dl
getci1b:
    loop    getci1a            ; loop until line is written
    pop    es            ; restore es
;
    mov    dx,curpost        ; restore the cursor to where it was
    mov    ax,0200h
    int    SCREEN            ; above code assumes dx is preserved
                    ;   through bios calls    [end jhw]
    pop    bx            ; restore bx [jhw]


    mov    cx,swidth        ; length for Topview
    push    cx            ; save again
getci2v:
    call    scrsync            ; synch Topview
    mov    si,[bx].pp        ; get ptr again
    pop    cx
    add    cx,cx            ; words
    cmp    bx,offset bwnd        ; bottom window?
    je    getci6            ; e = yes
    sub    si,cx            ; top window, move back
    jnc    getcir2            ; nc = still in buffer, continue
    mov    si,twnd.bend        ; else use end of buffer
    sub    si,cx            ; minus length of a piece
    inc    si
getcir2:mov    twnd.pp,si        ; update ptr
    dec    twnd.lcnt        ; decrement # of lines in buffer
    clc                ; make sure no carry
    ret
getci6:                    ; bottom window
    add    si,cx            ; words, move back (bot buf = reverse)
    cmp    si,bwnd.bend        ; still in buffer?
    jb    getci7            ; b = yes
    mov    si,0            ; else use beginning of buffer
getci7: mov    bwnd.pp,si        ; update ptr
    dec    bwnd.lcnt        ; decrement # of lines in buffer
    clc                ; make sure no carry
    ret
getcirc endp

;
; CHGDSP  dummy routine which sets 132 col display boards on ibm
;        not useful on 550
chgdsp    proc    near
    ret                ; return to caller
chgdsp    endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
    pop    bp
    add    bp,3
    push    bp
    ret
RSKP    ENDP

; Jumping here is the same as a ret.

R    PROC    NEAR
    ret
R    ENDP

code    ends

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

    end
