Commit bb1dd8c6 authored by Tim Allen's avatar Tim Allen

Update to v106r77 release.

byuu says:

So this turned out to be a rather unproductive ten-hour rabbit hole, but
...

I reworked nall/primitives.hpp a lot. And because the changes are
massive, testing of this WIP for regressions is critically important. I
really can't stress that enough, we're almost certainly going to have
some hidden regressions here ...

We now have a nall/primitives/ subfolder that splits up the classes into
manageable components. The bit-field support is now shared between both
Natural and Integer. All of the assignment operator overloads are now
templated and take references instead of values. Things like the
GSU::Register class are non-copyable on account of the function<>
object inside of it, and previously only operator= would work with
classes like that.

The big change is nall/primitives/operators.hpp, which is a really
elaborate system to compute the minimum number of bits needed for any
operation, and to return a Natural<T> or Integer<T> when one or both of
the arguments are such a type.

Unfortunately, it doesn't really work yet ... Kirby's Dream Land 3
breaks if we include operators.hpp. Zelda 3 runs fine with this, but I
had to make a huge amount of core changes, including introducing a new
ternary(bool, lhs, rhs) function to nall/algorithm to get past
Natural<X> and Natural<Y> not being equivalent (is_integral types get a
special exemption to ternary ?: type equivalence, yet it's impossible to
simulate with our own classes, which is bullshit.) The horrifying part
is that ternary() will evaluate both lhs and rhs, unlike ?:

I converted some of the functions to test ? uint(x) : uint(y), and
others to ternary(test, x, y) ... I don't have a strong preference
either way yet.

But the part where things may have gotten broken is in the changes to
where ternary() was placed. Some cases like in the GBA PPU renderer, it
was rather unclear the order of evaluations, so I may have made a
mistake somewhere.

So again, please please test this if you can. Or even better, look over
the diff.

