;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; 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 = STRBLTC.ASM
;*
;* DESCRIPTIVE NAME = Common routines from strblt.asm
;*
;*
;* VERSION      V2.0
;*
;* DATE         09/01/88
;*
;* DESCRIPTION  This module contains common routines and declarations from
;*              STRBLT.ASM
;*
;* FUNCTIONS    TotallyBogusCProc
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   10/01/88                     Written by Robert J. Carragher
;*   ??/??/92              53536  PRK
;*
;*****************************************************************************/


        .386

        .xlist

        OPTION  OLDSTRUCTS

INCL_GRE_CLIP           equ     1
INCL_FONTFILEFORMAT     equ     1
INCL_GRE_FONTS          equ     1

        include pmgre.inc

DINCL_ROPS              equ     1
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1

        include driver.inc
        include fontmap.inc
        include strblt.inc
        include assert.mac
        include extern.inc
        include protos.inc

        .list

        .MODEL FLAT

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

        .DATA

;/*
;**       These functions are found in STRBLT.ASM and are driver dependent
;**
;**       abc_extent
;**       worst_case_ext
;**
;**       These two tables are used to map the foreground and background
;**       mix modes onto flags to set in the accel flags variable.
;**
;**       If the foreground mix mode is leavealone then we do some hacks:
;**
;**           if the background is leavealone, then we set the ROP_IS_NOP
;**           flag in wc_flags, which lets everything happen except drawing
;**           text;
;**
;**           if the background is overpaint, then we set the foreground
;**           mode to overpaint (by virtue of not setting any flags), and
;**           set the foreground color to the background color  --  this
;**           causes invisible text.
;*/

        EXTERN  abc_extent:NEAR
        EXTERN  worst_case_ext:NEAR

char_fore_mix   label   byte

        db      IS_OR_TEXT                        ;CHAR_ROP_DPO
        db      0                                 ;CHAR_ROP_P
        db      0                                 ;CHAR_ROP_D -- looks like CHAR_ROP_P
        db      IS_XOR_TEXT                       ;CHAR_ROP_DPX

char_back_mix   label   byte

        db      0                                 ;BA_IS_XPARENT
        db      IS_OPAQUE                         ;BA_IS_OPAQUE
        db      0                                 ;this should never be used!
        db      0                                 ;this should never be used!

dwTranslateStk  DWORD   0

dwTranslateBuf  DWORD   1024 dup (0)

pTranslateBuf   equ     $

XLATE_BUF_LEN   equ     size dwTranslateBuf / 4

        .CODE

page

