6502bench/CommonUtil/BitTwiddle.cs

113 lines
4.4 KiB
C#

/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace CommonUtil {
/// <summary>
/// Some bit-twiddling functions.
/// </summary>
public static class BitTwiddle {
/// <summary>
/// Returns the argument, rounded up to the next highest power of 2. If the argument
/// is an exact power of two, it is returned unmodified.
/// </summary>
public static int RoundUpPowerOf2(int val) {
val--; // handle exact power of 2 case; works correctly for val=0
return NextHighestPowerOf2(val);
}
/// <summary>
/// Returns the first power of 2 value that is higher than val. If the argument is
/// an exact power of two, the next power of 2 is returned.
/// </summary>
/// <remarks>
/// Classic bit-twiddling approach. I can't find a "count leading zeroes" function
/// in C# that turns into a CPU instruction; if we had that, we could just use 1&lt;&lt;N.
/// </remarks>
public static int NextHighestPowerOf2(int val) {
val |= val >> 1; // "smear" bits across integer
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return val + 1;
}
/// <summary>
/// Returns an integer in which the only bit set is the least-significant set bit in
/// the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 00000100.
///
/// Two's complement negation inverts and adds one, so 01100 --&gt; 10011+1 --&gt; 10100.
/// The only set bit they have in common is the one we want.
/// </remarks>
public static int IsolateLeastSignificantOne(int val) {
return val & -val;
}
/// <summary>
/// Returns the number of bits that are set in the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 4.
///
/// This comes from http://aggregate.org/MAGIC/#Population%20Count%20(Ones%20Count) .
/// </remarks>
public static int CountOneBits(int val) {
// 32-bit recursive reduction using SWAR...
// but first step is mapping 2-bit values
// into sum of 2 1-bit values in sneaky way
val -= ((val >> 1) & 0x55555555);
val = (((val >> 2) & 0x33333333) + (val & 0x33333333));
val = (((val >> 4) + val) & 0x0f0f0f0f);
val += (val >> 8);
val += (val >> 16);
return (val & 0x0000003f);
}
/// <summary>
/// Returns the number of trailing zero bits in the argument.
/// </summary>
/// <remarks>
/// If you pass in 10110100, this will return 2.
///
/// Also from http://aggregate.org/MAGIC/ .
/// </remarks>
public static int CountTrailingZeroes(int val) {
// Lazy: reduce to least-significant 1 bit, subtract one to clear that bit and set
// all the bits to the right of it, then just count the 1s.
return CountOneBits(IsolateLeastSignificantOne(val) - 1);
}
/// <summary>
/// Sign-extends an integer value.
/// </summary>
/// <param name="val">Value to extend.</param>
/// <param name="byteLen">Number of significant bytes (1-4).</param>
/// <returns>Sign-extended value, or original value if byteLen is invalid.</returns>
public static int SignExtend(int val, int byteLen) {
if (byteLen < 1 || byteLen >= 4) {
// invalid, or nothing to do
return val;
}
int shiftCount = (4 - byteLen) * 8;
return (val << shiftCount) >> shiftCount;
}
}
}