;SCCSID = @(#)bvstty.asm	6.5 92/01/24
        PAGE    58,132
        TITLE   BvsTTY.Asm -- Base Video Subsystem Write TTY Routines
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************

;/*********************** START OF SPECIFICATIONS ***********************/
;/*                                                                     */
;/* SOURCE FILE NAME:    BvsTTY.Asm                                     */
;/*                                                                     */
;/* DESCRIPTIVE NAME:    BVS Write TTY routine                          */
;/*                                                                     */
;/* COPYRIGHT:   Microsoft Corp. (C) 1988                               */
;/*                                                                     */
;/* STATUS:      na                                                     */
;/*                                                                     */
;/* NOTES:       Executes on level 3 - Protect Mode ONLY                */
;/*                                                                     */
;/*    DEPENDENCIES:     none                                           */
;/*                                                                     */
;/*    RESTRICTIONS:     none                                           */
;/*                                                                     */
;/*    PATCH LABEL:      na                                             */
;/*                                                                     */
;/* ENTRY POINT: BvsWrtTTY                                              */
;/*                                                                     */
;/* EXTERNAL REFERENCES: see below                                      */
;/************************ END OF SPECIFICATIONS ************************/

        .xlist
        INCLUDE bvscb.inc               ; BVS Data Structures
        INCLUDE bvsparms.inc            ; bvs parameter equates
        INCLUDE bvsextrn.inc            ;BVS external definitions       ;@T52
        INCLUDE error2.inc              ; Error codes
        INCLUDE struc.inc               ; Structured Macros
        INCLUDE bvsdbcs.inc             ;@P1
        .list

        .386p                   ;@D549

        EXTRN   DOSGETCP:FAR            ;Dos Get Code Page routine      ;B715221

        EXTRN   DOSBEEP:FAR             ; Dos Beep Speaker routine
        EXTRN   DOSDEVIOCTL:FAR         ; DoS IOCtl (DevDrvr interface)
        EXTRN   DOSOPEN:FAR             ; Dos Open routine
        EXTRN   DOSCLOSE:FAR            ; Dos Close routine
        EXTRN   DOSWRITE:FAR            ; Dos Write routine
        EXTRN   ANSIINTERP:FAR          ; ANSI Command Interpreter  ;@C50
        EXTRN   VIOSCROLLUP:FAR         ; VIO scroll up entry point ;@S22
        EXTRN   VIOREADCELLSTR:FAR      ; VIO read cell string          ;@T46
        EXTRN   VIOWRTCHARSTR:FAR       ; VIO write char                ;@T46
        EXTRN   VIOWRTCHARSTRATT:FAR    ; VIO write char/attr           ;@T46
        EXTRN   VIOSETCURPOS:FAR        ; VIO set cursor position       ;@C40

        EXTRN   BvsReadCellStr:NEAR     ; Read cell string
        EXTRN   BvsSetCurPos2:NEAR      ; BVS set cursor position ;D198
        EXTRN   BvsScrollUp:NEAR        ; BVS scroll up entry point
        EXTRN   BvsWrtCharStr:NEAR      ; BVS write char entry point
        EXTRN   BvsWrtCharStrAtt:NEAR   ; BVS write char/attr entry point

        EXTRN   AllVDH:NEAR             ; VDH Gateway routine ;D198
        EXTRN   SyncCursor:NEAR         ; Get Cursor Position ;D198
        EXTRN   OpenPrt:NEAR            ; Open Printer subroutine
;@C50;;;EXTRN   Thunker:NEAR            ;                               ;@T25
;@C50;;;EXTRN   ThunkerFarRet:FAR       ;                               ;@T25
;@C50;;;EXTRN   SZANSIMOD:BYTE          ;                               ;@T25

stacksize EQU   32                      ;@P1 ; size of BVS stack frame
CharAttPairSize equ 4                   ;@P1 ; size of a char/attr pair (bytes) J-KKJ2

BeepNote equ    800                     ;@P1 ; pitch for DOSBEEP
BeepDuration equ 200                    ;@P1 ; duration of sound for DOSBEEP

FuncActivateFont equ 48h                ;@P1 ; DOSDEVIOCTL
CatPrinter equ  5h                      ;@P1 ; DOSDEVIOCTL

TabSpace equ    8                       ;@P1 ; number of tab spaces, power of 2
TabMask equ     TabSpace - 1            ;@P1 ; used to do modulo by tab size.

BEL     EQU     07                      ; char code for a bel
BS      EQU     08                      ; char code for a backspace
TAB     EQU     09                      ; char code for a tab
LF      EQU     10                      ; char code for a line feed
CR      EQU     13                      ; char code for a carriage return
ESCAPE  EQU     27                      ; char code for an escape
SPACE   EQU     20h                     ;@P1    ; char code for a space

sm_framsiz EQU  4                       ;@P1
sm_cell EQU     -2                      ;@P1
sm_size EQU     -4                      ;@P1

BVSGSEG SEGMENT WORD PUBLIC 'FAR_DATA' USE16 ;Global Data   ;@D549      ;@T52
BVSGSEG ENDS

BVSISEG SEGMENT WORD PUBLIC 'DATA' USE16 ;Instance Data ;@D549          ;@T52

;C50;;THUNKDEF ANSIINTERP, szAnsiMod, Ansi_Interp ;@T25

BVSISEG ENDS

BVS3SEG SEGMENT BYTE PUBLIC 'CODE' USE16                ;@D549
        ASSUME  CS:BVS3SEG

SUBTTL  <BvsWrtTTY - write char string at cursor position in TTY mode>

;**     BvsWrtTTY - write char string at cursor position in TTY mode
;
;       Write a string starting at the current cursor location
;       in TTY mode to the display.  After the characters are
;       written the cursor will be positioned at the character
;       position after last character in the string.  Line wrap
;       occurs when the end of line is reached.  The contents
;       of the screen are scrolled up if the end of the screen
;       reached.  The carriage return (0DH), line feed (0AH),
;       tab (09H), backspace (08H), and the bell (07H)
;       characters are treated as commands rather than
;       as characters.
;       ANSI escape sequences are passed to ANSIINTERP and may be
;       spread across several calls.
;       DBCS characters are fully supported, with most of the word
;       wrapping logic to be supplied by WRTCHARSTR.  If the
;       macro(s) CheckLeadByte and CheckNotSingleCell are replaced
;       by stubs, this code will handle the regular case with very
;       little overhead.
;
;       Some functions are coupled rather more strongly than desirable:
;
;               StrScan expects CX, ES, DI to give input lenght and pointer.
;               It destroys CX and DI.  The new CX indicates the length
;               of unprocessed input.  It sets SI, which becomes a pointer
;               to the remaining string.
;
;               Print expects ES, DI and SI, with DI as BEFORE StrScan
;               was called and SI as RETURNED by StrScan.
;
;               Wrttty always restores ES,DI so Print is free to destroy ES.
;               Print also destroys DX which is used only in StrScan as well
;               as SI.
;
;       PSEUDOCODE:
;
;ENTRY: The stackframe set up by BVSMAIN contains these parameters
;
;       tty_strlen              length of input string in bytes
;       tty_stroff              offset and
;       tty_strsel              selector of input string
;
;       DS                      set to the selector of the BVSCB
;       bvsiseg:bvsi_tty_flag   set to tty_f_leading if retained
;                               leading byte on last call
;
;USAGE: CX                      length  of remaining input string
;       DI                      pointer to remaining input string
;       SI                      pointer to beyond current string
;                               the call to StrScan results in advancing SI
;                               as well as setting DI=SI
;       ES                      set to input string selector prior to the
;                               beginning of each loop in wrtty
;
;EXIT:  AX                      set to return code
;       bvsiseg:bvsi_tty_flag   set to tty_f_leading if retained
;                               leading byte
;       bvsiseg:bvsi_tty_dbcsl  set to retained leading byte.
;                               Undefined if tty_f_leading is not set
;
;who advances what pointer:
;
;       ANSIINTERP advances the start pointer for the string (TTYString)
;       ANSIINTERP can also reset row, col (depending on ANSI sequence)
;       StrScan returns in DI the new value of TTYString, but it is
;       called on other types of strings as well. wrttty takes care
;       whether to copy DI into TTYString
;       StrScan will maintain bvs_cursorcol and (via ScrollMaybe)
;       the bvs_cursorrow.
;
;BVSWRTTTY
;
;       [check printer status once per call]
;       PrinterInit()
;
;       [copy parameters into registers:]
;       length = tty_strlen
;       pointer= tty_strsel:tty_stroff
;
;       [save string pointer, length:]
;       TTYLen        = length
;       TTYString     = pointer                 [ = TTYSel:TTYOff ]
;
;       if alternate VIO
;               VIOGETCURPOS(bvs_cursorrow, bvs_cursorcol, 0)
;
;       while( length )
;               char = *pointer                 [ look at next character ]
;               if flags & retained             [ this is trailing byte ]
;                       fake[1] = char          [ make fake string ]
;                       clear retain flag
;                       if graphics mode
;                               set VIO_MODE_ERROR return
;                               goto ttywrapup  [ no further processing ]
;                       call StrScan( fake string, length = 2 )
;                       print( fake string, pointer )                   [ print up to current char ]
;                       TTYString++             [ fake of advance string pointer ]
;                       TTYLength--
;
;               else if (char == ESCAPE && ANSI processing)or ANSI_in progress
;                       call ANSIINTERP( BVSCB selector )
;
;               else if graphics mode
;                       set VIO_MODE_ERROR return
;                       goto ttywrapup          [ no further processing ]
;
;               else if char == TAB             [ expand to spaces ]
;                       tmp = col
;                       tmp &= tabmask  (i.e. tmp %= 8 )
;                       tmp -= 8
;                       tmp = -tmp;
;                       call StrScan( blank string, length = tmp)       [ StrScan does the wrap ]
;                       call print( blank string, pointer )             [ print up to current char ]
;                       TTYlength--
;                       TTYString ++            [ eat the TAB char ]
;               else
;                       call StrScan( TTYString, length )
;                       call print( TTYString, pointer )                [ print up to current char ]
;                       TTYlength = length
;               [restore registers w/ updated string pointer,length]
;               pointer= TTYString
;               length = TTYLength
;       }
;   ttywrapup:
;       [set cursor to new value]
;       bvsfunc(BVSSETCURPOS( cursorrow, cursorcol, 0 ))
;       return
;
;
;       LINKAGE:  Call BvsWrtTTY from BvsMain
;
;       ENTRY:  stack frame as described in BVSMAIN, where
;               tty_stroff - pointer to string to be output
;               tty_length - length of string
;               (ds)       - BVSCB
;
;       EXIT:   no error  - ax = 0
;               otherwise - ax = error
;
;       CALLS:  ANSIINTERP, BvsSetCurPos, ScrollMaybe, VIOSETCURPOS,
;               VIOGETCURPOS, StrScan
;
;       USES:   ax,cx,di,es
;
;       General usage of registers into the function:
;
;       CX    = string counter (decrement from length to 0)
;       ES:DI = address of string (current)
;       BX    = 0

