;-----------------------------Module-Header-----------------------------;
; Module Name: BUILDSTR.ASM 						; 
; This module contains the strblt functions which build 		; 
; up data on the stack for the actual output routines 			; 
;									;
; Created 17-Apr-1987 							; 
; Author: Walt Moore [waltm] 						; 
;									;
; Copyright (c) 1991 Microsoft Corporation 				; 
;-----------------------------------------------------------------------;
	.xlist
	include cmacros.inc
	include mflags.inc
	include gdidefs.inc
	include display.inc
	include macros.inc
	include strblt.inc
	include fontseg.inc
	.list

NLABEL	macro n
ifdef _286
public n&_286
n&_286:
else
public n&_386
n&_386:
endif
endm

PLABEL	macro n
n:
ifdef _286
public n&_286
n&_286:
else
public n&_386
n&_386:
endif
endm


ifdef _286
	externNP	comp_byte_interval_286
	externNP	worst_case_ext_286
	externNP	output_o_rect_286
 	externNP	DrawString_286
else
	externNP	comp_byte_interval_386
	externNP	worst_case_ext_386
	externNP	output_o_rect_386
 	externNP	DrawString_386
endif
	externA 	stack_top	;Stack probe location

ifndef _286	
;----------------------------------------------------------------------------;
; The following macro is a used as a part of the code to add a word to ebx   ;
; the word is added to bx and this macro adds 10000h to ebx if a carry is    ;
; generated.							             ;
;----------------------------------------------------------------------------;

updc_ebx  macro
	local	no_carry_from_bx

	jnc	short no_carry_from_bx
	add	ebx,10000h
no_carry_from_bx:
	endm
endif

;----------------------------------------------------------------------------;

ifdef _286
createSeg _REAL,rCode,word,public,CODE
sBegin	rCode
	.286p

	assumes cs,rCode
else

createSeg _PROTECT,pCode,word,public,CODE
sBegin	pCode
	.386p
	assumes cs,pCode
endif

;---------------------------Public-Routine------------------------------;
; build_string
;
;   build_string builds up data on the stack consisting of character
;   offsets and widths, then invokes the routine which processes
;   this data.
;
; Entry:
;	stack frame as per strblt
; Returns:
;	none
; Error Returns:
;	none
; Registers Preserved:
;	BP
; Registers Destroyed:
;	AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
; Calls:
;
; History:
;       Thu 06-Apr-1989 09:55:20 -by-  Amit Chatterjee [amitc]
;	Modified.
;          This TextOut function has 80386 soecific code in it, so
;          will non run on 8086 or 80286 protected mode. So now all
;          the text function is being put in a separate fixed segment
;          with the 8086 TextOut appearing in another fixed segment and
;          one of these two segments chosen at enable time.	
;		. Moved code to _PROTECT segment
;		. prefixed a 'p_' to all public labels
;
;	Fri 27-Jan-1989 14:18:04 -by-  Amit Chatterjee [amitc]
;       Modified code to support >64k fonts. 
;                . The header in the font segment now has 6 byte entries
;                  per character, with the last 4 bytes being a 32 bit 
;                  pointer to the bits in the same segment.	
;		
;		 . At this point 16 bit code and 16 bit data is still being
;                  used, however where ever necessary we have used the 
;		   extended register set and the address override to take
;		   advantage of 32 bit code and data capabilities.
;
;       Wed 18-Jan-1989 15:34:10 -by-  Amit Chatterjee [amitc]
;       included 16 bit gathering code from the stack and included
;       code for emboldening
;
;	Wed 06-May-1987 16:51:42 -by-  Walt Moore [waltm]
; wrote it
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,nothing

ifdef _286
define_frame strblt_dummy1_286		;Define strblt's frame
else
define_frame strblt_dummy1_386		;Define strblt's frame
endif
cBegin	<nogen>
cEnd	<nogen>

ifdef _286
public	build_string_286
build_string_286	proc	near
else
public	build_string_386
build_string_386	proc	near
endif
	mov	ax,ss:stack_top	 	;Compute minimum allowable SP
	add	ax,STACK_SLOP
	mov	min_stack,ax

	xor	ax,ax
	mov	nBackups,ax		;Initially, no backup tokens on stack.

	mov	ax,text_bbox.left	;Set up text bbox to be a null interval
	mov	text_bbox.right,ax	;  We'll grow it as we progress
	mov	current_lhs,ax		;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
	mov	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	cx,offset non_justified_text
	test	bl,WEIRD_SPACING
	jz	short build_have_proc

	mov	cx,offset justified_text	
	test	bl,NEG_SPACING+HAVE_WIDTH_VECT
	jz	short build_have_proc

PLABEL build_worst_ok
	mov	cx,offset worst_case
	mov	bl,accel
	mov	ah,excel

PLABEL build_have_proc
	mov	build_proc,cx		;Save build routine address

PLABEL build_restart
	mov	clear_stack,sp		;Used to clean the stack on exit
	mov	ax,sp			;Save buffer start pointer
	dec	ax
	dec	ax
	mov	buffer,ax

;-----------------------------------------------------------------------;
;	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)
;-----------------------------------------------------------------------;
	lds	si,lp_string
	assumes ds,nothing

	mov	es,seg_lp_font
	assumes es,FontSeg

	mov	al,excel
	mov	ah,accel
	mov	cx,count
	mov	di,x
	jmp	build_proc

PLABEL build_ret_addr 			;build routines return here
	mov	count,cx		;Save count
	xchg	x,ax			;Save next char's start
	mov	xOrigin,ax		;Save off original starting x.
	mov	off_lp_string,si	;Save next character
	call	pad_right_hand_side	;Fill to byte boundary if possible
	mov	current_rhs,di		;Save rhs
	mov	bx,di			;Compute the interval data
	mov	dx,current_lhs
ifdef _286
	call	comp_byte_interval_286
else
	call	comp_byte_interval_386
endif
	jc	short build_all_done		;If nothing shows, we're done
	mov	left_clip_mask,al
	mov	right_clip_mask,ah
	mov	inner_byte_count,si
	mov	scan_start,di
	push	0ffffh			;Push end sentinel.
ifdef _286
	call	DrawString_286
else
	call	DrawString_386
endif

