;*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 = STRDRAW.ASM
;*
;* DESCRIPTIVE NAME = VGA specific code for strblt
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/17/87
;*
;* DESCRIPTION  This module contains the ega specific code for the    
;*              strblt function and the ExtendedTextOut function.     
;*                                                                    
;* FUNCTIONS    output_o_rect
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   03/17/87                     Written by Walt Moore [waltm]  
;*   08/18/87                     Walt Moore [waltm] Added test of the disabled
;*                                flag.
;*   09/28/88                     Bob Grudem [bobgru] Fixed the way
;*                                special_bm_opaque_color was handled for mono
;*                                bitmaps.
;*****************************************************************************/

        .xlist
        include cmacros.inc
INCL_GRE_CLIP           equ     1
INCL_FONTFILEFORMAT     equ     1
        include pmgre.inc
        include driver.inc
        include display.inc
        include egafam.inc
        include egamemf.inc
        include strblt.inc
        include fontseg.inc
        .list

        ??_out  strdraw

        externFP InternalExtTextOut
        externFP far_exclude                ;Exclude area from screen

sBegin  Code
        assumes cs,Code
page

;/*
;**       Link time constants describing the size and color format
;**       that the EGA will be running in.
;*/

        externA SCREEN_CBSCAN           ;Screen width in bytes
        externA SCREEN_CX               ;Screen width in pixels

;/*
;**       Other functions required for strdraw.
;*/

        externNP color_bitmap_opaque
        externNP mono_bitmap_opaque
        externNP bitmap_opaque
        externNP comp_byte_interval
        externNP InitTonysBarNGrill     ;critsec.asm


sEnd    Code

sBegin  Code
        assumes cs,Code
        page

;/***************************************************************************
;*
;* FUNCTION NAME = output_o_rect 
;*
;* DESCRIPTION   =  Fill the opaque rectangle with the background color. 
;*
;*                  Registers Preserved:                
;*                        BP                            
;*                  Registers Destroyed:                
;*                        AX,BX,CX,DX,SI,DI,DS,ES,FLAGS 
;*
;* INPUT         = BL = excel flags          
;*                 stack frame as per strblt 
;*                 
;*                 
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame output_o_rect              ;Define strblt's frame

cBegin  <nogen>
cEnd    <nogen>

real_output_o_rect      proc    near

        mov     ax,seg_lp_surface       ;DS and ES will always point to the
        mov     ds,ax                   ;  segment of the destination
        assumes ds,nothing              ;  surface while in this routine
        mov     es,ax
        assumes es,nothing

        test    bl,IS_DEVICE
        jz      oor_not_ega

;/*
;**       This is the EGA.  Set up for opaquing the rectangle.
;*/

        mov     dx,EGA_BASE + SEQ_DATA
        mov     al,MM_ALL
        out     dx,al
        mov     dl,GRAF_ADDR
        mov     ah,byte ptr colors[BACKGROUND]
        and     ah,MM_ALL
        mov     al,GRAF_SET_RESET
        out16   dx,ax
        mov     ax,MM_ALL shl 8 + GRAF_ENAB_SR
        out16   dx,ax
        mov     ax,DR_SET shl 8 + GRAF_DATA_ROT
        out16   dx,ax
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax


oor_not_ega:
        shr     bl,2
        jnc     oor_exclusive_rects     ;No text, just output opaque rect
        errnz   TEXT_VISIBLE-00000010b

        test    accel,IS_OPAQUE         ;If in transparent mode, must always
        jz      oor_exclusive_rects     ;  opaque the rectangle

        shr     bl,1
        jc      oor_check_x_bounds      ;Skip Y range check
        errnz   BOUNDED_IN_Y-00000100b


;/*
;**       We have no idea if any of the text bounding box intersects
;**       with the opaque rectangle.  If the intersection is empty,
;**       then we can output the opaque rectangle using the same code
;**       for when there isn't any text.
;*/


