;*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

ifdef DBCS  ; DBCS codepage / DBCS device font support   ;IBMJ

; Variables for the MBCS string translation.
; This dsect is used only within the translate_string.

ts_count DWORD 0  ; number of characters translated
ts_fs  DWORD 0  ; string information flag (TSF_*)
count_in_bytes  DWORD   0  ; Number of bytes in the string
fsFont          DWORD   0  ; Font profile flags

TSF_FONT_SBCS equ 0001h ;ts_fs ; SBCS font
TSF_FONT_DBCS equ 0002h  ; DBCS font
TSF_FONT_MBCS equ 0004h  ; MBCS font
TSF_CONCAT equ 0008h  ; string will be concatinated later
TSF_MUST_REALIZE  equ 0020h  ; DBCS char found. realization required
TSF_MUST_MAP_SBCS equ 0040h  ; SBCS code-points must be translated
TSF_MUST_MAP_DBCS equ 0080h  ; SBCS/DBCS code-point detection needed

 .errnz TSF_FONT_SBCS   - NLSCA_FONT_SBCS
 .errnz TSF_FONT_DBCS   - NLSCA_FONT_DBCS
 .errnz TSF_FONT_MBCS   - NLSCA_FONT_MBCS
 .errnz TSF_CONCAT   - NLSCA_CONCAT
 .errnz TSF_MUST_MAP_SBCS - NLSCA_MAP_SBCS
 .errnz TSF_MUST_MAP_DBCS - NLSCA_MAP_DBCS

endif;DBCS        ;IBMJ

        .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
ifndef DBCS        ;IBMJ
        mov     eax,count
        mov     ecx,XLATE_BUF_LEN
        mov     edx,ecx
ifndef TMP        ;IBMJ
        or      ax,ax
else ;!TMP  ; !!! may be     ?!      ;IBMJ
 or eax,eax
endif;!TMP        ;IBMJ
        jns     @F
        neg     eax
        neg     ecx
@@:
        cmp     eax,edx
        jle     @F
        mov     count,ecx
        xchg    eax,edx
@@:

else ;DBCS  ; DBCS codepage / DBCS device font support   ;IBMJ

;-----------------------------------------------------------------------;
; Parse a string under the rule for specified codepage.
;
;   A parsing rule is selected by current logical codepage.
;
;   Code-point to index translation result will be:
;   - SBCS code-point --> SBCS glyph-index
;   - DBCS code-point --> DBCS code-point
;
;   If DBCS code-point is found, we will translate them to DBCS glyph
;   indexs using DBCS font manager. After DBCS translation, these DBCS
;   fonts are constructed as SBCS proportional font into the fontseg.
;
; Special Cases:
;
;   Under the MBCS/DBCS codepage, the DBCS leading byte at the end of
;   string will be treated as SBCS (invalid) code-point when NLSCA_CONCAT
;   parsing flag is not set. If this flag is set, the last byte is
;   truncated and will not be processed.
;
;   If the font doesn't have DBCS characters, they will be mapped to
;   SBCS default character.
;   This is the short cut of the following steps:
;   - DBCS font is requested ... font not defined --> DBCS defaultchar
;   - DBCS defaultchar is not available    --> SBCS defaultchar
;-----------------------------------------------------------------------;

 mov eax,lp_dx
 or eax,eax
 jz width_vector_adjusted
 call adjust_vector  ; force vector SBCS compatible

; 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.

width_vector_adjusted:
 mov eax,count
 mov ecx,XLATE_BUF_LEN
 mov ebx,ecx
 or eax,eax   ;!!! JFIX
 jns @F
 neg eax
 neg ecx
@@:
 cmp eax,ebx
 jle @F
 mov count,ecx
 xchg eax,ebx
@@:
 mov count_in_bytes,eax ; save given string length in bytes


; Check SBCS parsing rule or not.
; Be sure that the marker attribute doesn't include these flag bits.
; If MBCS/DBCS parsing, construct parsing flags for string translation.

 cmp npAttrs,ddc_ma  ; are we drawing marker ?
 jne translating_text ; no...

translating_marker:
 mov fsFont,NLSCA_FONT_SBCS
 jmp do_sbcs_translation ; marker is always SBCS

