;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = BUILDSTR.ASM
;*
;* DESCRIPTIVE NAME = Build data on the stack for strblt
;*
;*
;* VERSION      V2.0
;*
;* DATE         04/17/87
;*
;* DESCRIPTION  This module contains the strblt functions which build
;*              up data on the stack for the actual output routines
;*
;* FUNCTIONS    build_string
;*              
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/17/87                     Written by Walt Moore
;*
;*****************************************************************************/

        .386

        .xlist

INCL_GRE_CLIP           equ                      1
INCL_FONTFILEFORMAT     equ                      1
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1

        include pmgre.inc
        include fontmap.inc
        include driver.inc
        include egafam.inc
        include strblt.inc
        include extern.inc
        include protos.inc
if SCAN_CNT EQ 768
        include oem_macs.inc
endif

        .list

        .MODEL FLAT

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

        .DATA

        .CODE
_TUNE segment USE32 PUBLIC 'CODE'
        EXTERN preset_pro_text          :NEAR
        EXTERN worst_case_ext           :NEAR
        EXTERN comp_byte_interval       :NEAR
page
;/***************************************************************************
;*
;* FUNCTION NAME = build_string
;*
;* DESCRIPTION   = build_string builds up data on the stack consisting of character
;*                 offsets and widths, then invokes the routine which processes
;*                 this data.
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = stack frame as per strblt
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

ALIGN 4
build_string PROC SYSCALL, lp_DDC        :DWORD, lp_DestDev    :DWORD,
                           x             :DWORD, y             :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

        DebugMsg <build_string BUILDSTR CLIFFL>

        mov     eax,STACK_SLOP                    ; Don't let stack trash our vars.
        mov     min_stack,eax
        mov     eax,text_bbox.rcl_xLeft           ;Make text bbox a null interval
        mov     text_bbox.rcl_xRight,eax          ;  We'll grow it as we progress
        mov     current_lhs,eax                   ;Need to init this for interval calc

        mov     al,accel                          ;Save the actual state of the
        mov     bl,al                             ;  IS_OPAQUE flag since the
        and     al,IS_OPAQUE                      ;  worst case code may alter
        or      wc_flags,al                       ;  it
        mov     ah,excel                          ;Only set text visible once a string
        and     ah,not TEXT_VISIBLE               ; is actually displayed
        mov     excel,ah

        mov     ecx,OFFSET non_justified_text
        test    bl,HAVE_WIDTH_VECT or ABC_FONT or HAVE_CHAR_EXTRA
        jz      build_have_proc
        mov     ecx,OFFSET justified_text
        test    bl,HAVE_WIDTH_VECT or ABC_FONT
        jz      build_have_proc
        test    bl,IS_OPAQUE                      ;If transparent mode, then no
        jz      build_worst_ok                    ;  special work is required
        test    wc_flags,STEPPED_BACK             ;If we don't step backwards, no
        jz      build_worst_ok                    ;  special work is required
        call    prepare_for_overlap               ;Massive preprocessing required

build_worst_ok:

        mov     bl,accel
        mov     ah,excel
        mov     ecx,OFFSET worst_case
        test    bl,ABC_FONT
        jz      build_have_proc
        mov     ecx,OFFSET abc_case
        or      wc_flags,WC_FIRST                ;Very frist call is special

build_have_proc:

        mov     build_proc,ecx                    ;Save build routine address
        call    preset_pro_text                   ;Set frame/ss: locations as needed

build_restart:

        mov     clear_stack,esp                   ;Used to clean the stack on exit
        mov     eax,esp                           ;Save buffer start pointer
        sub     eax,4
        mov     buffer,eax

;/*
;**
;**  The routines which push text onto the stack expect the
;**  following registers to be set on entry:
;**
;**          DS:SI --> current character in the string
;**          ES:    =  font segment
;**          AL     =  excel flags
;**          AH     =  accel flags
;**          CX     =  number of characters in the string
;**          DI     =  current X position (and therefore starting x)
;**
;*/

        mov     esi,lp_string
        mov     al,excel
        mov     ah,accel
        mov     ecx,count
        mov     edi,x

        DebugMsg<building the stack BUILDSTR CLIFFL>

        jmp     build_proc
ALIGN 4

build_ret_addr:                                   ;build routines return here


        DebugMsg<looping BUILDSTR CLIFFL>

        mov     count,ecx                         ;Save count
        mov     x,eax                             ;Save next char's start
        mov     lp_string,esi                     ;Save next character
        call    pad_right_hand_side               ;Fill to byte boundary if possible
        mov     current_rhs,edi                   ;Save rhs
        mov     ebx,edi                           ;Compute the interval data
        mov     edx,current_lhs
        call    comp_byte_interval
        jc      build_all_done                    ;If nothing shows, we're done
        mov     left_clip_mask,al
        mov     right_clip_mask,ah
        mov     inner_byte_count,esi
        mov     scan_start,edi
        call    process_stack_data                ;Display the string
        mov     esp,clear_stack

        or      excel,TEXT_VISIBLE                ;Show something was displayed

;/*
;**
;**  If there is an opaquing rectangle, we must update the text
;**  bounding box so that it won't overwrite the text we just
;**  displayed when the rectangle is output.  IN transparent mode,
;**  OPAQUE_RECT would have been cleared after the rectangle was
;**  drawn and before we were called.
;**
;*/

        test    excel,OPAQUE_RECT
        jz      build_no_o_rect
        mov     eax,current_lhs
        mov     ebx,text_bbox.rcl_xLeft
        smin_ax ebx
        mov     text_bbox.rcl_xLeft,eax
        mov     eax,current_rhs
        mov     ebx,text_bbox.rcl_xRight
        smax_ax ebx
        mov     text_bbox.rcl_xRight,eax

build_no_o_rect:

        mov     ecx,count                         ;If no more characters
        jecxz   build_restore_opaque             ;  go home, have dinner, sleep


;/*
;**
;**   Prepare for the next string.  If in opaque mode, the
;**   CLIPPED_LEFT flag will have to be set so that we don't
;**   try padding the lhs.  If in transparent mode, because
;**   of stepping backwards, we might actually be clipped
;**   anyway, so we'll have to test for this also.
;**
;**   FIRST_IN_PREV must be cleared.  It will be set if the
;**   clipping code determines it needs to be.
;**
;*/

        mov     bl,excel                          ;Will be changing flags in here
        and     bl,not (CLIPPED_LEFT+FIRST_IN_PREV)
        mov     edi,x
        mov     eax,edi                          ;Assume start will be current_lhs
        test    accel,IS_OPAQUE                  ;When opaque, must clip on
        jnz     build_clip_next_time             ;  a restart
        cmp     edi,clip.rcl_xLeft
        jge     build_no_clip_next_time

build_clip_next_time:

        or      bl,CLIPPED_LEFT                  ;Clipping is required
        mov     eax,clip.rcl_xLeft               ;  and clip.rcl_xLeft for clipping check
        smax_ax edi                              ;  (but only if start X > old clip
        mov     clip.rcl_xLeft,eax               ;   lhs)

build_no_clip_next_time:

        mov     excel,bl
        mov     current_lhs,eax                   ;Need to update lhs for interval calc
        jmp     build_restart                     ;Try next part of the string
ALIGN 4

build_all_done:

        mov     esp,clear_stack

build_restore_opaque:

        mov     al,wc_flags
        test    al,WC_SET_LR                      ;If we stepped backwards, we'll
        jz      build_really_done                ;  have to restore the real lhs
        mov     ebx,wc_opaque_lhs                ;  and rhs incase we have an
        mov     text_bbox.rcl_xLeft,ebx       ;  opaque rectangle
        mov     ebx,wc_opaque_rhs
        mov     text_bbox.rcl_xRight,ebx

build_really_done:

        and     al,IS_OPAQUE                      ;Restore IS_OPAQUE incase worst_case
        or      accel,al                          ;  code cleared it, else the opaque
        ret                                       ;  rectangle may overwrite our text

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  non_justified_text
;*
;* DESCRIPTION   = This is the simple case for proportional text.  No justification,
;*                 no width vector.  Just run the string.  If we run out of stack
;*                 space, then that portion of the string which fits will be
;*                 displayed and we'll restart again after that.
;*
;*                 spcl - simple, proportional, clip lhs
;*                 sfcl - simple, fixed pitch,  clip lhs
;*                 sc   - simple, clip rhs
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 AL     =  excel flags
;*                 AH     =  accel flags
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position (and therefore starting x)
;*                 stack frame as per strblt
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters left in string
;*                 DI     =  string rhs
;*                 AX     =  next character's X
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  non_justified_text

ALIGN 4
non_justified_text::

        test    al,CLIPPED_LEFT
        jz      sc_no_left_clipping              ;No clipping needed on lhs
        mov     edx,clip.rcl_xLeft               ;Characters become visible here
        test    ah,FIXED_PITCH
        jz      spcl_next_char                    ;Proportional font


