;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE    60,132
        TITLE   WDHSCROL.ASM -- Buffer Scroll Routines for Windowable sessions

;/*****************************************************************************
;*
;* SOURCE FILE NAME = WDHSCROL.ASM
;*
;* DESCRIPTIVE NAME = BUFFERUPDATE scroll routines
;*
;*
;* VERSION      V2.0
;*
;* DATE
;*
;* DESCRIPTION  Buffer Scroll Routines for Windowable sessions
;*
;* FUNCTIONS    ScrollUp
;*              ScrollDown
;*              ScrollLeft
;*              ScrollRight
;*              ValidateScrollParms
;*              GetScrollByteCount
;*              GetLineSkipByteCount
;*              GetLVB_PVB_Cell
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVIY =
;*   DATE      FLAG       APAR    CHANGE DESCRIPTION
;*   --------  ---------- -----   --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx xxxxx   xxxxxxx
;*   09/14/94  @95837             WKB, pmwinp.inc -> pmwinx.inc
;*****************************************************************************/

        .286c                                     ; 286 protect mode instructions

        .xlist
        include pmwinx.inc                        ;@95837
        include pmaviop.inc

        INCLUDE struc.inc                         ; Structure macro
        INCLUDE error2.inc                        ; Subsystem error equates
        INCLUDE vdhstruc.inc                      ; Buffer update data structures
        INCLUDE vdhequ.inc                        ; Buffer update equates
        .list

R2CSEG  SEGMENT WORD    PUBLIC 'CODE'
        ASSUME  CS:R2CSEG,DS:NOTHING,ES:NOTHING

;/****************************************************************************
;*
;* SUBROUTINE NAME:     ScrollUp
;*
;* DESCRIPTIVE NAME:    Video device handler scroll up routine
;*
;* FUNCTION:    Process scroll up sub-function
;*              Scrolls a portion of the PVB, LVB up a specified
;*              number of times and then fill the vacant portion
;*              with the user specified cell.
;*
;* ENTRY POINT: ScrollUp
;*   LINKAGE:   Near Call from BUFFERUPDATE rouinte
;*
;* INPUT:
;*
;* AX = 0
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* PARAMETER BLOCK FORMAT:
;*
;*   SIZE   DESCRIPTION
;*   ----   -----------
;*
;*   WORD   Parameter length
;*   WORD   Flags                                      (target buffer - LVB, PVB)
;*   DWORD  Application data address
;*   DWORD  Application data2 address (cell to be used to fill void)
;*   WORD   Index (3)
;*   WORD   Starting row                               (top row)
;*   WORD   Starting column                            (left column)
;*   WORD   Secondary row                              (bottom row)
;*   WORD   Secondary column                           (right column)
;*   WORD   RepeatFactor                               (# of times to scroll)
;*   WORD   LogicalBufSel
;*
;* OUTPUT:
;*
;* EXIT-NORMAL: AX = 0
;*
;* EXIT-ERROR:  AX = ValidateScrollParms
;*
;* EFFECTS:     All
;*
;* INTERNAL REFERENCES: ValidateScrollParms,
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  ScrollUp
ScrollUp PROC   NEAR

;/*
;**   Validate input row, column parameters.  The carry flag is used
;**   to indicate whether an abnormal condition occured. On exit the
;**   following registers are also setup if the carry flag is not set:
;**       AX, [bp].TopRow    = Adjusted starting row
;**       BX, [bp].BottomRow = Adjusted ending row
;**       CX, [bp].LeftCol   = Adjusted starting column
;**       DX, [bp].RightCol  = Adjusted ending column
;*/

        call    ValidateScrollParms              ; Validate row, column values
        .if     <nc>    NEAR                      ; Continue?

;/*
;**  Locate the offset to the upper right hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the TopRow
;**  value, multiply it by the screen width and then add LeftCol value
;**  to the result.
;*/

            mul     es:[di].TextCols             ; Target offset = (TopRow
            add     ax, cx                        ;   * screen columns
            add     ax, ax                        ;   + LeftCol) * 2
            push    ax                            ; Save Target offset


;/*
;**   Use the scroll scale factor to calculate the total number of bytes
;**   neccessary to determine where the source offset is located.
;**   GetScrollByteCount returns this value in AX.
;*/

            call    GetScrollByteCount           ; AX = byte count

            pop     bx                            ; BX = Target offset


;/*
;**   Determine the source offset by taking the byte count obtained
;**   above and add to it the target buffer offset.
;*/

            add     ax, bx                        ; Source offset = Target offset
            push    ax                            ;   + (# of lines to scroll *
                                                  ;   screen columns * 2)