translating_text:
 mov esi,lp_DDC
 add esi,npAttrs  ; ESI --> ddc.ddc_ca

 ASSUME esi:PTR CHAR_ATTRS

 mov ebx,[esi].ca_fsFont ; font profile flags
 mov edx,[esi].ca_fsNls ; parsing rule flags
 mov fsFont,ebx
 and [esi].ca_fsNls,not NLSCA_CONCAT
     ; clear parsing result flag
 test edx,NLSCA_SBCS  ; SBCS parsing rule ?
 jnz do_sbcs_translation ; yes...

 mov ecx,edx
 and ecx,TSF_CONCAT+TSF_MUST_MAP_SBCS+TSF_MUST_MAP_DBCS
 and ebx,TSF_FONT_SBCS+TSF_FONT_DBCS+TSF_FONT_MBCS
 or ecx,ebx

 mov ts_fs,ecx  ; set parsing flags
 mov ts_count,0  ; clear translated char count

 test edx,NLSCA_DBCS
 jnz do_dbcs_translation ; DBCS parsing rule


;-----------------------------------------------------------------------;
; MBCS string translation
;-----------------------------------------------------------------------;

 public do_mbcs_translation
do_mbcs_translation::

; The MBCS translation loops will use the following registers:
;
; Registers:
;    EAX      = work
;    EBX      = SBCS lfd.first_char
;    ECX      = string length in bytes
;    EDX      = SBCS lfd.last_char
;    ESI      = string pointer
;    EDI      = translation vector
;    EBP      = SBCS lfd.default_char
;    ESP      = translated string
;    ts_count = character count xlated
;    ts_fs    = flags (TSF_*)
; Result:
; EBX = MBCS parsing result
; ECX = parsed character count

 mov ecx,eax   ; ECX = number of bytes to xlate
 cld    ; will walk the string forewards
 mov esi,lp_string  ; ESI --> first char of string
 movzx edx,lfd.last_char
 movzx ebx,lfd.first_char
 mov edi,lp_DDC
 add edi,npAttrs

 ASSUME edi:PTR CHAR_ATTRS

 mov edi,[edi].ca_paus ; EDI --> extended translation vector
 push ebp
 mov dwTranslateStk,esp
 lea esp,dwTranslateBuf
 mov lp_string,esp
 movzx ebp,lfd.default_char
 add esp,4   ; adjust ESP for push EAX.

 test ts_fs,TSF_MUST_MAP_SBCS
 jnz next_translate_mbcs_char


; SBCS code-points need not be translated to glyph-index.

next_expand_mbcs_char:
 xor eax,eax
 lodsb
 xchg ebx,eax
 or bh,[edi].cmt_DBCSLeading[ebx]
 xchg eax,ebx   ; AH!=0 when char is DBCS leading byte
 jnz expand_mbcs_char_is_dbcs_1st
 sub eax,ebx
 cmp eax,edx
 ja expand_mbcs_char_out_of_range
expand_mbcs_char_in_range:
 push eax
 add esp,8
 inc ts_count  ; increment translated char count
 loop next_expand_mbcs_char
 jmp exit_translate_mbcs_string


expand_mbcs_char_is_dbcs_1st:
 cmp ecx,1   ; string last byte ?
 jbe expand_mbcs_invalid_char
 lodsb    ; get DBCS char 2nd byte
 dec ecx
 test ts_fs,TSF_FONT_SBCS
 jnz expand_mbcs_char_out_of_range
 or ts_fs,TSF_MUST_REALIZE
 jmp expand_mbcs_char_in_range

expand_mbcs_invalid_char:
 test ts_fs,TSF_CONCAT
 jnz trunc_translate_mbcs_string
     ; last byte should be truncated
expand_mbcs_char_out_of_range:
 mov eax,ebp   ; SBCS defaultchar
 jmp expand_mbcs_char_in_range


; SBCS code-points must be translated to glyph-index.

next_translate_mbcs_char:
 xor eax,eax
 lodsb
 xchg ebx,eax
 or bh,[edi].cmt_DBCSLeading[ebx]
 xchg eax,ebx   ; AH!=0 when char is DBCS leading byte
 jnz translate_mbcs_char_is_dbcs_1st
 add eax,eax   ; SBCS char. Index by words
 xchg ebx,eax
 movzx ebx,WORD PTR [edi][ebx]
 xchg eax,ebx
 sub eax,ebx
 cmp eax,edx
 ja translate_mbcs_char_out_of_range
translate_mbcs_char_in_range:
 push eax
 add esp,8
 inc ts_count  ; increment translated char count
 loop next_translate_mbcs_char
 jmp exit_translate_mbcs_string