;/*
;**
;** Fixed pitch, no justification, left hand clipping
;**
;*/

        movzx   ebx,lfd.font_width               ;Fixed pitch font.

sfcl_next_char:

        add     edi,ebx                           ;Does this character become visible?
        cmp     edx,edi                   ;DX is clip.rcl_xLeft
        jl      sfcl_current_is_visible ;This char is visible
        add     esi,4
        loop    sfcl_next_char                    ;See if next character

sfcl_no_chars_visible:

        jmp     build_ret_addr                    ;Return to caller
ALIGN 4

sfcl_current_is_visible:

        sub     edi,ebx                           ;Restore staring address of character
                                                  ;  and just fall into the proportional
                                                  ;  code which will handle everything

;/*
;**
;** Proportional, no justification, left hand clipping
;**
;*/

spcl_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        mov     eax,pFont

        ASSUME  eax:PTR FONTMAP, ebx:PTR FONT_ENTRIES

        movzx   eax,[eax].fsCharOffset[ebx].fe_width
        add     edi,eax                           ;0 width chars won't change x position
        cmp     edx,edi                   ;DX is clip.rcl_xLeft
        jl      spcl_current_is_visible ;This char is visible

spcl_see_if_next:

        loop    spcl_next_char                    ;See if next character
        jmp     build_ret_addr                    ;Return to caller
ALIGN 4

spcl_current_is_visible:

        sub     edi,eax                           ;Restore starting x of character
        push    eax
        mov     eax,pFont
        mov     ebx,[eax].fsCharOffset[ebx].fe_dBits
        pop     eax
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping


;/*
;**
;**   Instead of incrementing the current position by 8 and
;**   having to recover the real current position, we just
;**   slide the clip region left.  It has the same effect.
;**
;*/

        sub     edx,edi                           ;Compute bits until we're visible
        je      spcl_save_first                  ;Starts on clip edge
        sub     edx,8                            ;Is current byte visible?
        jl      spcl_have_vis_start              ;  Yes

spcl_step_clipped_char:

        sub     eax,8                            ;Shorten the width of the character
        add     edi,8                            ;Move current position right
        add     bx,lfd.font_height               ;Move to next column of character
        sub     edx,8                            ;Is current byte visible?
        jge     spcl_step_clipped_char           ;  No


;/*
;**
;**  If the lhs of the clip region and the starting X of the
;**  character are in different bytes, then the FIRST_IN_PREV
;**  flag must be set.  Only a clipped character can set this
;**  flag.
;**
;*/

spcl_have_vis_start:

        mov     edx,clip.rcl_xLeft
        xor     edx,edi
        and     edx,not 7
        jz      spcl_save_first                  ;In same byte
        or      excel,FIRST_IN_PREV


;/*
;**
;**  We have the start of the first character which is visible
;**  Determine which loop (clipped/non-clipped) will process it.
;**  We let the routine we're about to call push the character
;**  since it will handle both right clipping (if needed) and
;**  fat characters.
;**
;*/

spcl_save_first:

        jmp     scc_clip_enters_here
ALIGN 4


;/*
;**
;**  There was no left hand clipping.  Whenever this is the case,
;**  we want to try and pad the lhs out to a byte boundary so that
;**  full byte code can be used.
;**
;*/

sc_no_left_clipping:

        call    pad_left_hand_side               ;Might be able to pad lhs


;/*
;**
;**     scc - simple case, rhs clipping.
;**
;**     This loop is used when it is possible for the character
;**     to be clipped on the rhs.  lhs clipping has already
;**     been performed.  There is no justification.
;**
;**     Currently:
;**             DS:SI --> current character in the string
;**             ES:    =  font segment
;**             DI     =  current X position
;**             CX     =  number of bytes left in the string
;**
;*/

scc_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        push    edi
        mov     edi,pFont

        ASSUME  edi:PTR FONTMAP, ebx:PTR FONT_ENTRIES

        movzx   eax,[edi].fsCharOffset[ebx].fe_width
        mov     ebx,[edi].fsCharOffset[ebx].fe_dBits
        pop     edi
        or      eax,eax                           ;If width is 0, ignore character
        jz      scc_see_if_next
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping

scc_clip_enters_here:

        mov     edx,edi                           ;Compute phase
        and     dl,7
        add     edi,eax                           ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     scc_char_is_clipped              ;Clipped (or first pixel of next is)

scc_been_clipped:

        mov     dh,dl                             ;Need phase in DH
        cmp     eax,8                             ;If character is less than 8 bits
        jbe     scc_width_ok                      ;  wide, push it's data
        mov     dl,8                              ;Need 8 for size in DL

scc_still_wide:

        push    edx                               ;Push data showing phase,
        push    ebx                               ;  character is 8 wide, then
        sub     eax,8                             ;  create another character
        add     bx,lfd.font_height               ;  of the remaining width
        cmp     eax,8
        ja      scc_still_wide

scc_width_ok:

        mov     ah,dh
        push    eax                               ;Save width and phase
        push    ebx                               ;Save offset to bits
        cmp     esp,min_stack                     ;Stack compare must be unsigned
        jb      scc_restart                       ;Not enough stack for another character

scc_see_if_next:

        loop    scc_next_char                     ;Until all characters pushed
        mov     eax,edi                           ;Next character starts here
        jmp     build_ret_addr
ALIGN 4


;/*
;**
;**   This character is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**   Adjust it's width so it fits, set the remaining character
;**   count to 1 so the loop will terminate, and reenter the
;**   code where we came from.
;**
;*/

scc_char_is_clipped:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                           ;  which have to be clipped
        sub     eax,edi                           ;Set new character width
        mov     edi,ecx                           ;Set new rhs
        mov     ecx,1                             ;Show this as last character
        jmp     scc_been_clipped                 ;Finish here
ALIGN 4


;/*
;**
;**       There is no more space on the stack to build characters.
;**       If this was the last character, then don't bother with the
;**       restart.
;**
;*/

scc_restart:

        dec     ecx                               ;Adjust count for char just pushed
        mov     eax,edi                           ;Next character starts here
        jmp     build_ret_addr
ALIGN 4

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  justified_text
;*
;* DESCRIPTION   = This is the justification case for text, when positive character
;*                 extra is given.  If we run out of stack space, then that portion
;*                 of the string which fits will be displayed, and we'll restart
;*                 again after that.
;*
;*                 jc  - justify clipped
;*                 jcl - justify clip left
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 AL     =  excel flags
;*                 AH     =  accel flags
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position (and therefore starting x)
;*                 stack frame as per strblt
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters left in string
;*                 DI     =  string rhs
;*                 AX     =  next character's X
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  justified_text

justified_text::

        test    excel,CLIPPED_LEFT
        jnz     jcl_next_char                     ;Clipping needed
        call    pad_left_hand_side               ;Might be able to pad lhs
        jmp     jc_next_char
ALIGN 4

jcl_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        mov     eax,pFont
        movzx   eax,[eax].fsCharOffset[ebx].fe_width
        mov     edx,char_extra

jcl_have_width:

        add     edi,eax                          ;DI = next chars starting X
        cmp     clip.rcl_xLeft,edi
        jl      jcl_current_is_visible           ;This char is visible
        add     edi,edx
        cmp     clip.rcl_xLeft,edi
        jl      jcl_dummy_is_visible             ;Dummy is first visible character

jcl_see_if_next:

        loop    jcl_next_char                    ;See if next character
        jmp     build_ret_addr                   ;Return to caller
ALIGN 4


;/*
;**
;**  The dummy character is the first character which became
;**  visible.  Just set the starting X to clip.rcl_xLeft, and shorten
;**  the width of the dummy character appropriately.
;**
;*/

jcl_dummy_is_visible:

        mov     edx,edi
        mov     edi,clip.rcl_xLeft                ;Starting X is clip.rcl_xLeft
        sub     edx,edi                           ;DX is # pixels in dummy
        xor     eax,eax                           ;Show no real character
        jmp     jcl_all_done
ALIGN 4


;/*
;**
;**  We just encountered the first character which will be visible
;**  Clip it on the lhs as needed.
;**
;*/

jcl_current_is_visible:

        sub     edi,eax                          ;Restore starting x of character
        push    eax
        mov     eax,pFont
        mov     ebx,[eax].fsCharOffset[ebx].fe_dBits
        pop     eax
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping


;/*
;**
;**   Instead of incrementing the current position by 8 and
;**   having to recover the real current position, we just
;**   slide the clip region left.  It has the same effect.
;**
;*/

        push    edx                              ;Save extra pixels
        mov     edx,clip.rcl_xLeft
        sub     edx,edi                          ;Compute bits until we're visible
        je      jcl_save_first                   ;Starts on clip edge
        sub     edx,8                            ;Is current byte visible?
        jl      jcl_have_vis_start               ;  Yes

