
;************************************************************************
; Smooth simultaneous scroll in horizontal and vertical direction       *
; Entry:        X_Offset- Current horizontal offset                     *
;               Y_Offset- Current vertical offset                       *
;************************************************************************

X_Offset        EQU     [BP+4]
Y_Offset        EQU     [BP+6]

        PUBLIC  _Smooth_Scroll

_Smooth_Scroll  PROC    NEAR
        PUSH    BP                      ;Standard entry from high level
        MOV     BP,SP
        PUSH    ES

        XOR     AX,AX                   ;Point ES to segment zero
        MOV     ES,AX

        ;--- Set CRTC ADDRESS register to offset/8
        ;--- Compute offset into display buffer to be loaded into CRTC
        ;    START ADDRESS register as:
        ;    X_Offset/8 + Y_Offset/Char_Height * columns

        MOV     AX,Y_Offset             ;Fetch Y_Offset
        XOR     DX,DX                   ;Clear DX for divide
        MOV     BX,ES:[BIOS_Height]     ;Get character height
        DIV     BX                      ;Divide offset by character height
        MOV     BX,AX                   ;Save number of text lines to skip
        MOV     CX,DX                   ;Save number of scan lines to skip
        MOV     AX,ES:[BIOS_Columns]    ;Fetch number of bytes per text line
        SHL     AH,1                    ; (adjust for attributes)
        MUL     BX                      ;Convert to number of bytes to skip

        MOV     BX,X_Offset             ;Compute how many bytes to skip due
        SHR     BX,1                    ;to X_Offset
        SHR     BX,1
        SHR     BX,1
        ADD     BX,AX                   ;and add to previously computed offset
                                        ;due to Y_Offset

        ;--- Wait for an end of vertical retrace

        MOV     DX,ES:[BIOS_CRT_Addr]   ;Get address of CRT controller
        ADD     DX,6                    ;Wait for retrace to change registers
SS_Wait1:                               ;Wait for vertical to start
        IN      AL,DX
        JMP     $+2
        TEST    AL,8
        JZ      SS_Wait1
SS_Wait2:                               ;Wait for vertical to end
        IN      AL,DX
        JMP     $+2
        TEST    AL,8
        JNZ     SS_Wait2

        ;--- Set START ADDRESS register in CRTC to the value just computed

        MOV     DX,ES:[BIOS_CRT_Addr]   ;Fetch address of CRTC
        MOV     AL,0DH                  ;Index for START ADDRESS register LO
        OUT     DX,AL                   ;Select index
        INC     DX
        MOV     AL,BL                   ;Fetch the scroll value we computed
        OUT     DX,AL                   ;Set START register
        DEC     DX
        MOV     AL,0CH                  ;Select index for START ADDRESS hi
        OUT     DX,AL
        INC     DX
        MOV     AL,BH                   ;Set START register
        OUT     DX,AL
        DEC     DX

        ; Wait for vertical retrace to start

        ADD     DX,6
SS_Wait3:                               ;Wait for vertical to start
        IN      AL,DX
        JMP     $+2
        TEST    AL,8
        JZ      SS_Wait3
        SUB     DX,6

        ;--- Set CRTC PRESET ROW SCAN register to number of scan lines to skip
        ;    it was earlier computed as (Y_Offset MOD Char_Height)

        MOV     AL,8                    ;Index of PRESET ROW SCAN register
        OUT     DX,AL                   ;Select PRESET ROW SCAN register
        MOV     AL,CL                   ;Fetch scan lines to skip
        INC     DX
        OUT     DX,AL                   ;Set PRESET ROW SCAN register

        ;--- Set Attribute PANNING register to Y_Offset MOD 8

        ADD     DX,5                    ;Compute Attr Read register address
        IN      AL,DX                   ;Reset Attr index/data flip-flop
        MOV     DX,3C0H                 ;Fetch address of Attr write register
        MOV     AL,33H                  ;Fetch index
        OUT     DX,AL                   ;Select PANNING register
        MOV     AX,X_Offset             ;Fetch offset
        AND     AL,7                    ;Keep last 3 bits (offset mod 8)
        OUT     DX,AL                   ;Set PANNING register

        POP     ES                      ;Standard exit to high level
        MOV     SP,BP
        POP     BP
        RET
_Smooth_Scroll  ENDP