PLABEL build_clr_stack
	mov	sp,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	short build_no_o_rect
	mov	ax,current_lhs
	mov	bx,text_bbox.left
	min_ax	bx
	mov	text_bbox.left,ax
	mov	ax,current_rhs
	mov	bx,text_bbox.right
	max_ax	bx
	mov	text_bbox.right,ax

PLABEL build_no_o_rect
	mov	cx,count		;If no more characters
	jcxz	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	di,x
	mov	ax,di			;Assume start will be current_lhs
	test	accel,IS_OPAQUE 	;When opaque, must clip on
	jnz	short build_clip_next_time  ;  a restart
	cmp	di,clip.left
	jge	short build_no_clip_next_time

PLABEL build_clip_next_time
	or	bl,CLIPPED_LEFT 	;Clipping is required
	mov	ax,clip.left		;  and clip.left for clipping check
	max_ax	di			;  (but only if start X > old clip
	mov	clip.left,ax		;   lhs)

PLABEL build_no_clip_next_time
	mov	excel,bl
	mov	current_lhs,ax		;Need to update lhs for interval calc
	jmp	build_restart		;Try next part of the string

PLABEL build_all_done
	mov	sp,clear_stack

PLABEL build_restore_opaque
	mov	al,wc_flags
	test	al,WC_SET_LR		;If we stepped backwards, we'll
	jz	short build_really_done     ;  have to restore the real lhs
	mov	bx,wc_opaque_lhs	;  and rhs incase we have an
	mov	text_bbox.left,bx	;  opaque rectangle
	mov	bx,wc_opaque_rhs
	mov	text_bbox.right,bx

PLABEL 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

ifdef _286
build_string_286 endp
else
build_string_386 endp
endif

;---------------------------Public-Routine------------------------------;
; non_justified_text
;
;   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
;
; 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)
;	stack frame as per strblt
; Returns:
;	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
; Error Returns:
;	none
; Registers Preserved:
;	BP
; Registers Destroyed:
;	AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
; Calls:
;	None
; History:
;	Tue 05-May-1987 18:27:29 -by-  Walt Moore [waltm]
; wrote it
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,FontSeg

non_justified_text proc near

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

;-----------------------------------------------------------------------;
;
;	Fixed pitch, no justification, left hand clipping
;
;-----------------------------------------------------------------------;
	mov	bx,lfd.font_width	;Fixed pitch font.

sfcl_next_char:
	add	di,bx			;Does this character become visible?
	cmp	dx,di			;DX is clip.left
	jl	short sfcl_current_is_visible ;This char is visible
	inc	si
	loop	sfcl_next_char		;See if next character

sfcl_no_chars_visible:
	jmp	build_ret_addr		;Return to caller

sfcl_current_is_visible:
	sub	di,bx			;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:
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	jbe	short spcl_good_character
	mov	al,lfd.default_char	;Character was out of range

spcl_good_character:
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	mov	bx,ax
	shl	bx,2
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
endif
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	add	di,ax			;0 width chars won't change x position
	cmp	dx,di			;DX is clip.left
	jl	short 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

spcl_current_is_visible:
	sub	di,ax			;Restore starting x of character
ifdef _286
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
else
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
endif
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

;-----------------------------------------------------------------------;
;
;	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	dx,di			;Compute bits until we're visible
	je	short spcl_save_first ;Starts on clip edge
	sub	dx,8			;Is current byte visible?
	jl	short spcl_have_vis_start; Yes

spcl_step_clipped_char:
	sub	ax,8			;Shorten the width of the character
	add	di,8			;Move current position right
	add	bx,lfd.font_height	;Move to next column of character

ifndef _286	
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

	sub	dx,8			;Is current byte visible?
	jge	short 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	dx,clip.left
	xor	dx,di
	and	dx,not 7
	jz	short 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	short scc_clip_enters_here


;-----------------------------------------------------------------------;
;
;	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
	jmp	short scc_next_char


;-----------------------------------------------------------------------;
;
;	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_bad_char:
	mov	al,lfd.default_char	;Character was out of range,
	jmp	short scc_good_char

scc_next_char:
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	ja	short scc_bad_char

scc_good_char:
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	mov	bx,ax
	shl	bx,2
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
endif
	or	ax,ax			;If width is 0, ignore character
	jz	short scc_see_if_next
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

scc_clip_enters_here:
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	cmp	di,clip.right
	jge	short scc_char_is_clipped	;Clipped (or first pixel of
						;next is)
scc_been_clipped:
	mov	dh,dl			;Need phase in DH
	cmp	ax,8			;If character is less than 8 bits
	jbe	short scc_width_ok	;  wide, push it's data
	mov	dl,8			;Need 8 for size in DL

scc_still_wide:
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	add	bx,lfd.font_height	;  of the remaining width

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif
	cmp	ax,8
	ja	short scc_still_wide

scc_width_ok:
	mov	ah,dh
	push	ax			;Push data showing phase,
ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif
	cmp	sp,min_stack		;Stack compare must be unsigned
	jb	short scc_restart	;Not enough stack for another character

scc_see_if_next:
	loop	scc_next_char		;Until all characters pushed
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr


;-----------------------------------------------------------------------;
;
;	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	cx,clip.right		;Compute number of pixels
	sub	di,cx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,cx			;Set new rhs
	mov	cx,1			;Show this as last character
	jmp	scc_been_clipped	;Finish here


;-----------------------------------------------------------------------;
;
;	These 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	cx			;Adjust count for char just pushed
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr

non_justified_text endp

;---------------------------Public-Routine------------------------------;
;
; justified_text
;
;   This is the justification case for text, when positive character
;   extra and/or a positive DDA are present.  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
;
; 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)
;	stack frame as per strblt
; Returns:
;	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
; Error Returns:
;	none
; Registers Preserved:
;	BP
; Registers Destroyed:
;	AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
; Calls:
;
; History:
;	Tue 05-May-1987 18:27:29 -by-  Walt Moore [waltm]
; wrote it
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,FontSeg

justified_text	proc	near

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


jcl_bad_character:
	mov	al,lfd.default_char	;Character was out of range
	jmp	short jcl_good_character


;-----------------------------------------------------------------------;
;
;	This is the code which runs the DDA to intersperse pixels
;	into the string
;
;	Compute the amount of white space that will be introduced by
;	this character.  This will be the sum of any character extra,
;	any break extra (if a break character), and dda interspersed
;	pixels (if a break character)
;
;-----------------------------------------------------------------------;