BvsWrtTTY PROC  NEAR                    ;D198
        PUBLIC  BvsWrtTTY

        call    PrinterInit

        test    [bp].p_regax,rf_GetCurPos                               ;@T46
        .if     <nz>                    ;@S11
            Call    SyncCursor          ;Synch with AVS cursor ;D198;@S11
        .endif                          ;@S11

        mov     ax,es:bvsi_lvbsel                                       ;@T52
        mov     [bp].vp_tt_lvbsel,ax

        mov     ax,[bp].tty_stroff
        mov     [bp].vp_tt_offset,ax
        mov     ax,[bp].tty_strsel
        mov     [bp].vp_tt_selector,ax

        mov     ax,[bp].tty_length
        mov     [bp].vp_tt_length,ax

        mov     ax,es:bvsi_prthandle                                    ;@T52
        mov     [bp].vp_tt_handle,ax

        .if     <BIT [bx].bvs_flags AND bvs_f_wrtthru>
            or      [bp].vp_flags,vp_f_logical ;Pass WriteThru flag to VDH
        .endif
        .if     <BIT [bx].bvs_flags AND bvs_f_ansi>
            or      [bp].vp_flags,vp_f_ansi ;Pass ANSI flag to VDH
        .endif
        .if     <BIT [bx].bvs_flags AND bvs_f_cprtsc>
            or      [bp].vp_flags,vp_f_ctrlprtsc ;Pass ANSI flag to VDH
        .endif

        mov     [bp].vp_parmlength,14   ;Set parameter list length
        mov     di,fn_WriteTTY
        jmp     AllVDH