translate_mbcs_char_is_dbcs_1st:
 cmp ecx,1   ; string last byte ?
 jbe translate_mbcs_invalid_char
 lodsb    ; get DBCS char 2nd byte
 dec ecx
 test ts_fs,TSF_FONT_SBCS
 jnz translate_mbcs_char_out_of_range
 or ts_fs,TSF_MUST_REALIZE
 jmp translate_mbcs_char_in_range

translate_mbcs_invalid_char:
 test ts_fs,TSF_CONCAT
 jnz trunc_translate_mbcs_string
     ; last byte should be truncated
translate_mbcs_char_out_of_range:
 mov eax,ebp   ; SBCS defaultchar
 jmp translate_mbcs_char_in_range


; Now clean up the stack mess we just created

exit_translate_mbcs_string:
 and ts_fs,not TSF_CONCAT
trunc_translate_mbcs_string:  ; truncated. don't clear TSF_CONCAT
 mov ebx,ts_fs
 mov ecx,ts_count
 mov esp,dwTranslateStk
 pop ebp   ; restore frame pointer

 jmp translate_string_validate


;-----------------------------------------------------------------------;
; Pure DBCS string translation
;-----------------------------------------------------------------------;

 public do_dbcs_translation
do_dbcs_translation::

; The DBCS translation loops will use the following registers:
;
; Registers:
;    EAX      = work
;    EBX      = SBCS lfd.default_char
;    ECX      = string length in bytes
;    ESI      = string pointer
;    ESP      = translated string
;    ts_count = character count xlated
;    ts_fs    = flags (TSF_*)
; Result:
;    EBX      = DBCS parsing result
;    ECX      = parsed character count

 mov ecx,eax   ; ECX = number of bytes to xlate
 std    ; will walk the string backwards
 mov esi,lp_string
 add esi,ecx
 sub esi,2   ; ESI --> last char of string
 movzx ebx,lfd.default_char ; EBX = SBCS default char
 mov dwTranslateStk,esp
 lea esp,pTranslateBuf


; Check string length. Special consideration is required for odd length.

 shr ecx,1   ; CX = number of chars to xlate
 mov ts_count,ecx
 jnc dbcs_not_truncated ; not truncated
 dec esi   ; remove the last byte of string
 test ts_fs,TSF_CONCAT
 jnz dbcs_start_parsing ; concatination call...
 push ebx   ; last byte --> SBCS default char
 inc ts_count  ; adjust result character count
dbcs_not_truncated:
 and ts_fs,not TSF_CONCAT
dbcs_start_parsing:   ; truncated. don't clear TSF_CONCAT
 or ecx,ecx
 jz exit_translate_dbcs_string
 test ts_fs,TSF_FONT_SBCS
 jnz next_force_sbcs_default


; The font has DBCS capability. Chop string by words.

next_translate_dbcs_char:
 xor eax,eax
 lodsw    ; AH = 2nd byte, AL = 1st byte
 push eax   ; copy string
 loop next_translate_dbcs_char
 or ts_fs,TSF_MUST_REALIZE
 jmp exit_translate_dbcs_string


; The font does not have DBCS capability. Fill with SBCS defaultchar.

next_force_sbcs_default:
 push ebx
 loop next_force_sbcs_default


; Now clean up the stack mess we just created

exit_translate_dbcs_string:
 mov ebx,ts_fs
 mov ecx,ts_count
 mov lp_string,esp        ; save pointer to string
 mov esp,dwTranslateStk
 cld


;-----------------------------------------------------------------------;
; Check MBCS/DBCS parsing result
; Currently: BX = parsing result flags
;  CX = parsed (truncated) character count
;-----------------------------------------------------------------------;

 public translate_string_validate
translate_string_validate::

 mov esi,lp_DDC
 add esi,npAttrs      ; ESI --> ddc.ddc_ca

 ASSUME esi:PTR CHAR_ATTRS

; Check parsing result and update string state.
; If truncation occurres during parsing, report it via ddc state.
; If the font is SBCS font, all of DBCS code-points (2bytes) were mapped
; to SBCS default char (1byte). So adjust given string length in bytes.

 test ebx,TSF_CONCAT      ; was string truncated ?
 jz check_font_subset     ; no...
 or [esi].ca_fsNls,NLSCA_CONCAT ; indicate truncation occurred
 dec count_in_bytes      ; actual length being processed