jcl_step_clipped_char:

        sub     eax,8                            ;Shorten the width of the character
        add     edi,8                            ;Move current position right
        add     bx,lfd.font_height               ;Move to next column of character
        sub     edx,8                            ;Is current byte visible?
        jge     jcl_step_clipped_char            ;  No

;/*
;**
;**   If the lhs of the clip region and the starting X of the
;**   character are in different bytes, then the FIRST_IN_PREV
;**   flag must be set.  Only a clipped character can set this
;**   flag.
;**
;*/

jcl_have_vis_start:

        mov     edx,clip.rcl_xLeft
        xor     edx,edi
        and     edx,not 7
        jz      jcl_save_first                   ;In same byte
        or      excel,FIRST_IN_PREV

;/*
;**
;**   We have the start of the first character which is visible
;**   We let the routine we're about to call push the character
;**   since it will handle both right clipping (if needed) and
;**   fat characters.
;**
;*/


jcl_save_first:

        pop     edx                              ;Restore extra pixels

jcl_all_done:

        jmp     jc_clip_enters_here
ALIGN 4


;/*
;**
;**    jc - justified with clipping
;**
;**    This loop is used for justified text.  It will perform
;**    rhs clipping.  lhs clipping has already been performed.
;**
;**    Currently:
;**            DS:SI --> current character in the string
;**            ES:    =  font segment
;**            DI     =  current X position
;**            CX     =  number of bytes left in the string
;**
;*/

jc_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        mov     eax,pFont
        movzx   eax,[eax].fsCharOffset[ebx].fe_width
        mov     edx,char_extra

jc_have_width:

        or      eax,eax
        jz      jc_check_dummy                   ;If width is 0, might still have dummy
        push    eax
        mov     eax,pFont
        mov     ebx,[eax].fsCharOffset[ebx].fe_dBits
        pop     eax
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping

jc_clip_enters_here:

        mov     num_null_pixels,edx              ;Save # null pixels
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     jc_char_is_clipped               ;Clipped (or first pixel of next is)

jc_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If character is less than 8 bits
        jbe     jc_width_ok                      ;  wide, push it's data

jc_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        add     bx,lfd.font_height               ;  of the remaining width
        cmp     eax,8
        ja      jc_still_wide

jc_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits
        mov     edx,num_null_pixels

jc_check_dummy:

        or      edx,edx
        jz      jc_see_if_next                   ;No pixels for justification
        xchg    eax,edx                          ;Set ax = number of pixels to fill
        mov     ebx,null_char_offset
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     jc_dummy_is_clipped              ;Clipped (or first pixel of next is)

jc_dummys_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If dummy is less than 8 bits
        jbe     jc_dummy_width_ok                ;  wide, push it's data

jc_dummy_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        cmp     eax,8
        ja      jc_dummy_still_wide

jc_dummy_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits

jc_see_if_next:

        cmp     esp,min_stack                    ;Stack compare must be unsigned
        jb      jc_restart                       ;Not enough stack for another character
        dec     ecx
        jle     jc_all_done
        jmp     jc_next_char                     ;Until all characters pushed
ALIGN 4

jc_all_done:

        mov     eax,edi                          ;Next character starts here
        jmp     build_ret_addr
ALIGN 4


;/*
;**
;**    This character is either clipped, or it's last pixel is
;**    the last pixel which will fit within the clipping rhs.
;**    Adjust it's width so it fits, set the remaining character
;**    count to 1 so the loop will terminate, and reenter the
;**    code where we came from.
;**
;**    Might as well set num_null_pixels to zero to skip that code.
;**
;*/

jc_char_is_clipped:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,ecx                          ;Set new rhs
        xor     ecx,ecx                          ;Dont't want any extra pixels
        mov     num_null_pixels,ecx
        inc     ecx                              ;Show this as last character
        jmp     jc_been_clipped                  ;Finish here
ALIGN 4


;/*
;**
;**   The dummy is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**   Adjust it's width so it fits, set the remaining character
;**   count to 1 so the loop will terminate, and reenter the
;**   code where we came from.
;**
;*/

jc_dummy_is_clipped:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                           ;  which have to be clipped
        sub     eax,edi                           ;Set new character width
        mov     edi,ecx                           ;Set new rhs
        mov     ecx,1                             ;Show this as last character
        jmp     jc_dummys_been_clipped           ;Finish here
ALIGN 4


;/*
;**
;**   These is no more space on the stack to build characters.
;**   If this was the last character, then don't bother with the
;**   restart.
;**
;*/

jc_restart:

        dec     ecx                               ;Adjust count for char just pushed
        mov     eax,edi                           ;Next character starts here
        jmp     build_ret_addr
ALIGN 4

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  worst_case
;*
;* DESCRIPTION   = This is the worst case text code, where there is some combination
;*                 of the width vector and character extra.
;*
;*                 If we step backwards or we run out of stack space, then that
;*                 whatever has been built up on the stack will be displayed, and
;*                 we'll restart again after that.
;*
;*                 wcc - worse case clipped
;*                 wccl - worse case clip left
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 AL     =  excel flags
;*                 AH     =  accel flags
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position (and therefore starting x)
;*                 stack frame as per strblt
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters left in string
;*                 DI     =  string rhs
;*                 AX     =  next character's X
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  worst_case

worst_case::

        test    excel,CLIPPED_LEFT
        jnz     wccl_next_char                   ;Clipping needed
        call    pad_left_hand_side               ;Might be able to pad lhs
        jmp     wcc_next_char
ALIGN 4


;/*
;**
;**  This is the start of the real loop for left hand clipping.
;**
;*/

wccl_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        mov     eax,pFont
        movzx   eax,[eax].fsCharOffset[ebx].fe_width
        push    ebx                              ;Must save character index
        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]              ;DX is delta to next char's start
        add     ebx,4
        mov     lp_dx,ebx
        sub     edx,eax                          ;Compute adjustment to width


;/*
;**
;**  DX contains any adjustment to the next characters starting
;**  position.  If this number is negative, then we'll be stepping
;**  over the previous character.
;**
;**  We don't allow a character to start before the previous
;**  character.  We'll have to enforce this at this point by
;**  making sure that any negative adjustment is less than or
;**  equal to the character width.
;**
;*/

wccl_have_width:

        or      edx,edx
        jge     wccl_adjustment_ok
        add     edx,eax                           ;Don't allow the backwards step
        sbb     ebx,ebx                           ;  to go beyond the start of this
        and     edx,ebx                           ;  character
        sub     edx,eax

wccl_adjustment_ok:

        pop     ebx                               ;Restore char index
        add     eax,edi                           ;AX = rhs of character
        cmp     clip.rcl_xLeft,eax
        jl      wccl_current_is_visible ;Some part of char is visible


;/*
;**
;**   If the adjustment for the character width is greater than the
;**   actual width of the character, then it is possible that the
;**   dummy pixels could become visible.  If the adjustment for the
;**   character width is less than the actual character width, then
;**   the dummy pixel (negative dummy pixels?) cannot become visible.
;**
;*/

        add     eax,edx                          ;Next char starts at AX
        mov     edi,clip.rcl_xLeft
        cmp     edi,eax
        jl      wccl_dummy_is_visible            ;Some part of dummy char became visible
        xchg    edi,eax                          ;Set start of next character

wccl_see_if_next:

        loop    wccl_next_char                   ;See if next character
        jmp     build_ret_addr                    ;Return to caller
ALIGN 4



;/*
;**
;**   The dummy character is the first character which became
;**   visible.  Just set the starting X to clip.rcl_xLeft, and shorten
;**   the width of the dummy character appropriately.
;**
;*/

wccl_dummy_is_visible:

        xchg    eax,edx                           ;Set DX = # pixels in dummy
        sub     edx,edi
        xor     eax,eax                           ;Show no real character
        jmp     wccl_all_done
ALIGN 4


;/*
;**
;**    So here we are, we have a character which will become visible,
;**    and possibly have some adjustment to the character.
;**
;**    Our registers currently contain:
;**
;**            AX = rhs of character
;**            BX = index into offset/width table
;**            CX = # of characters left in the string
;**            DX = # of extra pixels (zero or positive)
;**            DI = starting X offset
;**            ES = FontSeg
;**            DS:SI --> string
;**
;*/


wccl_current_is_visible:

        sub     eax,edi                          ;Restore width of the character
        push    eax
        mov     eax,pFont
        mov     ebx,[eax].fsCharOffset[ebx].fe_dBits
        pop     eax
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping


;/*
;**
;**   Instead of incrementing the current position by 8 and
;**   having to recover the real current position, we just
;**   slide the clip region left.  It has the same effect.
;**
;*/

        push    edx                              ;Save extra pixels to be added
        mov     edx,clip.rcl_xLeft
        sub     edx,edi                          ;Compute bits until we're visible
        je      wccl_save_first                  ;Starts on clip edge
        sub     edx,8                            ;Is current byte visible?
        jl      wccl_have_vis_start              ;  Yes