;       ret                             ;Return via AllVDH
BvsWrtTTY ENDP                          ;D198

VdhWrtTTY PROC  FAR                     ;D198
        public  VDHWRTTTY               ;D198

        Call    RegSave                 ;D198

        mov     ax,-1                   ;Preset PMERR_DEV_FUNC_NOT_INSTALLED
        .if     <<word ptr [bp].p_vdh_function> eq fn_WriteTTY> near ;D198

            mov     ax,es:[di].vp_tt_handle ;D198
            mov     word ptr [bp].p_vdh_function,ax ;Save handle for later ;D198

            mov     [bp].p_regsp,bx     ; Preset good return code ;D198
            mov     cx,es:[di].vp_tt_length ; (cx) = length of string
            les     di,dword ptr es:[di].vp_tt_offset ; (es:di) = &string

            mov     [BX].bvs_TTYOff,di  ; string offset
            mov     [BX].bvs_TTYSel,es  ; string selector
            mov     [BX].bvs_TTYLen,cx  ; string length

            mov     ax,[bx].bvs_ScrlRectBtm
            .if     <ax b [bx].bvs_cursorrow> ; cursor is out of the scroll rect

                test    [bp].p_regax,rf_SetCurPos                         ;@C40
                .if     <nz>                  ; function registered?      ;@C40
                    push    ax                ; new row position          ;@C40
                    push    [bx].bvs_cursorcol; new cursor position       ;@C40
                    push    bx                ; Always use 0, VioRegister ;@C40
                                              ; is not supported for AVIO ;@C40
                    Call    VIOSETCURPOS                                  ;@C40
                .else                                                     ;@C40
                    mov     cx,[bx].bvs_cursorcol ; set the cursor to the appropriate col
                    Call    BvsSetCurPos2     ; set cursor position via BVS
                .endif                                                    ;@C40

            .endif

ttyloop:
            mov     cx,[bx].bvs_TTYLen  ; remaining length
            .while  <cx ne bx> NEAR     ; while ( length )

                les     di,dword ptr [bx].bvs_TTYOff ; (es:di) = new StrBase
                mov     al, es:[di]     ; char = *StrBase

                mov     si,bvsiseg      ; (ES) = ppbvs selector
                mov     es,si
                .if     <BIT es:bvsi_tty_flag AND tty_f_leading> ; retained lead byte?

                    xor     es:bvsi_tty_flag,tty_f_leading ; clear retained flag

                    .if     <BIT [bx].bvs_type AND mode_f_graphics> ; exit if non-text mode
                        mov     [bp].p_regsp,error_vio_mode ; wrong mode ;D198
                    .leave  NEAR
                    .endif

                    test    [bx].bvs_flags2,bvs_f2_dbcscp ;Also clears CY
                    .if     <z>         ; if active codepage is not DBCS ;J-KKJ1
                        jmp     short ttyloop ; so far, we've just cleared the
                                        ; flag, so try again as SBCS
                    .endif

                    mov     es:bvsi_tty_dbcst,al ; Dbcs[1] = char
                    mov     di,offset bvsi_tty_dbcsl ; StrBase = DBCS
                    mov     cx,2        ; length  = 2
                    call    StrScan     ; StrScan( DBCS, 2 )
                    mov     di,offset bvsi_tty_dbcsl ; StrBase = DBCS   [reset]
                    call    Print       ; print dbcs char
                    dec     [bx].bvs_TTYLen ;
                    inc     [bx].bvs_TTYOff ; eat trailing  byte

                .else
                    .if     <BIT [bx].bvs_flags AND bvs_f_ansiprog> OR ; ansi in progress or
                    .if     <al e ESCAPE>                         AND ; char == ESC ?
                    .if     <BIT [bx].bvs_flags AND bvs_f_ansi> ; and ansi active ?
                        push    ds
                        call    ANSIINTERP ; process ansi sequence      ;@C50
;@C50;;;;;;;;;;;;;;;;;;;THUNKCALL ANSIINTERP ;@T25
                    .else

                        .if     <BIT [bx].bvs_type AND mode_f_graphics> ; non-text mode ?
                            mov     [bp].p_regsp,error_vio_mode ; wrong mode ;D198
                        .leave          ; do only ansi stuff if graphics
                        .endif

                        .if     <al e TAB> ; char == TAB ?
                            mov     ax,bvsgseg
                            mov     es,ax ; (ES)= BVS Global segment
                            mov     di,offset bvsg_blanks ; StrBase = bvsg_blanks ;@T52

                            mov     cx,[bx].bvs_cursorcol
                            and     cx,TabMask
                            sub     cx,TabSpace
                            neg     cx  ; Calculate length = 8 - (col % 8)

                            call    StrScan ; StrScan( blanks, tablength )
                            mov     di,offset bvsg_blanks ; StrBase = bvsg_blanks [reset] ;@T52
                            call    Print ; print blanks

                            dec     [bx].bvs_TTYLen
                            inc     [bx].bvs_TTYOff ; eat TAB
                        .else
                            les     di,dword ptr [bx].bvs_TTYOff ; StrBase = (es:di)
                            call    StrScan ; StrScan( StrBase, length )
                            mov     di,word ptr [bx].bvs_TTYOff ; StrBase = (es:di) [reset]
                            mov     [bx].bvs_TTYLen,cx ; remaining length
                            mov     [bx].bvs_TTYOff,si ; new StrBase
                            call    Print ; print string
                        .endif
                    .endif
                .endif
                mov     cx,[bx].bvs_TTYLen ; remaining length
            .endwhile                   ; loop back to top

