2025-04-27 07:49:33 -04:00

687 lines
27 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

page ,132
;*****************************Module*Header*******************************\
; Module Name: cache.asm
;
;
; Created: 9-Dec-1992
; Author: Paul Butzi
;
; Copyright (c) 1990-1999 Microsoft Corporation
;
;*************************************************************************/
; Note: This module would be more efficient with Frame Pointer Omission
; (FPO), whereby the stack is addressed off ESP rather than EBP, so we
; could stop pushing and popping EBP. However, the ASM macros currently
; don't support FPO.
;*************************************************************************/
.386
.model small,c
assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
assume fs:nothing,gs:nothing
.xlist
include callconv.inc
include gdii386.inc
.list
.code
EXTRNP xprunFindRunRFONTOBJ,2
EXTRNP xpgdDefault,1
EXTRNP xInsertMetricsRFONTOBJ,3
EXTRNP xInsertMetricsPlusRFONTOBJ,3
EXTRNP xInsertGlyphbitsRFONTOBJ,3
ifdef FE_SB
EXTRNP xwpgdGetLinkMetricsRFONTOBJ,2
EXTRNP xwpgdGetLinkMetricsPlusRFONTOBJ,3
endif
FAST_WCHAR_BASE equ 20h ; Most ANSI fonts have a base of 32
;******************************Public*Routine******************************\
;
; BOOL RFONTOBJ::bGetGlyphMetrics (
; COUNT c,
; GLYPHPOS *pgp,
; WCHAR *pwc
; );
;
; Translate wchars into an array of GLYPHPOS structures, filling in
; the pointer to GLYPHDATA field. Only the metrics are assured to be
; valid; no attempt is made to ensure that the glyph data itself is
; present in the cache before the return to the caller.
;
; This routine is to be used primarily by GetTextExtent and GetCharWidths,
; which have no need for anything except metrics.
;
; A zero return means that we failed to insert the metrics due to some
; hard error, most likely a failure to commit memory in the glyph
; insertion routine.
;
; History:
; 21-Dec-92 -by- Michael Abrash
; Fixed bug in detecting run inclusion
; 9-Dec-92 -by- Paul Butzi
; Wrote it.
;**************************************************************************/
cPublicProc xGetGlyphMetrics,4,< \
uses ebx esi edi, \
pThis: ptr dword, \
c_: dword, \
pgp: ptr dword, \
pwc: dword >
mov eax, pThis ; (eax) = ptr to RFONTOBJ
mov eax, [eax].prfnt ; (eax) = ptr to RFONT
mov esi, pwc ; pointer to wchar data
;*486 pipelining
mov eax, [eax].wcgp ; (eax) = ptr to wcgp
mov edi, pgp ; pointer to glyphpos array to fill
;*486 pipelining
lea ebx, [eax].agprun ; (ebx) = ptr to first wcgp run
;*486 pipelining
mov ecx, [ebx].wcLow ; index of start of run
mov edx, [ebx].cGlyphs ; # of glyphs in run
mov ebx, [ebx].apgd ; pointer to run's pointers to cached
; glyphdata
push ebp
mov ebp,c_ ; we'll use EBP for the glyph count
; ***stack frame no longer available***
;
; Invariants:
; (esi) = ptr to wchar
; (edi) = ptr to pgp
; (ebx) = ptr to base of current run
; (ecx) = wcLow of current run
; (edx) = cGlyphs of current run
; (ebp) = count of glyphs remaining
loop_top:
sub eax, eax
mov ax, [esi] ; (eax) = wchar, zero extended
sub eax, ecx ; get index relative to current run base
cmp eax, edx ; is wchar in this run?
jae short find_run ; wrong run, better go get right one
run_found:
mov eax, [ebx+4*eax] ; (ecx) = ptr to glyphdata
;
; Invariants:
; (esi, edi, ebx, ecx, edx) as above at loop top
; (eax) = ptr to glyphdata
;
default_found:
and eax, eax ; is cached glyphdata there?
jz short get_glyph ; no glyphdata, better go get one
;
; Here, we are assured that the glyphdata is present in the cache
;
glyph_found:
mov [edi].gp_pgdf, eax ; set ptr to glyphdata in glyphpos
mov eax, [eax].gd_hg
mov [edi].gp_hg, eax ; set hg in glyphpos
add edi, SIZE_GLYPHPOS ; next glyphpos to fill in
add esi, 2 ; next wchar to look up
dec ebp ; any more glyphs?
jnz loop_top
pop ebp ; ***stack frame available***
mov eax, 1 ; success
stdRET xGetGlyphMetrics
;=======================================================================
; We got here because the current run does not contain the wchar we're
; interested in. Note that the following invariants must hold when
; we re-enter the loop (starred items must hold on entry, as well):
; (esi) = ptr to wchar*
; (edi) = ptr to pgp*
; (ebx) = ptr to apgd of new run
; (ecx) = wcLow of new run
; (edx) = cGlyphs of new run
; (eax) = ptr to glyphdata
; (ebp) = count of glyphs remaining*
;=======================================================================
align 4
find_run:
push ebp ; preserve count of remaining glpyhs
mov ebp,[esp+4] ; ***stack frame available***
; *****************************************
; * Note that the approach here is to get *
; * EBP off the stack, where it was *
; * pushed, by addressing off ESP. EBP *
; * remains pushed, so we don't need to *
; * re-push it when we're done. *
; * XCHGing EBP with the top of the stack *
; * would be cleaner, but XCHG locks the *
; * bus. *
; * This trick is repeated several times *
; * below. *
; *****************************************
sub eax, eax
mov ax, [esi] ; (eax) = current wchar
mov ebx, pThis
stdcall xprunFindRunRFONTOBJ, <ebx, eax> ; find the wchar's run
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
mov ecx, [eax].wcLow ; index of start of new run
mov edx, [eax].cGlyphs ; # of glyphs in new run
mov ebx, [eax].apgd ; pointer to new run's pointers to
; cached glyphdata
sub eax, eax
mov ax, [esi] ; (eax) = current wchar
sub eax, ecx ; get index relative to new run base
cmp eax, edx ; is wchar in this run?
jb run_found ; yes, run with it
; not in any run; use default character
push ebp ; preserve count of remaining glyphs
mov ebp,[esp+4] ; ***stack frame available***
ifdef FE_SB ; call off to linked font handler
push ebx
push ecx
push edx
mov eax, pThis ; (eax) = pointer to RFONTOBJ
sub ebx,ebx ; (ebx) = 0
mov bx, [esi] ; (ebx) = current wchar
stdcall xwpgdGetLinkMetricsRFONTOBJ,<eax,ebx>
pop edx
pop ecx
pop ebx
else
mov eax, pThis
push ecx
push edx
stdcall xpgdDefault, <eax> ; eax = ptr to default character
pop edx
pop ecx
endif
pop ebp ; <ebp> = glyph count
jmp default_found ; go with the default character
;=======================================================================
; We got here because the glyph pointer in the wcgp run was null, meaning
; we don't yet have the metrics for this glyph. Note that the following
; invariants must be true on exit (starred items must hold on entry, as
; well):
; (esi) = ptr to wchar*
; (edi) = ptr to pgp*
; (ebx) = ptr to apgd of new run*
; (ecx) = wcLow of new run*
; (edx) = cGlyphs of new run*
; (eax) = ptr to glyphdata
; (ebp) = count of glyphs remaining*
;=======================================================================
align 4
get_glyph:
push ebp ; preserve count of remaining glpyhs
mov ebp,[esp+4] ; ***stack frame available***
push ebx
push edx
push ecx
sub eax, eax
mov ax, [esi] ; (eax) = wchar
mov edx, eax ; set aside wchar
sub eax, ecx ; (eax) = index into run
lea ebx, [ebx+eax*4] ; (ebx) = ptr to entry
mov ecx,pThis
stdcall xInsertMetricsRFONTOBJ, <ecx, ebx, edx>
pop ecx
pop edx
and eax, eax ; were we able to get the glyph metrics?
jz short failed ; no
mov eax, [ebx] ; get ptr to glyphdata from cache entry
pop ebx
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
jmp glyph_found
failed:
pop ebx
pop eax ; clear glyph count from stack
pop eax ; clear pushed stack frame pointer from stack
sub eax, eax ; failure
stdRET xGetGlyphMetrics
stdENDP xGetGlyphMetrics
;******************************Public*Routine******************************\
;
; BOOL RFONTOBJ::bGetGlyphMetricsPlus (
; COUNT c,
; GLYPHPOS *pgp,
; WCHAR *pwc,
; BOOL *pbAccel
; );
;
; Translate wchars into an array of GLYPHPOS structures, filling in
; the pointer to GLYPHDATA field. Although only the metrics are assured to be
; valid, an attempt is made to ensure that the glyph data itself is
; present in the cache before the return to the caller. Failure in this
; attempt is indicated by clearing the flag *pbAccel. This allows the
; text code to tell the device driver that the STROBJ_bEnum callback is
; not needed.
;
; This routine is to be used primarily by TextOut and its kin.
;
; A zero return means that we failed to insert the metrics due to some
; hard error, most likely a failure to commit memory in the glyph
; insertion routine.
;
; This is a replacement for the C++ version in cache.cxx
;
; History:
; 21-Dec-92 -by- Michael Abrash
; Fixed bug in detecting run inclusion
; 9-Dec-92 -by- Paul Butzi
; Wrote it.
;**************************************************************************/
cPublicProc xGetGlyphMetricsPlus,5,< \
uses ebx esi edi, \
pThis: ptr dword, \
c_: dword, \
pgp: ptr dword, \
pwc: dword, \
pbAccel: ptr dword >
local pbacceltmp : dword ;1 if all glyph bits have been gotten
; so far, 0 if not
mov eax, pThis ; (eax) = ptr to RFONTOBJ
mov pbacceltmp,1 ; set bAccel to true (all glyph bits
; realized so far)
;*486 pipelining
mov eax, [eax].prfnt ; (eax) = ptr to RFONT
mov esi, pwc ; pointer to wchar data
;*486 pipelining
mov eax, [eax].wcgp ; (eax) = ptr to wcgp
mov edi, pgp ; pointer to glyphpos array to fill
;*486 pipelining
lea ebx, [eax].agprun ; (ebx) = ptr to first wcgp run
;*486 pipelining
push ebp ;*486 pipelining
mov ecx, [ebx].wcLow ; index of start of run
mov edx, [ebx].cGlyphs ; # of glyphs in run
mov ebx, [ebx].apgd ; pointer to run's pointers to cached
; glyphdata
mov ebp,c_ ; we'll use EBP for the glyph count
; ***stack frame no longer available***
;--------------------------------------------------------------------------
; Fast two-glyphs-at-a-time Pentium pipe-lined loop
;
; The philosophy of this loop is that for the vast majority of the time,
; all the glyphs will be in the proper range, have cached glyphdata, and
; have cached glyphbits -- and it takes advantage of that to better make
; use of both Pentium pipes by doing two glyphs at once. If one of those
; conditions fail, it converts to the single-glyph-at-a-time loop when
; that happens.
;
; Invariants:
; (esi) = ptr to 1st wchar
; (edi) = ptr to pgp
; (ecx) = ptr to 2nd wchar
; (ecx) = wcLow of current run
; (edx) = cGlyphs of current run
; (ebp) = count of glyphs remaining
; [esp] = wcLow of current run
cmp ecx, FAST_WCHAR_BASE ; fast loop handles wcLow values only
jne gmp_slow_loop_top ; of 32
dec ebp ; pre-decrement count
jz gmp_slow_loop_top ; only one character. it's okay to
; pop this through to gmp_slow_loop_top
; with a zero count -- it's effectively
; the same as a one count
push ecx ; wcLow is now at [esp]
gmp_fast_loop_top:
mov eax, [esi] ; 1U (eax) = first two glyphs
add edi, 2*SIZE_GLYPHPOS ; 1V each iteration does two glyphpos's.
; we do this now to eat up a free V
; pipe instruction
mov ecx, eax ; 1U
add esi, 4 ; 1V each iteration does two wchars.
; we do this now to eat up a free V
; pipe instruction
shr ecx, 16 ; 1U (ecx) = 2nd wchar, zero extended
and eax, 0ffffh ; 1V (eax) = 1st wchar, zero extended
sub eax, FAST_WCHAR_BASE ; 1U get index relative to current base
sub ecx, FAST_WCHAR_BASE ; 1V get index relative to current base
cmp eax, edx ; 1U is 1st wchar in this run?
jae gmp_restart_in_slow_loop; 1V wrong run, exit fast loop
cmp ecx, edx ; 1U is 2nd wchar in this run?
jae gmp_restart_in_slow_loop; 1V wrong run, exit fast loop
mov eax, [ebx+4*eax] ; 2U (eax) = ptr to 1st glyphdata
mov ecx, [ebx+4*ecx] ; 2V (ecx) = ptr to 2nd glyphdata
; (could be 3V if in same cache
; bank)
test eax, eax ; 1U is 1st cached glyphdata there?
jz gmp_restart_in_slow_loop; 1V no glyphdata, exit fast loop
test ecx, ecx ; 1U is 2nd cached glyphdata there?
jz gmp_restart_in_slow_loop; 1V no glyphdata, exit fast loop
cmp dword ptr [eax].gd_gdf, 0 ; 2U are 1st glyph bits in cache?
je gmp_restart_in_slow_loop ; 1V no, go get them
cmp dword ptr [ecx].gd_gdf, 0 ; 2U are 2nd glyph bits in cache?
je gmp_restart_in_slow_loop ; 1V no, go get them
mov [edi-2*SIZE_GLYPHPOS].gp_pgdf, eax
; 1U set ptr to glyphdata in glyphpos
mov eax, [eax].gd_hg ; 1V read 1st glyph handle
mov [edi-1*SIZE_GLYPHPOS].gp_pgdf, ecx
; 1U set ptr to glyphdata in glyphpos
mov ecx, [ecx].gd_hg ; 1V read 2nd glyph handle
mov [edi-2*SIZE_GLYPHPOS].gp_hg,eax ; 1U set handle in glyphpos
mov [edi-1*SIZE_GLYPHPOS].gp_hg,ecx ; 1V set handle in glyphpos
sub ebp, 2 ; 1U each iteration does two glyphs
jg gmp_fast_loop_top ; 1V more loops to do
pop ecx ; account for pushed wcLow
jz gmp_slow_loop_top ; odd number of glyphs, handle last one
pop ebp ; ***stack frame available***
mov ebx,pbaccel ; where we'll store whether all glyph
; bits found
mov eax, 1 ; success
mov [ebx], 1 ; success
stdRET xGetGlyphMetricsPlus
;--------------------------------------------------------------------------
; Not-so-fast one-glyph-at-a-time loop
;
; Invariants:
; (esi) = ptr to wchar
; (edi) = ptr to pgp
; (ebx) = ptr to base of current run
; (ecx) = wcLow of current run
; (edx) = cGlyphs of current run
; (ebp) = count of glyphs remaining
gmp_restart_in_slow_loop:
pop ecx ; (ecx) = wcLow of current run
inc ebp ; account for fast-loop preadjustments
sub edi, 2*SIZE_GLYPHPOS
sub esi, 4
gmp_slow_loop_top:
sub eax, eax
mov ax, [esi] ; (eax) = wchar, zero extended
sub eax, ecx ; get index relative to current run base
cmp eax, edx ; is wchar in this run?
jae short gmp_find_run ; wrong run, better go get right one
gmp_run_found:
mov eax, [ebx+4*eax] ; (ecx) = ptr to glyphdata
;
; Invariants:
; (esi, edi, ebx, ecx, edx) as above at loop top
; (eax) = ptr to glyphdata
;
gmp_default_found:
and eax, eax ; is cached glyphdata there?
jz gmp_get_glyph ; no glyphdata, better go get one
;
; Here, we are assured that the glyphdata is present in the cache
;
gmp_glyph_found:
cmp dword ptr [eax].gd_gdf, 0 ; are the glyph bits in the cache?
jz gmp_get_bits ; no, go get them
;
; Here, we have tried to get the glyphbits in the cache
;
gmp_got_bits:
mov [edi].gp_pgdf, eax ; set ptr to glyphdata in glyphpos
mov eax, [eax].gd_hg
mov [edi].gp_hg, eax ; set hg in glyphpos
add edi, SIZE_GLYPHPOS ; next glyphpos to fill in
add esi, 2 ; next wchar to look up
dec ebp ; any more glyphs?
jg gmp_slow_loop_top ; must be 'greater than' check
pop ebp ; ***stack frame available***
mov ebx,pbaccel ; where we'll store whether all glyph bits found
mov eax,pbacceltmp ; 0 if not all found, 1 if all found
mov [ebx],eax ; return whether we found all glyph bits or not
mov eax, 1 ; success
stdRET xGetGlyphMetricsPlus
;=======================================================================
; We got here because the current run does not contain the wchar we're
; interested in. Note that the following invariants must hold when
; we re-enter the loop (starred items must hold on entry, as well):
; (esi) = ptr to wchar*
; (edi) = ptr to pgp*
; (ebx) = ptr to apgd of new run
; (ecx) = wcLow of new run
; (edx) = cGlyphs of new run
; (eax) = ptr to glyphdata
; (ebp) = count of glyphs remaining*
;=======================================================================
align 4
gmp_find_run:
push ebp ; preserve count of remaining glpyhs
mov ebp,[esp+4] ; ***stack frame available***
sub eax, eax
mov ax, [esi] ; (eax) = current wchar
mov ebx, pThis
stdcall xprunFindRunRFONTOBJ, <ebx, eax>
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
mov ecx, [eax].wcLow ; index of start of new run
mov edx, [eax].cGlyphs ; # of glyphs in new run
mov ebx, [eax].apgd ; pointer to new run's pointers to
; cached glyphdata
sub eax, eax
mov ax, [esi] ; (eax) = current wchar
sub eax, ecx ; get index relative to new run base
cmp eax, edx ; is wchar in this run?
jb gmp_run_found ; yes, run with it
; not in any run; use default character
push ebp ; preserve count of remaining glyphs
mov ebp,[esp+4] ; ***stack frame available***
ifdef FE_SB
push ebx
push ecx
push edx
mov eax, pThis ; (eax) = pointer to RFONTOBJ
lea ebx, pbacceltmp ; (ebx) = pointer to pbacceltmp
; (esi) = pointer to current wchar
stdcall xwpgdGetLinkMetricsPlusRFONTOBJ,<eax,esi,ebx>
pop edx
pop ecx
pop ebx ; (eax) is now proper wpgd
else
mov eax,pThis
push ecx
push edx
stdcall xpgdDefault, <eax> ; eax = ptr to default character
pop edx
pop ecx
endif
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
jmp gmp_default_found ; go with the default character
;=======================================================================
; We got here because the glyph pointer in the wcgp run was null, meaning
; we don't yet have the metrics for this glyph. Note that the following
; invariants must be true on exit (starred items must hold on entry, as
; well):
; (esi) = ptr to wchar*
; (edi) = ptr to pgp*
; (ebx) = ptr to apgd of new run*
; (ecx) = wcLow of new run*
; (edx) = cGlyphs of new run*
; (eax) = ptr to glyphdata
; (ebp) = count of glyphs remaining*
;=======================================================================
align 4
gmp_get_glyph:
push ebp ; preserve count of remaining glpyhs
mov ebp,[esp+4] ; ***stack frame available***
push ebx
push edx
push ecx
sub eax, eax
mov ax, [esi] ; (eax) = wchar
mov edx, eax ; set aside wchar
sub eax, ecx ; (eax) = index into run
lea ebx, [ebx+4*eax] ; (ebx) = ptr to entry
mov ecx,pThis
stdcall xInsertMetricsPlusRFONTOBJ, <ecx, ebx, edx>
pop ecx
pop edx
and eax, eax ; were we able to get the glyph metrics?
jz short gmp_failed ; no
mov eax, [ebx] ; get ptr to glyphdata from it
pop ebx
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
jmp gmp_glyph_found
gmp_failed:
pop ebx
pop eax ; clear glyph count from stack
pop eax ; clear pushed stack frame pointer from stack
sub eax, eax ;failure
stdRET xGetGlyphMetricsPlus
;=======================================================================
; We get here if we need to try to load the bits for the glyph we're
; interested in (because the bits havene't been realized yet). We only
; even bother to try if all bits have successfully been realized so far,
; because it's only useful to realize glyphs if we can get all of them.
; Note that the following invariants must be true on both entry and exit.
; (esi) = ptr to wchar
; (edi) = ptr to pgp
; (ebx) = ptr to apgd of new run
; (ecx) = wcLow of new run
; (edx) = cGlyphs of new run
; (eax) = ptr to glyphdata
; (ebp) = count of glyphs remaining
;=======================================================================
align 4
gmp_get_bits:
push ebp ; preserve count of remaining glpyhs
mov ebp,[esp+4] ; ***stack frame available***
cmp pbacceltmp,0 ;if we already failed to get glyph bits
je short got_the_bits_done ; once, no point in trying again
push ecx
mov ecx, pThis
push edx ;*486 pipelining
mov edx, [ecx].prfnt
push eax ;*486 pipelining
cmp dword ptr [edx].ulContent, FO_HGLYPHS
je short got_the_bits
sub edx,edx
cmp pwc,esi
sbb edx,-1 ; EDX == TRUE if first wc, FALSE else
stdcall xInsertGlyphbitsRFONTOBJ, <ecx, eax, edx> ;try to get the bits
and eax, eax ; did we succeed in getting the bits?
jnz short got_the_bits ; yes, we got the bits
mov pbacceltmp, eax ; didn't get the glyph (note: EAX is zero)
got_the_bits:
pop eax
pop edx
pop ecx
got_the_bits_done:
pop ebp ; <ebp> = glyph count
; ***stack frame no longer available***
jmp gmp_got_bits
stdENDP xGetGlyphMetricsPlus
end