;/***************************************************************************
;*
;* FUNCTION NAME = TotallyBogusCProc
;*
;* DESCRIPTION   = Creates the frame which several procs below reference.  No
;*                 code is actually generated, just the definitions, hence the
;*                 name "Totally- BogusCProc."
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

TotallyBogusCProc PROC SYSCALL, lp_DDC        :DWORD, lp_DestDev :DWORD,
   locx             :DWORD,
   locy             :DWORD,
   lp_clip_rect  :DWORD,
   lp_string     :DWORD,
   count         :DWORD,
   npAttrs       :DWORD,
   lp_dx         :DWORD,
   lp_opaque_rect:DWORD,
   eto_options   :DWORD
        LOCAL   accel           :BYTE
        LOCAL   excel           :BYTE
        LOCAL   wc_flags        :BYTE
        LOCAL   fbTranslate     :BYTE
        LOCAL   colors          :DWORD
        LOCAL   char_extra      :DWORD
        LOCAL   num_null_pixels :DWORD
        LOCAL   null_char_offset:DWORD
        LOCAL   lhs             :DWORD
        LOCAL   rhs             :DWORD
        LOCAL   concat          :DWORD
        LOCAL   pFont           :DWORD
        LOCAL   lfd             :local_font_def
        LOCAL   clip            :RECTL
        LOCAL   text_bbox       :RECTL
        LOCAL   o_rect          :RECTL
        LOCAL   smart_flags            :BYTE
        LOCAL   special_bm_opaque_color:BYTE
        LOCAL   num_planes             :BYTE
        LOCAL   left_clip_mask         :BYTE
        LOCAL   right_clip_mask        :BYTE
        LOCAL   cell_adjust            :DWORD
        LOCAL   buffer                 :DWORD
        LOCAL   fix_total_bytes        :DWORD
        LOCAL   next_scan              :DWORD
        LOCAL   next_plane             :DWORD
        LOCAL   next_scan_xor          :DWORD
        LOCAL   opaque_routine         :DWORD
        LOCAL   opaque_height          :DWORD
        LOCAL   lp_surface             :DWORD
        LOCAL   temp_off_lp_bits       :DWORD
        LOCAL   scan_start             :DWORD
        LOCAL   clipped_font_height    :DWORD
        LOCAL   amt_clipped_on_top     :DWORD
        LOCAL   clear_stack            :DWORD
        LOCAL   min_stack              :DWORD
        LOCAL   build_proc             :DWORD
        LOCAL   phase                  :DWORD
        LOCAL   x2                     :DWORD
        LOCAL   inner_loop             :DWORD
        LOCAL   clipped_table          :DWORD
        LOCAL   non_clipped_table      :DWORD
        LOCAL   inner_byte_count       :DWORD
        LOCAL   current_lhs            :DWORD
        LOCAL   current_rhs            :DWORD
        LOCAL   wc_opaque_lhs          :DWORD
        LOCAL   wc_opaque_rhs          :DWORD
        LOCAL   string_start_bias      :DWORD
        LOCAL   ret_addr               :DWORD
        LOCAL   unrolled_entry_point   :DWORD
        LOCAL   vect_one_in_first      :DWORD
        LOCAL   vect_two_in_first      :DWORD
        LOCAL   vect_one_in_middle     :DWORD
        LOCAL   vect_two_in_middle     :DWORD
        LOCAL   vect_one_in_last       :DWORD
        LOCAL   vect_two_in_last       :DWORD
        LOCAL   ret_addr_2             :DWORD
        LOCAL   ega_oc_saved_data      :DWORD
        LOCAL   current_banks                           :byte
        LOCAL   bitmap_type                             :byte

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  quick_clip
;*
;* DESCRIPTION   = Quick Clip Test
;*
;*                 A quick test is made to see if the given string is entirely
;*                 clipped, possibly clipped, or completely visible.
;*
;*                 An entirely clipped string can be discarded immediately.  A
;*                 completely visible string can skip the clipping code while
;*                 the stack data is being constructed.  A possibly clipped
;*                 string may or may not be clipped.  The clipping code must
;*                 be run while the stack data is being constructed in this
;*                 case.
;*
;*                 At the same time, the string's bounding box minimum left and
;*                 maximum right edges are computed and saved.  This will be used
;*                 by the cursor exclusion code.
;*
;*                 Registers Preserved:
;*                       None
;*                 Registers Destroyed:
;*                       AX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         =  BH = accel flags
;*                  BL = excel flags
;*
;* OUTPUT        = BH = new accel flags
;*                 BL = excel flags
;*                 text_bbox structure initialized if any text is visible
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


                public  quick_clip

quick_clip::

;/*
;**
;**                                                   ----
;**                                                      | compute how many scans are in
;**       stuff here is clipped                           | this area.  These scans are
;**                                                       | clipped out
;**                                                       |
;**        -----------------------   ----
;**       | top                                    |      | these scans fit
;**       |                                        |  ----
;**       |                                        |
;**       |   clip rectangle                       |
;**       |                                        |
;**       |                                        |
;**       | bottom                                 |
;**        -----------------------
;**
;**
;**       stuff here is clipped
;**
;**
;*/


        xor     edx,edx                           ;Amount clipped on top is 0
        mov     ecx,locy                          ;Need starting Y coordinate
        movzx   edi,lfd.font_height              ;  and height of the font
        mov     eax,ecx                           ;Don't destroy Y coordinate
        sub     eax,clip.rcl_yBottom             ;Is Y clipped on top?
        jge     quick_clip_y_fits_on_top;  No, Y fits within top

;/*
;** The Y coordinate is less than the top of the clip rectangle. Move
;** the Y coordinate down the screen (+Y) by the amount clipped. If the
;** amount clipped is >= the font height, then the entire string is clipped
;** and can be discarded.
;*/

        add     edi,eax                           ;Subtract clip overhang from height
        jle     quick_clip_doesnt_show           ;Nothing shows, exit now
        sub     ecx,eax                           ;Offset starting Y coordinate
        neg     eax
        xchg    eax,edx

quick_clip_y_fits_on_top:

        mov     text_bbox.rcl_yBottom,ecx        ;Save new Y coordinate
        mov     amt_clipped_on_top,edx            ;Save for output routines

;/*
;**
;**       The starting Y has been computed.  Compute how many scans must
;**       be clipped off the bottom of the font.   The clipping can be done
;**       by adjusting the height.
;**
;**       stuff here is clipped
;**
;**
;**        -----------------------
;**       | top                                    |
;**       |                                        |
;**       |                                        |
;**       |   clip rectangle                       |
;**       |                                        |
;**       |                                        |  ----
;**       | bottom                                 |      | these scans fit
;**        -----------------------   ----
;**                                                       | compute how many scans are in
;**                                                       | this area.  These scans are
;**       stuff here is clipped                           | clipped out
;**                                                       |
;**                                                   ----
;**
;*/

        mov     eax,ecx                   ;Leave CX = text_bbox.rcl_yBottom
        add     eax,edi                   ;Compute text_bbox.rcl_yTop
        mov     edx,eax
        sub     eax,clip.rcl_yTop                ;Is Y clipped on the bottom?
        jl      quick_clip_have_y_ext            ;  No, height fits within clip rect
        sub     edi,eax                           ;Clip height
        jle     quick_clip_doesnt_show           ;Nothing shows, exit
        sub     edx,eax                           ;Clip bottom of text box