;--- end of loop ---

            test    [bp].p_regax,rf_SetCurPos                         ;@C40
            .if     <nz>                  ; function registered?      ;@C40
                push    [bx].bvs_cursorrow; new row position          ;@C40
                push    [bx].bvs_cursorcol; new cursor position       ;@C40
                push    bx                ; Always use 0, VioRegister ;@C40
                                          ; is not supported for AVIO ;@C40
                Call    VIOSETCURPOS                                  ;@C40
            .else                                                     ;@C40
                mov     ax,[bx].bvs_cursorrow ; final cursorrow    ;D198
                mov     cx,[bx].bvs_cursorcol ; final cursorcol    ;D198
                Call    BvsSetCurPos2         ;Update it using BVS ;D198
            .endif                                                    ;@C40

            mov     ax,[bp].p_regsp     ; (ax) = return code ;D198

        .endif                          ;D198
        Call    RegRest                 ;D198
        ret     12                      ;D198
VdhWrtTTY ENDP                          ;D198

;       ADVANCE - step along string
;
;       step along in the input string by advanceing the pointer
;       and decrementing the length.

Advance MACRO
        inc     si                      ; pointer++
        dec     cx                      ; length--
        ENDM

;       STRSCAN - Scan along string
;
;       scans the input string and performs DBCS word wrap where
;       necessary.  Also performs special actions rewuired by
;       BS, BEL, LF, CR codes.  Passes back the remaing string
;       to BVSWRTTTY when it encounters ESC or TAB characters
;
;       PSEUDOCODE
;
;
;       StrBase = pointer
;       col = bvs_cursorcol
;       while( length )                         [ not eof ]
;       char = * pointer                        [ look at char ]
;       if DBCS code page && dbcs_lead( char )
;            length--
;            if length == 0                     [ eof ]
;                set retain flag                [ do not advance pointer !! ]
;                Dbcs[0] = char                 [ retain the char ]
;            else
;                pointer++                      [ advance pointer ]
;                char = *pointer                [ get next character ]
;                if notSinglecell
;                    col++                      [ advance col ]
;       else if char == ESCAPE  && ANSI processing enabled
;            exit loop
;       else if char > ESCAPE                   [ not a special char ]
;            goto normal
;       else if char == TAB
;            exit loop
;       else if char == BS
;            flush( StrBase, pointer )          [ flush up to before current char ]
;            if col                             [ echo backspace (always single!! ) ]
;               col--
;            goto AdvanceAndEcho
;       else if char == BELL
;            flush( StrBase, pointer )          [ flush up to before current char ]
;            dosbeep( note, duration )          [ beep ]
;            goto AdvanceAndEcho
;       else if char == LF
;            flush( StrBase, pointer )          [ flush up to before current char ]
;            ScrollMaybe                        [ linefeed ]
;            goto AdvanceAndEcho
;       else if char == CR
;            flush( StrBase, pointer )          [ flush up to before current char ]
;            col = 0                            [ echo carriage return ]
;            goto AdvanceAndEcho
;       else
; normal:                                       [ try, step column ]
;            col++                              [ NOTE: maxcol == lastcol + 1 ]
;            if col == (lastcol + 1)            [ last char will fit entirely ]
;               Advance
;               flush( StrBase, pointer )       [ flush up to current char ]
;               if wrap
;                  ScrollMaybe
;                  col = 0                      [ i.e. do a CR/LF action ]
;               else
;                  col = lastcol                [ leave the cursor at eol ]
;               goto SetColAndEcho
;            else if col > (lastcol + 1)        [ DBCS word wrap ]
;               flush( StrBase, pointer )       [ flush up to lastcol ]
;               if wrap
;                 length++
;                  pointer--                    [ re-scan the wrapped DBCS ]
;                 ScrollMaybe                   [ ... on the next line ]
;                 col = 0
;               else                            [ i.e. do a CR/LF action ]
;                  Advance
;                  col = lastcol                [ step past DBCS lost at eol ]
;                  goto SetColAndEcho           [ leave the cursor at eol ]
;            else
;               Advance                         [ the plain vanilla ]
;       continue                                [ loop to top ]
;
;       AdvanceAndEcho:
;            Advance
;       SetColAndEcho:
;            bvs_cursorcol = col                [ update cursor position ]
;            StrBase = pointer                  [ update start offset ]
;       }
;
;       flush( StrBase, pointer )
;       bvs_cursorcol = col                     [ update cursor position ]
;       StrBase = pointer
;
;return
;
;END OF STRSCAN
;       ENTRY   CX      (length)        initial length of input string
;               ES                      selector and
;               DI      (StrBase)       offset for input string
;               DS                      selector for BVSCB
;
;       USAGE   SI      (pointer)       offset for current byte
;               CX      (length)        currently remaining length
;               AL      (char)          current character (byte)
;               AX                      full DBCS character, where needed
;               DX      (col)           output column for current char
;
;       EXIT    DI      (StrBase)       set to next character to be processed
;               SI      (pointer)       set to next character to be processed
;               CX      (length)        set to remaining length of string
;
;       NOTE    we intentionally pass un-paired DBCS leading bytes at the end
;               of a line to Flush. Flush and the VIO/BVS functions it calls
;               will replace this byte by a space, which is what we want.
;

StrScan PROC    NEAR
        PUBLIC  StrScan

        mov     si,di                   ; pointer = StrBase
        mov     dx,[bx].bvs_cursorcol   ; col = bvs_cursorcol

; main loop, once for each character (not byte)

