
%SUBTTL "Scroll Window Left routine with sample driver"
;
; Scroll2.Asm
;
; see ScrollLeft description for complete explanation of this program
;
; written on 21 November 1991 by Ed Beroset
; released to the public domain
;
        MODEL   small
        IDEAL
        STACK   200h        ; this is more than enough stack
        CODESEG
start:
;
; this test stub scrolls the window at (4,3)-(22,68) left 9 columns,
; filling in with spaces of attribute white on black (07)
;
        mov     al,9        ; number of columns to scroll
        mov     bh,07       ; attribute to use for blanked chars
        mov     ch,4        ; row of upper left hand corner of window
        mov     cl,3        ; col of upper left hand corner of window
        mov     dh,22       ; row of lower right hand corner of window
        mov     dl,68       ; col of lower right hand corner of window

        call    ScrollLeft

        mov     ah,4ch      ; exit with errorlevel from scroll routine
        int     21h
;
; ScrollLeft
;
; PURPOSE:  scrolls a rectangular region of a text screen (window) to the
;           left by a variable number of columns
;
; HISTORY:  written on 21 November 1991 by Ed Beroset -- public domain
;
; INPUTS:   AL = number of columns to scroll (if zero, no effect)
;           BH = attribute to use for blanked characters
;        CH,CL = row, col for upper left hand corner of window
;        DH,DL = row, col for lower right hand corner of window
;
; NOTES: upper left hand corner of screen (home) is (0,0)
;        video adapter is checked to see if it's in text mode
;        screen dimensions are taken from BIOS data
;        calling sequence is nearly identical to BIOS scroll routine
;        display pages supported
;        dual monitors supported
;        MDA/CGA/EGA/VGA supported
;
; RETURNS:  On error, Carry flag set, AX contains error number
;           otherwise carry is clear, AX contains 0
;
;     Error codes:  0   -   no error
;                  -1   -   some error occurred
;
PROC    ScrollLeft
        ScrRows   equ   0484h     ; location of BIOS rows data
        ScrCols   equ   044ah     ; location of BIOS cols data
        ScrMode   equ   0449h     ; location of BIOS mode data
        ScrOffs   equ   044eh     ; location of BIOS scrn offset data

;       first, save all of the registers we're going to trash
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    bp
        push    es
        push    ds

        mov     bl,al         ; stow cols in BL
        xor     ax,ax
        mov     ds,ax         ; point data seg to 0000 to read BIOS data
        cmp     ch,dh         ; srow > erow ?
        ja      @@badcoords
        cmp     cl,dl         ; scol > ecol ?
        ja      @@badcoords
;
;  now we need to load ScrRows, if we haven't got EGA or better
;
        mov     al,[ScrRows]  ; is MAXROWS = 0 ?
        cmp     al,0          ; if so, then assume we've got MDA or CGA
        jne     @@ega_plus    ; if not, we've got correct value already
        mov     al,24         ; otherwise, assume 25 rows (and load 25-1)
@@ega_plus:
        cmp     dh,al         ; erow >= MAXROWS?
;
;  note that BIOS actually stores MAXROWS-1 in this location, so the
;  actual jump instruction is (correctly) written as ja
;
        ja      @@badcoords
        cmp     dl,[ScrCols]  ; ecol >= MAXCOLS?
        jae     @@badcoords
        mov     ah,bl         ; remember cols
        add     ah,cl         ; cols + scol
        cmp     ah,dl         ; (cols + scol) > ecol ?
        ja      @@badcoords

; figure out where the video buffer starts
        mov     bp,0b800h     ; first guess
        mov     al,[ScrMode]  ; get mode from BIOS' RAM
        cmp     al,4          ; if mode is 0 through 3, we're all set
        jb      @@modeOK
        mov     bp,0b000h     ; second guess
        cmp     al,7          ; is it mode 7 (monochrome 80 col) ?
        je      @@modeOK      ; if so, we're still OK
@@badcoords:
        mov     ax,-1         ; set error code
        stc                   ; and set error (carry) flag
        jmp     @@exit
@@modeOK:
        mov     es,bp         ; set up our video pointer segment

; ES:DI = endloc  = Screen + 2 * (MAXCOLS * srow + scol)

        mov     di,[ScrOffs]  ; offset of screen buffer
        xor     ah,ah         ; clear out high half of AX
        mov     al,[ScrCols]  ; get the width of the screen
        mul     ch            ; multiply width by the row number
        add     al,cl         ; now add the column number
        adc     ah,0          ; propagate carry bit to other half of AX
        shl     ax,1          ; now multiply by 2 bytes/char
        add     di,ax         ; add the offset to the screen start address

; DS:DI = startloc = endloc + 2 * cols

        xor     ah,ah         ; clear top half of ax
        mov     al,bl         ; recall cols
        mov     si,di         ; start address = end address
        add     si,ax         ; add cols
        add     si,ax         ; and again

; start on count calculation (figure ecol-scol+1)
        sub     dl,cl         ; ecol - scol
        inc     dl            ; now dl = ecol - scol + 1
  
; calculate increment and stow in BP
;   increment = (MAXCOLS - (ecol - scol + 1)) * 2
        xor     ah,ah         ; clear top half of ax
        mov     al,dl         ; use partial count calculation
        neg     ax
        add     al,[ScrCols]  ; now add in screen width
        adc     ah,0          ; propagate carry bit to hi half of AX
        shl     ax,1          ; now double it (2 bytes/char)
        mov     bp,ax

; finish count calculations and put in DL
;   count = (ecol - scol + 1) - cols

        sub     dl,bl         ; figure in cols

        mov     ax,es         ; recall our video pointer
        mov     ds,ax         ; now duplicate it

; load up AX with char and attribute for blank space
        mov     al,32         ; ASCII space character
        mov     ah,bh         ; passed attribute byte

        sub     dh,ch         ; get row count
        mov     dh,bh         ; save loop count (rows to move) in bh
        xor     ch,ch         ; zero out hi half of CX
        cld                   ; assure that we move the right direction (up)
@@looptop:
        mov     cl,dl         ; load in count (words to move)
        rep     movsw         ; move the line over
        mov     cl,bl         ; recall cols   (blanks to insert)
        rep     stosw         ; fill in the rest of the line
        add     di,bp         ; advance to next line by adding increment
        mov     si,di         ; now set up the other (source) pointer
        mov     cl,bl         ; recall cols
        add     si,cx         ; add in cols
        add     si,cx         ; and again (because 2 bytes/char)
        dec     bh            ; decrement loop counter
        ja      @@looptop     ; skip back if we're not done yet
        mov     ax,0          ; set error code to zero (all OK)
        clc                   ; and clear error (carry) flag
@@exit:
        pop     ds            ; restore all of our registers
        pop     es
        pop     bp
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        ret                   ; mosey on home again

ENDP    ScrollLeft
        END     start
-+------- cut here -----------