wccl_step_clipped_char:

        sub     eax,8                            ;Shorten the width of the character
        add     edi,8                            ;Move current position right
        add     bx,lfd.font_height               ;Move to next column of character
        sub     edx,8                            ;Is current byte visible?
        jge     wccl_step_clipped_char           ;  No


;/*
;**
;**   If the lhs of the clip region and the starting X of the
;**   character are in different bytes, then the FIRST_IN_PREV
;**   flag must be set.  Only a clipped character can set this
;**   flag.
;**
;*/

wccl_have_vis_start:

        mov     edx,clip.rcl_xLeft
        xor     edx,edi
        and     edx,not 7
        jz      wccl_save_first                  ;In same byte
        or      excel,FIRST_IN_PREV


;/*
;**
;**  We have the start of the first character which is visible.
;**  We let the routine we're about to call push the character
;**  since it will handle both right clipping (if needed) and
;**  fat characters.
;**
;*/

wccl_save_first:

        pop     edx                               ;Restore extra pixels

wccl_all_done:

        jmp     wcc_clip_enters_here
ALIGN 4


;/*
;**
;**  wcc - worse case, with rhs clipping
;**
;**  Currently:
;**          DS:SI --> current character in the string
;**          ES:    =  font segment
;**          DI     =  current X position
;**          CX     =  number of bytes left in the string
;**
;*/

wcc_next_char:

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

        .errnz  (size FONT_ENTRIES) - 6

        mov     edx,pFont
        movzx   eax,[edx].fsCharOffset[ebx].fe_width
        mov     ebx,[edx].fsCharOffset[ebx].fe_dBits
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping
        push    ebx
        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]              ;DX is delta to next char's start
        add     ebx,4
        mov     lp_dx,ebx
        sub     edx,eax                          ;Compute adjustment to width

;/*
;**
;**   DX contains any adjustment to the next characters starting
;**   position.  If this number is negative, then we'll be stepping
;**   over the previous character.
;**
;**   We don't allow a character to start before the previous
;**   character.  We'll have to enforce this at this point by
;**   making sure that any negative adjustment is less than or
;**   equal to the character width.
;**
;*/

wcc_have_width:

        or      edx,edx
        jge     wcc_adj_is_ok
        add     edx,eax                           ;Don't allow the backwards step
        sbb     ebx,ebx                           ;  to go beyond the start of this
        and     edx,ebx                           ;  character
        sub     edx,eax

wcc_adj_is_ok:

        pop     ebx                              ;Restore bits offset

wcc_clip_enters_here:

        or      eax,eax                          ;If character width is 0,
        jz      wcc_check_dummy                  ;  might still have dummy char
        or      edx,edx                          ;Any adjustment to the width?
        jle     wcc_have_adj_width               ;No extra pixels to add
        push    ebx
        mov     ebx,eax                          ;  into the empty space of the
        neg     eax                              ;  character
        and     eax,7                            ;AX = # extra pixels which will fit
        jz      wcc_have_entire_width            ;None will fit
        cmp     eax,edx
        jl      wcc_have_what_fits               ;Some extra pixels will not fit
        mov     eax,edx                          ;All pixels will fit, make DX = 0

wcc_have_what_fits:

        sub     edx,eax                          ;DX = extra for the dummy character

wcc_have_entire_width:

        add     eax,ebx                          ;Set number of pixels to use in char
        pop     ebx

wcc_have_adj_width:

        mov     num_null_pixels,edx              ;Save number of dummy pixels
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     wcc_char_is_clipped              ;Clipped (or first pixel of next is)

wcc_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If character is less than 8 bits
        jbe     wcc_width_ok                     ;  wide, push it's data

wcc_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        add     bx,lfd.font_height               ;  of the remaining width
        cmp     eax,8
        ja      wcc_still_wide

wcc_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits
        mov     edx,num_null_pixels

wcc_check_dummy:

        xchg    eax,edx                          ;Just incase we go backwards
        or      eax,eax
        jz      wcc_see_if_next                  ;No pixels for justification
        jl      wcc_going_back
        mov     ebx,null_char_offset
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     wcc_dummy_is_clipped             ;Clipped (or first pixel of next is)

wcc_dummys_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If dummy is less than 8 bits
        jbe     wcc_dummy_width_ok               ;  wide, push it's data

wcc_dummy_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        cmp     eax,8
        ja      wcc_dummy_still_wide

wcc_dummy_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits

wcc_see_if_next:

        cmp     esp,min_stack                    ;Stack compare must be unsigned
        jb      wcc_restart                      ;Not enough stack for another character
        dec     ecx
        jle     wcc_all_done
        jmp     wcc_next_char                    ;Until all characters pushed
ALIGN 4

wcc_all_done:

        mov     eax,edi                          ;Next character starts here
        jmp     build_ret_addr
ALIGN 4



;/*
;**
;**   The dummy is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**   Adjust it's width so it fits, set the remaining character
;**   count to 1 so the loop will terminate, and reenter the
;**   code where we came from.
;**
;**   If the width adjustment was negative, we would never have
;**   reached this code, so ignore any restart.
;**
;*/

wcc_dummy_is_clipped:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,ecx                          ;Set new rhs
        mov     ecx,1                            ;Show this as last character
        jmp     wcc_dummys_been_clipped ;Finish here
ALIGN 4



;/*
;**
;**   This character is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**
;**   If there is a  negative adjustment to the width of the
;**   character, it is possible that the next character could
;**   be partially visible.  We'll have set up for a restart
;**   if this is the case.
;**
;**   If this is the last character of the string, then there
;**   is no problem
;**
;**   If no negative adjustment, adjust it's width so it fits,
;**   set the remaining character count to 1 so the loop will
;**   terminate, and reenter the code where we came from.
;**
;*/

wcc_char_is_clipped:

        push    edx                              ;If num_null_pixels < 0, then
        mov     edx,num_null_pixels              ;  a restart might be possible
        or      edx,edx
        jl      wcc_might_need_restart           ;Might need a restart

wcc_clipped_no_restart:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,ecx                          ;Set new rhs
        xor     ecx,ecx                          ;Don't want any extra pixels
        mov     num_null_pixels,ecx
        inc     ecx                              ;Show this as last character
        pop     edx
        jmp     wcc_been_clipped                 ;Finish here
ALIGN 4


;/*
;**
;**       Might be looking a restart in the face.  Compute where the
;**       next character would start, and if it is to the left of
;**       clip.rcl_xRight, then a restart is needed.
;**
;*/

wcc_might_need_restart:

        cmp     ecx,1                            ;If last character
        jle     wcc_clipped_no_restart           ;  then no restart needed
        add     edx,edi                          ;Compute next starting x
        sub     edx,clip.rcl_xRight
        jge     wcc_clipped_no_restart           ;Rest of string is clipped


;/*
;**
;**   Will have to force a restart for the next character.  We
;**   can do this by computing the number of pixels between where
;**   where the next character starts and clip.rcl_xRight.  This negative
;**   number will be stuffed into num_null_pixels, so when we reenter
;**   the main loop, we'll force a restart after pushing the character
;**   data
;**
;*/

        mov     num_null_pixels,edx
        mov     edx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,edx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,edx                          ;Set new rhs
        pop     edx
        jmp     wcc_been_clipped                 ;Finish here
ALIGN 4


;/*
;**
;**  I have the unfortunate task of informing you that the next
;**  character will start somewhere in the middle of the current
;**  character.  If this is the last character of the string,
;**  then nothing need be done.  If this isn't the last character,
;**  then a restart will be needed.
;**
;*/

wcc_going_back:

        add     eax,edi                           ;Set position of next character
        dec     ecx                               ;Adjust count for char just pushed
        jmp     build_ret_addr
ALIGN 4



;/*
;**
;**       Out of stack space.  Set up for a restart.
;**
;*/

wcc_restart:

        mov     eax,edi
        dec     ecx                               ;Adjust count for char just pushed
        jmp     build_ret_addr
ALIGN 4

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  abc_case
;*
;* DESCRIPTION   = This is the abc spaced font code
;*
;*                 If we step backwards or we run out of stack space, then that
;*                 whatever has been built up on the stack will be displayed, and
;*                 we'll restart again after that.
;*
;*                 abc - abc clipped
;*                 abcl - abc clip left
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 AL     =  excel flags
;*                 AH     =  accel flags
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position (and therefore starting x)
;*                 stack frame as per strblt
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters left in string
;*                 DI     =  string rhs
;*                 AX     =  next character's X
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  abc_case

abc_case::

;/*
;** If this is the very first call to this routine, we'll have to
;** process any positive "a" space by faking up a dummy character
;** for any positive "a" space.  If it isn't the very first call,
;** we fake a real dummy char of 0 width (its 3:00 am).
;*/

ifdef FIREWALLS
        int 3