jcl_have_break_char:
	mov	bl,accel
	and	bl,DDA_NEEDED+HAVE_BRK_EXTRA
	jz	short jcl_have_tot_extra;Must have only been char extra
	add	dx,brk_extra		;Extra every break (0 if none)
	test	bl,DDA_NEEDED
	jz	short jcl_have_tot_extra
	mov	bx,brk_err		;The dda is required for this char
	sub	bx,brk_rem		;  Run it and add in an extra pixel
	jg	short jcl_dont_distribute;  if needed.
	add	bx,brk_count		;Add one pixel for the dda
	inc	dx

jcl_dont_distribute:
	mov	brk_err,bx		;Save rem for next time
	jmp	short jcl_have_tot_extra

;-----------------------------------------------------------------------;
;
;	This is the code which computes the number of DDA interspersed
;	pixels to be added to the string
;
;	If all the extra pixels will fit on the end of this character,
;	just adjust it's width, otherwise a null character should be
;	created for the extra.
;
;-----------------------------------------------------------------------;
jcl_extra_pixels:
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	neg	ax
	and	ax,7			;AX = # extra pixels which will fit
	jz	short jcl_have_entire_width ;None will fit
	cmp	ax,dx
	jl	short jcl_have_what_fits    ;Some extra pixels will not fit
	mov	ax,dx			;All pixels will fit, make DX = 0

jcl_have_what_fits:
	sub	dx,ax			;DX = extra for the dummy character

jcl_have_entire_width:
	add	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	jmp	short jcl_have_width


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

jcl_next_char:
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	ja	short jcl_bad_character

jcl_good_character:
	mov	dx,char_xtra		;Base amount of extra pixels needed
	cmp	cx,1			;Last character?
	jnz	short @f
	add	dx,fontweight		;Add in final character overhang.
@@:
	cmp	al,lfd.break_char
	je	short jcl_have_break_char ;Go compute dda added pixels

jcl_have_tot_extra:
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	mov	bx,ax
	shl	bx,2
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
endif
	or	dx,dx
	jnz	jcl_extra_pixels
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]

jcl_have_width:
	add	di,ax			;DI = next chars starting X
	cmp	clip.left,di
	jl	short jcl_current_is_visible	;This char is visible
	add	di,dx
	cmp	clip.left,di
	jl	short 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

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

jcl_dummy_is_visible:
	mov	dx,di
	mov	di,clip.left		;Starting X is clip.left
	sub	dx,di			;DX is # pixels in dummy
	xor	ax,ax			;Show no real character
ifdef _286
	mov	bx, null_char_offset	
else
	mov	ebx, null_char_offset	
endif

	jmp	short jcl_all_done


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

jcl_current_is_visible:
	sub	di,ax			;Restore starting x of character
ifdef _286
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
else
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
endif
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif


;-----------------------------------------------------------------------;
;
;	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	dx			;Save extra pixels
	mov	dx,clip.left
	sub	dx,di			;Compute bits until we're visible
	je	short jcl_save_first	;Starts on clip edge
	sub	dx,8			;Is current byte visible?
	jl	short jcl_have_vis_start    ;  Yes

jcl_step_clipped_char:
	sub	ax,8			;Shorten the width of the character
	add	di,8			;Move current position right
	add	bx,lfd.font_height	;Move to next column of character

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

	sub	dx,8			;Is current byte visible?
	jge	short 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	dx,clip.left
	xor	dx,di
	and	dx,not 7
	jz	short 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	dx			;Restore extra pixels

jcl_all_done:
	jmp	short jc_clip_enters_here

;-----------------------------------------------------------------------;
;
;	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_bad_char:
	mov	al,lfd.default_char	;Character was out of range,
	jmp	short jc_good_character

;-----------------------------------------------------------------------;
;
;	This is the code which runs the DDA to intersperse pixels
;	into the string
;
;	Compute the amount of white space that will be introduced by
;	this character.  This will be the sum of any character extra,
;	any break extra (if a break character), and dda interspersed
;	pixels (if a break character)
;
;-----------------------------------------------------------------------;
jc_have_break_char:
	mov	bl,accel
	and	bl,DDA_NEEDED+HAVE_BRK_EXTRA
	jz	short jc_have_tot_extra   ;Must have only been char extra
	add	dx,brk_extra		;Extra every break (0 if none)
	test	bl,DDA_NEEDED
	jz	short jc_have_tot_extra
	mov	bx,brk_err		;The dda is required for this char
	sub	bx,brk_rem		;  Run it and add in an extra pixel
	jg	short jc_dont_distribute  ;  if needed.
	add	bx,brk_count		;Add one pixel for the dda
	inc	dx

jc_dont_distribute:
	mov	brk_err,bx		;Save rem for next time
	jmp	short jc_have_tot_extra

;-----------------------------------------------------------------------;
;
;	If all the extra pixels will fit on the end of this character,
;	just adjust it's width, otherwise a null character should be
;	created for the extra.
;
;-----------------------------------------------------------------------;

jc_extra_pixels:
	neg	ax
	and	ax,7			;AX = # extra pixels which will fit
	jz	short jc_have_entire_width  ;None will fit
	cmp	ax,dx
	jl	short jc_have_what_fits     ;Some extra pixels will not fit
	mov	ax,dx			;All pixels will fit, make DX = 0

jc_have_what_fits:
	sub	dx,ax			;DX = extra for the dummy character

jc_have_entire_width:
	add	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	jmp	short jc_have_width


;-----------------------------------------------------------------------;
;
;	This is the start of the real loop
;
;-----------------------------------------------------------------------;

jc_next_char:
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	ja	short jc_bad_char

jc_good_character:
	mov	dx,char_xtra		;Base amount of extra pixels needed
	cmp	cx,1			;Last character?
	jnz	short @f
	add	dx,fontweight		;Add in final character overhang.
@@:
	cmp	al,lfd.break_char
	je	short jc_have_break_char    ;Go compute dda added pixels

jc_have_tot_extra:
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	shl	ax,2
	mov	bx,ax
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
endif
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	or	dx,dx
	jnz	short jc_extra_pixels ;Extra pixels required

jc_have_width:
	or	ax,ax
	jz	short jc_check_dummy	;If width is 0, might still have dummy
ifdef _286
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
else
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
endif
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

jc_clip_enters_here:
	mov	num_null_pixels,dx	;Save # null pixels
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	cmp	di,clip.right
	jge	short 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	ax,8			;If character is less than 8 bits
	jbe	short jc_width_ok	;  wide, push it's data

