;*DDK*************************************************************************/
;
; 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    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = DRAWBITS.ASM
;*
;* DESCRIPTIVE NAME = DrawBits at level of device driver. 
;*
;*
;* VERSION      V2.0
;*
;* DATE         05/11/90
;*
;* DESCRIPTION  Draws a portion of standard-formatted Bitmap directly onto the 
;*              screen
;*                                                            
;*               . The standard-formatted Bitmap can have 1/4/8/24 bits/pel   
;*                 but only one plane.                           
;*               . length in byte of 1 scan does not exceed 64k    
;*               . The target device 3/4 plane EGA/VGA display                
;*               . No RasterOperations are supported and direct copy is done. 
;*               . Bound checking of the source rectangle is done against the 
;*                 screen extents before in DrawBits, BitBlt,                 
;*                 enumerate_clip_rects, and do_drawbits.     
;*               . Returns 1 in AX to indicate success (0 => failure)         
;*              
;*
;* FUNCTIONS    OEMDrawBits
;*              
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   12/06/88                     Amit Chatterjee  [amitc] Created for Windows
;*                                for DIScreenBlt
;*   05/11/90                     Viroon Touranachun [viroont] Adapted for PM
;*
;*****************************************************************************/

        .386

        .xlist

INCL_GPIBITMAPS         equ     1                ; for bitmap info structure
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1

        include pmgre.inc
        include driver.inc
        include display.inc
        include egafam.inc
        include assert.mac
        include extern.inc
        include protos.inc

        .list

        .MODEL FLAT
    
        ASSUME  CS:FLAT,SS:FLAT,DS:FLAT,ES:FLAT

        .DATA

;/*
;**   Tables for BitCount-dependent routines
;*/

FullXFer        equ     this DWORD               ; has the xfer routine addresses

                DWORD   copy_1_bp_full
                DWORD   copy_4_bp_full
                DWORD   copy_8_bp_full
                DWORD   copy_24_bp_full

InitProc        equ     this DWORD               ; has the init routine addresses

                DWORD   odb_init_1_bit_per_pel
                DWORD   odb_init_4_bits_per_pel
                DWORD   odb_init_8_bits_per_pel
                DWORD   odb_init_24_bits_per_pel

ODB_SRC_HUGE    equ     01h             ;indicate the huge source bitmap

        .CODE

        EXTERN  clean_up_before_exit_no_test:NEAR

;/***************************************************************************
;*
;* FUNCTION NAME = OEMDrawBits
;*
;* DESCRIPTION   = This function is a working routine to draw a              
;*                 standard-formatted bitmap directly (ROP_SRCCOPY) to the   
;*                 screen.                                                   
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN   4

OEMDrawBits     PROC SYSCALL USES esi edi,      pddcDst:DWORD, xDst:DWORD,
                                                yDst:DWORD, psdSrc:DWORD,
                                                xSrc:DWORD, ySrc:DWORD,
                                                cxExt:DWORD, cyExt:DWORD
                LOCAL   lpBits:DWORD,         
                        cbSrcScan:DWORD,         
                        lpScreen:DWORD,          
                        cbDstScan:DWORD,   
                        cBitCount:DWORD,         
                        lpClrTab:DWORD    
                LOCAL   bBitMask:DWORD,          
                        fbFlags:DWORD,            
                        pfnFullXfer:DWORD,       
                        cbSrcScanBlt:DWORD,
                        bUnusedPel:DWORD,        
                        cPelsAtATime:DWORD

;/*
;** Get the bitmap information
;*/

        DebugMsg <OEMDrawBits DRAWBITS CLIFFL>

        cld
        mov     esi,psdSrc              ; source surface definition

        ASSUME  esi:PTR BITMAP

        mov     eax,[esi].bm_sd.sd_cbScan
        mov     cbSrcScan,eax           ; source scan width in bytes
        mov     eax,[esi].bm_sd.sd_pBits
        mov     lpBits,eax              ; pointer to the caller-supplied bits
        mov     eax,[esi].bm_sd.sd_pClrTab
        mov     lpClrTab,eax            ; pointer to the color mapping table
        mov     esi,[esi].bm_sd.sd_pbmi ; pointer to the bitmap info

        ASSUME  esi:PTR BITMAPINFOHEADER

        movzx   ecx,[esi].bmp_cBitCount ; the number of bits per pel (assume old)

        ASSUME  esi:PTR BITMAPINFOHEADER2

        cmp     [esi].bmp2_cbFix,size BITMAPINFOHEADER
        je      odb_have_info           ; it's old header, we know it's uncomp.