oor_check_y_bounds:
        mov     ax,text_bbox.rcs_pts1.pts_y
        cmp     ax,o_rect.rcs_pts2.pts_y
        jge     oor_exclusive_rects
        mov     ax,text_bbox.rcs_pts2.pts_y
        cmp     ax,o_rect.rcs_pts1.pts_y
        jle     oor_exclusive_rects


;/*
;**       The rectangles overlap in Y.  Check to see if they overlap in X.
;*/

oor_check_x_bounds:
        mov     ax,text_bbox.rcs_pts1.pts_x
        cmp     ax,o_rect.rcs_pts2.pts_x
        jge     oor_exclusive_rects
        mov     ax,text_bbox.rcs_pts2.pts_x
        cmp     ax,o_rect.rcs_pts1.pts_x
        jnle    oor_inclusive_rects                ;Rectangles intersect somehow



;/*
;**       The rectangles are exclusive.  Just fill the opaque rectangle
;**       and exit.
;*/

oor_exclusive_rects:
        mov     bx,o_rect.rcs_pts2.pts_x
        mov     dx,o_rect.rcs_pts1.pts_x
        call    comp_byte_interval
        xchg    ax,cx                              ;Save first/last mask
        mov     ax,o_rect.rcs_pts1.pts_y           ;Compute height of box and leave
        mov     bx,o_rect.rcs_pts2.pts_y           ;  it in BX for the opaquing
        sub     bx,ax                              ;  subroutines
        mul     next_scan                          ;Add in Y component
        add     di,ax
        add     di,off_lp_surface       ;Add any original offset into surface
        xchg    ax,cx                   ;First/last masks need to be in AX
        mov     dx,next_scan            ;Need increment to next scan line
        jmp     opaque_routine

oor_an_exit:
        ret


;/*
;**     The rectangles overlap in some unknown manner.  Compute
;**     all the areas which need to be opaqued, and opaque them
;**     (easier said than done).
;**
;**
;**     First the area completely above and below the text bounding
;**     box will be opaqued.  The scanline information will be the
;**     same for both areas (same first/last masks and inner loop
;**     counts).  Remember that any of the areas could be null.
;**
;**
;**     heights
;**
;**             ------------------------------------  o_rect.rcs_pts1.pts_y
;**            |                                    |
;**      top   |          opaque rectangle          |
;**            |                                    |
;**            |     --------------------------     | text_bbox.rcs_pts1.pts_y
;**            |    |                          |    |
;**     middle |    |    text bounding box     |    |
;**            |    |                          |    |
;**            |     --------------------------     | text_bbox.rcs_pts2.pts_y
;**     bottom |                                    |
;**            |                                    |
;**             ------------------------------------  o_rect.rcs_pts2.pts_y
;**
;**
;**     For the top rectangle:
;**
;**             starting Y is o_rect.rcs_pts1.pts_y
;**             height is MAX(text_bbox.rcs_pts1.pts_y-o_rect.rcs_pts1.pts_y,0)
;**
;**     For the bottom rectangle:
;**
;**             starting Y is text_bbox.rcs_pts1.pts_y
;**             height is MAX(o_rect.rcs_pts2.pts_y-text_bbox.rcs_pts2.pts_y,0)
;**
;**     For the middle rectangle:
;**
;**             starting Y is o_rect.rcs_pts1.pts_y + height of top
;**             height is MAX(text_bbox.rcs_pts2.pts_y-starting Y,0)
;**
;*/


oor_inclusive_rects:
        mov     dx,text_bbox.rcs_pts1.pts_y        ;Compute opaque rect area above the
        mov     ax,o_rect.rcs_pts1.pts_y           ;  text bounding box
        sub     dx,ax
        sbb     cx,cx
        not     cx
        and     cx,dx                   ;CX = height of top, or 0
        jz      oor_comp_bottom
        mul     next_scan
        xchg    bx,ax                   ;Set BX = offset to start of top scan