jc_still_wide:
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	add	bx,lfd.font_height	;  of the remaining width

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

	cmp	ax,8
	ja	short jc_still_wide

jc_width_ok:
	mov	ah,dh
	push	ax			;Push data showing phase,

ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif
	mov	dx,num_null_pixels

jc_check_dummy:
	or	dx,dx
	jz	short jc_see_if_next	;No pixels for justification
	xchg	ax,dx			;Set ax = number of pixels to fill
ifdef _286
	mov	bx,null_char_offset
else
	mov	ebx,null_char_offset
endif
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	cmp	di,clip.right
	jge	short 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	ax,8			;If dummy is less than 8 bits
	jbe	short jc_dummy_width_ok   ;  wide, push it's data

jc_dummy_still_wide:
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	cmp	ax,8
	ja	short jc_dummy_still_wide

jc_dummy_width_ok:
	mov	ah,dh
	push	ax			;Push data showing phase,

ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif

jc_see_if_next:
	cmp	sp,min_stack		;Stack compare must be unsigned
	jb	short jc_restart	;Not enough stack for another character
	dec	cx
	jle	short jc_all_done
	jmp	jc_next_char		;Until all characters pushed

jc_all_done:
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr


;-----------------------------------------------------------------------;
;
;	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	cx,clip.right		;Compute number of pixels
	sub	di,cx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,cx			;Set new rhs
	xor	cx,cx			;Dont't want any extra pixels
	mov	num_null_pixels,cx
	inc	cx			;Show this as last character
	jmp	jc_been_clipped 	;Finish here


;-----------------------------------------------------------------------;
;
;	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	cx,clip.right		;Compute number of pixels
	sub	di,cx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,cx			;Set new rhs
	mov	cx,1			;Show this as last character
	jmp	jc_dummys_been_clipped	;Finish here


;-----------------------------------------------------------------------;
;
;	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	cx			;Adjust count for char just pushed
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr

justified_text	endp

	page
;---------------------------Public-Routine------------------------------;
;
; worst_case
;
;   This is the worst case text code, when there is some combination
;   of the width vector, negative character extra, and negative dda.
;   If we run out of stack space, then 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
;
; 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)
;	stack frame as per strblt
; Returns:
;	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
; Error Returns:
;	none
; Registers Preserved:
;	BP
; Registers Destroyed:
;	AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
; Calls:
;
; History:
;	Tue 05-May-1987 18:27:29 -by-  Walt Moore [waltm]
; wrote it
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,FontSeg

worst_case	proc near
NLABEL	worst_case
	mov	RHSInk,di
	test	excel,CLIPPED_LEFT
	jnz	short wccl_next_char	;Clipping needed
	call	pad_left_hand_side	;Might be able to pad lhs
	mov	FirstStackEntryX,di
	jmp	wcc_next_char

;-----------------------------------------------------------------------;
;
;	Set current character to the default character
;
;-----------------------------------------------------------------------;
PLABEL wccl_bad_character
	mov	al,lfd.default_char
	jmp	short wccl_good_character


;-----------------------------------------------------------------------;
;
;	This runs the DDA to intersperse pixels into the string
;
;	Compute the adjustment to the character's width.  This will
;	be the sum of any character extra, any break extra (if a
;	break character), and dda interspersed pixels (if a break
;	character)
;
;	The dda must be capable of handling positive and negative
;	justification.	Character extra may be negative.
;
;-----------------------------------------------------------------------;
PLABEL wccl_have_break_char
	mov	bl,accel
	and	bl,DDA_NEEDED+HAVE_BRK_EXTRA
	jz	short wccl_have_tot_extra ;Must have only been char extra
	add	dx,brk_extra		;Extra every break (0 if none)
	test	bl,DDA_NEEDED
	jz	short wccl_have_tot_extra

	mov	bx,brk_rem		;If the dda is stepping left instead
	or	bx,bx			;  of stepping right, then brk_rem
	jl	short wccl_neg_dda	;  will be negative
	sub	brk_err,bx		;Run dda and add in an extra pixel
	jg	short wccl_have_tot_extra ;  if needed.
	mov	bx,brk_count
	add	brk_err,bx
	inc	dx			;Add one pixel for the dda
	jmp	short wccl_have_tot_extra

PLABEL wccl_neg_dda
	sub	brk_err,bx		;Run dda and subtract an extra pixel
	jl	short wccl_have_tot_extra ;  if needed.
	mov	bx,brk_count
	sub	brk_err,bx		;Subtract one pixel for the dda
	dec	dx
	jmp	short wccl_have_tot_extra

;-----------------------------------------------------------------------;
;
;	This is the start of the real loop for left hand clipping.
;
;-----------------------------------------------------------------------;
PLABEL wccl_next_char
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	ja	short wccl_bad_character

PLABEL wccl_good_character
	mov	dx,char_xtra		;Base amount of extra pixels needed
	cmp	al,lfd.break_char
	je	short wccl_have_break_char  ;Go compute dda added pixels

PLABEL wccl_have_tot_extra
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	shl	ax,2
	mov	bx,ax
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
endif
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	push	bx			;Must save character index
	test	accel,HAVE_WIDTH_VECT
	jz	short wccl_have_width

PLABEL wccl_get_user_width
	cmp	cx,1
	je	short wccl_have_width
	les	bx,lp_dx
	assumes es,nothing
	add	dx,wptr es:[bx] 	;DX is delta to next char's start
	inc	bx
	inc	bx
	mov	off_lp_dx,bx
	mov	es,seg_lp_font
	assumes es,FontSeg
	sub	dx,ax			;Compute adjustment to width

PLABEL wccl_have_width
	pop	bx			;Restore char index
	add	ax,di			;AX = rhs of character
	cmp	clip.left,ax
	jl	short 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	ax,dx			;Next char starts at AX
	mov	di,clip.left
	cmp	di,ax
	jl	short wccl_dummy_is_visible ;part of dummy char became visible
	xchg	di,ax			;Set start of next character

PLABEL wccl_see_if_next
	loop	wccl_next_char		;See if next character
	jmp	build_ret_addr		;Return to caller

