
;************************************************************************
; Transfer Bit Aligned Block of dimensions Width x Height, from         *
; source, with upper left corner at (X_Src,Y_Src), to destination with  *
; upper left corner at (X_Dst,Y_Dst), using function fn.                *
; Entry:        X_Srs, Y_Src  - Source upper left                       *
;               X_Dst, Y_Dst  - Destination upper left                  *
;               BWidth,BHeight- Dimensions of the block                 *
;               Fn            - Logical function (Copy, AND, OR, XOR)   *
;************************************************************************

;xxxx add later traversing x in reverse for overlapped src and dst

X_Src   EQU     [BP+4]                  ;Formal parameters on the stack
Y_Src   EQU     [BP+6]
X_Dst   EQU     [BP+8]
Y_Dst   EQU     [BP+10]
BWidth  EQU     [BP+12]
BHeight EQU     [BP+14]
Fn      EQU     [BP+16]

HBYTES  EQU     80                      ;Number of bytes per scanline
GRF_SEG EQU     0A000H                  ;Segment for display buffer

Y_Incr          EQU     WORD PTR [BP-2] ;Local variables
First_Mask      EQU     BYTE PTR [BP-4]
InFirst         EQU     WORD PTR [BP-6]
Last_Mask       EQU     BYTE PTR [BP-8]
Read_Plane      EQU     BYTE PTR [BP-10]
Lower_Mask      EQU     BYTE PTR [BP-12]
Upper_Mask      EQU     BYTE PTR [BP-14]
Src_Address     EQU     WORD PTR [BP-16]
Dst_Address     EQU     WORD PTR [BP-18]
Block_Dx        EQU     WORD PTR [BP-20]
Block_Dy        EQU     WORD PTR [BP-22]

        PUBLIC  _BitBlt

_BitBlt PROC    NEAR
        PUSH    BP
        MOV     BP,SP

        SUB     SP,24                   ;Setup local variables

        PUSH    DS                      ;Presrve segment registers
        PUSH    ES
        PUSH    DI
        PUSH    SI

        ;-------------------------------------------------------------
        ;--- COMPUTE MASKS FOR FIRST AND LAST BYTE WITHIN A RASTER
        ;    ROTATION FACTOR, AND MASKS FOR HI AND LOW BITS AFTER ROTATION
        ;    ENABLE ROTATION AND LOGICAL FUNCTIONS IN GRAPHICS CONTROLLER
        ;-------------------------------------------------------------

        MOV     CX,X_Dst                ; NUMBER OF CLEAR BITS IN FIRST BYTE
        AND     CX,7
        MOV     First_Mask,0FFH         ; COMPUTE MASK TO CLEAR LEADING BITS
        SHR     First_Mask,CL
        MOV     AX,8
        SUB     AX,CX
        MOV     InFirst,AX

        ;MASK TO KEEP BITS IN LAST BYTE (PARTIAL BYTE)

        MOV     CX,X_Dst                ; GET ADDRESS OF LAST BIT
        ADD     CX,BWidth
        AND     CX,7                    ; POSITION JUST AFTER LAST BIT
        MOV     Last_Mask,0FFH          ; COMPUTE MASK TO KEEP BITS AFTER
        SHR     Last_Mask,CL
        NOT     Last_Mask               ; COMPLEMENT TO KEEP LEADING BITS

        ;BITS FROM SOURCE BYTE MUST BE ALLIGNED WITH BITS IN THE
        ;DESTINATION BYTE.  HERE IS COMPUTED THE ROTATION AND THE
        ;TWO MASKS NEEDED TO ISOLOTATE TWO HALVES OF THE BYTE

        ; COMPUTE ROTATION

        MOV     CX,X_Dst                ; COMPUTE BIT DISTANCE BETWEEN
        SUB     CX,X_Src                ;... BIT POSITIONS IN FIRST BYTES
        AND     CX,7                    ; ... OF SOURCE AND DESTINATION BYTES
                                        ; ... AS (SRC-DST)&7
        MOV     AX,00FFH                ;Compute masks for non-zero rotation
        ROR     AX,CL
        MOV     Lower_Mask,AL           ;Save the masks
        MOV     Upper_Mask,AH

        MOV     DX,3CEH                 ;Address of GRAPHICS controller
        MOV     AL,3                    ;Index for DATA ROTATE & FN SELECT
        OUT     DX,AL                   ;Select DATA ROTATE & FN SELECT REG
        INC     DX
        MOV     AL,CL                   ;Fetch rotate count
        MOV     AH,Fn                   ;Fetch logical function
        AND     AH,3                    ;Move logical function into
        SHL     AH,1                    ;bits 3 and 4
        SHL     AH,1
        SHL     AH,1
        OR      AL,AH
        OUT     DX,AL                   ;Write value into DATA ROTATE... reg

        ;-------------------------------------------------------------
        ;--- COMPUTE ABSOLUTE ADDRESS OF SOURCE AND DESTINATION
        ;-------------------------------------------------------------

        MOV     WORD PTR Y_Incr,HBYTES  ;Initialize y traversal as normal

        ;Because of possible overlap we must adjust direction of traversal
        ;If source block is above the destination then reverse Y traversal
        ;If source block is to the left, then reverse X traversal

        MOV     AX,Y_Src                ;Compare source and destination Y
        MOV     BX,Y_Dst
        CMP     AX,BX
        JLE     Compute_Address         ;...Leave alone if src <= dst
        ADD     AX,BHeight              ;Begin with last raster in block
        DEC     AX
        ADD     BX,BHeight
        DEC     BX
        MOV     Y_Src,AX
        MOV     Y_Dst,BX
        NEG     WORD PTR Y_Incr         ;And traverse backwards