;/*
;** The string has been clipped in Y, and at least one scan will be visible.
;*/

quick_clip_have_y_ext:

        mov     clipped_font_height,edi          ;Save height of what will be drawn
        mov     text_bbox.rcl_yTop,edx

;/*
;** If the vertical portion of the text fits within the opaque rectangle,
;** then set the BOUNDED_IN_Y flag for the code which creates the stack
;** data.  When this flag is set, that code should attempt to fill to byte
;** boundaries.
;*/

        test    bl,OPAQUE_RECT
        jz      quick_clip_do_x
        cmp     edx,o_rect.rcl_yTop
        jg      quick_clip_do_x                  ;Text extends below opaque bottom
        cmp     ecx,o_rect.rcl_yBottom
        jl      quick_clip_do_x                  ;Text extends above opaque top
        or      bl,BOUNDED_IN_Y


;/*
;**
;**       Clip the string in X.  An attempt will be made to detect
;**       the following five cases
;**
;**                -----------------------
;**               |                                 |
;**               |                                 |
;**    case1   case2       case3                         case4   case5
;**               |                                 |
;**               |   clip rectangle               |
;**               |                                 |
;**               |                                 |
;**               |                                 |
;**                -----------------------
;**              left                                    right
;**
;**
;*/

;/*
;** Slime-of-hand here.  If the lhs is negative, it means that the font
;** had abc spacing and the first "a" space was negative.  We'll adjust
;** the starting X if we detect this.
;*/

quick_clip_do_x:

        mov     edi,locx
        add     edi,lhs                           ;Will be 0 or negative
        mov     locx,edi
        mov     text_bbox.rcl_xLeft,edi ;Bounding box left hand side
        mov     ecx,clip.rcl_xRight
        cmp     edi,ecx
        jge     quick_clip_doesnt_show           ;Case 5
        mov     eax,rhs
        add     eax,edi                           ;AX = maximum right hand side
        movsx   eax,ax
        js      peg_out_at_max_int

;/*
;** If the starting X is less than the left side of the clip rectangle,
;** then the test will be made for cases 1,2, and 4.  If the starting X
;** is within the clip rectangle, then the test will be made for cases
;** 3 or 4.
;*/

quick_clip_have_right_x:

        mov     edx,clip.rcl_xLeft
        cmp     edi,edx
        jge     quick_clip_case_3_or_4

quick_clip_case_1_or_2:

        cmp     eax,edx                           ;Crossing into the left side?
        jl      quick_clip_doesnt_show           ;Case 1
        or      bl,CLIPPED_LEFT                  ;Show clipped on left side
        mov     text_bbox.rcl_xLeft,edx ;Set new lhs for text

quick_clip_case_3_or_4:

        cmp     eax,ecx                           ;Overhanging the right side?
        jl      quick_clip_exit                  ;  No
        or      bl,CLIPPED_RIGHT                 ;Right clipping needed

quick_clip_exit:

        or      bl,TEXT_VISIBLE                  ;Some part of the text may show
        mov     text_bbox.rcl_xRight,eax ;Save bbox right hand side

quick_clip_doesnt_show:

        ret

;/*
;** We handle the case where the string extent is unknown by setting the
;** left and right hand sides of the strings to the most negative and most
;** positive numbers and falling through the normal case code.
;*/

peg_out_at_max_int:

        mov     eax,MAXSHORT                      ;Make rhs as large as possible
        jmp     quick_clip_have_right_x

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE   comp_byte_interval
;*
;* DESCRIPTION   = A interval will be computed for byte boundaries.  A first
;*                 mask and a last mask will be calculated, and possibly
;*                 combined into the inner loop count.  If no first byte
;*                 exists, the start address will be incremented to adjust for
;*                 it.
;*
;*                 Registers Preserved:
;*                       ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,FLAGS
;*
;* INPUT         = BX    = right coordinate (exclusive)
;*                 DX    = left coordinate  (inclusive)
;* OUTPUT        = 'C' clear
;*                 DI    = offset to first byte to be altered in the scan
;*                 SI    = inner loop count
;*                 AL    = first byte mask (possibly 0)
;*                 AH    = last  byte mask (possibly 0)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        public  comp_byte_interval

comp_byte_interval::


        sub     ebx,edx                           ;Compute extent of interval
        jle     return_null_interval             ;Null interval, skip it
        dec     ebx                               ;Make interval inclusive
        mov     edi,edx                           ;Don't destroy starting X

        test    excel,IS_DEVICE                   ;Only preprocess if this is
        jnz     @f              ;          
        cmp     bitmap_type,8   ;          
        jne     cbi_001         ;          
