/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Author: ShonK Project: Kauai Reviewed: Copyright (c) Microsoft Corporation Data movement routines. WARNING: Must be in a fixed (pre-loaded) seg on Mac. ***************************************************************************/ #include "util.h" ASSERTNAME /*************************************************************************** Fill a block with a specific byte value. ***************************************************************************/ void FillPb(void *pv, long cb, byte b) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv, cb); #ifdef IN_80386 __asm { // Setup the registers for using REP STOS instruction to set memory. // NOTE: Alignment does not effect the speed of STOS. // // edi -> memory to set // eax = value to store in destination // direction flag is clear for auto-increment mov edi,pv mov al,b // set the longs mov ecx,cb shr ecx,2 jz LBytes shl eax,8 mov al,b mov ebx,eax shl eax,16 mov ax,bx rep stosd // set the extra bytes LBytes: mov ecx,cb and ecx,3 rep stosb } #else //!IN_80386 byte *pb; for (pb = (byte *)pv; cb != 0; cb--) *pb++ = b; #endif //!IN_80386 } /*************************************************************************** Clear a block. ***************************************************************************/ void ClearPb(void *pv, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv, cb); #ifdef IN_80386 __asm { // Setup the registers for using REP STOS instruction to set memory. // NOTE: Alignment does not effect the speed of STOS. // // edi -> memory to set // eax = value to store in destination // direction flag is clear for auto-increment mov edi,pv xor eax,eax // clear the longs mov ecx,cb shr ecx,2 rep stosd // clear the extra bytes mov ecx,cb and ecx,3 rep stosb } #else //!IN_80386 byte *pb; for (pb = (byte *)pv; cb != 0; cb--) *pb++ = 0; #endif //!IN_80386 } /*************************************************************************** Reverse a block. Useful for exchanging two blocks or avoiding recursion. ***************************************************************************/ void ReversePb(void *pv, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv, cb); #ifdef IN_80386 __asm { // esi - high end of block // edi - low end of block // ecx - number of bytes to swap mov edi,pv mov esi,edi add esi,cb mov ecx,cb shr ecx,1 or ecx,ecx jz LDone LLoop: dec esi mov al,[edi] mov bl,[esi] mov [edi],bl mov [esi],al inc edi dec ecx jnz LLoop LDone: } #else //!IN_80386 byte *pb1, *pb2; byte b; for (pb2 = (pb1 = (byte *)pv) + cb - 1; pb1 < pb2; ) { b = *pb1; *pb1++ = *pb2; *pb2-- = b; } #endif //!IN_80386 } /*************************************************************************** Reverse a list of shorts. ***************************************************************************/ void ReverseRgsw(void *pv, long csw) { AssertIn(csw, 0, kcbMax); AssertPvCb(pv, csw * size(short)); #ifdef IN_80386 __asm { // esi - high end of block // edi - low end of block // ecx - number of shorts to swap mov edi,pv mov esi,edi mov ecx,csw shl ecx,1 add esi,ecx shr ecx,2 or ecx,ecx jz LDone LLoop: sub esi,2 mov ax,[edi] mov bx,[esi] mov [edi],bx mov [esi],ax add edi,2 dec ecx jnz LLoop LDone: } #else //!IN_80386 long *psw1, *psw2; long sw; for (psw2 = (psw1 = (short *)pv) + csw - 1; psw1 < psw2; ) { sw = *psw1; *psw1++ = *psw2; *psw2-- = sw; } #endif //!IN_80386 } /*************************************************************************** Reverse a list of longs. ***************************************************************************/ void ReverseRglw(void *pv, long clw) { AssertIn(clw, 0, kcbMax); AssertPvCb(pv, clw * size(long)); #ifdef IN_80386 __asm { // esi - high end of block // edi - low end of block // ecx - number of longs to swap mov edi,pv mov esi,edi mov ecx,clw shl ecx,2 add esi,ecx shr ecx,3 or ecx,ecx jz LDone LLoop: sub esi,4 mov eax,[edi] mov ebx,[esi] mov [edi],ebx mov [esi],eax add edi,4 dec ecx jnz LLoop LDone: } #else //!IN_80386 long *plw1, *plw2; long lw; for (plw2 = (plw1 = (long *)pv) + clw - 1; plw1 < plw2; ) { lw = *plw1; *plw1++ = *plw2; *plw2-- = lw; } #endif //!IN_80386 } /*************************************************************************** Swap two adjacent blocks of size cb1 and cb2 respectively. ***************************************************************************/ void SwapBlocks(void *pv, long cb1, long cb2) { AssertIn(cb1, 0, kcbMax); AssertIn(cb2, 0, kcbMax); AssertPvCb(pv, cb1 + cb2); ReversePb(pv, cb1); ReversePb(PvAddBv(pv, cb1), cb2); ReversePb(pv, cb1 + cb2); } /*************************************************************************** Swap the contents of two blocks of the same size. ***************************************************************************/ void SwapPb(void *pv1, void *pv2, long cb) { AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); AssertIn(cb, 0, kcbMax); #ifdef IN_80386 __asm { // edi -> memory to swap, first pointer // esi -> memory to swap, second pointer mov edi,pv1 mov esi,pv2 mov ecx,cb shr ecx,2 jz LBytes LLongLoop: mov eax,[edi] mov ebx,[esi] mov [edi],ebx mov [esi],eax add edi,4 add esi,4 dec ecx jnz LLongLoop; LBytes: mov ecx,cb and ecx,3 jz LDone LByteLoop: mov al,[edi] mov bl,[esi] mov [edi],bl mov [esi],al inc edi inc esi dec ecx jnz LByteLoop LDone: } #else //!IN_80386 byte *pb1 = (byte *)pv1; byte *pb2 = (byte *)pv2; byte b; Assert(pb1 + cb <= pb2 || pb2 + cb <= pb1, "blocks overlap"); while (cb-- > 0) { b = *pb1; *pb1++ = *pb2; *pb2++ = b; } #endif //!IN_80386 } /*************************************************************************** Move the entry at ivSrc to be immediately before the element that is currently at ivTarget. If ivTarget > ivSrc, the entry actually ends up at (ivTarget - 1) and the entry at ivTarget doesn't move. If ivTarget < ivSrc, the entry ends up at ivTarget and the entry at ivTarget moves to (ivTarget + 1). Everything in between is shifted appropriately. prgv is the array of elements and cbElement is the size of each element. ***************************************************************************/ void MoveElement(void *prgv, long cbElement, long ivSrc, long ivTarget) { AssertIn(cbElement, 0, kcbMax); AssertIn(ivSrc, 0, kcbMax); AssertIn(ivTarget, 0, kcbMax); AssertPvCb(prgv, LwMul(cbElement, ivSrc + 1)); AssertPvCb(prgv, LwMul(cbElement, ivTarget)); if (ivTarget == ivSrc || ivTarget == ivSrc + 1) return; //swap the blocks if (ivSrc < ivTarget) { SwapBlocks(PvAddBv(prgv, LwMul(ivSrc, cbElement)), cbElement, LwMul(ivTarget - 1 - ivSrc, cbElement)); } else { SwapBlocks(PvAddBv(prgv, LwMul(ivTarget, cbElement)), LwMul(ivSrc - ivTarget, cbElement), cbElement); } } /*************************************************************************** Check for equality of two blocks. ***************************************************************************/ bool FEqualRgb(void *pv1, void *pv2, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); #ifdef IN_80386 bool fRet; __asm { // edi -> memory to compare, first pointer // esi -> memory to compare, second pointer xor eax,eax //assume false return mov edi,pv1 mov esi,pv2 // compare longs mov ecx,cb // (ecx) = length in bytes shr ecx,2 // (ecx) = length in longs repe cmpsd // compare longs jnz LDone // mismatch, go report // compare extra bytes mov ecx,cb and ecx,3 // (ecx) = length mod 4 repe cmpsb // compare odd bytes jnz LDone // mismatch, go report inc eax // successful compare LDone: mov fRet,eax } return fRet; #else //!IN_80386 byte *pb1 = (byte *)pv1; byte *pb2 = (byte *)pv2; while (cb != 0 && *pb1++ == *pb2++) cb--; return cb == 0; #endif //!IN_80386 } /*************************************************************************** Compare the two buffers byte for byte and return a the number of bytes that match. ***************************************************************************/ long CbEqualRgb(void *pv1, void *pv2, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); #ifdef IN_80386 byte *pb; __asm { // edi -> memory to swap, first pointer // esi -> memory to swap, second pointer mov edi,pv1 mov esi,pv2 // compare extra bytes. mov ecx,cb and ecx,3 // (ecx) = length mod 4 repe cmpsb // compare odd bytes jnz LMiss // mismatch, go report how far we got // compare longs mov ecx,cb // (ecx) = length in bytes shr ecx,2 // (ecx) = length in longs repe cmpsd // compare longs jz LHit // matched all the way // esi (and edi) points to the long after the one which caused the // mismatch. Back up 1 long and find the byte. Since we know the // long didn't match, we can assume one of the bytes won't. sub esi,4 // back up sub edi,4 // back up mov ecx,5 // ensure that ecx doesn't count out repe cmpsb // find mismatch byte // esi points to the byte after the one that did not match. LMiss: dec esi dec edi mov pb,edi } return pb - (byte *)pv1; LHit: // We matched all the way to the end. return cb; #else //!IN_80386 byte *pb1 = (byte *)pv1; byte *pb2 = (byte *)pv2; for ( ; cb-- > 0 && *pb1 == *pb2; pb1++, pb2++) ; return pb1 - (byte *)pv1; #endif //!IN_80386 } /*************************************************************************** Compare the two buffers byte for byte and return an fcmp indicating their relationship to each other. ***************************************************************************/ ulong FcmpCompareRgb(void *pv1, void *pv2, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); long cbMatch = CbEqualRgb(pv1, pv2, cb); AssertIn(cbMatch, 0, cb + 1); if (cb == cbMatch) return fcmpEq; return ((byte *)pv1)[cbMatch] < ((byte *)pv2)[cbMatch] ? fcmpLt : fcmpGt; } /*************************************************************************** Copy data without overlap. ****************************************************************************/ void CopyPb(void *pv1, void *pv2, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); Assert((byte *)pv1 + cb <= (byte *)pv2 || (byte *)pv2 + cb <= (byte *)pv1, "blocks overlap"); #ifdef IN_80386 __asm { // Setup the registers for using REP MOVS instruction to move memory. // // esi -> memory to move // edi -> destination of move // direction flag is clear for auto-increment mov esi,pv1 mov edi,pv2 // move the longs mov ecx,cb shr ecx,2 rep movsd // move the extra bytes mov ecx,cb and ecx,3 rep movsb } #else //!IN_80386 byte *pb1 = (byte *)pv1; byte *pb2 = (byte *)pv2; while (cb-- != 0) *pb2++ = *pb1++; #endif //!IN_80386 } /*************************************************************************** Copy data with possible overlap. ***************************************************************************/ void BltPb(void *pv1, void *pv2, long cb) { AssertIn(cb, 0, kcbMax); AssertPvCb(pv1, cb); AssertPvCb(pv2, cb); #ifdef IN_80386 __asm { // Setup the registers for using REP MOVS instruction to move memory. // // esi -> memory to move // edi -> destination of move // direction flag is clear for auto-increment mov esi,pv1 mov edi,pv2 mov ecx,cb cmp esi,edi ja LForward // if source > destination je LDone // if source == destination // source < destination, see if they overlap mov eax,edi sub eax,esi cmp ecx,eax jbe LForward // they overlap with source < destination, so have to do a backward copy std add esi,ecx add edi,ecx dec esi dec edi // move the extra bytes and ecx,3 rep movsb // move the longs mov ecx,cb shr ecx,2 jz LDone sub esi,3 sub edi,3 rep movsd jmp LDone LForward: // move the longs shr ecx,2 rep movsd // move the extra bytes mov ecx,cb and ecx,3 rep movsb LDone: cld } #else //!IN_80386 byte *pb1 = (byte *)pv1; byte *pb2 = (byte *)pv2; if (pb1 > pb2) { while (cb-- != 0) *pb2++ = *pb1++; } else { pb1 += cb; pb2 += cb; while (cb-- != 0) *--pb2 = *--pb1; } #endif //!IN_80386 }