ssloop:
        .while  <nonzero cx> near       ; while( length ) ;@S11

            mov     al,es:[si]          ; (al) = char (= *pointer)

            test    [bx].bvs_flags2,bvs_f2_dbcscp ;Also clears CY
            .if     <nz>                ; if active codepage is DBCS ;J-KKJ1
                                        ; deal with DBCS support first
                CheckLeadByte           ; set CY if lead Byte
                .if     <c>             ; if lead byte (and NOT SBCS)
                    dec     cx          ; length --
                    .if     <nz>        ; if length == 0 retain lead
                        xchg    al,ah   ; ah = leading byte
                        inc     si      ; pointer ++  (get trail byte)
                        mov     al,es:[si] ; ax = full dbcs char
                        CheckNotSingleCell
                        .if     <c>     ; if not single cell
                            inc     dx  ; col ++  for double cell
                        .endif
                        jmp     short endspecial
                    .endif

                                        ; need to retain lead byte
                    push    ds          ; save es
                    push    bvsiseg     ;
                    pop     ds          ; (ds) = ppbvs selector
                    or      ds:bvsi_tty_flag,tty_f_leading ; set retain flag
                    mov     ds:bvsi_tty_dbcsl,al ; save leading byte in DBCS
                    pop     ds          ; restore es
                .leave  NEAR            ; wrap up and exit
                .endif
            .endif                      ;@S11
                                        ; test special characters (SBCS only)
                                        ; Escape (in Ansi mode) and TAB are handled in calling function.
            .if     <al be ESCAPE>      ; char <= ESC ?
                .if     <e>             AND ; if ansi sequence
                .if     <BIT [bx].bvs_flags AND bvs_f_ansi> ; and ansi mode
                .leave  NEAR
                .endif

                .if     <al e TAB>      ; char == TAB ?
                .leave  NEAR
                .endif

                .if     <al e BEL>      ; char == BEL ?
                    call    Flush       ; output up to before char
                    push    BeepNote    ; set the note
                    push    BeepDuration ; set the duration
                    call    DOSBEEP     ; output sound

                .else
                    .if     <al e BS>   ; char == BS ?
                        call    Flush   ; output up to before char
                        .if     <nonzero dx> ; col == 0 ?
                            dec     dx  ; backspace: col --
                        .else                                                           ;AG05456
                            push    ax                                                  ;AG05456
                            push    cx                                                  ;AG05456
                            mov     ax,[bx].bvs_cursorrow ; final cursorrow    ;D198    ;AG05456
                            .if <nonzero ax>                                            ;AG05456
                               dec     ax                                               ;AG05456
                               mov     cx,[bx].bvs_cols                                 ;AG05456
                               dec     cx                                               ;AG05456
                               mov     dx,cx                                            ;AG05456
                               Call    BvsSetCurPos2         ;Update it using BVS ;D198 ;AG05456
                            .endif                                                      ;AG05456
                            pop     cx                                                  ;AG05456
                            pop     ax                                                  ;AG05456
                        .endif                                                          ;AG05456

                    .else
                        .if     <al e LF> ; char == LF ?
                            call    Flush ; output up to before char
                            call    ScrollMaybe ; go to next line

                        .else
                            .if     <al ne CR> ; char != CR ?
                                jmp     short endspecial
                            .endif

                            xor     dx,dx ; col = 0  <--  char == CR
                            call    Flush ; output up to before char
                        .endif
                    .endif
                .endif

; common actions for many of the special cases:

; optionally:  eat character, which will also set the pointer
; so print will echo the special character (i.e. BS, CR, LF, BEL )

                Advance                 ; eat char

            .else

; end of special characters.  Now we know we have something that
; is not a special case.  Worry about the end of line at this point.
; Perform line-wrapping and scrolling as needed.

endspecial:
                inc     dx              ; try step column
                .if     <dx be [bx].bvs_cols> ; if fit all on this line

                    Advance             ; eat this char

                    .if     <b>         ; if not at eol:
                                        ; this is the default case
                        jmp     ssloop  ; loop back to top
                    .endif

                                        ; if exact fit, do SBCS eol processing.

                    call    Flush       ; flush 80 columns
                    .if     <BIT [bx].bvs_flags AND bvs_f_ansiwrap> ; wrap mode ?

                                        ; word wrapping enabled:

                        call    ScrollMaybe ; go to next screen line
                        sub     dx,dx   ; col = 0
                    .else               ; too wide to fit line

                                        ; word wrapping not enabled:

                        mov     dx,[bx].bvs_cols ; for non_wrap eol do:
                        dec     dx      ; col = lastcol
                    .endif
                .else

; This is the DBCS word wrap case
;
; The call to Flush relies on the fact that if
; VIOWRTCHARSTR is called with a leading byte at
; the end of the input string, it will just display a
; single space. This is what we want if the l.b. is at
; the end of the line.  In wrap mode, we back up the
; pointer so the l.b. gets re-read and put on the next line;
; in non-wrap, we advance the pointer to simply skip the tail
; byte, since it doesn't show.

                    call    Flush       ; flush 80 colums
                    .if     <BIT [bx].bvs_flags AND bvs_f_ansiwrap> ; wrap mode ?

                                        ; word wrapping enabled:

                        inc     cx      ; back up, so we re-scan the
                        dec     si      ; ... wrapped DBCS next line
                        call    ScrollMaybe ; go to next screen line
                        sub     dx,dx   ; col = 0
                    .else

                                        ; word wrapping not enabled:

                        Advance         ; eat char past end of line
                                        ; always == second half of DBCS.
                        mov     dx,[bx].bvs_cols ; for non_wrap eol do:
                        dec     dx      ; col = lastcol
                    .endif
                .endif
            .endif

SetColAndEcho:
; always: set the cursor pointer to the next output column and move
; the string base beyond the part of the string we have processed.

            mov     [bx].bvs_cursorcol,dx ; cursorcol = col
            mov     di,si               ; StrBase = pointer
        .endwhile                       ; loop back to top
;
;---- end of loop -----

; wrap up: Output and echo to the printer, set the cursor pointer to
; the next output column and move the string base beyond the part of
; the string we have processed.

        call    Flush                   ; output up to current char
        mov     [bx].bvs_cursorcol,dx   ; cursorcol = col
        mov     di,si                   ; StrBase = pointer
        ret
StrScan ENDP