@@:                             ;          
        mov     esi,ebx         ;          
        inc     esi             ;          
        xor     eax,eax         ;          
        clc                     ;          
        ret                     ;          
cbi_001:
        shr     edi,3                             ;/8 for byte address

;/*
;**       We now have to determine how many bits will be affected,
;**       and how they are aligned within the bytes.
;**
;**       (left_x MOD word_size) will give us the starting pixel
;**       within the left byte/word.  Adding the inclusive extent
;**       of the interval to left_x MOD word_size) and taking the
;**       result MOD word_size will give us the last pixel affected
;**       in the last byte/word.                   These pixel indexes (0:7 for bytes,
;**       0:15 for words), can be used to create the first and last
;**       altered bits mask.
;**
;**
;**       To compute the number of bytes/words in the inner loop,
;**       use the second calculation above
;**
;**               (left_x MOD word_size) + inclusive_extent
;**
;**       and divide it by the word size (8/16).   This gives you
;**       the following:
;**
;**
;**           1)  If the result is 0, then only one destination
;**               byte/word is being altered.  In this case, the
;**               start & ending masks should be ANDed together,
;**               the innerloop count set to zero, and last_mask
;**               set to to all 0's (don't alter any bits).
;**
;**                       |      x x x x x|         |
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 3,  extent-1 = 4
;**                       3+7 DIV 8 = 0, only altering one byte
;**
;**
;**
;**           2)  If the result is 1, then only two bytes/words
;**               will be altered.  In this case, the start and
;**               ending masks are valid, and all that needs to
;**               be done is set the innerloop count to 0.
;**
;**                       |  x x x x x x x|x x x x x x x|
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 1,  extent-1 = 14
;**                       3+14 DIV 8 = 1.  There is a first and last
;**                       byte but no innerloop count
;**
;**
;**
;**           3)  If the result is > 1, then some number of entire
;**               bytes/words will be altered by the innerloop.  In
;**               this case the number of innerloop bytes/words will
;**               be the result - 1.
;**
;**                       |                               x|x x x x x x x x|x
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 7,  extent-1 = 9
;**                       7+9  DIV 8 = 2.  There is a first and last
;**                       byte and an innerloop count of 1 (result - 1)
;*/


;/*
;**       Compute the starting bit position on the left and the ending
;**       bit position on the right
;*/


        and     dl,00000111b                      ;Compute bit index for left side
        movzx   edx,dl
        add     ebx,edx                           ;Compute bit index for right side
        mov     esi,ebx                           ;(save for inner loop count)
        and     bl,00000111b
        mov     cl,dl                             ;Compute left side altered bits mask
        mov     eax,-1
        mov     edx,eax                           ;Need this here later
        shr     al,cl                             ;AL = left side altered bytes mask
        mov     cl,bl                             ;Compute right side altered bits mask
        mov     ah,80h
        sar     ah,cl                             ;AH = right side altered bits mask
        shr     esi,3                             ;Compute inner byte count
        jnz     comp_byte_dont_combine           ;loop count + 1 > 0, check it out

;/*
;**       Only one byte will be affected.  Combine the first and
;**       last byte masks, and set the loop count to 0.
;*/

        and     al,ah                             ;Will use first byte mask only
        movzx   eax,al                            ;Want last byte mask to be 0
        inc     esi                               ;Fall through to set 0

comp_byte_dont_combine:

        dec     esi                               ;Dec inner loop count (might become 0)

;/*
;**       If all pixels in the first byte are altered, combine the
;**       first byte into the inner loop and clear the first byte
;**       mask.  Ditto for the last byte
;*/

        cmp     al,dl                             ;Set 'C' if not all pixels 1
        sbb     esi,edx                           ;If no 'C', sub -1 (add 1), else sub 0
        cmp     al,dl                             ;Set 'C' if not all pixels 1
        sbb     al,dl                             ;If no 'C', sub -1 (add 1), else sub 0

        cmp     ah,dl                             ;Set 'C' if not all pixels 1
        sbb     esi,edx                           ;If no 'C', sub -1 (add 1), else sub 0
        cmp     ah,dl                             ;Set 'C' if not all pixels 1
        sbb     ah,dl                             ;If no 'C', sub -1 (add 1), else sub 0
        clc                                       ;Carry clear to show there is something
        ret

return_null_interval:                             ;Null interval, skip it

        stc
        ret
page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  mono_bitmap_opaque
;*
;* DESCRIPTION   = Fill the given bitmap with all 1's or all 0's
;*
;*                 Registers Preserved:
;*                       DX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,DI,FLAGS
;*
;* INPUT         = AL = first byte mask
;*                 AH = last byte mask
;*                 BX = height
;*                 DX = next scan increment
;*                 SI = inner loop count
;*                 ES:DI --> destination
;*                 DS:DI --> destination
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        public  mono_bitmap_opaque