;/*
;**  Locate the offset to the bottom right hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the BottomRow
;**  value, multiply it by the screen width and then add RightCol value
;**  to the result.
;*/

            mov     ax, es:[di].TextCols ; End offset = (BottomRow *
            mul     [bp].BottomRow               ;   screen columns + RightCol
            add     ax, [bp].RightCol            ;   + 1) * 2
            inc     ax                            ; 
            add     ax, ax                        ; 
            mov     dx, ax                        ; DX = End offset

;/*
;**  Calculate the number of bytes to skip between the end of one line
;**  and the beginning of the next line.  This is achieved by taking
;**  the screen width and subtract it by the number of columns between
;**  the RightCol and LeftCol and then multiply it by 2.
;**  GetLineSkipByteCount returns this value in CX.
;*/

            call    GetLineSkipByteCount ; CX = # of bytes to skip

;/*
;**  Setup source and destination selectors.  If both LVB and PVB are
;**  selected for update then all changes will go to the LVB first
;**  before transfering the changes to the PVB.   GetLVB_PVB_Cell will
;**  also return the cell in AX.
;*/

                                                  ; BX = Target offset
                                                  ; DX = End offset
            pop     si                            ; DS:SI -> Source buffer
            mov     di, bx                        ; ES:DI -> Target buffer

            .if     <[bp].Retrace eq 4>
                add     si,si
                add     di,di
                add     dx,dx                     ;Adjust for 4-byte cells
            .endif

;/*
;**  If each line within the target rectangle to be scrolled occupies
;**  the entire width of the screen then a contiguous block move will
;**  be opted.
;**  For non-contiguous lines, scroll up is done by moving each line up
;**  one at a time until the requested number of lines are scrolled.
;*/

            .if     <ncxz>                        ; Non-contiguous lines?
                push    ax                        ; Save cell
                mov     ax, [bp].LineLength ; AX = line length
                mov     bx, [bp].SkipLength ; BX = skip length
                .if     <si b dx>                ; Any to scroll?
                    .repeat                       ; 
                        mov                      cx, ax ; CX = line length
                        rep                      movsw ; Copy one line
                        add                      si, bx ; Advance to start of
                        add                      di, bx ;   next line
                    .until  <si ae dx>           ; More to scroll?
                .endif                            ; 
                mov     si, ax                    ; SI = line length
                pop     ax                        ; AX = cell
                .repeat                           ; 
                    mov     cx, si               ; CX = line length
                    .if     <[bp].Retrace eq 4>
                        push                     si
                        mov                      si,[bp].PVB_sel ;Get extended attribute
                        .repeat
                            stosw
                            xchg    ax,si
                        .loop
                        pop                      si
                    .else
                        rep                      stosw ; Fill line with cell
                    .endif
                    add     di, bx               ; Advance to start of next line
                .until  <di ae dx>               ; More lines to fill?
            .else                                 ; Contiguous line scroll!!
                                                  ; DX = End offset
                mov     cx, dx                    ; CX = (End offset - Source
                sub     cx, si                    ;      offset) / 2
                shr     cx, 1                     ; CX = transfer count
                .if     <ncxz>                    ; Source = Destination?
                    rep     movsw                ; Block copy
                .endif                            ; 
                mov     cx, dx                    ; CX = (End offset - current
                sub     cx, di                    ;      Target offset) / 2
                shr     cx, 1                     ; CX = cell transfer count
                .if     <[bp].Retrace eq 4>
                    mov     si,[bp].PVB_sel ;Get extended attribute
                    .repeat
                        stosw
                        xchg                     ax,si
                    .loop
                .else
                    rep     stosw                ; Fill line with cell
                .endif
            .endif                                ; 
            sub     ax, ax                        ; Clear return code
        .endif                                    ; 
        ret                                       ; 