;/*
;** OEMDrawBits only draws the uncompressed bitmap. If it is a compressed bitmap
;** this is a good chance to pass it to RLEDrawBits.
;*/

odb_new_header:

        movzx   ecx,[esi].bmp2_cBitCount ;the number of bits per pel
        cmp     [esi].bmp2_cbFix,OFFSET BITMAPINFOHEADER2.bmp2_ulCompression
        jbe     odb_have_info           ;must be uncompressed

odb_check_compress:

        cmp     [esi].bmp2_ulCompression,BCA_UNCOMP
        je      odb_have_info

        INVOKE  RLEDrawBits, pddcDst, xDst, yDst, esi, xSrc, ySrc, cxExt, cyExt,
                                lpBits, lpClrTab

        jmp     odb_end                 ;already draw to screen

        ALIGN   4

odb_have_info:

        mov     cBitCount,ecx

;/*
;** we shall also calculate no of bytes in the source map that correspond to
;** cxExt bits, as we shall use this information to test whether we cross a seg-
;** ment on the source during the blt of cxExt pels on a scan.
;*/

        mov     eax,cxExt                        ; get the no of bits in 1 scan blt
        mul     ecx                              ; multiply by no of bits per pel

        assert  edx,E,0

        shr     eax,3                            ; get the number of bytes
        add     eax,1                            ; take the ceiling
        mov     cbSrcScanBlt,eax


;/*
;** Cursor Exclusion for a device destination
;*/

odb_cursor_exclude:

        mov     esi,cxExt
        mov     edi,cyExt
        dec     esi                               ;Make extents inclusive of last point
        dec     edi
        mov     ecx,xDst                ;Set left
        mov     edx,yDst                ;Set top
        add     esi,ecx                           ;Set right
        add     edi,edx                           ;Set bottom

        INVOKE  far_exclude                               ;Exclude the area from the screen

;/*
;** calculate the offset to the start byte in the map and the screen and the
;** position of the first pel in the screen byte and on the map.
;*/

odb_calc_src_offset:

        mov     eax,ySrc                         ; get the Y origin of the map
        mul     cbSrcScan                        ; the result is in DX:AX

        assert  edx,E,0

        mov     ebx,eax
        mov     eax,xSrc                         ; get the starting X pel no
        mul     cBitCount                        ; no of bits per pel

        assert  edx,E,0

        shr     eax,3                             ; get the no of bytes
        add     eax,ebx                           ; add in the contribution on CX:BX

;/*
;** add in the contribution of the original start offset of the bits.
;*/

        add     lpBits,eax                        ; update the offset

;/*
;**  lpbits now points to the first byte of the map in the blt area.           ;
;**  The starting scan no might not have crossed a segment but the blt area    ;
;**  might still cross a segment, test for that.           ;
;*/
                
        mov     eax,cyExt                         ; get the Y ext of the blt
        mul     cbSrcScan                         ; bytes per scan on the map
        add     eax,lpBits                        ; add the first byte offset


        .errnz  ODB_SRC_HUGE-1

;/*
;** now calculate the address of the start byte in the screen blt area. 
;*/

odb_calc_dst_offset:

        mov     cbDstScan,SCREEN_DSCAN         ; destination scan width in bytes
        mov     eax,yDst                       ; get the Y origin (top scan)
        add     eax,cyExt                      ; move it to the bottom of scan
        dec     eax
        mul     cbDstScan                      ; no of bytes in a scan

        assert  edx,E,0

        mov     ebx,xDst                       ; get the x origin
        mov     ecx,ebx
        shr     ebx,3                          ; get the no of bytes
        add     eax,ebx                        ; this is the start offset 
        mov     lpScreen,eax
        mov     esi,pddcDst                    ; ES:SI => the dest. ddc

        ASSUME  esi:PTR DDC

        mov     esi,[esi].ddc_npsd     ; ES:SI => the dest. surface

;/*
;** lpScreen now has the pointer to the first byte in the screen blt area
;** Calculate the position of the first bit in the first byte of the screen
;** blt area.
;*/

odb_set_bit_mask:

        and     cl,07h                            ; get the bit no in the starting byte
        mov     bl,80h                            ; starting mask
        shr     bl,cl                             ; bl has the correct bit set
        mov     BYTE PTR bBitMask,bl             ; save it

;/*
;** now get the address of the full and partial byte proces that we will be
;** using depending on the bits per pel.
;*/

