; $Id: ASMBitNextClear.asm $ ;; @file ; IPRT - ASMBitNextClear(). ; ; ; Copyright (C) 2006-2019 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; ; you can redistribute it and/or modify it under the terms of the GNU ; General Public License (GPL) as published by the Free Software ; Foundation, in version 2 as it comes in the "COPYING" file of the ; VirtualBox OSE distribution. VirtualBox OSE is distributed in the ; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. ; ; The contents of this file may alternatively be used under the terms ; of the Common Development and Distribution License Version 1.0 ; (CDDL) only, as it comes in the "COPYING.CDDL" file of the ; VirtualBox OSE distribution, in which case the provisions of the ; CDDL are applicable instead of those of the GPL. ; ; You may elect to license modified versions of this file under the ; terms and conditions of either the GPL or the CDDL or both. ; ;******************************************************************************* ;* Header Files * ;******************************************************************************* %include "iprt/asmdefs.mac" BEGINCODE ;; ; Finds the next clear bit in a bitmap. ; ; @returns (32/64:eax, 16:ax+dx) Index of the first zero bit. ; @returns (32/64:eax, 16:ax+dx) -1 if no clear bit was found. ; @param msc:rcx gcc:rdi pvBitmap Pointer to the bitmap. ; @param msc:edx gcc:rsi cBits The number of bits in the bitmap. Multiple of 32. ; @param msc:r8d gcc:rcx iBitPrev The previous bit, start searching after it. ; ; @remarks Not quite sure how much sense it makes to do this in assembly, but ; it started out with the ASMBit* API, so that's why we still have it. ; BEGINPROC_EXPORTED ASMBitNextClear %if ARCH_BITS == 16 push bp mov bp, sp %endif push xDI ; ; Align input registers: rdi=pvBitmap, ecx=iPrevBit ; %if ARCH_BITS == 64 %ifdef ASM_CALL64_GCC ; ecx = iBitPrev param, rdi=pvBitmap param. %else mov rdi, rcx ; rdi=pvBits mov ecx, r8d ; ecx=iPrevBit mov r8d, edx ; r8d=cBits (saved for .scan_dwords) %endif mov r9, rdi ; Save rdi for bit calculation. %elif ARCH_BITS == 32 mov edi, [esp + 8] ; edi=pvBits mov ecx, [esp + 8 + 8] ; ecx=iPrevBit %elif ARCH_BITS == 16 les di, [bp + 4] ; es:di=pvBits mov ecx, [bp + 4 + 8] ; ecx=iPrevBit %endif ; ; If iPrevBit and iPrevBit + 1 are in the same dword, inspect it for further bits. ; inc ecx mov eax, ecx shr eax, 5 shl eax, 2 ; eax=byte offset into bitmap of current dword. add xDI, xAX ; xDI=current dword address (of iPrevBit + 1). and ecx, 31 jz .scan_dwords %if ARCH_BITS == 16 mov edx, [es:di] %else mov edx, [xDI] %endif not edx ; edx = inverted current dword shr edx, cl ; Shift out bits that we have searched. jz .next_dword ; If zero, nothing to find. Go rep scasd. shl edx, cl ; Shift it back so bsf will return the right index. bsf edx, edx ; edx=index of first clear bit shl eax, 3 ; Turn eax back into a bit offset of the current dword. add eax, edx ; eax=bit offset .return: pop xDI %if ARCH_BITS == 16 mov edx, eax shr edx, 16 leave %endif ret ; ; Do dword scan. ; ; Skip empty dword. .next_dword: add xDI, 4 ; Skip the empty dword. add eax, 4 .scan_dwords: ; First load and align bit count. %if ARCH_BITS == 64 %ifdef ASM_CALL64_GCC mov ecx, esi %else mov ecx, r8d %endif %elif ARCH_BITS == 32 mov ecx, [esp + 8 + 4] %elif ARCH_BITS == 16 mov ecx, [bp + 4 + 4] %endif add ecx, 31 shr ecx, 5 ; ecx=bitmap size in dwords. ; Adjust ecx to how many dwords there are left to scan. (eax = current byte offset) shr eax, 2 ; eax=current dword offset. sub ecx, eax jbe .return_failure ; Do the scanning. cld mov eax, 0ffffffffh repe scasd je .return_failure ; Find the bit in question. sub xDI, 4 ; One step back. %if ARCH_BITS == 16 movzx edi, di xor eax, [es:xDI] %else xor eax, [xDI] %endif bsf eax, eax jz .return_failure ; race paranoia ; Calc the bit offset. %if ARCH_BITS == 16 sub di, [bp + 4] movzx edi, di %elif ARCH_BITS == 32 sub xDI, [esp + 8] %elif ARCH_BITS == 64 sub xDI, r9 %endif shl edi, 3 ; edi=bit offset of current dword. add eax, edi jmp .return .return_failure: mov eax, 0ffffffffh jmp .return ENDPROC ASMBitNextClear