endif
        test    wc_flags,WC_FIRST
        jz      abc_not_first_time
        mov     ebx,DWORD PTR [esi]               ;Get "a" space for this
        imul    ebx,(size ABC_FONT_ENTRIES)
        mov     edx,pFont

        ASSUME  ebx:PTR ABC_FONT_ENTRIES

        movsx   edx,[edx].fsCharOffset[ebx].abc_a_space
        or      edx,edx
        jns     abc_special_dummy_needed;Positive, must deal with it

abc_not_first_time:

        xor     edx,edx                          ;No dummy needed

abc_special_dummy_needed:

        and     wc_flags,not WC_FIRST
        test    al,CLIPPED_LEFT
        mov     eax,0                            ;Preseve flags
        jnz     abcl_very_first_enters_here ;Clipping needed
        mov     num_null_pixels,edx              ;Cannot push!!
        call    pad_left_hand_side               ;Might be able to pad lhs
        mov     edx,num_null_pixels              ;Dummy >= zero
        xor     eax,eax                          ;No real width
        jmp     abc_clip_enters_here
ALIGN 4


;/*
;**
;**  This is the start of the real loop for left hand clipping.
;**
;*/

        public  abcl_next_char

abcl_next_char::

        xor     edx,edx                           ;Zero extra pels so far
        lodsd                                     ;Get this char's offset
        add     eax,eax                           ;*2
        mov     ebx,eax
        add     eax,eax                           ;*4
        add     eax,eax                           ;*8
        add     ebx,eax                           ;*10

        .errnz  size ABC_FONT_ENTRIES - 10

        dec     ecx                               ;If last character, don't try and
        jz      abcl_have_nexts_a_space ;  load next char's "a" space
        mov     eax,DWORD PTR [esi]
        add     eax,eax                           ;*2
        mov     edx,eax
        add     eax,eax                           ;*4
        add     eax,eax                           ;*8
        add     eax,edx                           ;*10

        .errnz  size ABC_FONT_ENTRIES - 10

        xchg    eax,ebx
        mov     edx,pFont

        ASSUME  edx:PTR FONTMAP, ebx:PTR ABC_FONT_ENTRIES

        movzx   edx,[edx].fsCharOffset[ebx].abc_a_space ;dx = Next characters a_space
        xchg    eax,ebx

        public  abcl_have_nexts_a_space

abcl_have_nexts_a_space::

        push    esi
        mov     esi,pFont

        ASSUME  esi:PTR FONTMAP, ebx:PTR ABC_FONT_ENTRIES

        movzx   eax,[esi].fsCharOffset[ebx].abc_b_space
        add     dx,[esi].fsCharOffset[ebx].abc_c_space ;dx = c_space plus next a_space
        pop     esi
        add     edx,char_extra
        push    ebx                              ;Must save char index
        test    accel,HAVE_WIDTH_VECT
        jz      abcl_have_width
        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]              ;Next starting pos is the DX increment
        add     ebx,4
        mov     lp_dx,ebx
        sub     edx,eax                          ;Remove character's width

;/*
;**
;**   DX contains any adjustment to the next characters starting
;**   position.  If this number is negative, then we'll be stepping
;**   over the previous character.
;**
;**   We don't allow a character to start before the previous
;**   character.  We'll have to enforce this at this point by
;**   making sure that any negative adjustment is less than or
;**   equal to the character width.
;**
;*/

abcl_have_width:

        or      edx,edx
        jge     abcl_adjustment_ok
        add     edx,eax                           ;Don't allow the backwards step
        sbb     ebx,ebx                           ;  to go beyond the start of this
        and     edx,ebx                           ;  character
        sub     edx,eax

abcl_adjustment_ok:

        pop     ebx                               ;Restore char index

abcl_very_first_enters_here:

        add     eax,edi                           ;AX = rhs of character
        cmp     clip.rcl_xLeft,eax
        jl      abcl_current_is_visible ;Some part of char is visible


;/*
;**
;**   If the adjustment for the character width is greater than the
;**   actual width of the character, then it is possible that the
;**   dummy pixels could become visible.  If the adjustment for the
;**   character width is less than the actual character width, then
;**   the dummy pixel (negative dummy pixels?) cannot become visible.
;**
;*/

        add     eax,edx                          ;Next char starts at AX
        mov     edi,clip.rcl_xLeft
        cmp     edi,eax
        jl      abcl_dummy_is_visible            ;Some part of dummy char became visible
        xchg    edi,eax                          ;Set start of next character

abcl_see_if_next:

        or      ecx,ecx
        jnz     abcl_next_char                   ;See if next character
        jmp     build_ret_addr                   ;Return to caller
ALIGN 4



;/*
;**
;**   The dummy character is the first character which became
;**   visible.  Just set the starting X to clip.rcl_xLeft, and shorten
;**   the width of the dummy character appropriately.
;**
;*/

abcl_dummy_is_visible:

        xchg    eax,edx                           ;Set DX = # pixels in dummy
        sub     edx,edi
        xor     eax,eax                           ;Show no real character
        jmp     abcl_all_done
ALIGN 4


;/*
;**
;**   So here we are, we have a character which will become visible,
;**   and possibly have some adjustment to the character.
;**
;**   Our registers currently contain:
;**
;**           AX = rhs of character
;**           BX = index into offset/width table
;**           CX = # of characters left in the string
;**           DX = # of extra pixels (zero or positive)
;**           DI = starting X offset
;**           ES = FontSeg
;**           DS:SI --> string
;**
;*/


abcl_current_is_visible:

        sub     eax,edi                          ;Restore width of the character
        push    esi
        mov     esi,pFont

        ASSUME  ebx:PTR FONT_ENTRIES

        mov     ebx,[esi].fsCharOffset[ebx].fe_dBits
        pop     esi
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping


;/*
;**
;**  Instead of incrementing the current position by 8 and
;**  having to recover the real current position, we just
;**  slide the clip region left.  It has the same effect.
;**
;*/

        push    edx                              ;Save extra pixels to be added
        mov     edx,clip.rcl_xLeft
        sub     edx,edi                          ;Compute bits until we're visible
        je      abcl_save_first                  ;Starts on clip edge
        sub     edx,8                            ;Is current byte visible?
        jl      abcl_have_vis_start              ;  Yes

abcl_step_clipped_char:

        sub     eax,8                            ;Shorten the width of the character
        add     edi,8                            ;Move current position right
        add     bx,lfd.font_height               ;Move to next column of character
        sub     edx,8                            ;Is current byte visible?
        jge     abcl_step_clipped_char           ;  No


;/*
;**
;**  If the lhs of the clip region and the starting X of the
;**  character are in different bytes, then the FIRST_IN_PREV
;**  flag must be set.  Only a clipped character can set this
;**  flag.
;**
;*/

abcl_have_vis_start:

        mov     edx,clip.rcl_xLeft
        xor     edx,edi
        and     edx,not 7
        jz      abcl_save_first                  ;In same byte
        or      excel,FIRST_IN_PREV


;/*
;**
;**  We have the start of the first character which is visible.
;**  We let the routine we're about to call push the character
;**  since it will handle both right clipping (if needed) and
;**  fat characters.
;**
;*/

abcl_save_first:

        pop     edx                               ;Restore extra pixels

abcl_all_done:

        jmp     abc_clip_enters_here
ALIGN 4

;/*
;**
;**   abc - worse case, with rhs clipping
;**
;**   Currently:
;**           DS:SI --> current character in the string
;**           ES:    =  font segment
;**           DI     =  current X position
;**           CX     =  number of bytes left in the string
;**
;*/

        public  abc_next_char

abc_next_char::

        xor     edx,edx                          ;Zero extra pels so far
        lodsd                                    ;Get this char's offset
        add     eax,eax                          ;*2
        mov     ebx,eax
        add     eax,eax                          ;*4
        add     eax,eax                          ;*8
        add     ebx,eax                          ;*10

        .errnz  size ABC_FONT_ENTRIES - 10

        dec     ecx                              ;If last character, don't try and
        jz      abc_have_nexts_a_space           ;  load next char's "a" space
        mov     eax,DWORD PTR [esi]
        add     eax,eax                          ;*2
        mov     edx,eax
        add     eax,eax                          ;*4
        add     eax,eax                          ;*8
        add     eax,edx                          ;*10

        .errnz  size ABC_FONT_ENTRIES - 10

        xchg    eax,ebx
        mov     edx,pFont

        ASSUME  ebx:PTR ABC_FONT_ENTRIES

        movzx   edx,[edx].fsCharOffset[ebx].abc_a_space
        xchg    eax,ebx

        public  abc_have_nexts_a_space

abc_have_nexts_a_space::

        push    esi
        mov     esi,pFont
        movzx   eax,[esi].fsCharOffset[ebx].abc_b_space
        add     dx,[esi].fsCharOffset[ebx].abc_c_space
        add     edx,char_extra
        mov     ebx,[esi].fsCharOffset[ebx].abc_fe_dBits
        pop     esi
        add     ebx,amt_clipped_on_top           ;Adjust pointer for any clipping
        push    ebx
        test    accel,HAVE_WIDTH_VECT
        jz      abc_have_width
        mov     ebx,lp_dx
        mov     edx,DWORD PTR [ebx]
        add     ebx,4
        mov     lp_dx,ebx
        sub     edx,eax                          ;Remove character's width
                                                 ;DX is user's incr-b_space