Longer-term, I'd really like the enable nall/primitives/operators.hpp,
but right now I'm not sure why Kirby's Dream Land 3 is breaking. Help
would be appreciated, but ... it's gonna be really complex and difficult
to debug, so I'm probably gonna be on my own here ... sigh.
parent c9f7c6c4
Pipeline #43145556 passed with stage
in 13 minutes and 19 seconds
......@@ -30,7 +30,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "106.76";
static const string Version = "106.77";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";
......
......@@ -33,7 +33,7 @@ auto Interface::color(uint32 n) -> uint64 {
double brightness = 1.0;
double gamma = settings.colorEmulation ? 1.8 : 2.2;
int color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1;
int color = (n & 0x0f), level = color < 0xe ? int(n >> 4 & 3) : 1;
static const double black = 0.518, white = 1.962, attenuation = 0.746;
static const double levels[8] = {
......
......@@ -104,7 +104,7 @@ auto PPU::Background::affine(uint x, uint y) -> void {
}
uint screenSize = 16 << io.screenSize;
uint screenWrap = (1 << (io.affineWrap ? 7 + io.screenSize : 20)) - 1;
uint screenWrap = (1 << ternary(io.affineWrap, 7 + io.screenSize, 20)) - 1;
uint cx = (fx >> 8) & screenWrap;
uint cy = (fy >> 8) & screenWrap;
......@@ -148,7 +148,7 @@ auto PPU::Background::bitmap(uint x, uint y) -> void {
uint height = io.mode == 5 ? 128 : 160;
uint mode = depth ? Half : Byte;
uint baseAddress = io.mode == 3 ? 0 : 0xa000 * io.frame;
uint baseAddress = ternary(io.mode == 3, 0, 0xa000 * io.frame);
uint px = fx >> 8;
uint py = fy >> 8;
......
......@@ -8,12 +8,12 @@ auto PPU::Objects::scanline(uint y) -> void {
if(object.affine == 0 && object.affineSize == 1) continue; //hidden
if(py >= object.height << object.affineSize) continue; //offscreen
uint rowSize = io.mapping == 0 ? 32 >> object.colors : object.width >> 3;
uint rowSize = ternary(io.mapping == 0, 32 >> object.colors, object.width >> 3);
uint baseAddress = object.character << 5;
if(object.mosaic && io.mosaicHeight) {
int mosaicY = (y / (1 + io.mosaicHeight)) * (1 + io.mosaicHeight);
py = object.y >= 160 || mosaicY - object.y >= 0 ? mosaicY - object.y : 0;
py = object.y >= 160 || mosaicY - object.y >= 0 ? uint(mosaicY - object.y) : 0;
}
int16 pa = ppu.objectParam[object.affineParam].pa;
......
......@@ -70,7 +70,7 @@ auto VDP::Background::run(uint x, uint y) -> void {
uint16 tileData = vdp.vram.read(tileAddress);
uint4 color = tileData >> (((pixelX & 3) ^ 3) << 2);
output.color = color ? tileAttributes.bits(13,14) << 4 | color : 0;
output.color = ternary(color, tileAttributes.bits(13,14) << 4 | color, 0);
output.priority = tileAttributes.bit(15);
}
......
......@@ -27,7 +27,7 @@ auto YM2612::Channel::Operator::trigger(bool state) -> void {
}
auto YM2612::Channel::Operator::runEnvelope() -> void {
uint sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x3f0;
uint sustain = ternary(envelope.sustainLevel < 15, envelope.sustainLevel << 5, 0x3f0);
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
uint value = ym2612.envelope.clock >> envelope.divider;
......@@ -121,7 +121,7 @@ auto YM2612::Channel::Operator::updatePhase() -> void {
phase.delta = pitch.value + (pm >> 10 - msb) << 6 >> 7 - octave.value;
phase.delta = (!detune.bit(2) ? phase.delta + tuning : phase.delta - tuning) & 0x1ffff;
phase.delta = (multiple ? phase.delta * multiple : phase.delta >> 1) & 0xfffff;
phase.delta = ternary(multiple, phase.delta * multiple, phase.delta >> 1) & 0xfffff;
}
auto YM2612::Channel::Operator::updateLevel() -> void {
......
......@@ -64,7 +64,7 @@ auto YM2612::sample() -> void {
auto wave = [&](uint n, uint modulation) -> int {
int x = (modulation >> 1) + (op[n].phase.value >> 10);
int y = sine[x & 0x3ff] + op[n].outputLevel;
return y < 0x2000 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0;
return ternary(y < 0x2000, pow2[y & 0x1ff] << 2 >> (y >> 9), 0);
};
int feedback = modMask & op[0].output + op[0].prior >> 9 - channel.feedback;
......
......@@ -13,8 +13,8 @@ auto ARM7TDMI::ADD(uint32 source, uint32 modify, bool carry) -> uint32 {
auto ARM7TDMI::ASR(uint32 source, uint8 shift) -> uint32 {
carry = cpsr().c;
if(shift == 0) return source;
carry = shift > 32 ? source & 1 << 31 : source & 1 << shift - 1;
source = shift > 31 ? (int32)source >> 31 : (int32)source >> shift;
carry = ternary(shift > 32, source & 1 << 31, source & 1 << shift - 1);
source = ternary(shift > 31, (int32)source >> 31, (int32)source >> shift);
return source;
}
......@@ -30,16 +30,16 @@ auto ARM7TDMI::BIT(uint32 result) -> uint32 {
auto ARM7TDMI::LSL(uint32 source, uint8 shift) -> uint32 {
carry = cpsr().c;
if(shift == 0) return source;
carry = shift > 32 ? 0 : source & 1 << 32 - shift;
source = shift > 31 ? 0 : source << shift;
carry = ternary(shift > 32, 0, source & 1 << 32 - shift);
source = ternary(shift > 31, 0, source << shift);
return source;
}
auto ARM7TDMI::LSR(uint32 source, uint8 shift) -> uint32 {
carry = cpsr().c;
if(shift == 0) return source;
carry = shift > 32 ? 0 : source & 1 << shift - 1;
source = shift > 31 ? 0 : source >> shift;
carry = ternary(shift > 32, 0, source & 1 << shift - 1);
source = ternary(shift > 31, 0, source >> shift);
return source;
}
......
......@@ -95,7 +95,7 @@ auto ARM7TDMI::armInstructionDataRegisterShift
case 0: rm = LSL(rm, rs < 33 ? rs : (uint8)33); break;
case 1: rm = LSR(rm, rs < 33 ? rs : (uint8)33); break;
case 2: rm = ASR(rm, rs < 32 ? rs : (uint8)32); break;
case 3: if(rs) rm = ROR(rm, rs & 31 ? rs & 31 : 32); break;
case 3: if(rs) rm = ROR(rm, rs & 31 ? uint(rs & 31) : 32); break;
}
armALU(mode, d, n, rm);
......
......@@ -221,7 +221,7 @@ auto GSU::instructionAND_BIC(uint n) -> void {
//$80-8f(alt3) umult #N
auto GSU::instructionMULT_UMULT(uint n) -> void {
if(!regs.sfr.alt2) n = regs.r[n];
regs.dr() = (!regs.sfr.alt1 ? ((int8)regs.sr() * (int8)n) : ((uint8)regs.sr() * (uint8)n));
regs.dr() = (!regs.sfr.alt1 ? uint16((int8)regs.sr() * (int8)n) : uint16((uint8)regs.sr() * (uint8)n));
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
......
......@@ -63,14 +63,14 @@ struct M68K {
//registers.cpp
struct DataRegister {
explicit DataRegister(uint number_) : number(number_) {}
explicit DataRegister(uint64 number_) : number(number_) {}
uint3 number;
};
template<uint Size = Long> auto read(DataRegister reg) -> uint32;
template<uint Size = Long> auto write(DataRegister reg, uint32 data) -> void;
struct AddressRegister {
explicit AddressRegister(uint number_) : number(number_) {}
explicit AddressRegister(uint64 number_) : number(number_) {}
uint3 number;
};
template<uint Size = Long> auto read(AddressRegister reg) -> uint32;
......
......@@ -570,7 +570,7 @@ auto SPC700::instructionTestSetBitsAbsolute(bool set) -> void {
ZF = (A - data) == 0;
NF = (A - data) & 0x80;
read(address);
write(address, set ? data | A : data & ~A);
write(address, ternary(set, data | A, data & ~A));
}
auto SPC700::instructionTransfer(uint8& from, uint8& to) -> void {
......
......@@ -21,10 +21,12 @@ template<> auto TLCS900H::parity<uint32>(uint32 data) const -> bool {
template<typename T> auto TLCS900H::algorithmAdd(T target, T source, uint1 carry) -> T {
T result = target + source + carry;
CF = result < target || result == target && carry;
T carries = target ^ source ^ result;
T overflow = (target ^ result) & (source ^ result);
CF = T(carries ^ overflow).negative();
NF = 0;
VF = T((target ^ result) & (source ^ result)).negative();
HF = T(target ^ source ^ result).bit(4);
VF = overflow.negative();
HF = carries.bit(4);
if constexpr(T::bits() == 32) HF = Undefined;
ZF = result.zero();
SF = result.negative();
......@@ -79,10 +81,12 @@ template<typename T> auto TLCS900H::algorithmOr(T target, T source) -> T {
template<typename T> auto TLCS900H::algorithmSubtract(T target, T source, uint1 carry) -> T {
T result = target - source - carry;
CF = result > target || result == target && carry;
T carries = target ^ source ^ result;
T overflow = (target ^ result) & (source ^ target);
CF = T(carries ^ overflow).negative();
NF = 1;
VF = T((target ^ source) & (target ^ result)).negative();
HF = T(target ^ source ^ result).bit(4);
VF = overflow.negative();
HF = carries.bit(4);
if constexpr(T::bits() == 32) HF = Undefined;
ZF = result.zero();
SF = result.negative();
......
......@@ -32,13 +32,13 @@ auto WDC65816::fetch() -> uint8 {
}
auto WDC65816::pull() -> uint8 {
EF ? lo(S)++ : S++;
EF ? (void)lo(S)++ : (void)S++;
return read(S);
}
auto WDC65816::push(uint8 data) -> void {
write(S, data);
EF ? lo(S)-- : S--;
EF ? (void)lo(S)-- : (void)S--;
}
auto WDC65816::pullN() -> uint8 {
......
......@@ -437,7 +437,7 @@ auto SA1::writeIOSA1(uint24 address, uint8 data) -> void {
} else {
int16 dividend = mmio.ma;
uint16 divisor = mmio.mb;
uint16 remainder = dividend >= 0 ? dividend % divisor : (dividend % divisor + divisor) % divisor;
uint16 remainder = dividend >= 0 ? uint16(dividend % divisor) : uint16((dividend % divisor + divisor) % divisor);
uint16 quotient = (dividend - remainder) / divisor;
mmio.mr = remainder << 16 | quotient;
}
......
......@@ -18,7 +18,7 @@ auto SPC7110::dcuBeginTransfer() -> void {
decompressor->initialize(dcuMode, dcuAddress);
decompressor->decode();
uint seek = r480b & 2 ? r4805 | r4806 << 8 : 0;
uint seek = ternary(r480b & 2, r4805 | r4806 << 8, 0);
while(seek--) decompressor->decode();
r480c |= 0x80;
......
......@@ -26,7 +26,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void
uint hmask = (width << self.tileSize << self.screenSize.bit(0)) - 1;
uint vmask = (width << self.tileSize << self.screenSize.bit(1)) - 1;
uint y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
uint y = this->y - ternary(self.mosaicEnable, this->y % (1 + io.mosaicSize), 0);
if(hires) {
hscroll <<= 1;
if(io.interlace) y = y << 1 | ppu.field();
......
auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
int Y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
int Y = this->y - ternary(self.mosaicEnable, this->y % (1 + io.mosaicSize), 0);
int y = !io.mode7.vflip ? Y : 255 - Y;
int a = (int16)io.mode7.a;
......
......@@ -129,7 +129,7 @@ auto PPU::oamAddressReset() -> void {
}
auto PPU::oamSetFirstObject() -> void {
io.obj.first = !io.oamPriority ? 0 : io.oamAddress >> 2;
io.obj.first = !io.oamPriority ? 0 : uint(io.oamAddress >> 2);
}
auto PPU::readObject(uint10 address) -> uint8 {
......
......@@ -165,7 +165,7 @@ auto PPU::Background::run(bool screen) -> void {
uint8 color = getTileColor();
Pixel pixel;
pixel.priority = priority;
pixel.palette = color ? paletteIndex + color : 0;
pixel.palette = color ? uint(paletteIndex + color) : 0;
pixel.tile = tile;
if(x == 0) {
......
......@@ -6,7 +6,7 @@ auto PPU::Object::addressReset() -> void {
}
auto PPU::Object::setFirstSprite() -> void {
io.firstSprite = !ppu.io.oamPriority ? 0 : ppu.io.oamAddress >> 2;
io.firstSprite = !ppu.io.oamPriority ? 0 : uint(ppu.io.oamAddress >> 2);
}
auto PPU::Object::frame() -> void {
......
......@@ -8,7 +8,7 @@
namespace nall { namespace {
template<typename T, typename U> auto min(const T& t, const U& u) -> T {
return t < u ? t : u;
return t < u ? t : (T)u;
}
template<typename T, typename U, typename... P> auto min(const T& t, const U& u, P&&... p) -> T {
......@@ -16,11 +16,15 @@ template<typename T, typename U, typename... P> auto min(const T& t, const U& u,
}
template<typename T, typename U> auto max(const T& t, const U& u) -> T {
return t > u ? t : u;
return t > u ? t : (T)u;
}
template<typename T, typename U, typename... P> auto max(const T& t, const U& u, P&&... p) -> T {
return t > u ? max(t, forward<P>(p)...) : max(u, forward<P>(p)...);
}
template<typename T, typename U> auto ternary(bool test, const T& lhs, const U& rhs) -> T {
return test ? lhs : (T)rhs;
}
}}
#pragma once
#include <nall/primitives.hpp>
namespace nall {
template<typename Type, uint Bit> struct BooleanBitField {
......@@ -55,7 +57,7 @@ template<typename Type, uint Lo, uint Hi> struct NaturalBitField {
inline operator utype() const { return get(); }
inline auto& operator=(const NaturalBitField& value) { return set(value.data); }
template<typename T> inline auto& operator=(const T& value) { return set(value << lo); }
template<typename T> inline auto& operator=(T value) { return set(value << lo); }
inline auto operator++(int) { utype value = get(); set(data + (1 << lo)); return value; }
inline auto operator--(int) { utype value = get(); set(data - (1 << lo)); return value; }
......
......@@ -4,300 +4,20 @@
#include <nall/traits.hpp>
namespace nall {
struct Boolean;
template<uint Bits> struct Natural;
template<uint Bits> struct Integer;
struct Boolean {
inline Boolean() : data(false) {}
template<typename T> inline Boolean(const T& value) : data(value) {}
inline operator bool() const { return data; }
template<typename T> inline auto& operator=(const T& value) { data = value; return *this; }
inline auto flip() { return data ^= 1; }
inline auto raise() { return data == 0 ? data = 1, true : false; }
inline auto lower() { return data == 1 ? data = 0, true : false; }
inline auto flip(bool value) { return data != value ? (data = value, true) : false; }
inline auto raise(bool value) { return !data && value ? (data = value, true) : (data = value, false); }
inline auto lower(bool value) { return data && !value ? (data = value, true) : (data = value, false); }
inline auto serialize(serializer& s) { s(data); }
private:
bool data;
};
template<uint Bits> struct Natural {
using type =
typename conditional<Bits <= 8, uint8_t,
typename conditional<Bits <= 16, uint16_t,
typename conditional<Bits <= 32, uint32_t,
typename conditional<Bits <= 64, uint64_t,
void>::type>::type>::type>::type;
enum : type { Mask = ~0ull >> (64 - Bits) };
static inline constexpr auto bits() -> uint { return Bits; }
inline Natural() : data(0) {}
template<typename T> inline Natural(const T& value) { set(value); }
inline operator type() const { return data; }
template<typename T> inline auto& operator=(const T& value) { set(value); return *this; }
inline auto operator++(int) { type value = data; set(data + 1); return value; }
inline auto operator--(int) { type value = data; set(data - 1); return value; }
inline auto& operator++() { set(data + 1); return *this; }
inline auto& operator--() { set(data - 1); return *this; }
inline auto& operator &=(const type value) { set(data & value); return *this; }
inline auto& operator |=(const type value) { set(data | value); return *this; }
inline auto& operator ^=(const type value) { set(data ^ value); return *this; }
inline auto& operator<<=(const type value) { set(data << value); return *this; }
inline auto& operator>>=(const type value) { set(data >> value); return *this; }
inline auto& operator +=(const type value) { set(data + value); return *this; }
inline auto& operator -=(const type value) { set(data - value); return *this; }
inline auto& operator *=(const type value) { set(data * value); return *this; }
inline auto& operator /=(const type value) { set(data / value); return *this; }
inline auto& operator %=(const type value) { set(data % value); return *this; }
inline auto serialize(serializer& s) { s(data); }
struct Reference {
inline Reference(Natural& source, uint lo, uint hi) : source(source), Lo(lo), Hi(hi) {}
inline auto& operator=(Reference& source) { return set(source.get()); }
inline auto get() const -> type {
const type RangeBits = Hi - Lo + 1;
const type RangeMask = (((1ull << RangeBits) - 1) << Lo) & Mask;
return (source & RangeMask) >> Lo;
}
inline auto& set(const type value) {
const type RangeBits = Hi - Lo + 1;
const type RangeMask = (((1ull << RangeBits) - 1) << Lo) & Mask;
source = (source & ~RangeMask) | ((value << Lo) & RangeMask);
return *this;
}
inline operator type() const { return get(); }
inline auto& operator =(const type value) { return set( value); }
inline auto& operator &=(const type value) { return set(get() & value); }
inline auto& operator |=(const type value) { return set(get() | value); }
inline auto& operator ^=(const type value) { return set(get() ^ value); }
inline auto& operator<<=(const type value) { return set(get() << value); }
inline auto& operator>>=(const type value) { return set(get() >> value); }
inline auto& operator +=(const type value) { return set(get() + value); }
inline auto& operator -=(const type value) { return set(get() - value); }
inline auto& operator *=(const type value) { return set(get() * value); }
inline auto& operator /=(const type value) { return set(get() / value); }
inline auto& operator %=(const type value) { return set(get() % value); }
inline auto operator++(int) { auto value = get(); set(value + 1); return value; }
inline auto operator--(int) { auto value = get(); set(value - 1); return value; }
inline auto& operator++() { return set(get() + 1); }
inline auto& operator--() { return set(get() - 1); }
private:
Natural& source;
const type Lo;
const type Hi;
};
inline auto zero() const -> bool { return data == 0; }
inline auto positive() const -> bool { return (data >> bits() - 1) == 0; }
inline auto negative() const -> bool { return (data >> bits() - 1) == 1; }
inline auto bits(uint lo, uint hi) -> Reference { return {*this, lo < hi ? lo : hi, hi > lo ? hi : lo}; }
inline auto bit(uint index) -> Reference { return {*this, index, index}; }
inline auto byte(uint index) -> Reference { return {*this, index * 8 + 0, index * 8 + 7}; }
inline auto bits(uint lo, uint hi) const -> const Reference { return {(Natural&)*this, lo < hi ? lo : hi, hi > lo ? hi : lo}; }
inline auto bit(uint index) const -> const Reference { return {(Natural&)*this, index, index}; }
inline auto byte(uint index) const -> const Reference { return {(Natural&)*this, index * 8 + 0, index * 8 + 7}; }
inline auto clamp(uint bits) -> uintmax {
const uintmax b = 1ull << (bits - 1);
const uintmax m = b * 2 - 1;
return data < m ? data : m;
}
inline auto clip(uint bits) -> uintmax {
const uintmax b = 1ull << (bits - 1);
const uintmax m = b * 2 - 1;
return data & m;
}
inline auto integer() const -> Integer<Bits>;
private:
auto set(type value) -> void {
data = value & Mask;
}
type data;
};
template<uint Bits> struct Integer {
using type =
typename conditional<Bits <= 8, int8_t,
typename conditional<Bits <= 16, int16_t,
typename conditional<Bits <= 32, int32_t,
typename conditional<Bits <= 64, int64_t,
void>::type>::type>::type>::type;
using utype = typename Natural<Bits>::type;
enum : utype { Mask = ~0ull >> (64 - Bits), Sign = 1ull << (Bits - 1) };
static inline constexpr auto bits() -> uint { return Bits; }
inline Integer() : data(0) {}
template<typename T> inline Integer(const T& value) { set(value); }
inline operator type() const { return data; }
template<typename T> inline auto& operator=(const T& value) { set(value); return *this; }
inline auto operator++(int) { type value = data; set(data + 1); return value; }
inline auto operator--(int) { type value = data; set(data - 1); return value; }
inline auto& operator++() { set(data + 1); return *this; }
inline auto& operator--() { set(data - 1); return *this; }
inline auto& operator &=(const type value) { set(data & value); return *this; }
inline auto& operator |=(const type value) { set(data | value); return *this; }
inline auto& operator ^=(const type value) { set(data ^ value); return *this; }
inline auto& operator<<=(const type value) { set(data << value); return *this; }
inline auto& operator>>=(const type value) { set(data >> value); return *this; }
inline auto& operator +=(const type value) { set(data + value); return *this; }
inline auto& operator -=(const type value) { set(data - value); return *this; }
inline auto& operator *=(const type value) { set(data * value); return *this; }
inline auto& operator /=(const type value) { set(data / value); return *this; }
inline auto& operator %=(const type value) { set(data % value); return *this; }
inline auto serialize(serializer& s) { s(data); }
struct Reference {
inline Reference(Integer& source, uint lo, uint hi) : source(source), Lo(lo), Hi(hi) {}
inline auto& operator=(const Reference& source) { return set(source.get()); }
inline auto get() const -> utype {
const type RangeBits = Hi - Lo + 1;
const type RangeMask = (((1ull << RangeBits) - 1) << Lo) & Mask;
return ((utype)source & RangeMask) >> Lo;
}
inline auto& set(const utype value) {
const type RangeBits = Hi - Lo + 1;
const type RangeMask = (((1ull << RangeBits) - 1) << Lo) & Mask;
source = ((utype)source & ~RangeMask) | ((value << Lo) & RangeMask);
return *this;
}
inline operator utype() const { return get(); }
inline auto& operator =(const utype value) { return set( value); }
inline auto& operator &=(const utype value) { return set(get() & value); }
inline auto& operator |=(const utype value) { return set(get() | value); }
inline auto& operator ^=(const utype value) { return set(get() ^ value); }
inline auto& operator<<=(const utype value) { return set(get() << value); }
inline auto& operator>>=(const utype value) { return set(get() >> value); }
inline auto& operator +=(const utype value) { return set(get() + value); }
inline auto& operator -=(const utype value) { return set(get() - value); }
inline auto& operator *=(const utype value) { return set(get() * value); }