odb_get_xfer_addr:

        mov     ebx,cBitCount           ; get the bits per pel
        shl     ebx,4                   ; if BX = 1 => BX = 0
        shl     bh,6                    ; if BX = 4 => BX = 1
        add     bh,bl                   ; if BX = 8 => BX = 2
        shr     ebx,14                  ; if BX = 24 => BX = 3
        shl     ebx,1                   ; double for word offset
        mov     eax,FullXFer[ebx]       ; get the transfer routine
        mov     pfnFullXfer,eax
        mov     ebx,InitProc[ebx]       ; get the appropriate init procs addr
        jmp     ebx                     ; do the format specific inits

        ALIGN   4

;/*
;**  the format specific initialization routines follow here.     
;**  ES:DI points to the color table.                             
;**                                                                           
;**  For monochrome, we need to know how many bits in the first source byte   
;**     (from the left) are unused and init color table                       
;**  For 4 bpp, we need to know wheter we start with the high nibble or the   
;**     low nibble and init the color table.                                  
;**  For 8 bpp, we always have each pel starts at the byte boundary, so just  
;**     init the color table.                                                 
;**  For 24 bpp, do nothing.                                                  
;*/

odb_init_1_bit_per_pel::

        mov     eax,xSrc                          ; the start pel number in a scan
        and     eax,7                             ; the number of pel unused in 1st byte
        mov     BYTE PTR bUnusedPel,al
        jmp     odb_init_8_bits_per_pel
           
        ALIGN   4

odb_init_4_bits_per_pel::

        mov     eax,xSrc                          ; start BMP pel number in a byte
        shr     eax,1                             ; if odd we start with low nibble
        rcl     bUnusedPel,1                      ; LSB bit for low nibble usage

odb_init_8_bits_per_pel::

        mov     ebx,lpClrTab            ; the color mapping table

odb_init_24_bits_per_pel::

;/*
;** we will program the EGA/VGA GRX registers to be in write mode 0, where the
;** CPU value can be used to write masked pixels.
;*/

odb_init_board:

        mov     dx,EGA_BASE + GRAF_ADDR          ; the GRX controller address

;/*
;** set up for READ 0/WRITE 2
;*/

        mov     ax,(M_COLOR_WRITE shl 8)+GRAF_MODE
        mov     shadowed_graf_mode.vvr_value,ah ; Must shadow this for state code
        out     dx,ax
        jmp     $+2                               ; I/O delay

;/*
;** set up the GRX address register with the index for the bit mask
;** register.
;*/

        mov     al,GRAF_BIT_MASK
        out     dx,al
        jmp     $+2                               ; I/O delay

;/*
;** The sorce bytes in a scan may cross a segment boundary only if the flag - 
;** ODB_SRC_HUGE is set.                                  
;**                                                       
;** If the flag is set a test will be made at the start of every scan line to 
;** see whether that scan crosses the segment boundary or not. If it does cross
;** a segment boundary, every byte will be fetched with a test to detect the  
;** segment cross if not then 'LODSB' will be used to get the byte. 
;**                                                         
;** We assume that a scan will not cross two segment boundaries.        
;*/

odb_start_draw:

        mov     esi,lpBits                        ; the strting point of the bits
        mov     edi,lpScreen                      ; the start on the screen
        mov     ecx,cyExt                         ; the number of scans to copy
        mov     dx,EGA_BASE + GRAF_DATA ; will alaways have this value

;/*
;** We will draw one scan at a time so that we can determine which
;** byte-transfer routines we will use. First, assume that segment crossings
;** will not be required for this scan.
;*/

odb_blt_next_scan:

;/*
;** test to see if the current scan will span a segment
;*/


;/*
;** the scan will cross a segment somewhere, so use the pfnGetByte proc which tests
;** for segment crossings
;*/

odb_blt_next_scan_continue:

        push    ecx                              ; save scan loop count
        push    esi
        push    edi                              ; save the source and target pointers
        mov     ah,BYTE PTR bBitMask             ; the starting mask
        mov     ecx,cxExt                        ; the no of pels to blt

        Call    pfnFullXfer                      ; do the blt of a scan

        pop     edi
        pop     esi                              ; get back the pointers
        pop     ecx                              ; get back the no of scans left to blt
        sub     edi,cbDstScan                    ; we map top to bottom
        add     esi,cbSrcScan                    ; update to the next scan
        loop    odb_blt_next_scan                ; blt all the scans

;/*
;** the new scan crosses a segment, has to be in the next segment, so take DS
;** into the next segment
;*/