;/*
;**    AX is b_space of current character
;**
;**    DX contains any adjustment to the next characters starting
;**    position.  If this number is negative, then we'll be stepping
;**    over the previous character.
;**
;**    We don't allow a character to start before the previous
;**    character.  We'll have to enforce this at this point by
;**    making sure that any negative adjustment is less than or
;**    equal to the character width.
;**
;*/

        public  abc_have_width

abc_have_width::

        or      edx,edx
        jge     abc_adj_is_ok
        add     edx,eax                          ;Don't allow the backwards step
        sbb     ebx,ebx                          ;  to go beyond the start of this
        and     edx,ebx                          ;  character
        sub     edx,eax

abc_adj_is_ok:

        pop     ebx                              ;Restore bits offset

        public  abc_clip_enters_here

abc_clip_enters_here::

        or      eax,eax                          ;If character width is 0,
        jz      abc_check_dummy                  ; might still have dummy char
        or      edx,edx                          ;Any adjustment to the width?
        jle     abc_have_adj_width               ;No extra pixels to add
        push    ebx                              ;Add as many extra pixels as possible
        mov     ebx,eax                          ;  into the empty space of this
        neg     eax                              ;  character
        and     eax,7                            ;AX = # extra pixels which will fit
        jz      abc_have_entire_width            ;None will fit
        cmp     eax,edx
        jl      abc_have_what_fits               ;Some extra pixels will not fit
        mov     eax,edx                          ;All pixels will fit, make DX = 0

abc_have_what_fits:

        sub     edx,eax                          ;DX = extra for the dummy character

abc_have_entire_width:

        add     eax,ebx                          ;Set number of pixels to use in char
        pop     ebx

        public  abc_have_adj_width

abc_have_adj_width::

        mov     num_null_pixels,edx              ;Save number of dummy pixels
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     abc_char_is_clipped              ;Clipped (or first pixel of next is)

abc_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If character is less than 8 bits
        jbe     abc_width_ok                     ;  wide, push it's data

abc_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        add     bx,lfd.font_height               ;  of the remaining width
        cmp     eax,8
        ja      abc_still_wide

abc_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits
        mov     edx,num_null_pixels

abc_check_dummy:

        xchg    eax,edx                          ;Just incase we go backwards
        or      eax,eax
        jz      abc_see_if_next                  ;No pixels for justification
        jl      abc_going_back
        mov     ebx,null_char_offset
        mov     edx,edi                          ;Compute phase
        and     dl,7
        add     edi,eax                          ;DI = next char's X position
        cmp     edi,clip.rcl_xRight
        jge     abc_dummy_is_clipped             ;Clipped (or first pixel of next is)

abc_dummys_been_clipped:

        mov     dh,dl                            ;Need phase in DH
        mov     dl,8                             ;Need 8 for size in DL if fat
        cmp     eax,8                            ;If dummy is less than 8 bits
        jbe     abc_dummy_width_ok               ;  wide, push it's data

abc_dummy_still_wide:

        push    edx                              ;Push data showing phase,
        push    ebx                              ;  character is 8 wide, then
        sub     eax,8                            ;  create another character
        cmp     eax,8
        ja      abc_dummy_still_wide

abc_dummy_width_ok:

        mov     ah,dh
        push    eax                              ;Save width and phase
        push    ebx                              ;Save offset to bits

abc_see_if_next:

        cmp     esp,min_stack                    ;Stack compare must be unsigned
        jb      abc_restart                      ;Not enough stack for another character
        or      ecx,ecx
        jle     abc_all_done
        jmp     abc_next_char                    ;Until all characters pushed
ALIGN 4

abc_all_done:

        mov     eax,edi                          ;Next character starts here
        jmp     build_ret_addr
ALIGN 4


;/*
;**
;**   The dummy is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**   Adjust it's width so it fits, set the remaining character
;**   count to 0 so the loop will terminate, and reenter the
;**   code where we came from.
;**
;**   If the width adjustment was negative, we would never have
;**   reached this code, so ignore any restart.
;**
;*/

abc_dummy_is_clipped:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,ecx                          ;Set new rhs
        xor     ecx,ecx                          ;Show this as last character
        jmp     abc_dummys_been_clipped          ;Finish here
ALIGN 4



;/*
;**
;**   This character is either clipped, or it's last pixel is
;**   the last pixel which will fit within the clipping rhs.
;**
;**   If there is a  negative adjustment to the width of the
;**   character, it is possible that the next character could
;**   be partially visible.  We'll have set up for a restart
;**   if this is the case.
;**
;**   If this is the last character of the string, then there
;**   is no problem
;**
;**   If no negative adjustment, adjust it's width so it fits,
;**   set the remaining character count to 0 so the loop will
;**   terminate, and reenter the code where we came from.
;**
;*/

abc_char_is_clipped:

        push    edx                              ;If num_null_pixels < 0, then
        mov     edx,num_null_pixels              ;  a restart might be possible
        or      edx,edx
        jl      abc_might_need_restart           ;Might need a restart

abc_clipped_no_restart:

        mov     ecx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,ecx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,ecx                          ;Set new rhs
        xor     ecx,ecx                          ;Don't want any extra pixels
        mov     num_null_pixels,ecx
        pop     edx                              ;CX 0 to show no more chars
        jmp     abc_been_clipped                 ;Finish here
ALIGN 4


;/*
;**
;**       Might be looking a restart in the face.  Compute where the
;**       next character would start, and if it is to the left of
;**       clip.rcl_xRight, then a restart is needed.
;**
;*/

abc_might_need_restart:

        or      ecx,ecx                          ;If last character
        jz      abc_clipped_no_restart           ;  then no restart needed
        add     edx,edi                          ;Compute next starting x
        sub     edx,clip.rcl_xRight
        jge     abc_clipped_no_restart           ;Rest of string is clipped


;/*
;**
;**   Will have to force a restart for the next character.  We
;**   can do this by computing the number of pixels between where
;**   where the next character starts and clip.rcl_xRight.  This
;**   negative number will be stuffed into num_null_pixels, so when
;**   we reenter the main loop, we'll force a restart after pushing
;**   the character data,
;**
;*/

        mov     num_null_pixels,edx
        mov     edx,clip.rcl_xRight              ;Compute number of pixels
        sub     edi,edx                          ;  which have to be clipped
        sub     eax,edi                          ;Set new character width
        mov     edi,edx                          ;Set new rhs
        pop     edx
        jmp     abc_been_clipped                 ;Finish here
ALIGN 4


;/*
;**
;**  I have the unfortunate task of informing you that the next
;**  character will start somewhere in the middle of the current
;**  character.  If this is the last character of the string,
;**  then nothing need be done.  If this isn't the last character,
;**  then a restart will be needed.
;**
;*/

abc_going_back:

        add     eax,edi                           ;Set position of next character
        jmp     build_ret_addr
ALIGN 4



;/*
;**
;**  Out of stack space.  Set up for a restart.
;**
;*/

abc_restart:

        mov     eax,edi
        jmp     build_ret_addr
ALIGN 4

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  pad_left_hand_side
;*
;* DESCRIPTION   = This routine is called when the text string isn't clipped on the
;*                 left side.  It attempts to pad the character with 0's if at all
;*                 possible.
;*
;*                 If we can pad to the left of the first character with 0's, then
;*                 we can use the full byte code, which is many times faster than
;*                 the partial byte code.
;*
;*                 If we do pad, we must update both current_lhs and the starting
;*                 X coordinate which will be used by the main loop code.
;*
;*                 Registers Preserved:
;*                       CX,SI,DI,DS,ES,FLAGS
;*                 Registers Destroyed:
;*                       AX,BX,DX
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position (and therefore starting x)
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 CX     =  number of characters in the string
;*                 DI     =  current X position
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

pad_left_hand_side::

        mov     eax,edi                          ;Get starting x coordinate
        and     eax,7                            ;Address MOD 8 is amount to pad
        jz      plhs_all_done                    ;At byte boundary, don't need padding


;/*
;**  If we're in transparent mode, we can always create a dummy
;**  character of 0's for the left side to avoid any left clipping
;*/

        test    accel,IS_OPAQUE                  ;If in transparent mode, we can
        jz      plhs_add_padding                 ;  always add the padding


;/*
;**  In opaque mode.  If there is an opaquing rectangle, try and pad
;**  the left side up to the byte boundary. If we cannot make it to
;**  the byte boundary, go as far as we can so that the opaquing code
;**  can skip the byte.
;**
;**  If there isn't an opaque rectangle, we cannot do any padding.
;*/

        mov     bl,excel
        not     bl
        test    bl,OPAQUE_RECT+BOUNDED_IN_Y
        jnz     plhs_all_done                    ;Cannot add any padding
        mov     ebx,edi
        sub     ebx,o_rect.rcl_xLeft
        jle     plhs_all_done                    ;Cannot add any.  Darn
        smin_ax ebx                              ;Set AX = number of bits to pad