;       FLUSH - output string to screen
;
;       PSEUDOCODE
;
;       ENTRY  -  StrBase               set to first character to be output
;                 StrEnd                set to first character NOT to be output
;                 bvs_cursorrow         set to row for WrtCharStr
;                 bvs_cursorcol         set to col for WrtCharStr
;
;       USAGE  -  length                number of chars to put on screen
;
;
;       length = StrEnd - StrBase
;       if( length )
;         if( ansi )
;               get attribute
;         if( ansi )
;               bvsfunc(BVSWRTCHARSTRATT( StrBase, length, bvs_cursorrow,
;                                               bvs_cursorcol, attr, 0 ))
;         else
;               bvsfunc(BVSWRTCHARSTR( StrBase, length, bvs_cursorrow,
;                                               bvs_cursorcol, 0 ))
;
;       return
;
;       LINKAGE Called from StrScan
;
;       ENTRY   ES                      Selector
;               DI                      Offset of input string
;               SI                      Offset one past end of string
;               DS                      BVSCB selector
;
;       USAGE   CX                      length of output
;
;       CALLS  -  BvsWrtCharStr, BvsWrtCharStrAtt
;
;
;       EXIT    CX                      restored to original value
;               ES, DI, SI, DS          unchanged
;
;

Flush   PROC    NEAR
        PUBLIC  Flush

        push    cx                      ; save CX, use as length
        mov     cx,si                   ;
        sub     cx,di                   ; length = SI - DI
        .if     <nz> near               ; length = 0 => return
            push    es
            push    di                  ; StrBase
            push    cx                  ; length
            push    [bx].bvs_cursorrow  ; cursorrow at write start
            push    [bx].bvs_cursorcol  ; cursorcol at write start
            .if     <BIT [bx].bvs_flags AND bvs_f_ansi> near ; ansi active  ?
                push    ds              ; push attribute selector
                push    bvs_scrattr     ; push attribute offset
                push    bx              ; Always use 0 since VioRegister
                                        ;   is not supported for AVIO
                test    [bp].p_regax,rf_WrtCharStrAtt                   ;@T46
                .if     <nz>            ; function registered?          ;@T46
                    Call    VIOWRTCHARSTRATT                            ;@T46
                .else                                                   ;@T46
                    bvsfunc vf_WrtCharStrAtt 8 vp_f_attrxlate ; Else write attribute ;D198;J-KKJ1
                .endif                                                  ;@T46
            .else
                push    bx              ; Always use 0 since VioRegister
                                        ;   is not supported for AVIO
                test    [bp].p_regax,rf_WrtCharStr                      ;@T46
                .if     <nz>            ; function registered?          ;@T46
                    Call    VIOWRTCHARSTR                               ;@T46
                .else                                                   ;@T46
                    bvsfunc vf_WrtCharStr 6 ; don't write attribute ;D198
                .endif                                                  ;@T46
            .endif
        .endif
        pop     cx                      ; restore CX
        ret
Flush   ENDP

;       PRINT - echo string to printer
;
;       PSEUDOCODE
;
;       if( echo )
;           length = StrEnd - StrBase
;           if( length )
;                AX = DOSWRITE( handle, StrBase, length, &(far) returnleng ))
;       return
;
;       EXIT    ES              Selector of output string
;               DI              Offset of output string
;               SI              Offset of char beyond last string char
;               BX              0
;               DS              BVSCB selector
;
;       USAGE   SI              length ( SI = SI - DI )
;               DX              temporary
;               ES              ppbvs selector
;
;       EXIT    AX
;               ES              ppbvs selector
;
;
;       CALLS   DOSCLOSE, DOSDEVIOCTL, DOSWRITE, OpenPrt, DOSBEEP
;
;

Print   PROC    NEAR
        PUBLIC  Print

        .if     <BIT [bx].bvs_flags AND  bvs_f_cprtsc> ; check for cntl prtsc
            sub     si,di               ; (si) = string length

            .IF     nz                  ; stuff to echo to printer
                push    ax              ; allocate return length        ;@T46
                mov     ax,sp           ;                               ;@T46

                push    word ptr [bp].p_vdh_function ; get the handle ;D198
                push    es              ; string selector
                push    di              ; string offset
                push    si              ; string length
                push    ss              ; selector for the return length ;D198
;@T46           lea     ax,[bp].p_regax ;D198
                push    ax              ; offset for the return length ;D198
                CALL    DOSWRITE        ; print the line to the printer
                add     sp,2            ; discard return length         ;@T46
            .ENDIF

        .ENDIF

        ret
Print   ENDP

SUBTTL  <ScrollMaybe - scroll the screen text if necessary>

;**     ScrollMaybe - scroll the screen text if necessary
;
;       This function checks to see if the cursor postion is
;       past the end of the screen.  If it is it sets it to the
;       bottom row and scrolls the screen, otherwise nothing.
;
;       PSEUDOCODE:
;
;       BVSCB.CursorRow++
;       if (BVSCB.CursorRow > last row on screen) {
;           BVSCB.CursorRow = last row on screen
;           if (ansi is not active ) {                  [ get attribute ]
;               BvsReadCellStr ( sm_cell, sm_size=2, last row, first column)
;               sm_cell.char = SPACE                    [ blank character ]
;           }
;           BvsScrollUp (top row, left col, bottom row, right col, 1 row)
;       }
;
;       LINKAGE:  Call ScrollMaybe from BvsWrtTTY
;
;       ENTRY:  ds - BVSCB
;               bx   0
;               bvs_cursorrow   -    set to current row
;
;       EXIT:   bvs_cursorrow   -    set to new row
;
;       CALLS:  VIOREADCELLSTR, BvsReadCellStr, VIOSCROLLUP, BvsScrollUp
;
;       USES:   ax, stackframe: 2 words