oor_comp_bottom:
        mov     ax,text_bbox.rcs_pts2.pts_y     ;Compute opaque rect area below the
        mov     dx,o_rect.rcs_pts2.pts_y        ;  text bounding box
        sub     dx,ax
        sbb     si,si
        not     si
        and     si,dx                   ;SI = height of bottom, or 0
        jz      oor_comp_middle_y
        mul     next_scan               ;AX = offset to start of top scan

oor_comp_middle_y:
        mov     di,o_rect.rcs_pts1.pts_y           ;Add o_rect.rcs_pts1.pts_y and height of top
        add     di,cx                              ;  area to get middle Y

oor_see_if_top_or_bottom:
        mov     dx,si                   ;If both heights are 0, then
        or      dx,cx                   ;  no top and no bottom
        jz      oor_see_about_middle


;/*
;**       There is at least a top or a bottom.  Compute the scan interval
;**       and fill the areas.
;*/

        push    di                      ;Save middle area Y
        push    ax                      ;Save bottom area Y offset
        push    si                      ;Save bottom area height
        push    bx                      ;Save top    area Y offset
        push    cx                      ;Save top    area height
        mov     bx,o_rect.rcs_pts2.pts_x
        mov     dx,o_rect.rcs_pts1.pts_x
        call    comp_byte_interval
        mov     dx,next_scan            ;Set next scan increment incase bitmap
        add     di,off_lp_surface       ;Add any initial offset to bits/device
        pop     bx                      ;Get top area height
        pop     cx                      ;Get top area Y offset
        or      bx,bx
        jz      oor_see_about_bottom    ;There is no top area
        push    di                      ;Save X component of start address
        push    ax                      ;Save first/last mask
        add     di,cx                   ;--> first byte
        call    opaque_routine
        pop     ax                      ;Restore first/last mask
        pop     di                      ;Restore X component of start address

oor_see_about_bottom:
        pop     bx                      ;Get bottom area height
        pop     cx                      ;Get bottom area Y offset
        or      bx,bx
        jz      oor_no_bottom_area      ;There is no bottom area
        add     di,cx                   ;--> first byte
        call    opaque_routine

oor_no_bottom_area:
        pop     di                      ;Restore middle Y coordinate


        missing_code    <x picture goes here>

oor_see_about_middle:
        mov     ax,o_rect.rcs_pts2.pts_y        ;Bottom of middle bytes is
        mov     bx,text_bbox.rcs_pts2.pts_y     ;  minimum of these two
        smin_ax bx
        sub     ax,di                   ;Compute height of middle area
        jle     oor_a_return            ;This should never occur, but....
        mov     opaque_height,ax
        mov     ax,next_scan            ;Compute Y offset of middle start
        mul     di
        add     ax,off_lp_surface
        mov     temp_off_lp_bits,ax

        mov     dx,text_bbox.rcs_pts2.pts_x      ;Compute rhs interval
        mov     bx,o_rect.rcs_pts2.pts_x
        call    comp_byte_interval
        mov     bx,text_bbox.rcs_pts1.pts_x      ;For lhs interval computation
        mov     dx,o_rect.rcs_pts1.pts_x
        jc      oor_check_lhs_only      ;No rhs
        push    si                      ;Save inner loop count
        push    di                      ;Save X offset into scan
        push    ax                      ;Save first/last mask
        call    comp_byte_interval
        jnc     oor_output_both_sides   ;There was a left and a right


;/*
;**       There was only a rhs.  Restore the saved interval parameters
;**       and output the rectangle.
;*/

oor_process_rhs:
        pop     ax                      ;Restore first/last mask
        pop     di                      ;Restore X offset
        pop     si                      ;Restore inner loop count

oor_output_one_rect:
        add     di,temp_off_lp_bits     ;--> first byte
        mov     bx,opaque_height        ;Set height
        mov     dx,next_scan            ;Set index to next scanline
        jmp     opaque_routine          ;Invoke opaquing routine and exit



;/*
;**       There was only a lhs.  Compute the interval parameters
;**       and see if the text string was bounded in Y.  If it was,
;**       then any portion of the last byte of the lhs interval
;**       will have been handled by the string output code.
;*/