mono_bitmap_opaque::

        mov     cl,byte ptr colors[BACKGROUND] ;Set 'C' to fill color
        shr     cl,1

        errn$   bitmap_opaque

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE   bitmap_opaque
;*
;* DESCRIPTION   =  Fill the given bitmap with all 1's or all 0's
;*
;*                  Registers Preserved:
;*                        DX,SI,DS,ES,BP
;*                  Registers Destroyed:
;*                        AX,BX,CX,DX,DI,FLAGS
;*
;* INPUT         = AL = first byte mask
;*                 AH = last byte mask
;*                 BX = height
;*                 DX = next scan increment
;*                 SI = inner loop count
;*                 ES:DI --> destination
;*                 DS:DI --> destination
;*                 'C' set   if to fill with 1's
;*                 'C' clear if to fill with 0's NONE
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        public  bitmap_opaque

bitmap_opaque::

        cld
        jnc     bitmap_opaque_0s                 ;Filling with 0's


;/*
;**       Filling the bitmap with all 1's.  Simply OR in the first
;**       and/or last byte masks, and STOSW 0FFFF for the inner loop.
;*/

        or      eax,eax                           ;If no first or last byte
        jz      bo1_inner_start                  ;  then only inner loop exists
        mov     ecx,edi                           ;If there is a first byte, then
        cmp     al,1                              ;  increment destination pointer
        sbb     ecx,-1                            ;  for the inner loop.
        push    ecx                               ;Save inner loop start addr
        mov     ecx,ebx                           ;Set CX = height
        or      ah,ah
        jz      bo1_first_only                    ;No last byte
        or      al,al
        jz      bo1_last_only                     ;No first byte


bo1_first_and_last:

        xchg    ebx,esi                           ;Set BX = delta from first to last
        inc     ebx

bo1_both_loop:

        or      BYTE PTR [edi],al                 ;Do first
        or      BYTE PTR [edi][ebx],ah            ;Do last
        add     edi,edx                           ;--> next scan line
        loop    bo1_both_loop
        dec     ebx
        xchg    ebx,esi
        jmp     bo1_restore_start_addr


bo1_last_only:

        add     edi,esi                           ;--> last byte of first scan
        xchg    al,ah

bo1_first_only:

        or      BYTE PTR [edi],al                 ;Do first
        add     edi,edx                           ;--> next scan line
        loop    bo1_first_only

bo1_restore_start_addr:

        pop     edi                               ;--> to first byte for inner loop

bo1_inner_start:

        mov     eax,0FFFFh                        ;Filling with 1's

;/*
;**       Inner loop for filling with both 0's and 1's, or whatever
;**       is in AX.
;*/

bo_start_inner_loop:

        or      esi,esi                           ;SI = inner loop count
        jz      bo_done                           ;No inner loop
        sub     edx,esi                           ;BX = delta to next scan line

bo_inner_loop:

        mov     ecx,esi
        shr     ecx,1
        rep     stosw
        rcl     ecx,1
        rep     stosb
        add     edi,edx
        dec     ebx
        jnz     bo_inner_loop
        add     edx,esi                           ;Restore next scan increment

bo_done:

        ret


;/*
;**       Filling the bitmap with all 0's.  Simply AND in the inverse
;**       of the first and/or last byte masks, and STOSW 0000 for the
;**       inner loop.
;*/

bitmap_opaque_0s:

        or      eax,eax                           ;If no first or last byte
        jz      bo_start_inner_loop              ;  then only inner loop exists
        mov     ecx,edi                           ;If there is a first byte, then
        cmp     al,1                              ;  increment destination pointer
        sbb     ecx,-1                            ;  for the inner loop.
        push    ecx                               ;Save inner loop start addr
        mov     ecx,ebx                           ;Set CX = height
        or      ah,ah
        jz      bo0_first_only                    ;No last byte
        or      al,al                             ;'C' if AL was non-zero
        jz      bo0_last_only                     ;No first byte


bo0_first_and_last:

        xchg    ebx,esi                           ;Set BX = delta from first to last
        inc     ebx
        not     eax

bo0_both_loop:

        and     BYTE PTR [edi],al                 ;Do first
        and     BYTE PTR [edi][ebx],ah            ;Do last
        add     edi,edx                           ;--> next scan line
        loop    bo0_both_loop
        dec     ebx
        xchg    ebx,esi
        pop     edi                               ;--> to first byte for inner loop
        xchg    eax,ecx                           ;Set AX = 0
        jmp     bo_start_inner_loop


bo0_last_only:

        add     edi,esi                           ;--> last byte of first scan
        xchg    al,ah

bo0_first_only:

        not     al

bo0_one_only_loop:

        and     [edi],al
        add     edi,edx                           ;--> next scan line
        loop    bo0_one_only_loop