ScrollMaybe PROC NEAR                   ; Scroll 1 row if needed
        PUBLIC  ScrollMaybe

        INC     [bx].bvs_cursorrow      ; increase the row count
        MOV     AX,[bx].bvs_ScrlRectBtm ; (ax) = bottom most scroll row
        .IF     <AX b [bx].bvs_cursorrow> near ; scrolling is necessary
            MOV     [bx].bvs_cursorrow,AX ; set to last row
            mov     ax,word ptr [bx].bvs_ttycell ; (ax) = default fill cell
            push    dx                  ; save register
            push    cx
            push    di
            push    si                  ; save register
            push    word ptr [bx].bvs_ttycell+2 ; ss:sp -> attr2        ;J-KKJ2
            push    ax                  ; ss:sp -> default fill cell    ;@T46
            mov     si,sp               ; ss:si -> default fill cell    ;@T46
            .if     <BIT [bx].bvs_flags NAND bvs_f_ansi> near ; If ANSI not active
                push    CharAttPairSize ; ss:sp -> cell size            ;@T46
                mov     di,sp           ; ss:di -> cell size            ;@T46
                push    ss              ; segment for fill cell
                push    si              ; offset of fill cell           ;@T46
                push    ss              ; segment of length to read
                push    di              ; offset of length to read      ;@T46
                mov     ax,[bx].bvs_ScrlRectBtm ; (ax) = bottom most scroll row
                push    ax              ; Last row
                push    bx              ; First column
                push    bx              ; Always use 0 since VioRegister
                                        ;   is not supported for AVIO
                test    [bp].p_regax,rf_ReadCellStr                     ;@T46
                .if     <nz>            ; function registered?          ;@T46
                    Call    VIOREADCELLSTR                              ;@T46
                .else                                                   ;@T46
                    bvsfunc vf_ReadCellStr 7 ; Read current attr ;D198
                .endif                                                  ;@T46
                add     sp,2            ; Balance stack            ;@T46;@TL2
                mov     byte ptr ss:[si],SPACE ; space char        ;@T46;@TL2
            .endif

            push    bx                  ; top row - 0
            push    bx                  ; left column - 0
            push    -1                  ; bottom row
            push    -1                  ; right column
            push    1                   ; scroll 1 row
            push    ss                  ; segment of fill cell
            push    si                  ; offset of fill cell           ;@T46
            push    bx                  ; Always use 0 since VioRegister
                                        ;   is not supported for AVIO
            test    [bp].p_regax,rf_ScrollUp ;@S22

            .if     <nz>                ;@S22
                Call    VIOSCROLLUP     ;Call thru registered routine ;@S22
            .else near                  ;@S22                           ;J-KKJ3
                .if     <BIT [bx].bvs_flags NAND bvs_f_ansi> ; If ANSI not active ;J-KKJ3
                    bvsfunc vf_ScrollUp 8 ; scroll up one line using BVS ;J-KKJ3
                .else
                    bvsfunc vf_ScrollUp 8 vp_f_attrxlate ; scroll up one line using BVS ;D198;J-KKJ1
                .endif
            .endif                      ;@S22

            pop     si                  ; Balance stack                 ;J-KKJ2
            pop     si                  ; Balance stack                 ;@T46
            pop     si                  ; restore register
            pop     di
            pop     cx
            pop     dx                  ; restore register
        .ENDIF
        RET
ScrollMaybe ENDP

;**     PrinterInit -  Check printer status. Open/close if needed
;
;       PSEUDOCODE:
;
;       [check printer status]
;       if( printer needs closing && handle != 0)
;           DOSCLOSE( handle )                          [ close the printer ]
;           handle = 0                                  [ remove handle ]
;
;       if( echo )
;           if( !handle )
;               if( AX = OpenPrt( & (far)handle ) )     [ if open error .. ]
;                   set echo off                        [ toggle echo flag ]
;                   DOSBEEP( note, duration )           [ signal the error ]
;               else
;                   DOSDEVIOCTL( args..., handle )
;                   AX = 0
;
;       LINKAGE:        Called once per call from BvsWrtTTY
;
;       ENTRY:          ES - BVS_ppbvs selector
;                       DS - BVSCB selector
;
;       EXIT:           AX = 0
;
;       USES:           AX, CX
;
;       Make sure the printer is closed if no longer needed, or
;       opened if we are going to echo...