;/*
;**   Some number of 0's can be added to the left of the character.
;**   Add them, then move the lhs left by that amount.
;*/

plhs_add_padding:

        mov     edx,edi                          ;DI = start x = text_bbox.rcl_xLeft
        sub     edx,eax                          ;Set new lhs of text bounding box
        mov     current_lhs,edx
        mov     ah,dl                            ;Set phase (x coordinate) of char
        and     ah,7
        pop     edx
        push    eax                              ;Width and phase of null character
        push    null_char_offset                 ;Offset in font of null character
        jmp     edx
ALIGN 4

plhs_all_done:

        ret

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  pad_right_hand_side
;*
;* DESCRIPTION   = This routine is called once the text string has been pushed onto
;*                 the stack.  It pads the character on the right up to a byte
;*                 boundary if possible.
;*
;*                 We always pad out to a byte boundary with 0 since it makes the
;*                 stopping condition in the driving loop simpler since it never
;*                 has to check to see if it has any more bytes of data left, it
;*                 knows it does.  It just checks after each destination column
;*                 has been output to see if anything else exists.
;*
;*                 The clipping mask will be computed to the last pixel we can
;*                 alter.  In transparent mode, this will always be to the byte
;*                 boundary.  In opaque mode, it depends on the opaquing rectangle.
;*
;*                 Registers Preserved:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*                 Registers Destroyed:
;*                       AX,BX,DX
;*
;*
;* INPUT         = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 DI     =  X position where next char would have gone
;*
;* OUTPUT        = DS:SI --> current character in the string
;*                 ES:    =  font segment
;*                 DI     =  rhs of the string, padded to boundary if possible
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = 'C' set if no interval to output
;*
;**************************************************************************/

ALIGN 4
pad_right_hand_side::

        mov     eax,edi                          ;If next char would start at bit 0,
        and     al,7                             ;  then no padding is required.
        jz      prhs_have_rhs                    ;No padding needed
        mov     ah,al                            ;Save phase
        neg     al                               ;Set width needed
        and     al,7
        pop     ebx
        push    eax                              ;Width and phase of null character
        push    null_char_offset                 ;Offset in font of null character
        push    ebx
        movzx   eax,al                           ;Only want width of the nulls in AX
        test    accel,IS_OPAQUE                  ;If in transparent mode
        jz      prhs_new_rhs                     ;  move text_bbox to byte boundary
        mov     bl,excel
        not     bl
        test    bl,OPAQUE_RECT+BOUNDED_IN_Y
        jnz     prhs_have_rhs                    ;Cannot alter actual rhs
        mov     ebx,o_rect.rcl_xRight            ;Compute distance from where I am to
        sub     ebx,edi                          ;  where opaque rectangle ends
        jle     prhs_have_rhs                    ;Opaque rect is left of text end
        smin_ax ebx                              ;Compute amount to move rhs

prhs_new_rhs:

        add     edi,eax                          ;Slide rhs right

prhs_have_rhs:

        ret

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  process_stack_data
;*
;* DESCRIPTION   = The non-overlapped data which has been accumulated on the stack
;*                 is output using the supplied dispatch tables.
;*
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI
;*                 Registers Preserved:
;*                       DS,ES,BP
;*
;* INPUT         = GRAF_ENAB_SR set for all planes enabled
;*                 Tonys_bar_n_grill initialized to background color
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
process_stack_data::


;/*
;**
;**    The EGA needs a little special handling at this point.
;**
;**    The full opaque byte code expects the EGA to be correctly
;**    programmed for it's needs.  The EGA opaque partial byte
;**    code will correctly restore the adapter to the needed mode
;**    if it is called and RES_EGA_INNER is set.
;**
;**    So, if there is no first byte and we're in opaque mode,
;**    we must program the ega correctly for the inner loop.
;**    If there is a first byte and an inner loop, then we want
;**    to set the RES_EGA_INNER flag.
;**
;**    If in transparent mode, we must set the set/reset register
;**    to the foreground (text) color.
;**
;*/


psd_pre_proc:

        DebugMsg <process_stack_data BUILDSTR CLIFFL>

        mov     edi,lp_surface                   ;--> destination surface
        mov     bl,excel
        test    bl,IS_DEVICE                     ;Only preprocess if this is
        jz      psd_pp_done                      ;  the EGA
        test    accel,IS_OPAQUE
        jz      psd_pp_ega_trans
        mov     ecx,inner_byte_count
        jecxz   psd_pp_done                      ;No inner loop, don't set the bit
        or      bl,RES_EGA_INNER                 ;Assume first byte
        cmp     left_clip_mask,0
        jne     psd_pp_have_first                ;First byte
        call    set_ega_opaque_mode              ;No first, must set for inner loop
        jmp     psd_pp_done
ALIGN 4

psd_pp_have_first:

        mov     excel,bl                         ;Show reset needed
        jmp     psd_pp_done
ALIGN 4

psd_pp_ega_trans:

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ah,byte ptr colors[FOREGROUND]
        mov     al,GRAF_SET_RESET
        out     dx,ax

psd_pp_ega_xor:

        mov     al,accel
        mov     ah,DR_XOR
        shr     al,2
        jc      psd_pp_ega_have_mode

        .errnz  IS_XOR_TEXT-00000010b

        mov     ah,DR_OR
        shr     al,1
        jc      psd_pp_ega_have_mode

        .errnz  IS_OR_TEXT-00000100b

        mov     ah,DR_SET

psd_pp_ega_have_mode:

        mov     al,GRAF_DATA_ROT
        out     dx,ax

psd_pp_ega_no_xor:

        mov     ax,MM_ALL shl 8 + GRAF_ENAB_SR
        out     dx,ax
        mov     al,GRAF_BIT_MASK                 ;Transparent mode expects GRAF_ADDR
        out     dx,al                            ;  to be set to the bitmask register

psd_pp_done:

        mov     eax,text_bbox.rcl_yBottom        ;Compute Y component of the string
        mul     next_scan
        add     edi,eax
        add     edi,scan_start                   ;Add delta into scan



;       It is possible that the first character requires clipping.
;       If the clip mask is non-zero, then this is the case.


        mov     ebx,non_clipped_table            ;Table if not clipped
        mov     dl,0FFh                          ;initialize OR-mask for clipped bits
        mov     al,left_clip_mask
        or      al,al
        jz      psd_not_clipped                  ;No partial first byte
        inc     inner_byte_count                 ;One extra time through the loop
        mov     ebx,clipped_table
        inc     dl                               ;set OR-mask to 00h

psd_not_clipped:

        or      al,dl                            ;if no clipping, set AL to 0FFh
        mov     ss_clip_mask,al
        mov     al,16                            ;Looking to cross two byte boundaries
        test    excel,FIRST_IN_PREV              ;Does first char span a boundary?
        jnz     psd_collect_chars                ;Need to cross two
        mov     al,8                             ;Only 1 byte boundary (BL = 8)

;/*
;** This is the start of the real loop where we zip through
;** all the data we pushed onto the stack.
;*/

psd_collect_chars:

        xor     esi,esi                          ;Dispatch table index
        mov     edx,ebp                          ;Save frame pointer
        mov     ebp,buffer                       ;--> next character's data

        ASSUME  ebp:PTR frame_data

        movzx   ecx,word ptr [ebp].fd_width      ;CH = X position, CL = width

        errnz   frame_data.fd_phase-frame_data.fd_width-1

        add     ch,cl                            ;CH = next char's start bit position
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-1 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-2 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-3 * size frame_data]
        cmp     ch,al
        jb      psd_unlikely_cases
        ja      psd_have_more_than_enough
;       je      psd_have_exact_fit

psd_have_exact_fit:

        mov     ecx,esi                          ;# of source character involved
        shl     esi,2                            ;Compute index into dispatch table
        mov     ebx,DWORD PTR [ebx][esi] ;Get address of drawing function
        shl     esi,1

        errnz   <(size frame_data) - 8>

        neg     esi                              ;Going backwards
        lea     eax,[ebp][-(size frame_data)][esi]
        xchg    ebp,edx                          ;Restore frame pointer
        mov     buffer,eax                       ;Save new buffer pointer


;/*
;**  Call the procedure with the following register variables
;**
;**      DS:     =  Font bits segment
;**      ES:DI  --> destination
;**      DX      =  pointer to frame data
;**      CX      =  # of source characters - 1
;**      AX      =  Visible height
;*/

        mov     eax,clipped_font_height

        DebugMsg < call 1 BUILDSTR CLIFFL>

        call    ebx                              ;Invoke drawing routine

        DebugMsg < call 1 return BUILDSTR CLIFFL>

        mov     al,8                             ;First doesn't cross byte boundary
        mov     ebx,non_clipped_table            ;No clipping required
        mov     ss_clip_mask,0FFh
        dec     inner_byte_count
        jg      psd_collect_chars
        jmp     psd_see_about_last