bo0_restore_start_addr:

        pop     edi                               ;--> to first byte for inner loop
        xchg    eax,ecx                           ;Set AX = 0
        jmp     bo_start_inner_loop

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  translate_string
;*
;* DESCRIPTION   = Translate a string
;*
;*                 Registers Destroyed:
;*                       AX,CX,DX,SI,DI,DS,ES
;*                 Registers Preserved:
;*                       BP,BX
;*
;* INPUT         = ES = SS
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        PUBLIC  translate_string

translate_string::

;/*
;** See if the string will fit in the 1024 byte compile code data segment.
;** If it doesn't we'll truncate it to 512 chars in length.  This is a
;** temporary      to fix a PTR.  The claim is it'll be rewritten later.
;*/

        push    ebx
        mov     eax,count
        mov     ecx,XLATE_BUF_LEN
        mov     edx,ecx
        or      ax,ax
        jns     @F
        neg     eax
        neg     ecx
@@:
        cmp     eax,edx
        jle     @F
        mov     count,ecx
        xchg    eax,edx
@@:

;/*
;** The translation loops will use the following registers:
;**
;**       AX = work
;**    ES:BX = translation vector
;**       CX = character count                      cx
;**       DX = lfd.last_char                        dx
;**    DS:SI = string pointer                       ds:si
;**       DI = lfd.first_char                       di
;**       BP = lfd.default_char                     bp
;**    SS:SP = translated string                    ss:sp
;*/

        xchg    ecx,eax                           ;CX = number of characters to xlate
        std                                       ;Will walk the string backwards
        mov     esi,lp_string
        add     esi,ecx
        dec     esi                               ;DS:SI --> last char of string
        movzx   edx,lfd.last_char
        movzx   edi,lfd.first_char
        mov     ebx,lp_DDC
        add     ebx,npAttrs
        push    ebp
        movzx   ebp,lfd.default_char
        mov     dwTranslateStk,esp
        lea     esp,pTranslateBuf

        ASSUME  ebx:PTR CHAR_ATTRS

        test    [ebx].ca_fs,CA_MUST_MAP

        .errnz  CHAR_ATTRS.ca_fs-MARKER_ATTRS.ma_fs

        jz      dont_translate

translate:

        mov     ebx,[ebx].ca_paus                ;Get mapping array

next_translate_char:

        movzx   eax,al
        lodsb
        add     eax,eax                           ;Index by words
        xchg    edi,eax
        movzx   edi,WORD PTR [ebx][edi]
        xchg    eax,edi
        sub     eax,edi
        cmp     eax,edx
        ja      translate_char_out_of_range

translate_char_in_range:

        push    eax
        loop    next_translate_char
        jmp     exit_translate_string

translate_char_out_of_range:

        mov     eax,ebp
        jmp     translate_char_in_range

;/*
;** The default case -- we need to convert the byte index to a word
;** while we're at it, map out of range chars to the default char
;*/

expand_char_out_of_range:

        mov     eax,ebp
        jmp     expand_char_in_range

dont_translate:

next_expand_char:

        movzx   eax,al
        lodsb
        sub     eax,edi
        cmp     eax,edx
        ja      expand_char_out_of_range

expand_char_in_range:

        push    eax
        loop    next_expand_char

;/*
;** Now clean up the stack mess we just created
;*/

exit_translate_string:

        mov     eax,esp                           ;Save pointer to string
        mov     esp,dwTranslateStk
        pop     ebp                               ;Restore frame pointer
        mov     lp_string,eax
        cld
        pop     ebx

        ret
page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  comp_extent
;*
;* DESCRIPTION   = Compute Extents of the String
;*                  The extents of the string are computed and returned to the
;*                  caller.  Mode data and font information has already been
;*                  saved on the frame.  The string has already been mapped
;*                  into word offset, with the first char / last char check
;*                  being performed during the translation.
;*
;*                 Registers Preserved:
;*                       BP,BX
;*                 Registers Destroyed:
;*                       AX,CX,DX,SI,DI,ES,DS,FLAGS
;*
;* INPUT         = BH = accel flags
;*                 BL = excel flags
;*
;* OUTPUT        = AX = rhs (right hand side), zero based
;*                 CX = lhs (left hand side).  zero based
;*                 DI = concatenation point,  zero based
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                          ------Pseudo-Code---
;*   {
;*   // The computation for fixed-pitch font extents is:
;*   //
;*   //    Y extent = height of character
;*   //    X extent = (width+CharExtra) * #chars
;*   //
;*   //    since all characters are the same width, including the default
;*   //    character, and any character extra is applied to all characters.
;*   //
;*   //
;*   // The computation for proportional font extents is:
;*   //
;*   //    Y extent = height of character
;*   //
;*   //               count-1
;*   //                ---
;*   //                \
;*   //    X extent = +   >  width(char(n))+CharExtra
;*   //                /
;*   //                ---
;*   //               n = 0
;*   //
;*   // If the width vector is present, then the above doesn't hold true.
;*   // The extents must be computed by checking each and every character,
;*   // and tracking the justification.
;*   }
;*
;**************************************************************************/


                OPTION  PROLOGUE:None
                OPTION  EPILOGUE:None

                PUBLIC  comp_extent
