
;************************************************************************
; Scrolling text window in text modes                                   *
; Entry:        Up, Left        - Upper left row and column             *
;               Down, Right     - Lower right row and column            *
;               Count           - Number of lines to scroll             *
;************************************************************************

Up      EQU     WORD PTR [BP+4]
Left    EQU     WORD PTR [BP+6]
Down    EQU     WORD PTR [BP+8]
Right   EQU     WORD PTR [BP+10]
Count   EQU     WORD PTR [BP+12]

        PUBLIC  _Scroll_Text

_Scroll_Text    PROC NEAR
        PUSH    BP
        MOV     BP,SP

        PUSH    DS                      ;Preserve DS
        PUSH    ES                      ;Preserve ES
        PUSH    SI
        PUSH    DI

        ;--- Determine and load segement of the display buffer

        XOR     AX,AX                   ;Point ES to segment zero
        MOV     ES,AX
        MOV     AX,0B000H               ;Assume monochrome buffer address
        TEST    BYTE PTR ES:[BIOS_Equipment],2   ;Is mono attached?
        JNZ     Scroll_Addr_Ok          ;...Yes, go load segment
        MOV     AX,0B800H               ;...No, change address to color
Scroll_Addr_Ok:
        MOV     DS,AX                   ;Set segment of display buffer

        ;--- Compute pointers to source and destination

        CMP     Count,0                 ;Are we scrolling up?
        JL      Setup_Down              ;...No, setup for scrolling down
Setup_Up:
        MOV     BX,ES:[BIOS_Columns]    ;Compute address of where to move from
        MOV     AX,Up                   ;Compute first byte to move as
        ADD     AX,Count                ;(Upper row + count)*bytes per line
        MUL     BX                      ; + upper column
        ADD     AX,Left
        MOV     SI,AX                   ;Save source address in SI
        SHL     SI,1                    ;Adjust for two bytes per char

        MOV     AX,Up                   ;Compute address of where to move to
        MUL     BX                      ;as Upper row * bytes line + upper
        ADD     AX,Left                 ;   + column
        MOV     DI,AX                   ;Save destination
        SHL     DI,1                    ;Adjust for two bytes per char

        MOV     DX,Right                ;Words to move in each line
        SUB     DX,Left                 ;is (Right - Left + 1)
        INC     DX
        SUB     BX,DX                   ;Compute 'update'
        SHL     BX,1

        MOV     CX,Down                 ;Compute number of lines to move
        SUB     CX,Up
        SUB     CX,Count
        INC     CX
        PUSH    DS                      ;Copy DS into ES
        POP     ES
Loop_Up:
        PUSH    CX                      ;Save counter of lines
        MOV     CX,DX                   ;Set counter of bytes
        REP     MOVSW                   ;Move next line of bytes (word/char)
        ADD     DI,BX                   ;Set pointers to next line
        ADD     SI,BX
        POP     CX                      ;Restore line counter
        LOOP    Loop_Up                 ;If not done, go move next line
Clear_Up:
        MOV     CX,Count                ;Number of lines to clear
        MOV     AX,0720H                ;Value to use as 'clear'
Loop_Clear_Up:
        PUSH    CX
        MOV     CX,DX                   ;Fetch # bytes in each line
        REP     STOSW                   ;Clear next line
        ADD     DI,BX                   ;Set pointers to next line
        POP     CX
        LOOP    Loop_Clear_Up

        JMP     Scroll_Done

Setup_Down:
        NEG     Count
        MOV     BX,ES:[BIOS_Columns]    ;Compute bytes/line
        MOV     AX,Down                 ;Compute first source byte
        SUB     AX,Count                ;(Lower row - count)*bytes per line
        MUL     BX                      ; + upper column
        ADD     AX,Left
        MOV     SI,AX                   ;Save source address in SI
        SHL     SI,1                    ;Adjust for two bytes per char

        MOV     AX,Down                 ;Compute address of where to move
        MUL     BX                      ;as Lower row * bytes line + left
        ADD     AX,Left                 ;   + column
        MOV     DI,AX                   ;Save destination
        SHL     DI,1                    ;Adjust for two bytes per char

        MOV     DX,Right                ;Compute words to move in a line
        SUB     DX,Left                 ;as (Right - Left + 1)
        INC     DX
        ADD     BX,DX                   ;Compute 'update'
        SHL     BX,1

        MOV     CX,Down                 ;Compute number of lines to move
        SUB     CX,Up
        SUB     CX,Count
        INC     CX
        PUSH    DS                      ;Copy DS into ES
        POP     ES
Loop_Down:
        PUSH    CX                      ;Save counter of lines
        MOV     CX,DX                   ;Set counter of bytes
        REP     MOVSW                   ;Move next line of bytes
        SUB     DI,BX                   ;Set pointers to next line
        SUB     SI,BX
        POP     CX                      ;Restore line counter
        LOOP    Loop_Down               ;If not done, go move next line

Clear_Down:
        MOV     CX,Count                ;Number of lines to clear
        MOV     AX,0720H                ;Value to use as 'clear'
Loop_Clear_Down:
        PUSH    CX
        MOV     CX,DX                   ;Fetch # bytes in each line
        REP     STOSW                   ;Clear next line
        SUB     DI,BX                   ;Set pointers to next line
        POP     CX
        LOOP    Loop_Clear_Down

Scroll_Done:
        POP     DI
        POP     SI
        POP     ES
        POP     DS
        POP     BP
        RET
_Scroll_Text    ENDP