ALIGN 4

psd_have_more_than_enough:

        mov     ecx,esi                          ;# of source character involved
        shl     esi,2                            ;Compute index into dispatch table
        mov     ebx,DWORD PTR [ebx][esi] ;Get address of drawing function
        shl     esi,1

        errnz   <(size frame_data) - 8>

        neg     esi                              ;Going backwards
        lea     eax,[ebp][esi]
        xchg    ebp,edx                          ;Restore frame pointer
        mov     buffer,eax                       ;Save new buffer pointer

;/*
;**  Call the procedure with the following register variables
;**
;**      DS:     =  Font bits segment
;**      ES:DI  --> destination
;**      DX      =  pointer to frame data
;**      CX      =  # of source characters - 1
;**      AX      =  Visible height
;*/

        mov     eax,clipped_font_height

        DebugMsg < call 2 BUILDSTR CLIFFL>

        call    ebx                              ;Invoke drawing routine

        DebugMsg < call 2 return BUILDSTR CLIFFL>

        mov     al,16                            ;First crosses byte boundary
        mov     ebx,non_clipped_table            ;No clipping required
        mov     ss_clip_mask,0FFh
        dec     inner_byte_count
        jle     psd_see_about_last
        jmp     psd_collect_chars
ALIGN 4


psd_unlikely_cases:

        inc     esi
        add     ch,[ebp].fd_width[-4 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-5 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-6 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        je      psd_have_exact_fit

        inc     esi
        add     ch,[ebp].fd_width[-7 * size frame_data]
        cmp     ch,al
        ja      psd_have_more_than_enough
        jmp     psd_have_exact_fit               ;It had to have fit!
ALIGN 4


psd_see_about_last:

        and     excel,not RES_EGA_INNER          ;Don't restore ega after partial byte
        xor     ecx,ecx                          ;Zero last byte clipped mask
        xchg    cl,right_clip_mask               ;  and get it
        ;SEL 1-27-92
        jecxz   psd_exit                         ;No last byte to deal with
        mov     ss_clip_mask,cl                  ;have last byte, set clip mask
        mov     ebx,clipped_table                ;Must use the clipped table
        jmp     psd_collect_chars
ALIGN 4

psd_exit:

        ret

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  set_ega_opaque_mode
;*
;* DESCRIPTION   = The ega is programmed for entire byte opaquing
;*
;*                 Registers Destroyed:
;*                       AX, DX
;*                 Registers Preserved:
;*                       BX,CX,SI,DI,DS,ES
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  set_ega_opaque_mode

ALIGN 4
set_ega_opaque_mode::

        mov     dx,EGA_BASE + SEQ_DATA
        mov     al,MM_ALL
        out     dx,al
        mov     eax,colors
        mov     dl,accel
        test    dl,IS_XOR_TEXT or IS_OR_TEXT
        jz      seom_overwrite_text
        test    dl,IS_XOR_TEXT
        mov     dl,GRAF_ADDR
        jnz     seom_xor_text

;/*
;** ORed mode text doesn't use the EGA in XOR mode.  Instead we just use
;** the set/reset register for any plane where the foreground color is
;** the same as the background color, or the background color is a one,
;** and use the character pattern where the foreground color is a 1 and
;** the background is 0:
;**
;**       fgcolor  bkcolor |  result
;**       -------  ------- |  ------               This assumes that the character
;**          0        0    | bkcolor               pattern is a 1 where the fore-
;**          0        1    | bkcolor               ground color is to be used and
;**          1        0    | character             a 0 where the background color
;**          1        1    | bkcolor               is to be used.
;*/


        mov     al,GRAF_SET_RESET
        out     dx,ax

        .errnz  BACKGROUND-1                      ;Must be in AH

        mov     al,byte ptr colors[FOREGROUND]
        not     ah
        and     ah,al                             ;1 wherever FG = 1 and BK = 0
        not     ah                                ;1 wherever set/reset to be used
        mov     al,GRAF_ENAB_SR
        out     dx,ax
        mov     ax,DR_SET shl 8 + GRAF_DATA_ROT
        jmp     seom_finish_up
ALIGN 4

;/*
;** For XORed opaque text, use the EGA in XOR mode. !!!expand on this
;**
;**       fgcolor  bkcolor |  result
;**       -------  ------- |  ------               This assumes that the character
;**          0        0    | bkcolor               pattern is a 1 where the fore-
;**          0        1    | bkcolor               ground color is to be used and
;**          1        0    | character             a 0 where the background color
;**          1        1    | ~character            is to be used.
;**
;** The set/reset is loaded with 0's so that the xor has no effect.
;** The planes where the foreground color is zero will be enabled for
;** set/reset.  Other planes will take the host data and xor it into
;** the background color stored in the latches.
;*/

seom_xor_text:

        mov     ax,GRAF_SET_RESET                ;Want 0's for color
        out     dx,ax
        mov     ah,byte ptr colors[FOREGROUND]
        jmp     seom_xor_text_finish
ALIGN 4

seom_overwrite_text:

        xor     ah,al
        mov     al,GRAF_SET_RESET
        mov     dl,GRAF_ADDR
        out     dx,ax

seom_xor_text_finish:

        not     ah
        mov     al,GRAF_ENAB_SR
        out     dx,ax
        mov     ax,DR_XOR shl 8 + GRAF_DATA_ROT

seom_finish_up:

        out     dx,ax
        mov     ax,0FFh shl 8 + GRAF_BIT_MASK
        out     dx,ax
        push    ecx
        mov     ecx,pVRAMInstance

ifdef EGAMEM_IS_STRUCT
        ASSUME  ecx:PTR EGAMem
        mov     al,[ecx].tonys_bar_n_grill       ;Inintialize EGA latches (not used
else
        ASSUME  ecx:NOTHING
        OVAccess        ;access offscreen VRAM
        mov     al,[ecx+tonys_bar_n_grill]       ;Inintialize EGA latches (not used
        OVRestore       ;Restore VGA to previouse state
endif
        pop     ecx
        ret                                      ;  for OR mode text)

        page
;/***************************************************************************
;*
;* PUBLIC ROUTINE  prepare_for_overlap
;*
;* DESCRIPTION   = We will step backwards somewhere within the string.  Opaque the
;*                 area where the string will go and set transparent mode for the
;*                 actual text output routine.
;*
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES
;*                 Registers Preserved:
;*                       None
;*
;* INPUT         = bl = accel
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = to build_all_done if nothing will show
;*
;**************************************************************************/

        public prepare_for_overlap

ALIGN 4
prepare_for_overlap::

        mov     eax,clip.rcl_xRight
        mov     ecx,x                             ;x is screen lhs
        mov     ebx,rhs                           ;Zero based rhs
        add     ebx,ecx    ;SEL 1-27-92   ;BX = screen rhs      ;          
        ;movsx   EBX,BX   ;SEL 1-27-92                          ;          
        jo      pfo_have_rhs            ;Use clip.rcl_xRight for right side
        smin_ax ebx

pfo_have_rhs:

        xchg    eax,ebx                          ;Need rhs in BX
        mov     eax,clip.rcl_xLeft
        smax_ax ecx
        cmp     ebx,eax
        jle     pfo_exit_nothing_shows           ;Null or negative interval


;/*
;**
;**   The interval will show.  Dummy this up as a call to the
;**   opaque rectangle code to output the bounding box.  Since
;**   TEXT_VISIBLE was cleared by build_string, the opaque code
;**   will not perform an intersection of text_bbox and o_rect.
;**
;*/

        mov     wc_opaque_lhs,eax                ; Save lhs/rhs incase we have an
        mov     wc_opaque_rhs,ebx                ;   opaquing rectangle
        or      wc_flags,WC_SET_LR               ; Set left/right into text bbox
        push    o_rect.rcl_xLeft                 ; Save real o_rect bbox
        push    o_rect.rcl_xRight
        push    o_rect.rcl_yBottom
        push    o_rect.rcl_yTop
        mov     ecx,text_bbox.rcl_yBottom
        mov     edx,text_bbox.rcl_yTop
        mov     o_rect.rcl_xLeft,eax; Set text bbox as area to opaque
        mov     o_rect.rcl_xRight,ebx
        mov     o_rect.rcl_yBottom,ecx
        mov     o_rect.rcl_yTop,edx
        mov     bl,excel
        call    output_o_rect
        pop     o_rect.rcl_yTop
        pop     o_rect.rcl_yBottom
        pop     o_rect.rcl_xRight
        pop     o_rect.rcl_xLeft

        and     accel,not IS_OPAQUE              ; Will output text in transparent mode

pfo_exit:

        ret

ALIGN 4
pfo_exit_nothing_shows:

        pop     eax
        jmp     build_restore_opaque
ALIGN 4

build_string    ENDP
_TUNE ENDS
        end