;/*
;** now reset the EGA/VGA parameters and bring back the cursor
;*/

odb_exit:

        call    clean_up_before_exit_no_test    ; restore EGA registers

        INVOKE  far_unexclude                     ; re-draw the cursor

odb_end:

        RET

;/***************************************************************************
;*
;* FUNCTION NAME = copy_1_bp_full
;*
;* DESCRIPTION   = handles the blt of a scan or a part of it for 1 bits per
;*                 pixel case                                              
;*
;*                 Registers Destroyed:
;*                       AL,CX,flags   
;*
;* INPUT         = DS:SI     --  current byte in the map     
;*                 ES:DI     --  current byte in the screen     
;*                    AH     --  current bit position in the first byte 
;*                 GS:BX     --  address of color mapping table 
;*                    CX     --  number of pels to convert      
;*                    DX     --  BIT MASK register address      
;*
;* OUTPUT        = DS:SI,ES:DI -- next bytes in respective areas
;*                 AH          -- updated bit mask 
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
        
        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

        ALIGN   4

copy_1_bp_full::

;/*
;** the first source byte may yield partial number of pels
;** shift left the contents till the start pel is on left edge
;*/

        lodsb                                    ; get the first byte
        push    ecx                              ; save
        mov     cl,BYTE PTR bUnusedPel           ; no of pels not used in 1st byte
        shl     al,cl                            ; the first pel is on the edge
        neg     cl      
        add     cl,8                             ; the number of pels left in byte
        movzx   ecx,cl
        mov     cPelsAtATime,ecx
        pop     ecx

copy_1_bp_loop:

        xchg    al,ah                            ; get the current mask
        push    ecx                              ; save # to convert

;/*
;** cPelsAtATime has the number of pels in the byte, usually 8 except for first
;** and last
;*/

        cmp     ecx,cPelsAtATime                 ; 8 or more bits left
        jbe     copy_1_bp_same_byte              ; no partial byte
        mov     ecx,cPelsAtATime                 ; convert the 8 bits in the byte

copy_1_bp_same_byte:

        push    ecx                              ; save mask and byte
        out     dx,al                            ; set up the mask register
        shl     ah,1                             ; get the next oel into carry
        rcl     cl,1                             ; get the pel value
        and     cl,1                             ; only 1 bit significant
        xchg    al,cl                            ; save mask in cl, get pel in al
        xlat                                     ; get the converted value
        xchg    BYTE PTR [edi],al                ; write the pel
        mov     al,cl                            ; get the mask in al
        ror     al,1                             ; next bit position
        adc     edi,0                            ; update screen pointer if needed
        pop     ecx                              ; get back byte and mask
        loop    copy_1_bp_same_byte              ; convert the byte
        pop     ecx                              ; get back remaining bits
        mov     ah,al                            ; get back mask in ah
        sub     ecx,cPelsAtATime                 ; 8 or less were just converted
        jbe     copy_1_bp_done                   ; CX <= 0 means we have done all
        lodsb                                    ; get the next byte
        mov     cPelsAtATime,8                   ; do 8 bits in it
        jmp     copy_1_bp_loop                   ; continue in loop

        ALIGN   4

copy_1_bp_done:

        ret