;-----------------------------------------------------------------------;
;
;	The dummy character is the first character which became
;	visible.  Just set the starting X to clip.left, and shorten
;	the width of the dummy character appropriately.
;
;-----------------------------------------------------------------------;
PLABEL wccl_dummy_is_visible
	xchg	ax,dx			;Set DX = # pixels in dummy
	sub	dx,di
	xor	ax,ax			;Show no real character
	jmp	short wccl_all_done

;-----------------------------------------------------------------------;
;
;	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
;
;-----------------------------------------------------------------------;
PLABEL wccl_current_is_visible
	sub	ax,di			;Restore width of the character
ifdef _286
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
else
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
endif
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

;-----------------------------------------------------------------------;
;
;	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	dx			;Save extra pixels to be added
	mov	dx,clip.left
	sub	dx,di			;Compute bits until we're visible
	je	short wccl_save_first   ;Starts on clip edge
	sub	dx,8			;Is current byte visible?
	jl	short wccl_have_vis_start; Yes

PLABEL wccl_step_clipped_char
	sub	ax,8			;Shorten the width of the character
	add	di,8			;Move current position right
	add	bx,lfd.font_height	;Move to next column of character

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif

	sub	dx,8			;Is current byte visible?
	jge	short 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.
;
;-----------------------------------------------------------------------;
PLABEL wccl_have_vis_start
	mov	dx,clip.left
	xor	dx,di
	and	dx,not 7
	jz	short 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.
;
;-----------------------------------------------------------------------;
PLABEL wccl_save_first
	pop	dx			;Restore extra pixels

PLABEL wccl_all_done
	mov	FirstStackEntryX,di
	jmp	wcc_clip_enters_here


;-----------------------------------------------------------------------;
;
;	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
;
;-----------------------------------------------------------------------;
PLABEL wcc_bad_char
	mov	al,lfd.default_char	;Character was out of range,
	jmp	short wcc_good_character


;-----------------------------------------------------------------------;
;
;	This is the code which runs the DDA to intersperse pixels
;	into the string
;
;	Compute the adjustment to the character's width.  This will
;	be the sum of any character extra, any break extra (if a
;	break character), and dda interspersed pixels (if a break
;	character)
;
;	The dda must be capable of handling positive and negative
;	justification.	Character extra may be negative.
;
;-----------------------------------------------------------------------;
PLABEL wcc_have_break_char
	mov	bl,accel
	and	bl,DDA_NEEDED+HAVE_BRK_EXTRA
	jz	short wcc_have_tot_extra  ;Must have only been char extra
	add	dx,brk_extra		;Extra every break (0 if none)
	test	bl,DDA_NEEDED
	jz	short wcc_have_tot_extra

	mov	bx,brk_rem		;If the dda is stepping left instead
	or	bx,bx			;  of stepping right, then brk_rem
	jl	short wcc_neg_dda	;  will be negative
	sub	brk_err,bx		;Run dda and add in an extra pixel
	jg	short wcc_have_tot_extra;  if needed.
	mov	bx,brk_count
	add	brk_err,bx
	inc	dx			;Add one pixel for the dda
	jmp	short wcc_have_tot_extra

PLABEL wcc_neg_dda
	sub	brk_err,bx		;Run dda and subtract an extra pixel
	jl	short wcc_have_tot_extra;  if needed.
	mov	bx,brk_count
	sub	brk_err,bx
	dec	dx			;Subtract one pixel for the dda
	jmp	short wcc_have_tot_extra


;-----------------------------------------------------------------------;
;
;	This is the start of the real loop for right hand clipping.
;
;-----------------------------------------------------------------------;
PLABEL wcc_next_char
	lodsb
	sub	al,lfd.first_char
	cmp	al,lfd.last_char
	ja	short wcc_bad_char

PLABEL wcc_good_character
	mov	dx,char_xtra		;Base amount of extra pixels needed
	cmp	al,lfd.break_char
	je	short wcc_have_break_char ;Go compute dda added pixels

PLABEL wcc_have_tot_extra
	xor	ah,ah

;----------------------------------------------------------------------------;
; For version 2 fonts, the header has 2 byte pointer and entry size is       ;
; 4 per char.  For version 3 fonts, pointers are 4 byte and size of entry    ;
; is 6 bytes.                                                                ;
;----------------------------------------------------------------------------;
ifdef _286
	shl	ax,2
	mov	bx,ax
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	mov	bx,word ptr fsCharOffset[bx][PROP_OFFSET]
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping
else
	shl	ax,1
	mov	bx,ax
	shl	ax,1
	add	bx,ax
	mov	ax,wptr fsCharOffset[bx][PROP_WIDTH]
	mov	ebx,dword ptr fsCharOffset[bx][PROP_OFFSET]
	add	bx,amt_clipped_on_top	;Adjust pointer for any clipping

; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif
	test	accel,HAVE_WIDTH_VECT
	jz	short wcc_have_width

PLABEL wcc_get_users_width
	cmp	cx,1
	je	short wcc_have_width
	push	bx
	les	bx,lp_dx
	assumes es,nothing
	add	dx,wptr es:[bx] 	;DX is delta to next char's start
	inc	bx
	inc	bx
	mov	off_lp_dx,bx
	mov	es,seg_lp_font
	assumes es,FontSeg
	sub	dx,ax			;Compute adjustment to width
	pop	bx

PLABEL wcc_have_width
PLABEL wcc_clip_enters_here
	or	ax,ax			;If character width is 0,
	jz	short wcc_check_dummy   ;  might still have dummy char
	add	di,ax
	cmp	RHSInk,di
	ja	short @f
	mov	RHSInk,di
@@:	sub	di,ax
	or	dx,dx			;Any adjustment to the width?
	jle	short wcc_have_adj_width;No extra pixels to add
	push	bx
	mov	bx,ax			;  into the empty space of the
	neg	ax			;  character
	and	ax,7			;AX = # extra pixels which will fit
	jz	short wcc_have_entire_width ;None will fit
	cmp	ax,dx
	jl	short wcc_have_what_fits  ;Some extra pixels will not fit
	mov	ax,dx			;All pixels will fit, make DX = 0

PLABEL wcc_have_what_fits
	sub	dx,ax			;DX = extra for the dummy character

PLABEL wcc_have_entire_width
	add	ax,bx			;Set number of pixels to use in char
	pop	bx

PLABEL wcc_have_adj_width
	mov	num_null_pixels,dx	;Save number of dummy pixels
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	cmp	di,clip.right
ifdef _286
	jge	wcc_char_is_clipped_10	;Clipped (or first pixel of next is)