Compute_Address:
        MOV     AX,Y_Src                ;Compute offset for source
        MOV     BX,HBYTES               ;as offset = y * 80 + x/8
        MUL     BX
        MOV     SI,X_Src
        SHR     SI,1
        SHR     SI,1
        SHR     SI,1
        ADD     SI,AX
        MOV     Src_Address,SI

        MOV     AX,Y_Dst                ;Compute offset for destination
        MOV     BX,HBYTES               ;as offset = y * 80 + x/8
        MUL     BX
        MOV     DI,X_Dst
        SHR     DI,1
        SHR     DI,1
        SHR     DI,1
        ADD     DI,AX
        MOV     Dst_Address,DI

        MOV     AX,GRF_SEG              ;Setup segment
        MOV     ES,AX
        MOV     DS,AX

        ;-------------------------------------------------------------
        ;--- ENABLE NEXT PLANE FOR READ AND WRITE
        ;-------------------------------------------------------------

        MOV     AL,3                    ;Initialize counter of planes
        MOV     Read_Plane,AL
Plane_Loop:
        MOV     AX,BWidth               ;Copy dimensions of the block
        MOV     Block_Dx,AX
        MOV     AX,BHeight
        MOV     Block_Dy,AX
        MOV     SI,Src_Address          ;Fetch source address
        MOV     DI,Dst_Address          ;Fetch destination address
        MOV     CL,Read_Plane           ;Fetch next plane to do
        MOV     AH,1                    ;Use plane number to setup value
        SHL     AH,CL                   ;for WRITE PLANE SELECT register
        MOV     DX,3C4H                 ;Fetch address of SEQUENCER
        MOV     AL,2                    ;Index for WRITE PLANE SELECT register
        OUT     DX,AL                   ;Select register
        INC     DX
        MOV     AL,AH                   ;Fetch value
        OUT     DX,AL                   ;Write value into register
        MOV     DX,3CEH                 ;Fetch address of GRAPHICS controller
        MOV     AL,4                    ;Index for READ PLANE SELECT register
        OUT     DX,AL                   ;Select register
        INC     DX
        MOV     AL,CL                   ;Fetch plane number
        OUT     DX,AL                   ;Write plane number into register
        DEC     DX
        MOV     AL,8                    ;Index for BIT MASK register
        OUT     DX,AL                   ;Select BIT MASK register
        INC     DX

        ;-------------------------------------------------------------
        ; RASTER LOOP IS DONE IN FOUR STEPS
        ; (1) MASKS AND ROTATION FACTOR IS COMPUTED TO ALIGN SRC AND DEST
        ; (2) LEADIING PARTIAL BYTE OF DESTINATION IS MODIFIED
        ; (3) FULL BYTES OF DESTINATION ARE MODIFIED
        ; (4) TRAILLING PARTIAL BYTE OF DESTINATION IS MODIFIED
        ;-------------------------------------------------------------

        ;-------------------------------------------------------------
        ;--- GET ENOUGH BITS FROM SOUCE TO CONSTRUCT FIRST PARTIAL BYTE OF DST
        ;-------------------------------------------------------------

Raster_Loop:
        MOV     CX,Block_Dx             ;Number of bits to copy
        PUSH    SI                      ;Preserve addresses for next raster loo
        PUSH    DI

        MOV     AX,X_Src                ;Check if need one or two bytes
        AND     AX,7                    ;for first byte of destination
        MOV     BX,X_Dst
        AND     BX,7
        CMP     AX,BX
        JG      Get_2_Bytes
Get_1_Byte:
        MOV     AL,First_Mask           ;Fetch mask for first partial byte
        AND     AL,Lower_Mask           ;Combine with 'lower mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        LODSB                           ;Fetch first source byte
        MOV     AH,[DI]                 ;Latch destination
        STOSB                           ;Write first byte of destination
        MOV     AH,AL                   ;Save AL since it will be trashed by OU
        JMP     Leading_Done