oor_check_lhs_only:
        call    comp_byte_interval      ;Compute lhs interval
        jc      oor_a_return            ;No interval exists, exit
        jmp     oor_output_one_rect

oor_a_return:
        ret


;/*
;**       The parameters for the lhs and rhs have been computed.
;**       If the lhs stops in the same byte that the rhs starts,
;**       then we'll want to combine the lhs last byte mask with
;**       the rhs first byte mask.
;*/

oor_output_both_sides:
        mov     cx,text_bbox.rcs_pts1.pts_x      ;Compute the byte address of the
        and     cl,11111000b                     ;  start and end of the text string
        mov     dx,text_bbox.rcs_pts2.pts_x
        and     dl,11111000b
        cmp     dx,cx
        jne     oor_output_both_anyway           ;Ends/starts in different bytes


;/*
;**       We have to combine the last byte mask of the lhs with the
;**       first byte mask of the rhs.  If one of the intervals consists
;**       only of the mask, we want to make it disappear.  Both the last
;**       byte mask for the lhs and the first byte mask for the rhs must
;**       exist.
;*/

oor_overlapping_middle:
        pop     bx                      ;Restore rhs first/last mask
        pop     dx                      ;Restore rhs X offset
        pop     cx                      ;Restore rhs inner loop count

        or      si,si                   ;If an inner loop for lhs, combine
        jnz     oor_combine_with_lhs    ;  rhs first mask with lhs last mask
        or      ah,ah                   ;If a last byte mask for lhs, combine
        jnz     oor_combine_with_lhs    ;  rhs first byte mask with it


;/*
;**       There is only a first byte on the lhs.  Combine it with the
;**       rhs byte.  This will eliminate the entire lhs.  We don't
;**       have to worry about there not being a rhs first byte mask
;**       (and therefore decrementing the destination pointer) since
;**       there must be one for the bytes to overlap.
;*/

        or      ax,bx                   ;Combine lhs mask with rhs masks
        mov     si,cx                   ;Set inner loop count
        mov     di,dx
        jmp     oor_output_one_rect

oor_combine_with_lhs:
        or      ah,bl                   ;Combine the masks
        xor     bl,bl                   ;Clear old mask
        inc     dx                      ;Increment rhs start offset
        push    cx                      ;Save inner loop count
        push    dx                      ;Save X offset into scan
        push    bx                      ;Save first/last mask

oor_output_both_anyway:
        call    oor_output_one_rect     ;Do whatever for lhs
        jmp     oor_process_rhs         ;Do whatever for rhs

real_output_o_rect      endp
        page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  reset_ega
;*
;* DESCRIPTION   = Reset the card
;*
;*                 Registers Preserved:       
;*                       None                 
;*                 Registers Destroyed:       
;*                       AL,DX                
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

                public  reset_ega
reset_ega       proc   near

        mov     al,MM_ALL               ;Enable all planes
        mov     dx,EGA_BASE+SEQ_DATA
        out     dx,al
        mov     ax,GRAF_ENAB_SR         ;Disable set/reset
        mov     dl,GRAF_ADDR
        out16   dx,ax
        mov     ax,DR_SET shl 8 + GRAF_DATA_ROT
        out16   dx,ax
        mov     ax,0FF00h+GRAF_BIT_MASK ;Enable all bits
        out16   dx,ax
        ret

reset_ega       endp
        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  ega_opaque  
;*
;* DESCRIPTION   = Fill the given bitmap with the background color 
;*
;*                 Registers Preserved:                  
;*                       SI,DS,ES,BP                     
;*                 Registers Destroyed:                  
;*                       AX,BX,CX,DX,DI,FLAGS            
;*
;* INPUT         = AL = first byte mask  
;*                 AH = last byte mask   
;*                 BX = height           
;*                 SI = inner loop count 
;*                 ES:DI --> destination 
;*                 DS:DI --> destination 
;*
;*                 EGA programmed for:                  
;*                         Set/Reset enabled            
;*                         Set/Reset = background color 
;*                         M_PROC_WRITE, M_DATA_READ    
;*                         DR_SET                       
;*                         All planes enabled           
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

                public ega_opaque
