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, ; find the wchar's run pop 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, pop edx pop ecx pop ebx else mov eax, pThis push ecx push edx stdcall xpgdDefault, ; eax = ptr to default character pop edx pop ecx endif pop 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, 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 ; = 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, pop 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, pop edx pop ecx pop ebx ; (eax) is now proper wpgd else mov eax,pThis push ecx push edx stdcall xpgdDefault, ; eax = ptr to default character pop edx pop ecx endif pop 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, 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 ; = 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, ;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 ; = glyph count ; ***stack frame no longer available*** jmp gmp_got_bits stdENDP xGetGlyphMetricsPlus end