ScrollUp ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     ScrollDown
;*
;* DESCRIPTIVE NAME:    Video device handler scroll down routine
;*
;* FUNCTION:    Process scroll down sub-function
;*              Scrolls a portion of the PVB, LVB down a specified
;*              number of times and then fill the vacant portion
;*              with the user specified cell.
;*
;* ENTRY POINT: ScrollDown
;*   LINKAGE:   Near Call from BUFFERUPDATE rouinte
;*
;* INPUT:
;*
;* AX = 0
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* PARAMETER BLOCK FORMAT:
;*
;*   SIZE   DESCRIPTION
;*   ----   -----------
;*
;*   WORD   Parameter length
;*   WORD   Flags                                      (target buffer - LVB, PVB)
;*   DWORD  Application data address
;*   DWORD  Application data2 address (cell to be used to fill void)
;*   WORD   Index (4)
;*   WORD   Starting row                               (top row)
;*   WORD   Starting column                            (left column)
;*   WORD   Secondary row                              (bottom row)
;*   WORD   Secondary column                           (right column)
;*   WORD   RepeatFactor                               (# of times to scroll)
;*   WORD   LogicalBufSel
;*
;* OUTPUT:
;*
;* EXIT-NORMAL: AX = 0
;*
;* EXIT-ERROR:  AX = ValidateScrollParms
;*
;* EFFECTS:     All
;*
;* INTERNAL REFERENCES: ValidateScrollParms,
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  ScrollDown
ScrollDown PROC NEAR

;/*
;**  Validate input row, column parameters.  The carry flag is used
;**  to indicate whether an abnormal condition occured. On exit the
;**  following registers are also setup if the carry flag is not set:
;**      AX, [bp].TopRow    = Adjusted starting row
;**      BX, [bp].BottomRow = Adjusted ending row
;**      CX, [bp].LeftCol   = Adjusted starting column
;**      DX, [bp].RightCol  = Adjusted ending column
;*/

        call    ValidateScrollParms              ; Validate row, column values
        .if     <nc>    NEAR                      ; Continue?

;/*
;**  Locate the offset to the bottom right hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the BottomRow
;**  value, multiply it by the screen width and then add RightCol value
;**  to the result.
;*/

            mov     ax, es:[di].TextCols ; Target offset = (BottomRow
            mul     bx                            ;   * screen columns +
            add     ax, [bp].RightCol            ;   right column) * 2
            add     ax, ax                        ; 
            push    ax                            ; Save Target offset

;/*
;**   Use the scroll scale factor to calculate the total number of bytes
;**   neccessary to determine where the source offset is located.
;**   GetScrollByteCount returns this value in AX.
;*/

            call    GetScrollByteCount           ; AX = byte count

            pop     bx                            ; BX = Target offset

;/*
;**   Determine the source offset by taking the byte count obtained
;**   above and subtract from the target buffer offset.
;*/

            mov     dx, bx                        ; Source offset = Target offset
            sub     dx, ax                        ;   - (# of lines to scroll *
                                                  ;   screen columns * 2)
            push    dx                            ; Save Source offset

;/*
;**  Locate the offset to the top left hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the TopRow
;**  value, multiply it by the screen width and then add LeftCol value
;**  to the result.
;*/

            mov     ax, es:[di].TextCols ; End offset = (TopRow *
            mul     [bp].TopRow                  ;   screen columns + LeftCol
            add     ax, [bp].LeftCol             ;   -1) * 2
            dec     ax                            ; 
            add     ax, ax                        ; 
            mov     dx, ax                        ; DX = End offset

;/*
;**  Calculate the number of bytes to skip between the end of one line
;**  and the beginning of the next line.  This is achieved by taking
;**  the screen width and subtract it by the number of columns between
;**  the RightCol and LeftCol and then multiply it by 2.
;**  GetLineSkipByteCount returns this value in CX.
;*/

            call    GetLineSkipByteCount ; CX = # of bytes to skip

;/*
;**  Setup source and destination selectors.  If both LVB and PVB are
;**  selected for update then all changes will go to the LVB first
;**  before transfering the changes to the PVB.   GetLVB_PVB_Cell will
;**  also return the cell in AX.
;*/

            call    GetLVB_PVB_Cell              ; AX = cell
                                                  ; BX = Target offset
                                                  ; DX = End offset
            pop     si                            ; DS:SI -> Source buffer
            mov     di, bx                        ; ES:DI -> Target buffer

            .if     <[bp].Retrace eq 4>
                inc     si
                inc     di
                inc     dx
                add     si,si
                add     di,di
                add     dx,dx                     ;Adjust for 4-byte cells
            .endif
            std                                   ; Transfer from right to
                                                  ;   left, bottom up
;/*
;**  If each line within the target rectangle to be scrolled occupies
;**  the entire width of the screen then a contiguous block move will
;**  be opted.
;**  For non-contiguous lines, scroll down is done by moving each line
;**  down one at a time until the requested number of lines are scrolled.
;*/

            .if     <ncxz>                        ; Non-contiguous lines?
                mov     dx, [bp].BottomRow ; DX = row count between
                sub     dx, [bp].TopRow ;      TopRow and BottomRow
                inc     dx                        ; 
                push    ax                        ; Save cell
                sub     dx, [bp].ScrollCount ; DX = # of row to move down
                mov     ax, [bp].LineLength ; AX = line length
                mov     bx, [bp].SkipLength ; CX = skip length
                .if     <nonzero dx>             ; Any lines to scroll?
                    .repeat                       ; 
                        mov                      cx, ax ; CX = line length
                        rep                      movsw ; Copy one line
                        sub                      si, bx ; Advance to start of
                        sub                      di, bx ;   next line
                        dec                      dx ; Adjust row count
                    .until  <z>                  ; More lines to scroll?
                .endif                            ; 
                mov     si, ax                    ; SI = line length
                pop     ax                        ; AX = cell
                mov     dx, [bp].ScrollCount ; DX = remaining row count
                .repeat                           ; 
                    mov     cx, si               ; CX = line length
                    .if     <[bp].Retrace eq 4>
                        push                     si
                        mov                      si,[bp].PVB_sel ;Get extended attribute
                        .repeat
                            xchg    ax,si
                            stosw
                        .loop
                        pop                      si
                    .else
                        rep                      stosw ; Fill line with cell
                    .endif
                    sub     di, bx               ; Advance to start of next line
                    dec     dx                    ; Adjust row count
                .until  <z>                       ; More lines to fill?
            .else                                 ; Contiguous line scroll!!
                                                  ; DX = End offset
                mov     cx, si                    ; CX = (Source offset -
                sub     cx, dx                    ;      End offset) / 2
                shr     cx, 1                     ; CX = transfer count
                .if     <ncxz>                    ; Source = destination?
                    rep     movsw                ; Block copy
                .endif                            ; 
                mov     cx, di                    ; CX = (current destination
                sub     cx, dx                    ;      offset - End offset) / 2
                shr     cx, 1                     ; 
                .if     <[bp].Retrace eq 4>
                    mov     si,[bp].PVB_sel ;Get extended attribute
                    .repeat
                        xchg                     ax,si
                        stosw
                    .loop
                .else
                    rep     stosw                ; Fill line with cell
                .endif
            .endif                                ; 
            cld                                   ; 
            sub     ax, ax                        ; Clear return code
        .endif                                    ; 
        ret                                       ; 

ScrollDown ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     ScrollLeft
;*
;* DESCRIPTIVE NAME:    Video device handler scroll left routine
;*
;* FUNCTION:    Process scroll left sub-function
;*              Scrolls a portion of the PVB, LVB left a specified
;*              number of times and then fill the vacant portion
;*              with the user specified cell.
;*
;* ENTRY POINT: ScrollLeft
;*   LINKAGE:   Near Call from BUFFERUPDATE rouinte
;*
;* INPUT:
;*
;* AX = 0
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* PARAMETER BLOCK FORMAT:
;*
;*   SIZE   DESCRIPTION
;*   ----   -----------
;*
;*   WORD   Parameter length
;*   WORD   Flags                                      (target buffer - LVB, PVB)
;*   DWORD  Application data address
;*   DWORD  Application data2 address (cell to be used to fill void)
;*   WORD   Index (5)
;*   WORD   Starting row                               (top row)
;*   WORD   Starting column                            (left column)
;*   WORD   Secondary row                              (bottom row)
;*   WORD   Secondary column                           (right column)
;*   WORD   RepeatFactor                               (# of times to scroll)
;*   WORD   LogicalBufSel
;*
;* OUTPUT:
;*
;* EXIT-NORMAL: AX = 0
;*
;* EXIT-ERROR:  AX = ValidateScrollParms
;*
;* EFFECTS:     All
;*
;* INTERNAL REFERENCES: ValidateScrollParms,
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  ScrollLeft
ScrollLeft PROC NEAR

;/*
;**   Validate input row, column parameters.  The carry flag is used
;**   to indicate whether an abnormal condition occured. On exit the
;**   following registers are also setup if the carry flag is not set:
;**       AX, [bp].TopRow    = Adjusted starting row
;**       BX, [bp].BottomRow = Adjusted ending row
;**       CX, [bp].LeftCol   = Adjusted starting column
;**       DX, [bp].RightCol  = Adjusted ending column
;*/

        call    ValidateScrollParms              ; Validate row, column values
        .if     <nc>    NEAR                      ; Continue?

;/*
;**  Locate the offset to the upper right hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the TopRow
;**  value, multiply it by the screen width and then add LeftCol value
;**  to the result.
;*/

            mul     es:[di].TextCols             ; Target offset = (TopRow
            add     ax, cx                        ;   * screen columns
            add     ax, ax                        ;   + LeftCol) * 2
            push    ax                            ; Save Target offset

;/*
;**  Scroll left is accomplished by moving each line left starting from
;**  a source location within the same line.  The source location is
;**  obtained by advancing from the target location N cells to the right
;**  where each advancement of a cell represents one scroll factor to
;**  the left.
;*/

            mov     dx, ax                        ; DX = Source offset
            mov     ax, [bp].RightCol            ; AX = column count between
            sub     ax, [bp].LeftCol             ;      RightCol and LeftCol
            inc     ax                            ; 
            mov     cx, ax                        ; CX = column count
            .if     <cx a [si].RepeatFactor> ; Column count > scroll count?
                mov     ax, [si].RepeatFactor ; Source offset = N cells to
                add     dx, ax                    ;   the right of the target
                add     dx, ax                    ;   location
            .endif                                ; 
            push    dx                            ; Save Source offset
            mov     [bp].ScrollCount, ax ; Column count < scroll count
            sub     cx, ax                        ; Line length = column count -
            mov     [bp].LineLength, cx ;         scroll count

            sub     ax, es:[di].TextCols ; AX = # of bytes to skip
            neg     ax                            ;      between lines
            sub     ax, cx                        ; 
            add     ax, ax                        ; 
            mov     [bp].SkipLength, ax ; 

            mov     dx, [bp].ScrollCount ; DX = adjustment byte count to
            shl     dx, 1                         ;      compensate for cell fill

            mov     cx, [bp].BottomRow           ; CX = # of rows between
            sub     cx, [bp].TopRow              ;      TopRow and BottomRow
            inc     cx                            ; 

;/*
;**  Setup source and destination selectors.  If both LVB and PVB are
;**  selected for update then all changes will go to the LVB first
;**  before transfering the changes to the PVB.   GetLVB_PVB_Cell will
;**  also return the cell in AX.
;*/

            call    GetLVB_PVB_Cell              ; AX = cell

            pop     si                            ; DS:SI -> Source buffer
            pop     di                            ; DS:SI -> Target buffer

            mov     bx, [bp].SkipLength ; BX = # of bytes to skip

            .if     <[bp].Retrace eq 4>
                shl     [bp].ScrollCount,1
                add     dx,dx
                add     dx,bx
                add     si,si
                add     di,di                     ;Adjust for 4-byte cells

                .repeat
                    push    cx                    ; Save current row count
                    mov     cx, [bp].LineLength ; CX = line length
                    .if     <ncxz>               ; Source = destination?
                        rep                      movsw ; Shift line left
                    .endif                        ; 
                    mov     cx, [bp].ScrollCount ; CX = cell count
                    push    si
                    mov     si,[bp].PVB_Sel ;Get extended cell data
                    .repeat
                        stosw                     ;Fill the line
                        xchg                     ax,si
                    .loop
                    pop     si
                    add     si, dx               ; Account for cells filled and
                    add     di, bx               ;   advance to start of next line
                    pop     cx                    ; Restore current row count
                .loop
            .else
                add     dx,bx
                .repeat
                    push    cx                    ; Save current row count
                    mov     cx, [bp].LineLength ; CX = line length
                    .if     <ncxz>               ; Source = destination?
                        rep                      movsw ; Shift line left
                    .endif                        ; 
                    mov     cx, [bp].ScrollCount ; CX = cell count
                    rep     stosw                ; Fill line with cell
                    add     si, dx               ; Account for cells filled and
                    add     di, bx               ;   advance to start of next line
                    pop     cx                    ; Restore current row count
                .loop
            .endif
            sub     ax, ax                        ; Clear return code
        .endif
        ret

ScrollLeft ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     ScrollRight
;*
;* DESCRIPTIVE NAME:    Video device handler scroll right routine
;*
;* FUNCTION:    Process scroll right sub-function
;*              Scrolls a portion of the PVB, LVB right to a specified
;*              number of times and then fill the vacant portion with
;*              the user specified cell.
;*
;* ENTRY POINT: ScrollRight
;*   LINKAGE:   Near Call from BUFFERUPDATE rouinte
;*
;* INPUT:
;*
;* AX = 0
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* PARAMETER BLOCK FORMAT:
;*
;*   SIZE   DESCRIPTION
;*   ----   -----------
;*
;*   WORD   Parameter length
;*   WORD   Flags                                      (target buffer - LVB, PVB)
;*   DWORD  Application data address
;*   DWORD  Application data2 address (cell to be used to fill void)
;*   WORD   Index (6)
;*   WORD   Starting row                               (top row)
;*   WORD   Starting column                            (left column)
;*   WORD   Secondary row                              (bottom row)
;*   WORD   Secondary column                           (right column)
;*   WORD   RepeatFactor                               (# of times to scroll)
;*   WORD   LogicalBufSel
;*
;* OUTPUT:
;*
;* EXIT-NORMAL: AX = 0
;*
;* EXIT-ERROR:  AX = ValidateScrollParms
;*
;* EFFECTS:     All
;*
;* INTERNAL REFERENCES: ValidateScrollParms,
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  ScrollRight
ScrollRight PROC NEAR

;/*
;**   Validate input row, column parameters.  The carry flag is used
;**   to indicate whether an abnormal condition occured. On exit the
;**   following registers are also setup if the carry flag is not set:
;**       AX, [bp].TopRow    = Adjusted starting row
;**       BX, [bp].BottomRow = Adjusted ending row
;**       CX, [bp].LeftCol   = Adjusted starting column
;**       DX, [bp].RightCol  = Adjusted ending column
;*/

        call    ValidateScrollParms              ; Validate row, column values
        .if     <nc>    NEAR                      ; Continue?

;/*
;**  Locate the offset to the bottom right hand corner of the target
;**  rectangle to be scrolled.  This is achieved by taking the BottomRow
;**  value, multiply it by the screen width and then add RightCol value
;**  to the result.
;*/

            mov     ax, es:[di].TextCols ; Target offset = (BottomRow
            mul     bx                            ;   * screen columns +
            add     ax, [bp].RightCol            ;   RightCol) * 2
            add     ax, ax                        ; 
            push    ax                            ; Save Target offset

;/*
;**  Scroll right is accomplished by moving each line right starting
;**  from a source location within the same line.  The source location
;**  is obtained by descending from the target location N cells to the
;**  left where each descending movement of a cell represents one scroll
;**  factor to the right.
;*/

            mov     dx, ax                        ; DX = Source offset
            mov     ax, [bp].RightCol            ; AX = column count between
            sub     ax, [bp].LeftCol             ;      RightCol and LeftCol
            inc     ax                            ; 
            mov     cx, ax                        ; CX = column count
            .if     <cx a [si].RepeatFactor> ; Column count > scroll count?
                mov     ax, [si].RepeatFactor ; Source offset = N cells to
                sub     dx, ax                    ;   the left of the target
                sub     dx, ax                    ;   location
            .endif                                ; 
            push    dx                            ; Save Source offset
            mov     [bp].ScrollCount, ax ; Column count < scroll count
            sub     cx, ax                        ; Line length = column count -
            mov     [bp].LineLength, cx ;         scroll count

            sub     ax, es:[di].TextCols ; AX = # of bytes to skip
            neg     ax                            ;      between lines
            sub     ax, cx                        ; 
            add     ax, ax                        ; 
            mov     [bp].SkipLength, ax ; 

            mov     dx, [bp].ScrollCount ; DX = adjustment byte count to
            shl     dx, 1                         ;      compensate for cell fill

            mov     cx, [bp].BottomRow           ; CX = row count between
            sub     cx, [bp].TopRow              ;      TopRow and BottomRow
            inc     cx                            ; 

;/*
;**  Setup source and destination selectors.  If both LVB and PVB are
;**  selected for update then all changes will go to the LVB first
;**  before transfering the changes to the PVB.   GetLVB_PVB_Cell will
;**  also return the cell in AX.
;*/

            call    GetLVB_PVB_Cell              ; AX = cell

            pop     si                            ; DS:SI -> Source buffer
            pop     di                            ; ES:DI -> Target buffer

            mov     bx, [bp].SkipLength ; BX = # of bytes to skip
            std                                   ; Transfer from right to
                                                  ;   left, bottom up
            .if     <[bp].Retrace eq 4>
                shl     [bp].ScrollCount,1
                add     dx,dx
                add     dx,bx
                inc     si
                inc     di
                add     si,si
                add     di,di                     ;Adjust for 4-byte cells

                .repeat
                    push    cx                    ; Save current row count
                    mov     cx, [bp].LineLength ; CX = line length
                    .if     <ncxz>               ; Source = destination?
                        rep                      movsw ; Shift line right
                    .endif
                    mov     cx, [bp].ScrollCount ; CX = cell count
                    push    si
                    mov     si,[bp].PVB_Sel ;Get extended cell data
                    .repeat
                        xchg                     ax,si
                        stosw                     ;Fill the line
                    .loop
                    pop     si
                    sub     si, dx               ; Account for cells filled and
                    sub     di, bx               ;   advance to start of next line
                    pop     cx                    ; Restore current row count
                .loop
            .else
                add     dx,bx
                .repeat
                    push    cx                    ; Save current row count
                    mov     cx, [bp].LineLength ; CX = line length
                    .if     <ncxz>               ; Source = destination?
                        rep                      movsw ; Shift line right
                    .endif
                    mov     cx, [bp].ScrollCount ; CX = cell count
                    rep     stosw                ; Fill N cells
                    sub     si, dx               ; Account for cells filled and
                    sub     di, bx               ;   advance to start of next line
                    pop     cx                    ; Restore current row count
                .loop
            .endif
            sub     ax, ax                        ; Clear return code
            cld                                   ; 
        .endif                                    ; 
        ret                                       ; 

ScrollRight ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     ValidateScrollParms
;*
;* DESCRIPTIVE NAME:    Video device handler scroll parameter
;*                      validation procedure
;*
;* FUNCTION:    Determine whether the row, column parameters given
;*              are ambiguous.  For example:  ending row is greater
;*              than starting row or ending column is greater than
;*              starting column.
;*
;* ENTRY POINT: ValidateScrollParms
;*   LINKAGE:   Near Call from ScrollUp, ScrollDown,
;*                             ScrollLeft, ScrollRight
;*
;* INPUT:
;*
;* AX = 0
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* OUTPUT:      AX, [bp].TopRow    = starting row
;*              BX, [bp].BottomRow = ending row
;*              CX, [bp].LeftCol   = starting column
;*              DX, [bp].RightCol  = ending column
;*              CY = 1 for invalid parameters or [SI].RepeatCount = 0
;*
;* EXIT-NORMAL: CY = 0
;*              CY = 1  AND  AX = 0
;*
;* EXIT-ERROR:  CY = 1
;*              AX = ERROR_VIO_COL
;*              AX = ERROR_VIO_ROW
;*
;* EFFECTS:     AX, BX, CX, DX, CY
;*
;* INTERNAL REFERENCES: None
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  ValidateScrollParms
ValidateScrollParms PROC NEAR

        .if     <[si].RepeatFactor a ax> ; Anything to scroll?
            push    bp                            ; 

;/*
;**   If either of the starting and ending row value is beyond the
;**   normalized text resolution of the current display mode then
;**   convert it to the current maximum row value allowed.
;*/

            mov     bp, es:[di].TextRows ; Get max row resolution
            dec     bp                            ; Normalize it
            mov     ax, [si].Row                 ; Get input starting row value
            .if     <ax a bp>                     ; Starting row beyond limit?
                mov     ax, bp                    ; Set it to max allowed
            .endif                                ; 
            mov     bx, [si].Row2                ; Get input ending row value
            .if     <bx a bp>                     ; Ending row beyond limit?
                mov     bx, bp                    ; Set it to max allowed
            .endif                                ; 

;/*
;**   If either of the starting and ending column value is beyond the
;**   normalized text resolution of the current display mode then
;**   convert it to the current maximum column value allowed.
;*/

            mov     bp, es:[di].TextCols ; Get max column resolution
            dec     bp                            ; Normalize it
            mov     cx, [si].Col                 ; Get input starting col value
            .if     <cx a bp>                     ; Starting col beyond limit?
                mov     cx, bp                    ; Set it to max allowed
            .endif                                ; 
            mov     dx, [si].Col2                ; Get input ending col value
            .if     <dx a bp>                     ; Ending col beyond limit?
                mov     dx, bp                    ; Set it to max allowed
            .endif                                ; 

            pop     bp                            ; 

;/*
;** Validate input row, column values.   An error is returned if either
;**   the ending row value is greater than the starting row value or the
;**   the ending column value is greater than the starting column value.
;**
;**   Notice that the comparison is made in such a way that the carry
;**   flag is set when the comparison fails.
;*/

            .if     <bx ae ax>                   ; Ending row >= starting row?
                .if     <dx ae cx>               ; Ending col >= starting col?
                    mov     [bp].TopRow, ax ; Save the converted input
                    mov     [bp].BottomRow, bx ;   row, column values
                    mov     [bp].LeftCol, cx ; 
                    mov     [bp].RightCol, dx ; Carry flag bit must be clear
                .else                             ; 
                    mov     ax, ERROR_VIO_COL ; Carry flag should be set
                .endif                            ; 
            .else                                 ; 
                mov     ax, ERROR_VIO_ROW ; Carry flag shoud be set
            .endif                                ; 
        .else                                     ; 
            stc                                   ; Force normal exit
        .endif                                    ; 
        ret                                       ; 

ValidateScrollParms ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     GetScrollByteCount
;*
;* DESCRIPTIVE NAME:    Video device handler get scroll byte count
;*
;* FUNCTION:    Determine how many lines the caller is intended to
;*              scroll up within the rectangle specified and then
;*              use this number to calculate the number of bytes
;*              it takes from the beginning of the target location
;*              to the start of the source line.
;*
;* ENTRY POINT: GetScrollByteCount
;*   LINKAGE:   Near Call from ScrollUp, ScrollDown
;*
;* INPUT:
;*
;* BX = Bottom row
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* OUTPUT:      AX = # of bytes from the beginning of the buffer
;*                   to the start of the first scroll line
;*              [si].RepeatFactor = Actual # of lines to scroll
;*              [bp].ScrollCount  = Actual # of lines to scroll
;*
;* EXIT-NORMAL: None
;*
;* EXIT-ERROR:  None
;*
;* EFFECTS:     AX, DX
;*
;* INTERNAL REFERENCES: None
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  GetScrollByteCount
GetScrollByteCount PROC NEAR
        ASSUME  CS:R2CSEG, DS:NOTHING, ES:NOTHING

        mov     ax, bx                            ; Calculate the number of
        sub     ax, [bp].TopRow                  ;   rows between top row
        inc     ax                                ;   and bottom row
        .if     <ax b [si].RepeatFactor> ; Use the user supplied repeat
            mov     [si].RepeatFactor, ax ;   factor if it is less than
        .endif                                    ;   number of rows in the
        mov     ax, [si].RepeatFactor            ;   target rectangle
        mov     [bp].ScrollCount, ax             ; Save scroll count on stack
        mul     es:[di].TextCols                 ; Calculate the total byte
        add     ax, ax                            ;   count based on the # of
        ret                                       ;   lines to scroll

GetScrollByteCount ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     GetLineSkipByteCount
;*
;* DESCRIPTIVE NAME:    Video device handler get scroll byte count
;*
;* FUNCTION:    Determine the number of bytes to skip between the
;*              end of one line and the beginning of the next line.
;*
;* ENTRY POINT: GetLineSkipByteCount
;*   LINKAGE:   Near Call from ScrollUp, ScrollDown
;*
;* INPUT:
;*
;* CX = Left column
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* OUTPUT:      AX, [bp].LineLength = Actual length in cells for
;*                                                     each line
;*              CX, [bp].SkipLength = # of bytes to skip between
;*                                                     lines
;* EXIT-NORMAL: None
;*
;* EXIT-ERROR:  None
;*
;* EFFECTS:     AX, CX
;*
;* INTERNAL REFERENCES: None
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  GetLineSkipByteCount
GetLineSkipByteCount PROC NEAR
        ASSUME  CS:R2CSEG, DS:NOTHING, ES:NOTHING

        mov     ax, [bp].RightCol                ; Calculate the number of
        sub     ax, cx                            ;   columns between right
        inc     ax                                ;   column and left column
        mov     cx, es:[di].Textcols             ; Calculate the number of
        sub     cx, ax                            ;   bytes to skip between
        add     cx, cx                            ;   lines
        mov     [bp].LineLength, ax              ; Save line length
        mov     [bp].SkipLength, cx              ; CX = bytes to skip between
        ret                                       ;      lines

GetLineSkipByteCount ENDP

;/****************************************************************************
;*
;* SUBROUTINE NAME:     GetLVB_PVB_Cell
;*
;* DESCRIPTIVE NAME:    Video device handler get LVB, PVB and cell
;*
;* FUNCTION:    Determine which buffer address to use based on
;*              the input flags set.  If the user wishes to
;*              scroll both the LVB and the PVB then the LVB
;*              will be updated first before the PVB is refreshed.
;*
;* ENTRY POINT: GetLVB_PVB_Cell
;*   LINKAGE:   Near Call from ScrollUp, ScrollDown,
;*                             ScrollLeft, ScrollRight
;*
;* INPUT:
;*
;* CX = Left column
;* SS:BP  --->  Stack frame                        (see VDHSTRUC.INC)
;* DS:SI  --->  Parameter block buffer            (see WDHBUFUP.ASM)
;* ES:DI  --->  Mode data in environment buffer (see WDHBUFUP.ASM)
;*
;* OUTPUT:      DS = PVB or LVB
;*              ES = PVB or LVB
;*              [bp].PVB_Sel = PVB if user requests both LVB & PVB
;*              AX = Cell
;*
;* EXIT-NORMAL: None
;*
;* EXIT-ERROR:  None
;*
;* EFFECTS:     AX, SI, DI, DS, ES
;*
;* INTERNAL REFERENCES: None
;*
;* EXTERNAL REFERENCES: None
;*
;****************************************************************************/

        PUBLIC  GetLVB_PVB_Cell
GetLVB_PVB_Cell PROC NEAR
        ASSUME  CS:R2CSEG, DS:NOTHING, ES:NOTHING

        mov     di, [si].LogicalBufSel           ; Use Only the LVB

        lds     si, [si].AppCellAddr
        lodsw                                     ; AX = user supplied cell
        .if     <[bp].Retrace eq 4>
            push    ax
            lodsw
            mov     [bp].PVB_Sel,ax              ;Get the rest of the 4-byte cell

            shl     [bp].LineLength,1
            shl     [bp].SkipLength,1            ;Adjust for larger cells
            pop     ax
        .endif
        mov     ds, di                            ; DS, ES = PVB or LVB
        mov     es, di
        ret

GetLVB_PVB_Cell ENDP

R2CSEG  ENDS
        END