ega_opaque      proc near

        cld
        or      ax,ax                   ;If no first or last byte
        jz      eo_start_inner_loop     ;  then only inner loop exists
        mov     cx,di                   ;If there is a first byte, then
        cmp     al,1                    ;  increment destination pointer
        sbb     cx,-1                   ;  for the inner loop.
        push    cx                      ;Save inner loop start addr
        xchg    ah,al                   ;Need mask in AH
        or      ah,ah                   ;Does first partial byte exist?
        jz      eo_not_this_byte        ;Byte doesn't exist

eo_next_partial:
        mov     cx,ax                   ;Save last byte mask
        mov     dx,EGA_BASE+GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out16   dx,ax
        xchg    ax,cx                   ;Restore last byte mask
        mov     dx,di                   ;Save starting address for right side
        mov     cx,bx                   ;Set height

eo_partial_loop:
        xchg    [di],ah
        add     di,SCREEN_CBSCAN
        loop    eo_partial_loop

;/*
;**       If there was a left side partial byte, then to get to the
;**       right side partial byte, we need to add InnerLoopCount+1.
;**       to the original offset.  If there wasn't a left side byte,
;**       we only want to add InnerLoopCount.
;**
;**       So, we increment the address by one assuming that we just
;**       came through the loop for the first byte.  If this is the
;**       second time through the loop, it doesn't matter that we
;**       incremented the address.
;*/

        mov     di,dx                   ;Restore first byte address
        inc     di                      ;Incase this was first byte

eo_not_this_byte:
        add     di,si                   ;--> last byte
        and     ax,00FFh                ;Zero this byte's mask
        xchg    ah,al                   ;Need mask in AH
        jnz     eo_next_partial         ;Looks like a partial last byte
        pop     di                      ;Restore inner loop start address

eo_start_inner_loop:
        or      si,si                   ;SI = inner loop count
        jz      eo_done                 ;No inner loop
        mov     dx,EGA_BASE+GRAF_ADDR   ;Enable all bits for alteration
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax
        mov     dx,SCREEN_CBSCAN        ;Compute index to next scan
        sub     dx,si

eo_inner_loop:
        mov     cx,si
        rep     stosb
        add     di,dx
        dec     bx
        jnz     eo_inner_loop

eo_done:
        ret

ega_opaque      endp
        page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_device_data  
;*
;* DESCRIPTION   = All device specific local variables are intiialized.           
;*                                                                                
;*                 This function must be called after both get_clip and            
;*                 quick_clip (when the character count is positive) have been     
;*                 called.  This requirement is because of the manner in which     
;*                 huge bitmaps are handled), and for cursor exclusion.            
;*                                                                               
;*                 Registers Preserved:                                                                                   
;*                       BP                                                                                               
;*                 Registers Destroyed:                                                                                   
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS                                                                    
;*                                                                               
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        assumes ds,nothing
        assumes es,nothing

v_gdd_dev_is_bitmap:
        jmp     gdd_dev_is_bitmap

gdd_device_isnt_enabled:
        stc
        jmp     gdd_exit

                public get_device_data
get_device_data proc near

        lds     si,lp_device
        test    [si].sd_fb,SD_DEVICE
        jz      v_gdd_dev_is_bitmap
        or      excel,IS_DEVICE

        mov     ax,ds:[si].sd_pBits.off
        mov     off_lp_surface,ax
        mov     ax,ds:[si].sd_pBits.sel
        mov     seg_lp_surface,ax