else
	jge	wcc_char_is_clipped	;Clipped (or first pixel of next is)
endif

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

PLABEL wcc_still_wide
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	add	bx,lfd.font_height	;  of the remaining width

ifndef _286
; the following macro will add 10000h to EBX if carry is set above
	updc_ebx
endif
	cmp	ax,8
	ja	short wcc_still_wide

PLABEL wcc_width_ok
	mov	ah,dh
	push	ax			;Push data showing phase,

ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif
	mov	dx,num_null_pixels

PLABEL wcc_check_dummy
	xchg	ax,dx			;Just incase we go backwards
	or	ax,ax
	jz	short wcc_see_if_next   ;No pixels for justification
ifdef _286
	jl	wcc_going_back_10
	mov	bx,null_char_offset
else
	jl	wcc_going_back
	mov	ebx,null_char_offset
endif
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	cmp	di,clip.right
	jge	short wcc_dummy_is_clipped  ;Clipped (or first pixel of next is)

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

PLABEL wcc_dummy_still_wide
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	cmp	ax,8
	ja	short wcc_dummy_still_wide

PLABEL wcc_dummy_width_ok
	mov	ah,dh
	push	ax			;Save width and phase
ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif

PLABEL wcc_see_if_next
	cmp	sp,min_stack		;Stack compare must be unsigned
	jb	wcc_restart_10		;Not enough stack for another character
	dec	cx
	jle	short wcc_all_done
	jmp	wcc_next_char		;Until all characters pushed

;-----------------------------------------------------------------------
; Some heinous jump vectors.....
;-----------------------------------------------------------------------
wcc_restart_10:
	jmp	wcc_restart
ifdef _286
wcc_going_back_10:
	jmp	wcc_going_back
wcc_char_is_clipped_10:
	jmp	wcc_char_is_clipped
endif

PLABEL wcc_all_done
	mov	ax,RHSInk		;clip the RHSInk to the clip rect.
	mov	bx,clip.right
	sub	bx,ax
	jg	short @f
	add	ax,bx
@@:	sub	ax,di			;If x pos < RHSInk, then
	jg	short @f		; go pad with null pixels.
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr
@@:	jmp	wcc_pad_up_to_rhs

;-----------------------------------------------------------------------;
;	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.
;-----------------------------------------------------------------------;
PLABEL wcc_dummy_is_clipped
	mov	cx,clip.right		;Compute number of pixels
	sub	di,cx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,cx			;Set new rhs
	mov	cx,1			;Show this as last character
	jmp	wcc_dummys_been_clipped ;Finish here

;-----------------------------------------------------------------------;
;
;	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 to go through the backup
;	in 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.
;
;-----------------------------------------------------------------------;
PLABEL wcc_char_is_clipped
	push	dx			;If num_null_pixels < 0, then
	mov	dx,num_null_pixels	;  a restart might be possible
	or	dx,dx
	jl	short wcc_might_backup	; Might need to backup.

PLABEL wcc_clipped_no_backup
	mov	cx,clip.right		;Compute number of pixels
	sub	di,cx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,cx			;Set new rhs
	xor	cx,cx			;Don't want any extra pixels
	mov	num_null_pixels,cx
	inc	cx			;Show this as last character
	pop	dx
	jmp	wcc_been_clipped	;Finish here


;-----------------------------------------------------------------------;
;
;	Compute where the next character would start, and if it is 
;       to the left of clip.right, then a backup is necessary.
;
;-----------------------------------------------------------------------;

PLABEL wcc_might_backup
	cmp	cx,1			;If last character
	jle	short wcc_clipped_no_backup ;  then no restart needed
	add	dx,di			;Compute next starting x
	sub	dx,clip.right
	jge	short wcc_clipped_no_backup ;Rest of string is clipped

;-----------------------------------------------------------------------;
;
;	Will have to go through the backup code.  We
;	can do this by computing the number of pixels between 
;	where the next character starts and clip.right.  This negative
;	number will be stuffed into num_null_pixels, so when we reenter
;	the main loop, we'll force a backup after pushing the character
;	data.
;
;-----------------------------------------------------------------------;
	mov	num_null_pixels,dx
	mov	dx,clip.right		;Compute number of pixels
	sub	di,dx			;  which have to be clipped
	sub	ax,di			;Set new character width
	mov	di,dx			;Set new rhs
	pop	dx
	jmp	wcc_been_clipped	;Finish here


;-----------------------------------------------------------------------;
;	Limit all backward jumps to >= text_bbox.left (i.e., no 
;       characters will be allowed to appear to the right of the 
;       x coordinate).  This rule is preserved from the 3.0 version of 
;       the driver.
;	
;	If there is a backward jump that puts us to the left of the
;       left clipping boundary, then we will restart like the 3.0 
;       driver.
;-----------------------------------------------------------------------;
PLABEL wcc_going_back
	cmp	cx,1			;If last character
	je	wcc_see_if_next 	; don't backup.
	mov	dx,di			;
	add	di,ax			;DI = next char's X position
PLABEL wcc_check_string_origin
	cmp	di,FirstStackEntryX	;Don't allow backward jmp
	jl	short wcc_must_restart	;before the 1st stack entry.
	and	dx,7			;Compute phase
	or	dx,8000h		;Make it into backup sentinel.
	push	dx			;80h | phase
	push	ax			;# of pixels to backup.
	inc	nBackups		;Show that backup tokens exist
					;on the stack.
	jmp	wcc_see_if_next 	;Continue processing string.

PLABEL wcc_must_restart
	dec	cx			;dec string count.
	mov	bx,di
	sub	di,ax			;di=rhs string.
	mov	ax,bx			;ax=next char's start position.
	test	accel,IS_OPAQUE		;If we're TRANSPARENT, then 
	jz	short @f		;restart normally.
	pusha
	call	prepare_for_overlap
	popa
	and	accel,not IS_OPAQUE	;We are now transparent.
@@:	jmp	build_ret_addr

;-----------------------------------------------------------------------;
;
;	Out of stack space.  Set up for a restart.
;
;-----------------------------------------------------------------------;
PLABEL wcc_restart
	mov	ax,di
	dec	cx			;Adjust count for char just pushed
	jmp	build_ret_addr