check_font_subset:
 test ebx,TSF_FONT_SBCS
 jnz sbcs_font_used
 cmp count_in_bytes,ecx
 je update_char_count     ; no DBCS chars in the string
 or fsFont,NLSCA_FONT_DBCS_USED ; show DBCS chars found
 jmp update_char_count
sbcs_font_used:
 mov count_in_bytes,ecx

update_char_count:
 xchg count,ecx      ; update character count
 or ecx,ecx
 jns @f
 neg count
@@:

; Check if the fontseg is to be realized or not.
; !!! here we assumes no ABC font for DBCS !!!

 test fsFont,NLSCA_FONT_CACHED    ; cached fontseg ?
 jnz realize_fontseg      ; yes...
 test ebx,TSF_MUST_REALIZE     ; any DBCS character found ?
 jz dont_realize      ; no...

 cmp count,0       ; extent call ?
 jg realize_fontseg      ; no...
 test [esi].ca_fs,CA_FIXED_PITCH  ; fixed pitch ?
 jz realize_fontseg      ; no...
 mov eax,lp_dx
 or eax,eax       ; increment vector specified ?
 jnz realize_fontseg      ; yes...
dont_realize:
 jmp translate_string_done


; Realize fonts for the string into the selected fontseg.
; If pure DBCS codepage, code-point to index translation will be done
; here by invoking DbcsMapCharGlyph, for DBCS EBCDIC codepage support.

realize_fontseg:
 ASSUME esi:PTR CHAR_ATTRS

 mov eax,[esi].ca_lCodePage ; EAX = logical codepage for current font
 mov edx,[esi].ca_fsNls ; EDX = DBCS NLS accel flags
 mov esi,[esi].ca_pFont ; ESI --> font segment (font handle)
 mov edi,lp_string  ; EDI --> glyph index array

 test edx,NLSCA_DBCS  ; pure DBCS codepage ?
 jz do_realize  ; no...
 mov ecx,count_in_bytes ; ECX = string length in bytes
 and ecx,0fffffffeh  ; make sure even length
 call DbcsMapCharGlyph ; translate to glyph index
do_realize:
 mov ecx,count  ; ECX = signed character count
 call DbcsValidateFontSeg ; realize DBCS device fonts into fontseg
 jmp translate_string_done


;-----------------------------------------------------------------------;
; Pure SBCS string translation
;-----------------------------------------------------------------------;

 public do_sbcs_translation
do_sbcs_translation::

 and fsFont,not (NLSCA_FONT_MBCS+NLSCA_FONT_DBCS)
 or fsFont,NLSCA_FONT_SBCS ; acts like SBCS font...

endif;DBCS        ;IBMJ