;/*
;**       This is the display.  Exclude the cursor from the union of
;**       the opaque rectangle and the estimated text bounding box.
;**
;**       Because of the approximation technique used for the text
;**       bounding box, text X coordinates must be clipped to the
;**       EGA's physical dimensions.
;**
;**       Exclusion code expects:
;**               (cx) = left
;**               (dx) = top
;**               (si) = right  (inclusive)
;**               (di) = bottom (inclusive)
;*/


        mov     bl,excel
        test    bl,TEXT_VISIBLE             ;If no visible text, then opaque
        jz      gdd_exclude_is_opaque       ;  rect is the exclusion area
        test    bl,OPAQUE_RECT              ;If no opaque rectangle
        jz      gdd_exclude_text_or_both    ;  clip to text bbox
        test    eto_options,ETO_OPAQUE_CLIP ;If text isn't clipped to opaque
        jz      gdd_exclude_text_or_both    ;  rect, clip to the union

gdd_exclude_is_opaque:
        mov     cx,o_rect.rcs_pts1.pts_x
        mov     dx,o_rect.rcs_pts1.pts_y
        mov     si,o_rect.rcs_pts2.pts_x
        mov     di,o_rect.rcs_pts2.pts_y
        jmp     short gdd_exclude_have_rect


gdd_exclude_text_or_both:
        mov     ax,text_bbox.rcs_pts1.pts_x       ;Clip text left to screen left
        cwd                                       ;DX = FFFF if < 0
        not     dx                                ;DX = 0000 if < 0, FFFF if greater
        and     ax,dx                             ;AX = min(AX,0)
        xchg    cx,ax
        mov     ax,text_bbox.rcs_pts2.pts_x       ;Clip text right to screen right
        smin_ax  SCREEN_CX
        xchg    ax,si                             ;SI is now rhs
        mov     dx,text_bbox.rcs_pts1.pts_y
        mov     di,text_bbox.rcs_pts2.pts_y
        test    bl,OPAQUE_RECT
        jz      gdd_exclude_have_rect


;/*
;**       We have to exclude the union of the opaque rectangle and
;**       the text bounding box.
;*/

        mov     bx,dx                   ;BX = text_bbox.rcs_pts1.pts_y
        mov     ax,o_rect.rcs_pts1.pts_y
        smin_ax bx
        xchg    bx,ax
        mov     ax,o_rect.rcs_pts1.pts_x
        smin_ax cx
        xchg    ax,cx
        mov     ax,o_rect.rcs_pts2.pts_x
        smax_ax si
        xchg    si,ax
        mov     ax,o_rect.rcs_pts2.pts_y
        smax_ax di
        xchg    ax,di
        mov     dx,bx

gdd_exclude_have_rect:
        dec     si                      ;Want rhs and bottom to be inclusive
        dec     di
        call    far_exclude


;/*
;**       The exclusion has been taken care of.  Now initialize
;**       the frame variables for the EGA.  If there is an opaquing
;**       rectangle to be output, then initialize the EGA for doing
;**       it.
;*/

        mov     ax,SCREEN_CBSCAN        ;Compute adjustment from bottom
        mov     next_scan,ax            ;  of character to top of
        mul     clipped_font_height     ;  next character
        dec     ax
        mov     cell_adjust,ax
        mov     opaque_routine,CodeOFFSET ega_opaque
        mov     es,seg_lp_surface
        assumes es,EGAMem

        mov     bx,colors
        and     bx,MM_ALL shl 8 + MM_ALL
        mov     colors,bx

;/*
;**       Initialize Tony's Bar -n- Grill to the background color.
;*/

        mov     dx,EGA_BASE + GRAF_ADDR
        cCall   InitTonysBarNGrill
        mov     cx,1
        clc
        ret


;/*
;**       This is a memory bitmap.  Initialize the frame variables for
;**       either a color or a monochrome bitmap.
;*/

        assumes ds,nothing
        assumes es,nothing


gdd_dev_is_bitmap:
        test    [si].sd_fb,SD_HUGE      ;If huge bitmap,    s non-zero,
        jnz     gdd_huge_bitmap         ;  then handle separately

gdd_small_bitmap:
        mov     ax,[si].sd_pBits.sel
        mov     seg_lp_surface,ax