;-----------------------------------------------------------------------;
; If the last character in the string totally preceeds the rhs of the 
; string, then it is necessary to pad out to the rhs with blanks.  A 
; good example of this is an italic 'f' followed by a blank character. 
; The 'f' has a very small b-space and negative c-space, which means that
; the blank will start within the 'f' character.  Since the
; blank character is 1 pel wide, it won't extend beyond the black box 
; rhs of the 'f'.  If we don't pad with null pixels, then the output code
; will think that the string ends at the rhs of the blank and part of
; the 'f' will not be drawn.
;-----------------------------------------------------------------------;
PLABEL	wcc_pad_up_to_rhs
ifdef _286
	mov	bx,null_char_offset
else
	mov	ebx,null_char_offset
endif
;-----------------------------------------------------------------------;
; There is no need to add amt_clipped_on_top to the ptr of the null 
; character since the null character is always blank.
;-----------------------------------------------------------------------;
	mov	dx,di			;Compute phase
	and	dl,7
	add	di,ax			;DI = next char's X position
	mov	dh,dl			;Need phase in DH
	mov	dl,8			;Need 8 for size in DL if fat
	cmp	ax,8			;If padding is less than 8 bits
	jbe	short wcc_pad_width_ok;  wide, push it's data

PLABEL wcc_pad_still_wide
	push	dx			;Push data showing phase,
ifdef _286
	push	bx			;  character is 8 wide, then
else
	push	ebx			;  character is 8 wide, then
endif
	sub	ax,8			;  create another character
	cmp	ax,8
	ja	short wcc_pad_still_wide

PLABEL wcc_pad_width_ok
	mov	ah,dh
	push	ax			;Save width and phase
ifdef _286
	push	bx			;Save offset to bits
else
	push	ebx			;Save offset to bits
endif
	mov	ax,di			;Next character starts here
	jmp	build_ret_addr


worst_case	endp


;--------------------------Private-Routine------------------------------;
; pad_left_hand_side
;
;   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.
;
; Entry:
;	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)
; Returns:
;	DS:SI --> current character in the string
;	ES:    =  font segment
;	CX     =  number of characters in the string
;	DI     =  current X position
; Error Returns:
;	None
; Registers Preserved:
;	CX,SI,DI,DS,ES,FLAGS
; Registers Destroyed:
;	AX,BX,DX
; Calls:
;	None
; History:
;	Thu 16-Apr-1987 23:37:27 -by-  Walt Moore [waltm]
;	Created.
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,FontSeg

pad_left_hand_side	proc near
	mov	ax,di			;Get starting x coordinate
	and	ax,7			;Address MOD 8 is amount to pad
	jz	short 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	short 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	short plhs_all_done	;Cannot add any padding
	mov	bx,di
	sub	bx,o_rect.left
	jle	short plhs_all_done	;Cannot add any.  Darn
	min_ax	bx			;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	dx,di			;DI = start x = text_bbox.left
	sub	dx,ax			;Set new lhs of text bounding box
	mov	current_lhs,dx
	mov	ah,dl			;Set phase (x coordinate) of char
	and	ah,7
	pop	dx
	push	ax			;Width and phase of null character
	push	null_char_offset	;Offset in font of null character
	jmp	dx

plhs_all_done:
	ret

pad_left_hand_side endp

	page
;--------------------------Private-Routine------------------------------;
; pad_right_hand_side
;
;   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 opaque mode, this depends on the opaquing rectangle.
;
; Entry:
;	DS:SI --> current character in the string
;	ES:    =  font segment
;	DI     =  X position where next char would have gone
; Returns:
;	DS:SI --> current character in the string
;	ES:    =  font segment
;	DI     =  rhs of the string, padded to boundary if possible
; Error Returns:
;	'C' set if no interval to output
; Registers Preserved:
;	AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
; Registers Destroyed:
;	AX,BX,DX
; Calls:
;	comp_byte_interval
; History:
;	Thu 16-Apr-1987 23:37:27 -by-  Walt Moore [waltm]
;	Created.
;-----------------------------------------------------------------------;
	assumes ds,nothing
	assumes es,FontSeg

pad_right_hand_side	proc near
	mov	ax,di			;If next char would start at bit 0,
	and	al,7			;  then no padding is required.
	jz	short prhs_have_rhs	;No padding needed
	mov	ah,al			;Save phase
	neg	al			;Set width needed
	and	al,7
	pop	bx			;save return address in BX
	push	ax			;Width and phase of null character
	push	null_char_offset	;Offset in font of null character
	push	bx			;put return address back on stack
	xor	ah,ah
	test	accel,IS_OPAQUE 
	jz	short prhs_have_rhs	;Can't pad for transparent text.

prhs_in_opaque_mode:
	mov	bl,excel
	not	bl
	test	bl,OPAQUE_RECT+BOUNDED_IN_Y
	jnz	short prhs_have_rhs	;Cannot alter actual rhs
	mov	bx,o_rect.right 	;Compute distance from where I am to
	sub	bx,di			;  where opaque rectangle ends
	jle	short prhs_have_rhs	;Opaque rect is left of text end
	min_ax	bx			;Compute amount to move rhs
prhs_new_rhs:
	add	di,ax			;Slide rhs right

prhs_have_rhs:
	ret

pad_right_hand_side	endp

;---------------------------Public-Routine------------------------------;
;
; prepare_for_overlap
;
;   Possible negative justification and/or width vector.  If
;   opaque mode, then compute the extents of the string so that
;   the bounding box can be output if we step backwards.  If we
;   will step backwards, opaque the area where the string will
;   go and set transparent mode for the actual text output routine.
;
; Entry:
;	bl = accel
; Returns:
;	None
; Error Returns:
;	to build_all_done if nothing will show
; Registers Destroyed:
;	AX,BX,CX,DX,SI,DI,DS,ES
; Registers Preserved:
;	None
; Calls:
;	worst_case_ext
;	output_o_rect
; History:
;	Wed 06-May-1987 21:33:15 -by-  Walt Moore [waltm]
; wrote it
;-----------------------------------------------------------------------;
assumes ds,nothing
assumes es,nothing

prepare_for_overlap proc near
	push	brk_err 		;Must not destroy the justification
	push	brk_count		;  DDA parameters while we find out
	push	brk_rem 		;  how long the string is and if
	push	off_lp_dx		;  we stepped backwards
	mov	cx,count		;  we stepped backwards
ifdef _286
	call	worst_case_ext_286
else
	call	worst_case_ext_386