;/*
;** 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
ifdef DBCS  ; DBCS device font support     ;IBMJ

 test fsFont,NLSCA_FONT_CACHED    ; cached fontseg ?
 jz translate_string_done     ; no. !!! assumes no huge markers
 mov esi,lp_DDC
 add esi,npAttrs      ; ESI --> ddc.ddc_ca
 jmp realize_fontseg      ; realize fonts into the fontseg

    public translate_string_done
translate_string_done::
endif;DBCS        ;IBMJ
        pop     ebx

        ret

ifdef DBCS  ; DBCS codepage / DBCS device font support   ;IBMJ
page
;--------------------------Public-Routine-------------------------------;
; adjust_vector
;
;   Adjust width vector for specified codepage.
;
;   The width vectors are defined also for DBCS trailing bytes in the
;   given string. These vectors are actually not used but reserve
;   spaces which are coresponding to the character position in the
;   string.
;   However, after string translation, both SBCS characters and DBCS
;   characters have same size in the translated string, so these
;   reserved spaces are not necessary for us.
;   Here we will remove these reserved area from width vectors.
;
;   Note:   To store the adjust vector data, the additional space was
;     reserved just adter the original vector is saved.
;     See CharGenericPos() in winstr.asm.
;
; Entry:
; None
; lp_string must be original string     (not translated yet)
; lp_dx must be original increment vector (not adjusted yet)
; !!! we assume that the width vector was copied into stack !!!
; Returns:
; None
; Error Returns:
; None
; Registers Preserved:
; EBP,EBX
; Registers Destroyed:
; EAX,ECX,EDX,ESI,EDI,FLAGS
; Calls:
; None
; History:
;   Wed 17-Apr-1991 13:36:08 -by-  Soh Ohta [jl09057 @ ymtvm3]
; Created.
;   Mon 27-Jan-1992 18:09:00 -by-  Soh Ohta [jl09057 @ ymtvm3]
; The destructive adjustment causes unexpected result when the ExtTextOut
; routine is called more than once from the EnumClip function.
; We will keep the original vector and save result to another space,
; which was preserved onto the stack. See CharGenericPos() @ winstr.asm.
;   Fri 13-Mar-1992 22:45:00 -by-  Soh Ohta [jl09057 @ ymtvm3]
; Fix     on QueryCharPositions. We need not pack the vector when the
; string length is less or equal to 2 bytes. The possible cases are:
;   - DBC
;   - SBC+SBC
;   - SBC+DBC(leading)
; The QueryCharPositions calls ExtTextOut per character basis. If we
; pack the vector, the trailing vectors will be invalid.
; Be sure that the 'count' here is string length in bytes (!char count).
;-----------------------------------------------------------------------;

 public adjust_vector
adjust_vector::

 cld
 mov ecx,count
 jcxz exit_adjust_vector2
 xchg eax,ecx
 abs_ax    ; get absolute value of EAX
 xchg eax,ecx   ; need count in ECX

 cmp ecx,2
 jbe exit_adjust_vector ; need not pack the vector
 cmp npAttrs,ddc_ma
 je exit_adjust_vector ; marker is always SBCS

 mov edi,lp_DDC
 add edi,npAttrs

 ASSUME edi:PTR CHAR_ATTRS

 test [edi].ca_fsNls,NLSCA_SBCS
 jnz exit_adjust_vector
 test [edi].ca_fsNls,NLSCA_DBCS
 jnz adjust_dbcs_vector

; Adjust width vector for MBCS codepage string.

adjust_mbcs_vector:
 mov esi,lp_string  ; ESI --> first char of string
 mov edi,[edi].ca_paus ; EDI --> extended translation vector

 assume ebx:NOTHING, edi:PTR CpMapTable

 push ebp
 mov eax,lp_dx  ; lp_dx --> original vector address
 mov ebx,eax
 shl ecx,2
 add ebx,ecx   ; EBX --> width vector (adjusted)
 shr ecx,2
 mov lp_dx,ebx  ; lp_dx --> adjusted vector address
 mov ebp,eax   ; EBP --> width vector (original)

adjust_mbcs_loop:
 xor eax,eax
 lodsb
 xchg ebx,eax
 or bh,[edi].cmt_DBCSLeading[ebx]
 mov ebx,eax
 mov eax,[ebp]
 mov [ebx],eax
 jnz adjust_mbcs_dbcs1st
 add ebp,4
adjust_mbcs_dbcs2nd_skipped:
 add ebx,4
 loop adjust_mbcs_loop
exit_adjust_mbcs_vector:
 pop ebp
exit_adjust_vector2:
 jmp exit_adjust_vector
adjust_mbcs_dbcs1st:
 cmp ecx,1
 jbe exit_adjust_mbcs_vector ; no DBCS trailinig byte
 inc esi   ; skip DBCS 2nd byte
 dec ecx
 add ebp,8
 jmp adjust_mbcs_dbcs2nd_skipped

; Adjust width vector for DBCS codepage string.

adjust_dbcs_vector:
 mov esi,lp_dx  ; ESI --> width vector (original)
 mov edi,esi
 shl ecx,2
 add edi,ecx   ; EDI --> width vector (adjusted)
 shr ecx,2
 mov lp_dx,edi  ; lp_dx --> adjusted vector address

adjust_dbcs_loop:
 movs dword ptr [edi],dword ptr [esi]
 sub ecx,2
 jbe exit_adjust_vector
 add esi,4
 jmp adjust_dbcs_loop

exit_adjust_vector:
 ret

endif;DBCS        ;IBMJ

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
ifndef DBCS        ;IBMJ
        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
else ;DBCS  ; DBCS device font support     ;IBMJ

; In fixed pitch font case, DBCS character width is 2 x SBCS character width.
; However, char_extra is to be added per character basis.

 mov eax,char_extra
 imul ecx   ;extra * character count
 jo comp_ext_peg_right ;Overflow
 mov edi,eax   ;Sum of char_extra
 mov ecx,count_in_bytes ;SBCS char count+(DBCS char count)*2
 movzx eax,lfd.font_width ;Fixed pitch font
 imul ecx   ;Sum of character width
 jo comp_ext_peg_right ;Overflow
 add eax,edi   ;AX = string width in pixels
 jno comp_ext_rhs_in_ax ;Did not overflow

endif;DBCS        ;IBMJ

;/*
;** 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
