From ac2d0ba1cf31948e675bc301d47b602d1ee7c251 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 13 Aug 2016 09:47:30 +1000 Subject: [PATCH] Update to v101r05 release. byuu says: Changelog: - 68K: fixed bug that affected BSR return address - VDP: added very preliminary emulation of planes A, B, W (W is entirely broken though) - VDP: added command/address stuff so you can write to VRAM, CRAM, VSRAM - VDP: added VRAM fill DMA I would be really surprised if any commercial games showed anything at all, so I'd probably recommend against wasting your time trying, unless you're really bored :P Also, I wanted to add: I am accepting patches\! So if anyone wants to look over the 68K core for bugs, that would save me untold amounts of time in the near future :D --- higan/emulator/emulator.hpp | 2 +- higan/md/cpu/cpu.cpp | 6 + higan/md/interface/interface.cpp | 4 +- higan/md/vdp/background.cpp | 32 +++++ higan/md/vdp/dma.cpp | 18 +++ higan/md/vdp/io.cpp | 196 +++++++++++++++++++++----- higan/md/vdp/render.cpp | 36 +++++ higan/md/vdp/vdp.cpp | 36 +++-- higan/md/vdp/vdp.hpp | 89 +++++++++--- higan/processor/m68k/instruction.cpp | 14 +- higan/processor/m68k/instructions.cpp | 9 +- higan/processor/m68k/m68k.hpp | 14 +- hiro/windows/widget/canvas.cpp | 10 +- nall/primitives.hpp | 2 + 14 files changed, 371 insertions(+), 97 deletions(-) create mode 100644 higan/md/vdp/background.cpp create mode 100644 higan/md/vdp/dma.cpp create mode 100644 higan/md/vdp/render.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 338b95f4..2371ef69 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "101.04"; + static const string Version = "101.05"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/md/cpu/cpu.cpp b/higan/md/cpu/cpu.cpp index bbf065f3..435d95ed 100644 --- a/higan/md/cpu/cpu.cpp +++ b/higan/md/cpu/cpu.cpp @@ -15,6 +15,12 @@ auto CPU::boot() -> void { } auto CPU::main() -> void { + #if 0 + static file fp; + if(!fp) fp.open({Path::user(), "Desktop/trace.log"}, file::mode::write); + fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n"); + #endif + instruction(); } diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index 44386e47..35803183 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -61,9 +61,9 @@ auto Interface::videoColors() -> uint32 { } auto Interface::videoColor(uint32 color) -> uint64 { - uint B = color.bits(0,2); + uint R = color.bits(0,2); uint G = color.bits(3,5); - uint R = color.bits(6,8); + uint B = color.bits(6,8); uint64 r = image::normalize(R, 3, 16); uint64 g = image::normalize(G, 3, 16); diff --git a/higan/md/vdp/background.cpp b/higan/md/vdp/background.cpp new file mode 100644 index 00000000..e572721d --- /dev/null +++ b/higan/md/vdp/background.cpp @@ -0,0 +1,32 @@ +auto VDP::Background::scanline() -> void { +} + +auto VDP::Background::run(uint x, uint y) -> void { + output.priority = 0; + output.color = 0; + + uint tileX = x >> 3; + uint tileY = y >> 3; + uint15 nametableAddress = io.nametableAddress; + nametableAddress += (tileY << io.nametableWidth) + tileX; + + uint16 tileAttributes = vdp.vram[nametableAddress]; + uint15 tileAddress = tileAttributes.bits(0,10) << 4; + uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0); + uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0); + tileAddress += pixelY << 1 | pixelX >> 2; + + uint16 tileData = vdp.vram[tileAddress]; + uint4 palette = tileData >> (((pixelX & 3) ^ 3) << 2); + if(palette) { + output.color = tileAttributes.bits(13,14) << 4 | palette; + output.priority = tileAttributes.bit(15); + } +} + +auto VDP::Background::power() -> void { +} + +auto VDP::Background::reset() -> void { + memory::fill(&io, sizeof(IO)); +} diff --git a/higan/md/vdp/dma.cpp b/higan/md/vdp/dma.cpp new file mode 100644 index 00000000..ec8bb6e3 --- /dev/null +++ b/higan/md/vdp/dma.cpp @@ -0,0 +1,18 @@ +auto VDP::dmaRun() -> void { + if(!io.dmaEnable) return; + if(!io.dmaActive) return; + + if(io.dmaMode == 2) return dmaFillVRAM(); +} + +auto VDP::dmaFillVRAM() -> void { + auto address = io.address.bits(1,15); + auto data = io.dmaFillWord; + if(io.address.bit(0)) data = data >> 8 | data << 8; + vram[address] = data; + io.address += io.dataIncrement; + + if(--io.dmaLength == 0) { + io.dmaActive = false; + } +} diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index b7b9d75c..c2d3d3f0 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -1,10 +1,25 @@ +//todo: does data mirroring occur for all VDP addresses; or just data/control ports? + auto VDP::readByte(uint24 addr) -> uint8 { - return 0x00; + auto data = readWord(addr & ~1); + return data << 8 | data << 0; } +auto VDP::writeByte(uint24 addr, uint8 data) -> void { + return writeWord(addr & ~1, data << 8 | data << 0); +} + +// + auto VDP::readWord(uint24 addr) -> uint16 { switch(addr & 0xc0001f) { + //data port + case 0xc00000: case 0xc00002: { + return readDataPort(); + } + + //control port case 0xc00004: case 0xc00006: { return readControlPort(); } @@ -14,19 +29,19 @@ auto VDP::readWord(uint24 addr) -> uint16 { return 0x0000; } -auto VDP::writeByte(uint24 addr, uint8 data) -> void { -//print("[VDP] ", hex(addr, 6L), "=", hex(data, 2L), "\n"); -} - auto VDP::writeWord(uint24 addr, uint16 data) -> void { //print("[VDP] ", hex(addr, 6L), "=", hex(data, 4L), "\n"); switch(addr & 0xc0001f) { + //data port + case 0xc00000: case 0xc00002: { + return writeDataPort(data); + } + //control port case 0xc00004: case 0xc00006: { - if(!data.bit(15)) return; - return writeControlPort(data.bits(8,14), data.bits(0,7)); + return writeControlPort(data); } } @@ -34,15 +49,99 @@ auto VDP::writeWord(uint24 addr, uint16 data) -> void { // +auto VDP::readDataPort() -> uint16 { + io.commandPending = false; + + //VRAM read + if(io.command.bits(0,3) == 0) { + return 0x0000; + } + + //VSRAM read + if(io.command.bits(0,3) == 4) { + return 0x0000; + } + + //CRAM read + if(io.command.bits(0,3) == 8) { + return 0x0000; + } +} + +auto VDP::writeDataPort(uint16 data) -> void { + io.commandPending = false; + + //DMA VRAM fill + if(io.command.bits(4,5) == 2) { + io.dmaActive = true; + io.dmaFillWord = data; + return; + } + + //VRAM write + if(io.command.bits(0,3) == 1) { + auto address = io.address.bits(1,15); + if(io.address.bit(0)) data = data >> 8 | data << 8; + vram[address] = data; + io.address += io.dataIncrement; + return; + } + + //VSRAM write + if(io.command.bits(0,3) == 5) { + auto address = io.address.bits(1,6); + if(address >= 40) return; + //data format: ---- --yy yyyy yyyy + vsram[address] = data.bits(0,9); + io.address += io.dataIncrement; + return; + } + + //CRAM write + if(io.command.bits(0,3) == 3) { + auto address = io.address.bits(1,6); + //data format: ---- bbb- ggg- rrr- + cram[address] = data.bits(1,3) << 0 | data.bits(5,7) << 3 | data.bits(9,11) << 6; + io.address += io.dataIncrement; + return; + } +} + +// + auto VDP::readControlPort() -> uint16 { + io.commandPending = false; + uint16 result = 0b0011'0100'0000'0000; + result |= io.dmaActive << 1; return result; } -auto VDP::writeControlPort(uint7 addr, uint8 data) -> void { -//print("[VDPC] ", hex(addr, 2L), "=", hex(data, 2L), "\n"); +auto VDP::writeControlPort(uint16 data) -> void { +//print("[VDPC] ", hex(data, 4L), "\n"); - switch(addr) { + //command write (lo) + if(io.commandPending) { + io.commandPending = false; + + io.command.bits(2,5) = data.bits(4,7); + io.address.bits(14,15) = data.bits(0,1); + + return; + } + + //command write (hi) + if(data.bits(14,15) != 2) { + io.commandPending = true; + + io.command.bits(0,1) = data.bits(14,15); + io.address.bits(0,13) = data.bits(0,13); + return; + } + + //register write (d13 is ignored) + if(data.bits(14,15) == 2) + switch(data.bits(8,12)) { //mode register 1 case 0x00: { @@ -61,24 +160,27 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void { io.verticalBlankInterruptEnable = data.bit(5); io.displayEnable = data.bit(6); io.externalVRAM = data.bit(7); + + if(!io.dmaEnable) io.command.bit(5) = 0; + return; } //plane A name table location case 0x02: { - io.nametablePlaneA = data.bits(3,6); + planeA.io.nametableAddress = data.bits(3,6) << 12; return; } //window name table location case 0x03: { - io.nametableWindow = data.bits(1,6); + window.io.nametableAddress = data.bits(1,6) << 10; return; } //plane B name table location case 0x04: { - io.nametablePlaneB = data.bits(0,3); + planeB.io.nametableAddress = data.bits(0,3) << 12; return; } @@ -96,18 +198,7 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void { //background color case 0x07: { - io.backgroundIndex = data.bits(0,3); - io.backgroundPalette = data.bits(4,5); - return; - } - - //unused - case 0x08: { - return; - } - - //unused - case 0x09: { + io.backgroundColor = data.bits(0,5); return; } @@ -149,30 +240,64 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void { return; } - //VRAM auto-increment value + //data port auto-increment value case 0x0f: { - io.vramAutoIncrement = data.bits(0,7); + io.dataIncrement = data.bits(0,7); return; } //plane size case 0x10: { - io.horizontalPlaneSize = data.bits(0,1); - io.verticalPlaneSize = data.bits(4,5); + //0 = 32 tiles + //1 = 64 tiles + //2 = invalid (repeats first row for every scanline) + //3 = 128 tiles + static const uint shift[] = {5, 6, 0, 7}; + + planeA.io.nametableWidth = shift[data.bits(0,1)]; + window.io.nametableWidth = shift[data.bits(0,1)]; + planeB.io.nametableWidth = shift[data.bits(0,1)]; + + planeA.io.nametableHeight = shift[data.bits(4,5)]; + window.io.nametableHeight = shift[data.bits(4,5)]; + planeB.io.nametableHeight = shift[data.bits(4,5)]; + return; } //window plane horizontal position case 0x11: { - io.horizontalWindowPlanePosition = data.bits(0,4); - io.horizontalWindowPlaneRight = data.bit(7); + if(!data) { + //disable + io.windowHorizontalLo = ~0; + io.windowHorizontalHi = ~0; + } else if(data.bit(7) == 0) { + //left + io.windowHorizontalLo = 0; + io.windowHorizontalHi = (data.bits(0,4) << 4) - 1; + } else { + //right + io.windowHorizontalLo = (data.bits(0,4) << 4) - 1; + io.windowHorizontalHi = ~0; + } return; } //window plane vertical position case 0x12: { - io.verticalWindowPlanePosition = data.bits(0,4); - io.verticalWindowPlaneDown = data.bit(7); + if(!data) { + //disable + io.windowVerticalLo = ~0; + io.windowVerticalHi = ~0; + } else if(data.bit(7) == 0) { + //up + io.windowVerticalLo = 0; + io.windowVerticalHi = (data.bits(0,4) << 3) - 1; + } else { + //down + io.windowVerticalLo = (data.bits(0,4) << 3) - 1; + io.windowVerticalHi = ~0; + } return; } @@ -207,5 +332,10 @@ auto VDP::writeControlPort(uint7 addr, uint8 data) -> void { return; } + //unused + default: { + return; + } + } } diff --git a/higan/md/vdp/render.cpp b/higan/md/vdp/render.cpp new file mode 100644 index 00000000..571fc2a3 --- /dev/null +++ b/higan/md/vdp/render.cpp @@ -0,0 +1,36 @@ +auto VDP::scanline() -> void { + state.x = 0; + if(++state.y >= 262) state.y = 0; + if(state.y == 0) scheduler.exit(Scheduler::Event::Frame); + + state.output = buffer + (state.y * 2 + 0) * 1280; +} + +auto VDP::run() -> void { + if(!io.displayEnable) return outputPixel(0); + + bool windowed = false; //todo: broken + windowed &= state.x >= io.windowHorizontalLo && state.x <= io.windowHorizontalHi; + windowed &= state.y >= io.windowVerticalLo && state.y <= io.windowVerticalHi; + auto& planeA = windowed ? this->window : this->planeA; + + planeA.run(state.x, state.y); + planeB.run(state.x, state.y); + + auto output = io.backgroundColor; + if(auto color = planeB.output.color) output = color; + if(auto color = planeA.output.color) output = color; + if(planeB.output.priority) if(auto color = planeB.output.color) output = color; + if(planeA.output.priority) if(auto color = planeA.output.color) output = color; + + outputPixel(cram[output]); + state.x++; +} + +auto VDP::outputPixel(uint9 color) -> void { + for(auto n : range(4)) { + state.output[ 0 + n] = color; + state.output[1280 + n] = color; + } + state.output += 4; +} diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 30e8b3f1..3cde2ef1 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -7,32 +7,33 @@ namespace MegaDrive { VDP vdp; #include "io.cpp" +#include "dma.cpp" +#include "render.cpp" +#include "background.cpp" auto VDP::Enter() -> void { while(true) scheduler.synchronize(), vdp.main(); } auto VDP::main() -> void { - for(uint y : range(262)) { - auto outputLo = buffer + (y * 2 + 0) * 1280; - auto outputHi = buffer + (y * 2 + 1) * 1280; - for(uint x : range(342)) { - if(y < 240 && x < 320) { - for(uint n : range(4)) { - *outputLo++ = 511; - *outputHi++ = 511; - } - } - + scanline(); + if(state.y < 240) { + for(uint x : range(320)) { + run(); step(1); } - if(y == 240) scheduler.exit(Scheduler::Event::Frame); + } else { + step(342); } + step(22); } auto VDP::step(uint clocks) -> void { - Thread::step(clocks); - synchronize(cpu); + while(clocks--) { + dmaRun(); + Thread::step(1); + synchronize(cpu); + } } auto VDP::refresh() -> void { @@ -40,12 +41,19 @@ auto VDP::refresh() -> void { } auto VDP::power() -> void { + planeA.power(); + window.power(); + planeB.power(); } auto VDP::reset() -> void { create(VDP::Enter, system.colorburst() * 15.0 / 10.0); memory::fill(&io, sizeof(IO)); + + planeA.reset(); + window.reset(); + planeB.reset(); } } diff --git a/higan/md/vdp/vdp.hpp b/higan/md/vdp/vdp.hpp index 1015542c..f298f7eb 100644 --- a/higan/md/vdp/vdp.hpp +++ b/higan/md/vdp/vdp.hpp @@ -9,16 +9,67 @@ struct VDP : Thread { auto power() -> void; auto reset() -> void; + //io.cpp auto readByte(uint24 addr) -> uint8; - auto readWord(uint24 addr) -> uint16; auto writeByte(uint24 addr, uint8 data) -> void; + + auto readWord(uint24 addr) -> uint16; auto writeWord(uint24 addr, uint16 data) -> void; + auto readDataPort() -> uint16; + auto writeDataPort(uint16 data) -> void; + auto readControlPort() -> uint16; - auto writeControlPort(uint7 addr, uint8 data) -> void; + auto writeControlPort(uint16 data) -> void; + + //dma.cpp + auto dmaRun() -> void; + auto dmaFillVRAM() -> void; + + //render.cpp + auto scanline() -> void; + auto run() -> void; + auto outputPixel(uint9 color) -> void; + + //background.cpp + struct Background { + auto scanline() -> void; + auto run(uint x, uint y) -> void; + + auto power() -> void; + auto reset() -> void; + + struct IO { + uint15 nametableAddress; + uint3 nametableWidth; //1 << value + uint3 nametableHeight; //1 << value + } io; + + struct Output { + uint6 color; + boolean priority; + } output; + }; + Background planeA; + Background window; + Background planeB; + + uint16 vram[32768]; + uint16 vramExpansion[32768]; //not present in stock Mega Drive hardware + uint9 cram[64]; + uint10 vsram[40]; private: struct IO { + //internal state + boolean dmaActive; + uint8 dmaFillWord; + + //command + uint6 command; + uint16 address; + boolean commandPending; + //$00 mode register 1 uint1 displayOverlayEnable; uint1 counterLatch; @@ -33,15 +84,6 @@ private: uint1 displayEnable; uint1 externalVRAM; - //$02 plane A name table location - uint4 nametablePlaneA; - - //$03 window name table location - uint6 nametableWindow; - - //$04 plane B name table location - uint4 nametablePlaneB; - //$05 sprite attribute table location uint8 attrtableSprite; @@ -49,8 +91,7 @@ private: uint1 nametableBaseSprite; //$07 background color - uint4 backgroundIndex; - uint2 backgroundPalette; + uint6 backgroundColor; //$0a horizontal interrupt counter uint8 horizontalInterruptCounter; @@ -75,20 +116,16 @@ private: uint1 nametableBasePatternA; uint1 nametableBasePatternB; - //$0f VRAM auto-increment value - uint8 vramAutoIncrement; - - //$10 plane size - uint2 horizontalPlaneSize; - uint2 verticalPlaneSize; + //$0f data port auto-increment value + uint8 dataIncrement; //$11 window plane horizontal position - uint5 horizontalWindowPlanePosition; - uint1 horizontalWindowPlaneRight; + uint10 windowHorizontalLo; + uint10 windowHorizontalHi; //$12 window plane vertical position - uint5 verticalWindowPlanePosition; - uint1 verticalWindowPlaneDown; + uint10 windowVerticalLo; + uint10 windowVerticalHi; //$13-$14 DMA length uint16 dmaLength; @@ -98,6 +135,12 @@ private: uint2 dmaMode; } io; + struct State { + uint32* output = nullptr; + uint x; + uint y; + } state; + uint32 buffer[1280 * 480]; }; diff --git a/higan/processor/m68k/instruction.cpp b/higan/processor/m68k/instruction.cpp index 1ba4f101..dc8b7165 100644 --- a/higan/processor/m68k/instruction.cpp +++ b/higan/processor/m68k/instruction.cpp @@ -1,12 +1,14 @@ auto M68K::instruction() -> void { instructionsExecuted++; -//if(instructionsExecuted >= 2000010) trap(); -//if(instructionsExecuted >= 2000000) { -// print(disassembleRegisters(), "\n"); -// print(disassemble(r.pc), "\n"); -// print("\n"); -//} + #if 0 + if(instructionsExecuted >= 10000010) while(true) step(1); + if(instructionsExecuted >= 10000000) { + print(disassembleRegisters(), "\n"); + print(disassemble(r.pc), "\n"); + print("\n"); + } + #endif opcode = readPC(); return instructionTable[opcode](); diff --git a/higan/processor/m68k/instructions.cpp b/higan/processor/m68k/instructions.cpp index fce1965b..e774599a 100644 --- a/higan/processor/m68k/instructions.cpp +++ b/higan/processor/m68k/instructions.cpp @@ -248,13 +248,10 @@ auto M68K::instructionASR(EffectiveAddress modify) -> void { auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void { auto extension = readPC(); + if(displacement) r.pc -= 2; + if(condition >= 2 && !testCondition(condition)) return; if(condition == 1) push(r.pc); - if(condition >= 2 && !testCondition(condition)) { //0 = BRA; 1 = BSR - if(displacement) r.pc -= 2; - } else { - r.pc -= 2; - r.pc += displacement ? sign(displacement) : sign(extension); - } + r.pc += displacement ? (int8_t)displacement : (int16_t)extension - 2; } template auto M68K::instructionBCHG(DataRegister bit, EffectiveAddress with) -> void { diff --git a/higan/processor/m68k/m68k.hpp b/higan/processor/m68k/m68k.hpp index c9da9bd0..81b17a44 100644 --- a/higan/processor/m68k/m68k.hpp +++ b/higan/processor/m68k/m68k.hpp @@ -35,13 +35,13 @@ struct M68K { };}; struct Vector { enum : uint { - Illegal = 4, - DivisionByZero = 5, - BoundsCheck = 6, - Overflow = 7, - Unprivileged = 8, - IllegalLineA = 10, - IllegalLineF = 11, + Illegal = 4, + DivisionByZero = 5, + BoundsCheck = 6, + Overflow = 7, + Unprivileged = 8, + IllegalLineA = 10, + IllegalLineF = 11, };}; M68K(); diff --git a/hiro/windows/widget/canvas.cpp b/hiro/windows/widget/canvas.cpp index 72278cb1..99419c61 100644 --- a/hiro/windows/widget/canvas.cpp +++ b/hiro/windows/widget/canvas.cpp @@ -112,12 +112,12 @@ auto pCanvas::_paint() -> void { bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap - bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32); + bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t); void* bits = nullptr; HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); if(bits) { - auto source = (const uint8*)pixels.data(); - auto target = (uint8*)bits; + auto source = (const uint8_t*)pixels.data(); + auto target = (uint8_t*)bits; for(auto n : range(width * height)) { target[0] = (source[0] * source[3]) / 255; target[1] = (source[1] * source[3]) / 255; @@ -155,7 +155,7 @@ auto pCanvas::_rasterize() -> void { pixels.resize(width * height); if(auto& icon = state().icon) { - memory::copy(pixels.data(), icon.data(), width * height * sizeof(uint32)); + memory::copy(pixels.data(), icon.data(), width * height * sizeof(uint32_t)); } else if(auto& gradient = state().gradient) { auto& colors = gradient.state.colors; image fill; @@ -163,7 +163,7 @@ auto pCanvas::_rasterize() -> void { fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value()); memory::copy(pixels.data(), fill.data(), fill.size()); } else { - uint32 color = state().color.value(); + uint32_t color = state().color.value(); for(auto& pixel : pixels) pixel = color; } } diff --git a/nall/primitives.hpp b/nall/primitives.hpp index d2468a53..57aacd5e 100644 --- a/nall/primitives.hpp +++ b/nall/primitives.hpp @@ -59,6 +59,7 @@ template struct Natural { 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; @@ -162,6 +163,7 @@ template struct Integer { 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;