endif
	pop	off_lp_dx
	pop	brk_rem
	pop	brk_count
	pop	brk_err

;-----------------------------------------------------------------------;
;
;	Opaque the area where the string will go.  This area will 
;       have to be clipped.
;
;-----------------------------------------------------------------------;
	mov	ax,clip.right
	mov	cx,x			;CX = lhs
	add	bx,cx			;BX = rhs
	jo	short pfo_have_rhs	;Use clip.right for right side
	min_ax	bx

pfo_have_rhs:
	xchg	ax,bx			;Need rhs in BX
	mov	ax,clip.left
	max_ax	cx
	cmp	bx,ax
	stc				;JGE is SF = OF, doesn't use carry!
	jle	short 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 clear bu build_string, the opaque code
;	will not perform an intersection of text_bbox and o_rect.
;
;-----------------------------------------------------------------------;
	mov	wc_opaque_lhs,ax	;Save lhs/rhs incase we have an
	mov	wc_opaque_rhs,bx	;  opaquing rectangle
	or	wc_flags,WC_SET_LR	;Set left/right into text bbox
	push	o_rect.left		;Save real o_rect bbox
	push	o_rect.right
	push	o_rect.top
	push	o_rect.bottom
	mov	cx,text_bbox.top
	mov	dx,text_bbox.bottom
	mov	o_rect.left,ax		;Set text bbox as area to opaque
	mov	o_rect.right,bx
	mov	o_rect.top,cx
	mov	o_rect.bottom,dx
	mov	bl,excel
ifdef _286
	call	output_o_rect_286
else
	call	output_o_rect_386
endif
	pop	o_rect.bottom
	pop	o_rect.top
	pop	o_rect.right
	pop	o_rect.left

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

pfo_exit:
	ret

pfo_exit_nothing_shows:
	pop	ax
	jmp	build_restore_opaque

prepare_for_overlap endp

if 	0
	public	real_build_string
	public	build_worst_ok
	public	build_have_proc
	public	build_restart
	public	build_ret_addr
	public	build_no_o_rect
	public	build_clip_next_time
	public	build_no_clip_next_time
	public	build_all_done
	public	build_restore_opaque
	public	build_really_done
	public	non_justified_text
	public	sfcl_next_char
	public	sfcl_no_chars_visible
	public	sfcl_current_is_visible
	public	spcl_next_char
	public	spcl_good_character
	public	spcl_see_if_next
	public	spcl_current_is_visible
	public	spcl_step_clipped_char
	public	spcl_have_vis_start
	public	spcl_save_first
	public	sc_no_left_clipping
	public	scc_bad_char
	public	scc_next_char
	public	scc_good_char
	public	scc_clip_enters_here
	public	scc_been_clipped
	public	scc_still_wide
	public	scc_width_ok
	public	scc_see_if_next
	public	scc_char_is_clipped
	public	scc_restart
	public	justified_text
	public	jcl_bad_character
	public	jcl_have_break_char
	public	jcl_dont_distribute
	public	jcl_extra_pixels
	public	jcl_have_what_fits
	public	jcl_have_entire_width
	public	jcl_next_char
	public	jcl_good_character
	public	jcl_have_tot_extra
	public	jcl_have_width
	public	jcl_see_if_next
	public	jcl_dummy_is_visible
	public	jcl_current_is_visible
	public	jcl_step_clipped_char
	public	jcl_have_vis_start
	public	jcl_save_first
	public	jcl_all_done
	public	jc_bad_char
	public	jc_have_break_char
	public	jc_dont_distribute
	public	jc_extra_pixels
	public	jc_have_what_fits
	public	jc_have_entire_width
	public	jc_next_char
	public	jc_good_character
	public	jc_have_tot_extra
	public	jc_have_width
	public	jc_clip_enters_here
	public	jc_been_clipped
	public	jc_still_wide
	public	jc_width_ok
	public	jc_check_dummy
	public	jc_dummys_been_clipped
	public	jc_dummy_still_wide
	public	jc_dummy_width_ok
	public	jc_see_if_next
	public	jc_all_done
	public	jc_char_is_clipped
	public	jc_dummy_is_clipped
	public	jc_restart
	public	worst_case
	public	wccl_bad_character
	public	wccl_have_break_char
	public	wccl_neg_dda
	public	wccl_next_char
	public	wccl_good_character
	public	wccl_have_tot_extra
	public	wccl_get_user_width
	public	wccl_have_width
	public	wccl_adjustment_ok
	public	wccl_see_if_next
	public	wccl_dummy_is_visible
	public	wccl_current_is_visible
	public	wccl_step_clipped_char
	public	wccl_have_vis_start
	public	wccl_save_first
	public	wccl_all_done
	public	wcc_bad_char
	public	wcc_have_break_char
	public	wcc_neg_dda
	public	wcc_next_char
	public	wcc_good_character
	public	wcc_have_tot_extra
	public	wcc_get_users_width
	public	wcc_have_width
	public	wcc_adj_is_ok
	public	wcc_clip_enters_here
	public	wcc_have_what_fits
	public	wcc_have_entire_width
	public	wcc_have_adj_width
	public	wcc_been_clipped
	public	wcc_still_wide
	public	wcc_width_ok
	public	wcc_check_dummy
	public	wcc_dummys_been_clipped
	public	wcc_dummy_still_wide
	public	wcc_dummy_width_ok
	public	wcc_see_if_next
	public	wcc_all_done
	public	wcc_dummy_is_clipped
	public	wcc_char_is_clipped
	public	wcc_clipped_no_restart
	public	wcc_might_need_restart
	public	wcc_going_back
	public	wcc_restart
	public	true_type_text
	public	pad_left_hand_side
	public	plhs_add_padding
	public	plhs_all_done
	public	pad_right_hand_side
	public	prhs_have_rhs
	public	process_stack_data
	public	psd_pre_proc
	public	psd_pp_have_first
	public	psd_pp_ega_trans
	public	psd_pp_done
	public	psd_not_clipped
	public	psd_collect_chars
	public	psd_have_exact_fit
	public	psd_have_more_than_enough
	public	psd_unlikely_cases
	public	psd_see_about_last
	public	psd_exit
	public	prepare_for_overlap
	public	pfo_have_rhs
	public	pfo_exit
	public	pfo_exit_nothing_shows
endif	
ifdef _286
sEnd	rCode
else
sEnd	pCode
endif
	end