;/***************************************************************************
;*
;* FUNCTION NAME = copy_4_bp_full 
;*
;* DESCRIPTION   = handles the blt of a scan or a part of it for 4 bits per 
;*                 pixel case                                               
;*                                                                          
;*                 Registers Destroyed:                                                          
;*                       AL,CX,flags                                                             
;*                                                                          
;* INPUT         = DS:SI  --  current byte in the map      
;*                 ES:DI  --  current byte in the screen    
;*                 AH     --  current bit position in the first byte 
;*                 GS:BX  --  address of color mapping table                                                            
;*                 CX     --  number of pels to convert 
;*                 DX     --  BIT MASK register address  
;*                                                                          
;* OUTPUT        = DS:SI,ES:DI --  next bytes in respective areas 
;*                 AH          --  updated bit mask    
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN   4

copy_4_bp_full::

        lodsb                                    ; process first byte separately
        test    bUnusedPel,1                     ; is just the low nibble to be used
        jnz     copy_4_bp_low_nibble             ; yes
        jmp     @F                               ; already fetch the 1st byte

        ALIGN   4

copy_4_bp_loop:

        lodsb                                     ; get the next source byte
@@:
        xchg    al,ah                             ; get the mask into al
        out     dx,al                             ; set up the pel mask for 1 pel
        xchg    al,ah                             ; get back byte in al
        push    eax                               ; save the byte
        shr     al,4                              ; get the high nibble into low pos
        xlat                                      ; get the mapping index
        xchg    BYTE PTR [edi],al                ; write the pel
        pop     eax                               ; get back pel
        ror     ah,1                              ; rotate mask
        jnc     copy_4_bp_next_nibble   ; process low nibble of source
        inc     edi                               ; position to next target byte

copy_4_bp_next_nibble:

        dec     ecx                               ; one more pel done
        ;SEL 1-27-92
        jecxz   copy_4_bp_done

copy_4_bp_low_nibble:

        xchg    al,ah                             ; get the mask into al
        out     dx,al                             ; set up the mask
        xchg    al,ah                             ; get back the pel in al
        and     al,0fh                            ; get the lower 4 bits
        xlat                                      ; get the mapping index
        xchg    BYTE PTR [edi],al                 ; write the pel
        ror     ah,1                              ; the next bit position in the mask
        jnc     copy_4_bp_next_byte               ; continue xfer
        inc     edi                               ; next target  byte

copy_4_bp_next_byte:

        loop    copy_4_bp_loop                    ; process all the remaining pels

copy_4_bp_done:

        ret

;/***************************************************************************
;*
;* FUNCTION NAME = copy_8_bp_full
;*
;* DESCRIPTION   = handles the blt of a scan or a part of it for 8 bits per
;*                 pixel case                                              
;*                                                                         
;*                 Registers Destroyed:   
;*                      AL,CX,flags      
;*
;* INPUT         = DS:SI     --  current byte in the map     
;*                 ES:DI     --  current byte in the screen    
;*                    AH     --  current bit position in the first byte 
;*                 GS:BX     --  address of color mapping table 
;*                    CX     --  number of pels to convert      
;*                    DX     --  BIT MASK register address     
;*
;* OUTPUT        = DS:SI,ES:DI -- next bytes in respective areas
;*                          AH -- updated bit mask  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN   4

copy_8_bp_full::

        lodsb                                     ; get the next source byte
        xchg    al,ah                             ; get the mask into al
        out     dx,al                             ; set up the mask for 1 pel
        xchg    al,ah                             ; get back the byte in al
        xlat                                      ; get the mapping index value
        xchg    BYTE PTR [edi],al                ; write the pel
        ror     ah,1                              ; rotate the mask for the next bit
        jnc     copy_8_bp_loop                           ; continue with same target byte
        inc     edi                               ; the next screen byte

copy_8_bp_loop:

        loop    copy_8_bp_full                    ; repeat till all bytes processed
        ret

;/***************************************************************************
;*
;* FUNCTION NAME = copy_24_bp_full
;*
;* DESCRIPTION   = handles the blt of a scan or a part of it for 24 bits per
;*                 pixel case                                              
;*                                                                         
;*                 Registers Destroyed:   
;*                      AL,BX,CX,flags   
;*
;* INPUT         = DS:SI     --  current byte in the map     
;*                 ES:DI     --  current byte in the screen    
;*                    AH     --  current bit position in the first byte 
;*                    CX     --  number of pels to convert      
;*                    DX     --  BIT MASK register address     
;*
;* OUTPUT        = DS:SI,ES:DI -- next bytes in respective areas
;*                          AH -- updated bit mask  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN   4

copy_24_bp_full::

        push    edx
        push    eax                              ; save the bytes to be destroyed
        lodsb                                    ; get the blue pel
        mov     dl,al                            ; have blue in dl
        lodsb                                    ; get green
        mov     ah,al                            ; have it in ah
        lodsb                                    ; get red in al
        xchg    dl,al                            ; DL = red, AH = green, AL = blue

        INVOKE  rgb_to_ipc                       ; do the mapping => AL = physical color

        pop     edx                              ; DH = current bit position
        mov     ah,dh                            ; move it to AH
        pop     edx                              ; get the other values back
        and     al,MM_ALL                        ; have just the index bits
        xchg    al,ah                            ; get the mask into al
        out     dx,al                            ; set up the mask for the bit
        xchg    al,ah                            ; get the mask back into ah
        xchg    BYTE PTR [edi],al                ; xfer the pel
        ror     ah,1                             ; select the next bit for mask
        jnc     copy_24_bp_loop                  ; screen will have same byte
        inc     edi                              ; go the next screen byte

copy_24_bp_loop:

        loop   copy_24_bp_full                   ; process all the other bytes
        ret

OEMDrawBits     ENDP

        end