Get_2_Bytes:
        MOV     AL,First_Mask           ;Fetch mask for first partial byte
        AND     AL,Upper_Mask           ;Combine with 'upper mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        LODSB
        MOV     AH,[DI]                 ;Latch destination bits
        MOV     ES:[DI],AL              ;Write first byte

        MOV     AL,First_Mask           ;Fetch mask for first partial byte
        AND     AL,Lower_Mask           ;Combine with 'lower mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        LODSB
        MOV     AH,[DI]                 ;Latch destination bits
        STOSB                           ;Write first byte
        MOV     AH,AL                   ;Save AL since it will be trashed by OU

Leading_Done:
        SUB     CX,InFirst              ;Update number of bits to transfer
        JLE     Raster_Done

        ;-------------------------------------------------------------
        ;--- LOOP OVER COMPLETE BYTES WITHIN A SINGLE SOURCE RASTER
        ;-------------------------------------------------------------

Full_Loop:
        CMP     CX,8                    ;If less then 8 bits to do
        JL      Full_Done               ;quit this loop

        MOV     AL,Upper_Mask           ;Fetch the 'upper mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        MOV     AL,[DI]                 ;Latch destination bits
        MOV     [DI],AH                 ;Write half 2 for src to half 1 of dst

        MOV     AL,Lower_Mask           ;Fetch the lower mask
        OUT     DX,AL                   ;Write mask into BIT MASK register
        LODSB                           ;Fetch next source byte
        MOV     AH,[DI]                 ;Latch destination bits
        STOSB                           ;Write half 1 of src to half 2 of dst
        MOV     AH,AL                   ;Save AL since it will be trashed by OU

        SUB     CX,8                    ; DECREMENT WIDTH BY BITS TO BE DONE
        JMP     Full_Loop
Full_Done:

        ;-------------------------------------------------------------
        ;--- TRANSFER THE LAST PARTIAL BYTE
        ;-------------------------------------------------------------

Last_Partial:
        CMP     CX,0                    ;Any more bits to transfer?
        JLE     Raster_Done             ;...No, quit this raster

        MOV     AL,Last_Mask            ;Fetch mask for last partial byte
        AND     AL,Upper_Mask           ;Combine with 'upper mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        MOV     AL,[DI]                 ;Latch destination bits
        MOV     [DI],AH                 ;Write half 2 for src to half 1 of dst

        MOV     AL,Last_Mask            ;Fetch mask for last partial byte
        AND     AL,Lower_Mask           ;Combine with 'upper mask'
        OUT     DX,AL                   ;Write mask into BIT MASK register
        LODSB                           ;Fetch next source byte
        MOV     AH,[DI]                 ;Latch destination bits
        STOSB                           ;Write half 1 of src to half 2 of dst

        ;--- ADVANCE TO THE NEXT RASTER

Raster_Done:
        POP     DI                      ;Pointers to current raster
        POP     SI
        ADD     SI,Y_Incr               ;Update pointer to point to the next
        ADD     DI,Y_Incr               ;raster
        DEC     WORD PTR Block_Dy       ;Update number of rasters to do
        JLE     Test_Plane
        JMP     Raster_Loop             ;And repeat if not all done

Test_Plane:
        DEC     Read_Plane              ;Update number of planes to do
        JL      Blit_Done
        JMP     Plane_Loop              ;And do next plane if any left to do

        ;-------------------------------------------------------------
        ;--- Clean up and exit
        ;-------------------------------------------------------------

Blit_Done:
        ;restore rotate value
        MOV     DX,3CEH                 ;Address of GRAPHICS controller
        MOV     AL,3                    ;Index for ROTATE & FN SELECT
        OUT     DX,AL                   ;Select ROTATE & FN SELECT register
        INC     DX
        XOR     AL,AL                   ;Disable rotate and set fn=copy
        OUT     DX,AL                   ;Write value into register
        ;restore bit mask
        DEC     DX
        MOV     AL,8                    ;Index for BIT MASK register
        OUT     DX,AL                   ;Select BIT MASK register
        INC     DX
        MOV     AL,0FFH                 ;Enable all 8 bits for write
        OUT     DX,AL                   ;Write value into register
        ;enable all four planes for write
        MOV     DX,3C4H                 ;Address of SEQUENCER
        MOV     AL,2                    ;Index for PLANE ENABLE register
        OUT     DX,AL                   ;Select register
        INC     DX
        MOV     AL,0FH                  ;Value to enable all four planes
        OUT     DX,AL                   ;Write value into register

        POP     SI                      ;Restore segment registers
        POP     DI
        POP     ES
        POP     DS
        MOV     SP,BP
        POP     BP
        RET
_BitBlt ENDP