gdd_finish_bitmap:                      ;Huge bitmap code can jump here
        mov     ax,[si].sd_pBits.off
        mov     off_lp_surface,ax
        mov     ax,[si].sd_cbScan
        mov     next_plane,ax
        mov     ax,[si].sd_dScan
        mov     next_scan,ax
        mul     clipped_font_height     ;Compute adjustment from bottom
        dec     ax                      ;  of character to top of
        mov     cell_adjust,ax          ;  next character

        mov     dx,CodeOFFSET color_bitmap_opaque
        mov     cx,BITS_PEL
        .errnz  high BITS_PEL
        mov     ax,colors
        test    [si].sd_fb,SD_COLOR
        jnz     gdd_is_color_bitmap
        mov     cl,1                    ;1 plane
        mov     dx,CodeOFFSET mono_bitmap_opaque
        shr     ax,4                    ;Bitmap routines expect the mono bit
        errnz   MONO_BIT-00010000b      ;  in D0
        mov     colors,ax

gdd_is_color_bitmap:
        mov     opaque_routine,dx
        mov     num_planes,cl

;/*
;**       Create a more useful form of the background/foreground color:
;**
;**       XOR the two colors together.  This will give a 1 wherever
;**       the colors are different.  This 1 (sign extended) will be
;**       used as an AND mask with the character pattern.  The result
;**       of this will be XORed with the background color to give the
;**       destination byte.
;*/

        mov     dl,accel                
        test    dl,IS_XOR_TEXT          
        jnz     gdd_cbm_get_color       
        test    dl,IS_OR_TEXT
        jnz     gdd_cbm_set_or

gdd_cbm_xor_color:
        xor     al,ah

        mov     dl,cl                   ; save number of planes
gdd_cbm_get_color:
        shr     al,1
        rcr     bl,1
        shr     ah,1
        rcr     bl,1
        loop    gdd_cbm_get_color
        shr     dl,1                    ; Is number of planes odd (i.e. 1)?
        jnc     gdd_cbm_store_special_color
        shr     bl,6                    ; move colors to low bits if mono
gdd_cbm_store_special_color:
        mov     special_bm_opaque_color,bl
        mov     cx,1
        clc
        ret

gdd_cbm_set_or:
        or      al,ah                   ;OR background into foreground
        jmp     gdd_cbm_xor_color


;/*
;**       This is a huge bitmap.  If the text or opaque rectangle
;**       spans a segment boundary, then we'll recurse (curse?) to
;**       handle the string, setting a clipping rectangle which is
;**       appropriate for each 64K segment of the string.
;**
;**       Both the opaque rectangle and the text bounding box have
;**       already been clipped in Y to the actual clipping rectangle,
;**       so we are guaranteed not to overflow the real clipping
;**       rectangle's Y range.
;*/

gdd_huge_bitmap:
        mov     al,excel                ;If we have both, then the union
        not     al                      ;  of the two will be needed
        and     al,OPAQUE_RECT+TEXT_VISIBLE
        mov     ax,text_bbox.rcs_pts1.pts_y
        mov     bx,text_bbox.rcs_pts2.pts_y
        mov     cx,o_rect.rcs_pts1.pts_y
        mov     di,o_rect.rcs_pts2.pts_y
        jnz     gdd_dont_do_union       ;Only have one rectangle
        smin_ax cx
        xchg    ax,bx
        smax_ax di
        xchg    ax,bx                   ;AX = top, BX = bottom
        jmp     short gdd_have_y_ext

gdd_dont_do_union:
        test    excel,TEXT_VISIBLE      ;Y top and bottom for text only
        jnz     gdd_have_y_ext          ;  is already set in AX, BX
        xchg    ax,cx                   ;Need Y top and bottom for the
        xchg    bx,di                   ;  opaque rectangle

