1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

Ensure that reads can only read, accept that source is sometimes written to. E.g. XCHG.

This commit is contained in:
Thomas Harte 2023-10-31 15:06:19 -04:00
parent 02af08ffd2
commit 1d479ec2d7
3 changed files with 208 additions and 170 deletions

View File

@ -138,7 +138,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
PartialBlock(0x00, ADD); break;
case 0x06: Complete(PUSH, ES, None, data_size_); break;
case 0x07: Complete(POP, ES, None, data_size_); break;
case 0x07: Complete(POP, None, ES, data_size_); break;
PartialBlock(0x08, OR); break;
case 0x0e: Complete(PUSH, CS, None, data_size_); break;
@ -147,7 +147,7 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
// prefixed with $0f.
case 0x0f:
if constexpr (model < Model::i80286) {
Complete(POP, CS, None, data_size_);
Complete(POP, None, CS, data_size_);
} else {
phase_ = Phase::InstructionPageF;
}
@ -155,11 +155,11 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
PartialBlock(0x10, ADC); break;
case 0x16: Complete(PUSH, SS, None, DataSize::Word); break;
case 0x17: Complete(POP, SS, None, DataSize::Word); break;
case 0x17: Complete(POP, None, SS, DataSize::Word); break;
PartialBlock(0x18, SBB); break;
case 0x1e: Complete(PUSH, DS, None, DataSize::Word); break;
case 0x1f: Complete(POP, DS, None, DataSize::Word); break;
case 0x1f: Complete(POP, None, DS, DataSize::Word); break;
PartialBlock(0x20, AND); break;
case 0x26: segment_override_ = Source::ES; break;
@ -523,145 +523,147 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
// MARK: - Additional F page of instructions.
if(phase_ == Phase::InstructionPageF && source != end) {
// Update the instruction acquired.
const uint8_t instr = *source;
++source;
++consumed_;
if constexpr (model >= Model::i80286) {
if(phase_ == Phase::InstructionPageF && source != end) {
// Update the instruction acquired.
const uint8_t instr = *source;
++source;
++consumed_;
// NB: to reach here, the instruction set must be at least
// that of an 80286.
switch(instr) {
default: undefined();
// NB: to reach here, the instruction set must be at least
// that of an 80286.
switch(instr) {
default: undefined();
case 0x00: MemRegReg(Invalid, MemRegSLDT_to_VERW, data_size_); break;
case 0x01: MemRegReg(Invalid, MemRegSGDT_to_LMSW, data_size_); break;
case 0x02: MemRegReg(LAR, Reg_MemReg, data_size_); break;
case 0x03: MemRegReg(LSL, Reg_MemReg, data_size_); break;
case 0x05:
Requires(i80286);
Complete(LOADALL, None, None, DataSize::Byte);
break;
case 0x06: Complete(CLTS, None, None, DataSize::Byte); break;
case 0x00: MemRegReg(Invalid, MemRegSLDT_to_VERW, data_size_); break;
case 0x01: MemRegReg(Invalid, MemRegSGDT_to_LMSW, data_size_); break;
case 0x02: MemRegReg(LAR, Reg_MemReg, data_size_); break;
case 0x03: MemRegReg(LSL, Reg_MemReg, data_size_); break;
case 0x05:
Requires(i80286);
Complete(LOADALL, None, None, DataSize::Byte);
break;
case 0x06: Complete(CLTS, None, None, DataSize::Byte); break;
case 0x20:
RequiresMin(i80386);
MemRegReg(MOVfromCr, Reg_MemReg, DataSize::DWord);
break;
case 0x21:
RequiresMin(i80386);
MemRegReg(MOVfromDr, Reg_MemReg, DataSize::DWord);
break;
case 0x22:
RequiresMin(i80386);
MemRegReg(MOVtoCr, Reg_MemReg, DataSize::DWord);
break;
case 0x23:
RequiresMin(i80386);
MemRegReg(MOVtoDr, Reg_MemReg, DataSize::DWord);
break;
case 0x24:
RequiresMin(i80386);
MemRegReg(MOVfromTr, Reg_MemReg, DataSize::DWord);
break;
case 0x26:
RequiresMin(i80386);
MemRegReg(MOVtoTr, Reg_MemReg, DataSize::DWord);
break;
case 0x20:
RequiresMin(i80386);
MemRegReg(MOVfromCr, Reg_MemReg, DataSize::DWord);
break;
case 0x21:
RequiresMin(i80386);
MemRegReg(MOVfromDr, Reg_MemReg, DataSize::DWord);
break;
case 0x22:
RequiresMin(i80386);
MemRegReg(MOVtoCr, Reg_MemReg, DataSize::DWord);
break;
case 0x23:
RequiresMin(i80386);
MemRegReg(MOVtoDr, Reg_MemReg, DataSize::DWord);
break;
case 0x24:
RequiresMin(i80386);
MemRegReg(MOVfromTr, Reg_MemReg, DataSize::DWord);
break;
case 0x26:
RequiresMin(i80386);
MemRegReg(MOVtoTr, Reg_MemReg, DataSize::DWord);
break;
case 0x70: RequiresMin(i80386); Displacement(JO, data_size_); break;
case 0x71: RequiresMin(i80386); Displacement(JNO, data_size_); break;
case 0x72: RequiresMin(i80386); Displacement(JB, data_size_); break;
case 0x73: RequiresMin(i80386); Displacement(JNB, data_size_); break;
case 0x74: RequiresMin(i80386); Displacement(JZ, data_size_); break;
case 0x75: RequiresMin(i80386); Displacement(JNZ, data_size_); break;
case 0x76: RequiresMin(i80386); Displacement(JBE, data_size_); break;
case 0x77: RequiresMin(i80386); Displacement(JNBE, data_size_); break;
case 0x78: RequiresMin(i80386); Displacement(JS, data_size_); break;
case 0x79: RequiresMin(i80386); Displacement(JNS, data_size_); break;
case 0x7a: RequiresMin(i80386); Displacement(JP, data_size_); break;
case 0x7b: RequiresMin(i80386); Displacement(JNP, data_size_); break;
case 0x7c: RequiresMin(i80386); Displacement(JL, data_size_); break;
case 0x7d: RequiresMin(i80386); Displacement(JNL, data_size_); break;
case 0x7e: RequiresMin(i80386); Displacement(JLE, data_size_); break;
case 0x7f: RequiresMin(i80386); Displacement(JNLE, data_size_); break;
case 0x70: RequiresMin(i80386); Displacement(JO, data_size_); break;
case 0x71: RequiresMin(i80386); Displacement(JNO, data_size_); break;
case 0x72: RequiresMin(i80386); Displacement(JB, data_size_); break;
case 0x73: RequiresMin(i80386); Displacement(JNB, data_size_); break;
case 0x74: RequiresMin(i80386); Displacement(JZ, data_size_); break;
case 0x75: RequiresMin(i80386); Displacement(JNZ, data_size_); break;
case 0x76: RequiresMin(i80386); Displacement(JBE, data_size_); break;
case 0x77: RequiresMin(i80386); Displacement(JNBE, data_size_); break;
case 0x78: RequiresMin(i80386); Displacement(JS, data_size_); break;
case 0x79: RequiresMin(i80386); Displacement(JNS, data_size_); break;
case 0x7a: RequiresMin(i80386); Displacement(JP, data_size_); break;
case 0x7b: RequiresMin(i80386); Displacement(JNP, data_size_); break;
case 0x7c: RequiresMin(i80386); Displacement(JL, data_size_); break;
case 0x7d: RequiresMin(i80386); Displacement(JNL, data_size_); break;
case 0x7e: RequiresMin(i80386); Displacement(JLE, data_size_); break;
case 0x7f: RequiresMin(i80386); Displacement(JNLE, data_size_); break;
#define Set(x) \
RequiresMin(i80386); \
MemRegReg(SET##x, MemRegSingleOperand, DataSize::Byte);
case 0x90: Set(O); break;
case 0x91: Set(NO); break;
case 0x92: Set(B); break;
case 0x93: Set(NB); break;
case 0x94: Set(Z); break;
case 0x95: Set(NZ); break;
case 0x96: Set(BE); break;
case 0x97: Set(NBE); break;
case 0x98: Set(S); break;
case 0x99: Set(NS); break;
case 0x9a: Set(P); break;
case 0x9b: Set(NP); break;
case 0x9c: Set(L); break;
case 0x9d: Set(NL); break;
case 0x9e: Set(LE); break;
case 0x9f: Set(NLE); break;
case 0x90: Set(O); break;
case 0x91: Set(NO); break;
case 0x92: Set(B); break;
case 0x93: Set(NB); break;
case 0x94: Set(Z); break;
case 0x95: Set(NZ); break;
case 0x96: Set(BE); break;
case 0x97: Set(NBE); break;
case 0x98: Set(S); break;
case 0x99: Set(NS); break;
case 0x9a: Set(P); break;
case 0x9b: Set(NP); break;
case 0x9c: Set(L); break;
case 0x9d: Set(NL); break;
case 0x9e: Set(LE); break;
case 0x9f: Set(NLE); break;
#undef Set
case 0xa0: RequiresMin(i80386); Complete(PUSH, FS, None, data_size_); break;
case 0xa1: RequiresMin(i80386); Complete(POP, FS, None, data_size_); break;
case 0xa3: RequiresMin(i80386); MemRegReg(BT, MemReg_Reg, data_size_); break;
case 0xa4:
RequiresMin(i80386);
MemRegReg(SHLDimm, Reg_MemReg, data_size_);
operand_size_ = DataSize::Byte;
break;
case 0xa5:
RequiresMin(i80386);
MemRegReg(SHLDCL, MemReg_Reg, data_size_);
break;
case 0xa8: RequiresMin(i80386); Complete(PUSH, GS, None, data_size_); break;
case 0xa9: RequiresMin(i80386); Complete(POP, GS, None, data_size_); break;
case 0xab: RequiresMin(i80386); MemRegReg(BTS, MemReg_Reg, data_size_); break;
case 0xac:
RequiresMin(i80386);
MemRegReg(SHRDimm, Reg_MemReg, data_size_);
operand_size_ = DataSize::Byte;
break;
case 0xad:
RequiresMin(i80386);
MemRegReg(SHRDCL, MemReg_Reg, data_size_);
break;
case 0xaf:
RequiresMin(i80386);
MemRegReg(IMUL_2, Reg_MemReg, data_size_);
break;
case 0xa0: RequiresMin(i80386); Complete(PUSH, FS, None, data_size_); break;
case 0xa1: RequiresMin(i80386); Complete(POP, None, FS, data_size_); break;
case 0xa3: RequiresMin(i80386); MemRegReg(BT, MemReg_Reg, data_size_); break;
case 0xa4:
RequiresMin(i80386);
MemRegReg(SHLDimm, Reg_MemReg, data_size_);
operand_size_ = DataSize::Byte;
break;
case 0xa5:
RequiresMin(i80386);
MemRegReg(SHLDCL, MemReg_Reg, data_size_);
break;
case 0xa8: RequiresMin(i80386); Complete(PUSH, GS, None, data_size_); break;
case 0xa9: RequiresMin(i80386); Complete(POP, None, GS, data_size_); break;
case 0xab: RequiresMin(i80386); MemRegReg(BTS, MemReg_Reg, data_size_); break;
case 0xac:
RequiresMin(i80386);
MemRegReg(SHRDimm, Reg_MemReg, data_size_);
operand_size_ = DataSize::Byte;
break;
case 0xad:
RequiresMin(i80386);
MemRegReg(SHRDCL, MemReg_Reg, data_size_);
break;
case 0xaf:
RequiresMin(i80386);
MemRegReg(IMUL_2, Reg_MemReg, data_size_);
break;
case 0xb2: RequiresMin(i80386); MemRegReg(LSS, Reg_MemReg, data_size_); break;
case 0xb3: RequiresMin(i80386); MemRegReg(BTR, MemReg_Reg, data_size_); break;
case 0xb4: RequiresMin(i80386); MemRegReg(LFS, Reg_MemReg, data_size_); break;
case 0xb5: RequiresMin(i80386); MemRegReg(LGS, Reg_MemReg, data_size_); break;
case 0xb6:
RequiresMin(i80386);
MemRegReg(MOVZX, Reg_MemReg, DataSize::Byte);
break;
case 0xb7:
RequiresMin(i80386);
MemRegReg(MOVZX, Reg_MemReg, DataSize::Word);
break;
case 0xba: RequiresMin(i80386); MemRegReg(Invalid, MemRegBT_to_BTC, data_size_); break;
case 0xbb: RequiresMin(i80386); MemRegReg(BTC, MemReg_Reg, data_size_); break;
case 0xbc: RequiresMin(i80386); MemRegReg(BSF, MemReg_Reg, data_size_); break;
case 0xbd: RequiresMin(i80386); MemRegReg(BSR, MemReg_Reg, data_size_); break;
case 0xbe:
RequiresMin(i80386);
MemRegReg(MOVSX, Reg_MemReg, DataSize::Byte);
break;
case 0xbf:
RequiresMin(i80386);
MemRegReg(MOVSX, Reg_MemReg, DataSize::Word);
break;
case 0xb2: RequiresMin(i80386); MemRegReg(LSS, Reg_MemReg, data_size_); break;
case 0xb3: RequiresMin(i80386); MemRegReg(BTR, MemReg_Reg, data_size_); break;
case 0xb4: RequiresMin(i80386); MemRegReg(LFS, Reg_MemReg, data_size_); break;
case 0xb5: RequiresMin(i80386); MemRegReg(LGS, Reg_MemReg, data_size_); break;
case 0xb6:
RequiresMin(i80386);
MemRegReg(MOVZX, Reg_MemReg, DataSize::Byte);
break;
case 0xb7:
RequiresMin(i80386);
MemRegReg(MOVZX, Reg_MemReg, DataSize::Word);
break;
case 0xba: RequiresMin(i80386); MemRegReg(Invalid, MemRegBT_to_BTC, data_size_); break;
case 0xbb: RequiresMin(i80386); MemRegReg(BTC, MemReg_Reg, data_size_); break;
case 0xbc: RequiresMin(i80386); MemRegReg(BSF, MemReg_Reg, data_size_); break;
case 0xbd: RequiresMin(i80386); MemRegReg(BSR, MemReg_Reg, data_size_); break;
case 0xbe:
RequiresMin(i80386);
MemRegReg(MOVSX, Reg_MemReg, DataSize::Byte);
break;
case 0xbf:
RequiresMin(i80386);
MemRegReg(MOVSX, Reg_MemReg, DataSize::Word);
break;
}
}
}
@ -979,18 +981,20 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
// MARK: - ScaleIndexBase
if(phase_ == Phase::ScaleIndexBase && source != end) {
sib_ = *source;
++source;
++consumed_;
if constexpr (is_32bit(model)) {
if(phase_ == Phase::ScaleIndexBase && source != end) {
sib_ = *source;
++source;
++consumed_;
// Potentially record the lack of a base.
if(displacement_size_ == DataSize::None && (uint8_t(sib_)&7) == 5) {
source_ = (source_ == Source::Indirect) ? Source::IndirectNoBase : source_;
destination_ = (destination_ == Source::Indirect) ? Source::IndirectNoBase : destination_;
// Potentially record the lack of a base.
if(displacement_size_ == DataSize::None && (uint8_t(sib_)&7) == 5) {
source_ = (source_ == Source::Indirect) ? Source::IndirectNoBase : source_;
destination_ = (destination_ == Source::Indirect) ? Source::IndirectNoBase : destination_;
}
phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost;
}
phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost;
}
// MARK: - Displacement and operand.
@ -1041,6 +1045,18 @@ std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(con
// MARK: - Check for completion.
if(phase_ == Phase::ReadyToPost) {
// TODO: map to #UD where applicable; build LOCK into the Operation type, buying an extra bit for the operation?
//
// As of the P6 Intel stipulates that:
//
// "The LOCK prefix can be prepended only to the following instructions and to those forms of the instructions
// that use a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
// XADD, and XCHG."
//
// ... and the #UD exception will be raised if LOCK is encountered elsewhere. So adding 17 additional
// operations would unlock an extra bit of storage for a net gain of 239 extra operation types and thereby
// alleviating any concerns over whether there'll be space to handle MMX, floating point extensions, etc.
const auto result = std::make_pair(
consumed_,
InstructionT(

View File

@ -1523,9 +1523,14 @@ template <
using IntT = typename DataSizeType<data_size>::type;
using AddressT = typename AddressSizeType<address_size>::type;
// Establish source() and destination() shorthand to fetch data if necessary.
// Establish source() and destination() shorthands to fetch data if necessary.
//
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
// So the following division is in part a necessity.
//
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
IntT immediate;
const auto source = [&]() -> IntT& {
const auto source_r = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::Read>(
instruction,
instruction.source().source(),
@ -1535,11 +1540,16 @@ template <
nullptr,
&immediate);
};
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
// So the following division is in part a necessity.
//
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
const auto source_rmw = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::ReadModifyWrite>(
instruction,
instruction.source().source(),
instruction.source(),
registers,
memory,
nullptr,
&immediate);
};
const auto destination_r = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::Read>(
instruction,
@ -1661,26 +1671,26 @@ template <
case Operation::HLT: flow_controller.halt(); return;
case Operation::WAIT: flow_controller.wait(); return;
case Operation::ADC: Primitive::add<true>(destination_rmw(), source(), status); break;
case Operation::ADD: Primitive::add<false>(destination_rmw(), source(), status); break;
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source(), status); break;
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source(), status); break;
case Operation::CMP: Primitive::sub<false, false>(destination_rmw(), source(), status); break;
case Operation::TEST: Primitive::test(destination_r(), source(), status); return;
case Operation::ADC: Primitive::add<true>(destination_rmw(), source_r(), status); break;
case Operation::ADD: Primitive::add<false>(destination_rmw(), source_r(), status); break;
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source_r(), status); break;
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source_r(), status); break;
case Operation::CMP: Primitive::sub<false, false>(destination_rmw(), source_r(), status); return;
case Operation::TEST: Primitive::test(destination_r(), source_r(), status); return;
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source(), status); return;
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source(), status); return;
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source(), flow_controller); return;
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source(), flow_controller); return;
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source_r(), status); return;
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source_r(), status); return;
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source_r(), flow_controller); return;
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source_r(), flow_controller); return;
case Operation::INC: Primitive::inc(destination_rmw(), status); break;
case Operation::DEC: Primitive::dec(destination_rmw(), status); break;
case Operation::AND: Primitive::and_(destination_rmw(), source(), status); break;
case Operation::OR: Primitive::or_(destination_rmw(), source(), status); break;
case Operation::XOR: Primitive::xor_(destination_rmw(), source(), status); break;
case Operation::NEG: Primitive::neg(source(), status); break; // TODO: should be a destination.
case Operation::NOT: Primitive::not_(source()); break; // TODO: should be a destination.
case Operation::AND: Primitive::and_(destination_rmw(), source_r(), status); break;
case Operation::OR: Primitive::or_(destination_rmw(), source_r(), status); break;
case Operation::XOR: Primitive::xor_(destination_rmw(), source_r(), status); break;
case Operation::NEG: Primitive::neg(source_rmw(), status); break; // TODO: should be a destination.
case Operation::NOT: Primitive::not_(source_rmw()); break; // TODO: should be a destination.
case Operation::CALLrel:
Primitive::call_relative(instruction.displacement(), registers, flow_controller);
@ -1715,7 +1725,7 @@ template <
case Operation::LES: if constexpr (data_size == DataSize::Word) Primitive::ld<model, Source::ES>(instruction, destination_w(), memory, registers); return;
case Operation::LEA: Primitive::lea<model>(instruction, destination_w(), memory, registers); return;
case Operation::MOV: Primitive::mov(destination_w(), source()); break;
case Operation::MOV: Primitive::mov(destination_w(), source_r()); break;
case Operation::JO: jcc(status.condition<Condition::Overflow>()); return;
case Operation::JNO: jcc(!status.condition<Condition::Overflow>()); return;
@ -1750,9 +1760,9 @@ template <
case Operation::STI: Primitive::sti(status); return;
case Operation::CMC: Primitive::cmc(status); return;
case Operation::XCHG: Primitive::xchg(destination_rmw(), source()); break;
case Operation::XCHG: Primitive::xchg(destination_rmw(), source_rmw()); break;
case Operation::SALC: Primitive::salc(registers.al(), status); return;
case Operation::SALC: Primitive::salc(registers.al(), status); return;
case Operation::SETMO:
if constexpr (model == Model::i8086) {
Primitive::setmo(destination_w(), status);
@ -1775,10 +1785,10 @@ template <
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, memory, registers); return;
case Operation::POP: source() = Primitive::pop<IntT>(memory, registers); break;
case Operation::PUSH: Primitive::push<IntT>(source(), memory, registers); break;
case Operation::POPF: Primitive::popf(memory, registers, status); break;
case Operation::PUSHF: Primitive::pushf(memory, registers, status); break;
case Operation::POP: destination_w() = Primitive::pop<IntT>(memory, registers); break;
case Operation::PUSH: Primitive::push<IntT>(source_r(), memory, registers); break;
case Operation::POPF: Primitive::popf(memory, registers, status); break;
case Operation::PUSHF: Primitive::pushf(memory, registers, status); break;
case Operation::CMPS:
Primitive::cmps<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);

View File

@ -179,7 +179,16 @@ struct Memory {
return write_back_value_;
}
}
return access<IntT, type>(segment, address, Tag::Accessed);
auto &value = access<IntT, type>(segment, address, Tag::Accessed);
// For testing purposes: if the CPU indicated it'll only be reading, copy the requested value into a temporary
// location so that any writes will be discarded.
if(type == AccessType::Read) {
*reinterpret_cast<IntT *>(&read_value_) = value;
return *reinterpret_cast<IntT *>(&read_value_);
}
return value;
}
template <typename IntT>
@ -196,6 +205,7 @@ struct Memory {
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back.
uint32_t write_back_address_[2] = {NoWriteBack, NoWriteBack};
uint16_t write_back_value_;
uint16_t read_value_;
};
struct IO {
template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {}
@ -385,6 +395,8 @@ struct FailedExecution {
return hexInstruction;
};
EACCES;
const auto decoded = decoder.decode(data.data(), data.size());
const bool sizeMatched = decoded.first == data.size();
if(assert) {