PrinterInit PROC NEAR
        PUBLIC  PrinterInit

        .if     <BIT [bx].bvs_flags AND bvs_f_closeprt> AND ;Close required
        and     [bx].bvs_flags,NOT bvs_f_closeprt ;Reset the flag ;D198;@S7
        mov     cx,es:bvsi_prthandle ;Get printer handle ;D198          ;@T52
        .if     <ncxz>                  ;AND printer is open ;D198
            push    cx                  ;push the printer handle ;D198
            Call    DOSCLOSE            ;go close the printer
            mov     es:bvsi_prthandle,bx ;clear the printer handle      ;@T52
        .endif

        .IF     <BIT [bx].bvs_flags AND bvs_f_cprtsc> NEAR ; check for cntl prtsc

            xor     ax,ax               ; (ax) = 0, good return code
            .IF     <<word ptr es:bvsi_prthandle> b 1> near ; no print handle;@T52

                push    dx              ; save dx

                MOV     CX,ES           ; (cx) = selector of the print handle
                LEA     dx,ds:bvsi_prthandle ; (dx) = print handle off  ;@T52
                CALL    OpenPrt         ; open the printer
                push    ds              ; @D663
                mov     ax,BVSGSEG      ; @D663
                mov     ds,ax           ; @D663
                xor     dh,dh           ; @d663
                mov     dl, ds:bvsg_cprtsc_titlelen ; GET cprtsc LENGTH @D663;@T52
                mov     ds:bvsg_pjtitlelen,dx   ; SET cptrsc LENGTH@D663;@T52
                lea     si, ds:bvsg_cprtsc_title  ; DS:si -> title @D663;@T52
                mov     ds:bvsg_pjtitleoff,si   ; SET OFFSET       @D663;@T52
                mov     ds:bvsg_pjtitlesel,ds   ; SET SELECTOR     @D663;@T52

                push    ds                      ; @D663
                lea     dx,ds:bvsg_pjtitlelen   ; PASS DATA PACKET @D663;@T52
                push    dx                      ; @D663

                push    ds
                lea     dx,ds:bvsg_parmpkt      ; PASS PARM PACKET @D663;@T52
                push    dx                      ; @D663

                push    4Dh                     ; FUNC 4Dh SET PRT JOB TITLE @D663
                push    05h                     ; CATAGORY 5 PRINTER         @D663
                push    es:bvsi_prthandle       ; HANDLE FROM DOSOPEN @D663;@T52
                call    DosDevIOCtl             ; @D663
                pop     ds                      ; @D663

                pop     dx              ; restore dx


                .IF     <nonzero ax>    ; open failed, turn echo off
                    AND     [BX].bvs_flags,NOT bvs_f_cprtsc

                    push    BeepNote    ; set the note
                    push    BeepDuration ; set the duration
                    CALL    DOSBEEP     ; make the beep

                .ELSE
                    sub     sp,4        ;allocate 2 words on stack      ;B715221
                    mov     si,sp                                       ;B715221
                    push    2           ;push length                    ;B715221
                    push    ss                                          ;B715221
                    push    si          ;push codepagelist (returned)   ;B715221
                    push    ss                                          ;B715221
                    add     si,2                                        ;B715221
                    push    si          ;push datalength (returned)     ;B715221
                    Call    DOSGETCP    ;Get current system code page   ;B715221
                    add     sp,4        ;de-allocate 2 words on stack   ;B715221

                    mov     ax,ss:[si-2]                                ;B715221
                    .if     <[bx].bvs_ActiveCP ne 0>;video cp eq sys cp ;SM02955
                       .if     <[bx].bvs_ActiveCP ne ax>   ;video cp eq system cp ;B715221

                           push    di          ; save di

                                               ; NOTE: 3 more arguments pushed than function requires as per SPEC

                           push    bx          ; push command information on stack
                           mov     di,sp       ; (di) = offset to parm block
                           push    bx          ; data block on stack
                           push    [bx].bvs_ActiveCP ; current video CodePage
                           mov     ax,sp       ; (ax) = offset to data block
                           push    ss          ; segment of parm block
                           push    ax          ; offset to parm block
                           push    ss          ; segment of data block
                           push    di          ; offset to data block
                           push    FuncActivateFont ; activate font function
                           push    CatPrinter  ; category 5 (printer)
                           push    es:bvsi_prthandle ; printer handle      ;@T52
                           CALL    DOSDEVIOCTL ; set the mode
                           POP     AX          ; throw away the packets
                           POP     AX          ; throw away the packets
                           POP     AX          ; throw away the packets
                        ;;;SUB     AX,AX       ; (ax) = 0, good return code;B715221
                           pop     di          ; restore di
                       .endif
                    .endif                                              ;SM02955
                    sub     ax,ax           ; (ax) = 0, good return code;B715221
                .ENDIF
            .ENDIF
        .ENDIF
        ret

PrinterInit ENDP

;-----------------------------------------------------------------------
; RegSave/RegRest
;       Save and Restore all registers and flags for VDH routines.
;       RegSave also sets BX = 0, DS:BX = BVSCB, and ES:DI = ParmBlock
;-----------------------------------------------------------------------

RegSave PROC    NEAR                    ;Routine to save regs ;D198
        public  RegSave

        pop     ax                      ;Get return address
        pushf
        pusha
        push    ds
        push    es                      ;Save all registers and flags
        cld
        sub     sp,p_reges              ;Allocate VDH Parameter block
        mov     bp,sp
        push    ax                      ;Restore return address

        sub     bx,bx                   ;Get zero reg for code optimization
        mov     [bp].vp_parmlength,8
        mov     [bp].vp_flags,bx

        les     di,dword ptr [bp].p_vdh_parmblock ;ES:DI = Parameter Block
        mov     ax,bvsiseg
        mov     ds,ax
        mov     ax,ds:bvsi_register     ;Get registration bit mask ;@T46;@T52
        mov     [bp].p_regax,ax         ;Pass on registration info      ;@T46
        mov     ds,ds:bvsi_bvssel       ;DS:BX = BVSCB                  ;@T52

        ret
RegSave ENDP                            ;D198

RegRest PROC    NEAR                    ;Routine to restore regs ;D198
        public  RegRest

        mov     [bp].p_regax,ax         ;Save return code
        pop     ax
        xchg    [bp].p_regfl,ax         ;Swap return address w/flags
        lea     sp,[bp].p_reges         ;Deallocate VDH Parameter block
        push    ax

        popf
        pop     es
        pop     ds
        popa                            ;Restore all registers and flags

        ret
RegRest ENDP                            ;D198

BVS3SEG ENDS
        END

;@T25 = 04/04/89 TPL, Sizzle work, B700???
;D198 = 04/07/89 STJ, Install BvsPrtSc and BvsWrtTTY as default VDH routines
;@S7  = 04/25/89 STJ, Reset close print flag even if printer not open, B701391
;@S11 = 05/30/89 STJ, Performance enhancments, B702527 */
;J-KKJ1 = 07/14/89 KKJ, Bug Fix
;@S22 = 08/15/89 STJ, Pass registration information to WrtTTY, B785244
;@T46 = 09/05/89 TPL, Use the compress registration mask from VIO, PTR B70????
;@TL2 = 10/13/89 TPL, Do not replace cell attribute on scrollup, PTR B786224
;@D549 = 11/02/89 CJJ, Seltable changes due to rangeless sessions, DCR 549
;@D663 = 01/02/90 TLB, Put name of print screen print job in spool queue, DCR 663
;@C40 = 01/08/90 CJJ, Call VIOSetCurPos if AVS replaced, PTR B710876
;@T52 = 01/24/90 TPL, DCR 704 work
;J-KKJ2 = 02/12/90 KKJ, Use 4 byte data in bvs_ttycell for DBCS, P711351
;NOTAG  = 03/06/90 WKB, Remove redundant CA code, B711792
;B715221 = 08/29/90 WKB, Set printer codepage when different from video, B715221
;J-KKJ3 = 10/08/90 KKJ, Do not set vp_f_attrxlate flag if ANSI=OFF, J200184
;SM02955 = 05/16/91 WKB, Do not activate font when no code pages present, SM02955
;@C50 = 08/01/91 CJJ, B724984 Don't use thunk calls anymore.
;AG05456 = 01/24/92 WKB, Allow wrap back to previous line, AG05456