gdd_have_y_ext:
        mov     clip.rcs_pts2.pts_y,bx          ;Save bottom and top incase
        mov     clip.rcs_pts1.pts_y,ax             ;  we cross a segment
        mov     di,[si].sd_cySeg        ;#  of scanlines in each segment
        xor     dx,dx                   ;Compute segment and starting
        div     di                      ;  scan of top line
        xchg    ax,bx
        mov     cx,dx
        xor     dx,dx                   ;Compute segment and scan of the
        dec     ax                      ;  last line (want the calc to be
        div     di                      ;  inclusive of last line).

;/*
;**       We now have the following information:
;**
;**           BX = segment number of the starting scan line
;**           CX = starting scan line within the segment
;**           AX = segment number of the last scan line
;**           DX = ending scan line within the segment
;**           DI = # scans per segment
;**
;**       If everything is contained within one segment, then we blow
;**       off the recursion and just output the stuff.  If seperate
;**       segments are involved, we dummy up a clip rectangle and
;**       call ourselves again.  If only one segment is involved,
;**       we just alter the Y values a little to adjust for the actual
;**       segment, and pretend we're a small bitmap.
;*/

        cmp     ax,bx
        jne     gdd_different_segments
        or      ax,ax                   ;If in first segment, don't need
        jz      gdd_have_segment        ;  to adjust Y values
        mul     di                      ;Compute the number of scans to
        sub     text_bbox.rcs_pts1.pts_y,ax ;  subtract off of the original
        sub     text_bbox.rcs_pts2.pts_y,ax ;  Y's to bring them within the
        sub     o_rect.rcs_pts1.pts_y,ax    ;  allowable range for this segment
        sub     o_rect.rcs_pts2.pts_y,ax
        mov     ax,[si].sd_selIncr      ;Compute the adjustment to the
        mul     bx                      ;  selector

gdd_have_segment:
        add     ax,[si].sd_pBits.sel    ;Save pointer to the bits
        mov     seg_lp_surface,ax
        jmp     gdd_finish_bitmap


gdd_different_segments:
        push    clip.rcs_pts2.pts_y             ;Save until we need it

;/*
;**       We now have the following information:
;**
;**       BX = segment number of the starting scan line
;**       CX = starting scan line within the segment
;**       AX = segment number of the last scan line
;**       DX = ending scan line within the segment
;**       DI = # scans per segment
;*/

        sub     ax,bx                   ;Number of times we'll make the call
        xchg    ax,si                   ;Keep count in SI
        xchg    ax,bx                   ;Get segment index of first scan
        inc     ax
        mul     di                      ;Compute end of first segment

gdd_do_next_whole_segment:
        mov     clip.rcs_pts2.pts_y,ax

gdd_do_last_segment:

        lea     ax,clip
        farPtr  lp_new_clip_rect,ss,ax
        mov     cl,1                    ;String translation flag - translate
        arg     lp_DDC                  ;DDC
        arg     lp_device               ;Destination device
        arg     x                       ;Left origin of string
        arg     y                       ;Top  origin of string
        arg     lp_new_clip_rect        ;Clipping rectangle
        arg     lp_string               ;The string itself
        arg     count                   ;Number of characters in the string
        arg     npAttrs                 ;Attribute bundle to use
        arg     lp_dx                   ;Widths for the characters
        arg     lp_opaque_rect          ;Opaquing rectangle
        arg     eto_options             ;ExtTextOut options
        cCall   InternalExtTextOut


;/*
;**       If all went well, the first portion of the text has been
;**       displayed.  Let's just keep doung it until we've done all
;**       remaining segments.
;*/

        mov     ax,clip.rcs_pts2.pts_y          ;Set start of next segment
        mov     clip.rcs_pts1.pts_y,ax
        add     ax,di
        dec     si
        jg      gdd_do_next_whole_segment


;/*
;/**       A little trick here folks.  The first time we'll pop the real
;/**       bottom scan line.  The second time, we'll pop the return address.
;*/

        pop     clip.rcs_pts2.pts_y             ;Set last scan
        jz      gdd_do_last_segment
        push    clip.rcs_pts2.pts_y
        xor     cx,cx
        clc
gdd_exit:
        ret
get_device_data endp

sEnd    Code
        end