comp_extent::


        push    ebx
        cld
        mov     ecx,count
        jecxz   comp_ext_rhs_in_ax
        xchg    eax,ecx
        abs_ax
        xchg    eax,ecx                           ;Need count in CX
        test    bh,HAVE_WIDTH_VECT or ABC_FONT
        jnz     comp_ext_hard_way                ;Could step backwards
        test    bh,FIXED_PITCH
        jz      comp_ext_proportional            ;Proportional font
        movzx   eax,lfd.font_width               ;Fixed pitch font
        add     eax,char_extra                    ;Always add in character extra
        imul    ecx                               ;AX = string width in pixels

        cmp     eax,010000h
        jl      comp_ext_rhs_in_ax               ;Did not overflow

;/*
;** The string has overflowed 32K.  Return the max short int.
;*/

comp_ext_peg_right:

        mov     eax,MAXSHORT
        jmp     comp_ext_rhs_in_ax


;/*
;**       Compute the extent for a string which has a width vector.
;**
;**       The actual extent code is a subroutine since it can be
;**       called by the drawing code at output time, in which case
;**       the dda locations must not be updated.
;**
;**       Currently:
;**           CX     =  character count
;*/

comp_ext_hard_way:

        test    bh,ABC_FONT
        jnz     comp_ext_for_abc_font
        call    worst_case_ext
        jmp     comp_ext_have_ext

comp_ext_for_abc_font:

        call    abc_extent
        jmp     comp_ext_have_ext


;/*
;**       The font is a proportional font.  Sum up the widths of all
;**       the characters.  The summation loop will use the following
;**       registers:
;**
;**           DS:SI --> string
;**           ES    --> font
;**           AX     =  word register
;**           BX     =  work register
;**           CX     =  character count
;**           DX     =  sum
;**       Currently:
;**           CX     =  character count
;**           BH     =  accel flags
;**
;** This code will never be executed for an ABC spaced font.
;*/

comp_ext_proportional:

        mov     eax,char_extra
        mul     ecx
        mov     esi,lp_string
        mov     edi,pFont
        xchg    eax,edx                           ;Sum length into dx

comp_ext_prop_loop:

        lodsd
        add     eax,eax
        mov     ebx,eax
        add     eax,eax
        add     ebx,eax

        .errnz  (size FONT_ENTRIES) - 6

        ASSUME  edi:PTR FONTMAP, ebx:PTR FONT_ENTRIES

        add     dx,[edi].fsCharOffset[ebx].fe_width
        loop    comp_ext_prop_loop
        xchg    eax,edx                           ;Want width on AX
        and     eax,MAXSHORT            ;Dev COORD max if 7FFF

comp_ext_rhs_in_ax:

        mov     edi,eax                           ;Concatenation point is extent
        xor     ecx,ecx                           ;lhs is always 0
        mov     lhs,ecx                           ;Save these incase needed
        mov     rhs,eax
        mov     concat,edi

comp_ext_have_ext:

        movzx   edx,lfd.font_height ;Set height

        pop     ebx

        ret
page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_mode_and_font
;*
;* DESCRIPTION   =
;*   Get Mode Parameters and Font Parameters
;*
;*   The text/background colors and any justification information
;*   is saved on the frame.  Font parameters are alos saved on the
;*   frame.
;*
;*   The following "accel" flags will be set accordingly:
;*
;*       IS_OPAQUE
;*       HAVE_WIDTH_VECT
;*       IS_XOR_TEXT
;*       IS_OR_TEXT
;*       FIXED_PITCH
;*       WIDTH_IS_8
;*
;*   The following "excel" flags will be set accordingly:
;*
;*       none (set to 0)
;*
;*   The following frame variables are initialized:
;*
;*       colors                                   Text/background colors
;*       char_extra                               # extra pixels to add each char
;*       lfd                                      local font definition structure
;*       null_char_offset                         offset of the null character
;*       selFont                                  selector of the font
;*
;*       Registers Preserved:
;*             CX,DX,BP
;*       Registers Destroyed:
;*             AX,SI,DI,DS,FLAGS
;*
;* INPUT         = BH = accel flags
;*                 BL = excel flags
;*                 ES = SS
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


        public  get_mode_and_font

get_mode_and_font::

        mov     esi,lp_DDC

        ddc?    esi


        add     esi,npAttrs                       ;--> ddc_ca or ddc_ma

        ASSUME  esi:PTR CHAR_ATTRS

        mov     ebx,[esi].ca_fs
        xor     eax,eax
        test    ebx,CA_CHAR_EXTRA
        jz      have_extra_pel_count
        mov     eax,[esi].ca_cExtraPels

have_extra_pel_count:

        mov     char_extra,eax                    ;Must save regardless of being 0

        and     ebx,CA_FIXED_PITCH or CA_WIDTH_IS_8 or CA_ABC_SPACE or CA_CHAR_EXTRA

        .errnz  CA_FIXED_PITCH - FIXED_PITCH

        .errnz  CA_WIDTH_IS_8 - WIDTH_IS_8

        xchg    bl,bh                             ;Accel flags are in BH
        shl     bl,3                              ;Align ABC_FONT and HAVE_CHAR_EXTRA

        .errnz  CA_ABC_SPACE-0100h

        .errnz  ABC_FONT-08h

        .errnz  CA_CHAR_EXTRA-0200h

        .errnz  HAVE_CHAR_EXTRA-10h

        or      bh,bl
        xor     bl,bl                             ;Excel flags are zero

        mov     ax,word ptr [esi].ca_ba.ba_bmix  ;get mix modes

        .errnz  BASIC_ATTRS.ba_bkmix-BASIC_ATTRS.ba_bmix-1

        xchg    al,ah                             ; 
        mov     edi,eax                           ; 
        and     edi,0FFh                          ;isolate back mix
        or      bh,char_back_mix[edi]            ;or in back mix flags
        mov     al,ah                             ; 
        xchg    eax,edi                           ; 
        and     edi,0FFh                          ;isolate fore mix
        or      bh,char_fore_mix[edi]            ;or in fore mix flags

        mov     al,BYTE PTR [esi].ca_ba.ba_ipc   ;Get text color info
        mov     ah,BYTE PTR [esi].ca_ba.ba_ipcBack;Get background color info

        cmp     edi,CHAR_ROP_D                    ;fore mix LeaveAlone?
        jne     wc_flags_ok                       ;if not, don't change anything

        mov     al,ah                             ;set fore color same as back color
        test    bh,IS_OPAQUE
        jnz     wc_flags_ok
        or      wc_flags,ROP_IS_NOP

wc_flags_ok:

        mov     colors,eax

        errnz   FOREGROUND

        errnz   BACKGROUND-1

        mov     eax,lp_dx
        or      eax,eax
        jz      @F                                ;'C' set if width vector
        or      bh,HAVE_WIDTH_VECT

;/*
;** The mode data has been saved.  Now copy font data to the frame.
;*/

@@:
        mov     esi,[esi].ca_pFont               ;--> font

        ASSUME  esi:PTR FOCAFONT

        mov     pFont,esi
        lea     edi,lfd                           ;Set ES:DI --> local font definition

        lea     esi,[esi].ff_fdDefinitions.fdh_xCellWidth
        movsw

        .errnz  local_font_def.font_width

        movsw

        .errnz  local_font_def.font_height-local_font_def.font_width-2

        .errnz  FOCAFONT.ff_fdDefinitions.fdh_yCellHeight-FOCAFONT.ff_fdDefinitions.fdh_xCellWidth-2

;/*
;** Save the maximum width of a character.  This can be useful if a quick
;** clippping test were made to see if any clipping is required for the string.
;*/

        mov     esi,pFont

        ASSUME  esi:PTR FOCAFONT

        lea     esi,[esi].ff_fmMetrics.foca_usFirstChar
        movsw

        .errnz  local_font_def.first_char-local_font_def.font_height-2

        lodsw
        stosw                                     ;Save last char for null char offset

        .errnz  local_font_def.last_char-local_font_def.first_char-2 ;  calculation

        .errnz  FOCAFONT.ff_fmMetrics.foca_usLastChar-FOCAFONT.ff_fmMetrics.foca_usFirstChar-2

        movsw

        .errnz  local_font_def.default_char-local_font_def.last_char-2

        .errnz  FOCAFONT.ff_fmMetrics.foca_usDefaultChar-FOCAFONT.ff_fmMetrics.foca_usLastChar-2

;/*
;** Compute and save the offset of the null character.  The pointer to the
;** null character follows the last character.
;*/

        MOVZX   EAX,AX
        inc     eax                               ;Null character follows last character
        mov     esi,edx                           ;Don't destroy DX
        mov     edx,size ABC_FONT_ENTRIES
        test    bh,ABC_FONT
        jnz     @F
        mov     edx,size FONT_ENTRIES
@@:
        mul     edx
        xchg    eax,esi                           ;SI --> null character
        xchg    eax,edx                           ;Restore DX
        mov     eax,pFont

        ASSUME  eax:PTR FONTMAP, esi:PTR FONT_ENTRIES

        mov     eax,[eax].fsCharOffset[esi].fe_dBits
        mov     null_char_offset,eax

        ret

TotallyBogusCProc       ENDP

page

        end
