diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index 61da7f5bf..eb8f6d4f8 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -26,14 +26,14 @@ AcornCartridgesFrom(const std::vectorget_segments(); - // only one mapped item is allowed + // Only one mapped item is allowed. if(segments.size() != 1) continue; - // which must be 8 or 16 kb in size + // Cartridges must be 8 or 16 kb in size. const Storage::Cartridge::Cartridge::Segment &segment = segments.front(); if(segment.data.size() != 0x4000 && segment.data.size() != 0x2000) continue; - // is a copyright string present? + // Check copyright string. const uint8_t copyright_offset = segment.data[7]; if( segment.data[copyright_offset] != 0x00 || @@ -42,16 +42,16 @@ AcornCartridgesFrom(const std::vector= 0x80 && segment.data[2] < 0xc0) )) continue; - // is the service entry point valid? + // Check service entry point. if(!(segment.data[5] >= 0x80 && segment.data[5] < 0xc0)) continue; - // probability of a random binary blob that isn't an Acorn ROM proceeding to here: + // Probability of a random binary blob that isn't an Acorn ROM proceeding to here: // 1/(2^32) * // ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) * // 1/4 @@ -74,7 +74,7 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets( // Copy appropriate cartridges to the 8-bit target. target8bit->media.cartridges = AcornCartridgesFrom(media.cartridges); - // If there are any tapes, attempt to get data from the first. + // If there are tapes, attempt to get data from the first. if(!media.tapes.empty()) { std::shared_ptr tape = media.tapes.front(); auto serialiser = tape->serialiser(); @@ -85,26 +85,29 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets( bool is_basic = true; // If a file is execute-only, that means *RUN. - if(files.front().flags & File::Flags::ExecuteOnly) is_basic = false; + if(files.front().flags & File::Flags::ExecuteOnly) { + is_basic = false; + } - // check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code, - // so that's also justification to *RUN - std::size_t pointer = 0; - uint8_t *const data = &files.front().data[0]; - const std::size_t data_size = files.front().data.size(); - while(1) { - if(pointer >= data_size-1 || data[pointer] != 13) { - is_basic = false; - break; + // Check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code, + // so that's also justification to *RUN. + if(is_basic) { + std::size_t pointer = 0; + uint8_t *const data = &files.front().data[0]; + const std::size_t data_size = files.front().data.size(); + while(true) { + if(pointer >= data_size-1 || data[pointer] != 0x0d) { + is_basic = false; + break; + } + if((data[pointer+1]&0x7f) == 0x7f) break; + pointer += data[pointer+3]; } - if((data[pointer+1]&0x7f) == 0x7f) break; - pointer += data[pointer+3]; } // Inspect first file. If it's protected or doesn't look like BASIC // then the loading command is *RUN. Otherwise it's CHAIN"". target8bit->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n"; - target8bit->media.tapes = media.tapes; } } diff --git a/Analyser/Static/Acorn/Tape.cpp b/Analyser/Static/Acorn/Tape.cpp index d9f0e2e48..43253b28c 100644 --- a/Analyser/Static/Acorn/Tape.cpp +++ b/Analyser/Static/Acorn/Tape.cpp @@ -23,35 +23,29 @@ static std::unique_ptr GetNextChunk( int shift_register = 0; // TODO: move this into the parser - const auto shift = [&] { - shift_register = (shift_register >> 1) | (parser.get_next_bit(serialiser) << 9); + const auto find = [&](int target) { + while(!serialiser.is_at_end() && (shift_register != target)) { + shift_register = (shift_register >> 1) | (parser.get_next_bit(serialiser) << 9); + } }; - // find next area of high tone - while(!serialiser.is_at_end() && (shift_register != 0x3ff)) { - shift(); - } - - // find next 0x2a (swallowing stop bit) - while(!serialiser.is_at_end() && (shift_register != 0x254)) { - shift(); - } - + // Find first sync byte that follows high tone. + find(0x3ff); + find(0x254); // i.e. 0x2a wrapped in a 1 start bit and a 0 stop bit. parser.reset_crc(); parser.reset_error_flag(); - // read out name - char name[11]; + // Read name. + char name[11]{}; std::size_t name_ptr = 0; while(!serialiser.is_at_end() && name_ptr < sizeof(name)) { name[name_ptr] = char(parser.get_next_byte(serialiser)); if(!name[name_ptr]) break; ++name_ptr; } - name[sizeof(name)-1] = '\0'; new_chunk->name = name; - // addresses + // Read rest of header fields. new_chunk->load_address = uint32_t(parser.get_next_word(serialiser)); new_chunk->execution_address = uint32_t(parser.get_next_word(serialiser)); new_chunk->block_number = uint16_t(parser.get_next_short(serialiser)); @@ -59,24 +53,25 @@ static std::unique_ptr GetNextChunk( new_chunk->block_flag = uint8_t(parser.get_next_byte(serialiser)); new_chunk->next_address = uint32_t(parser.get_next_word(serialiser)); - const uint16_t calculated_header_crc = parser.get_crc(); - uint16_t stored_header_crc = uint16_t(parser.get_next_short(serialiser)); - stored_header_crc = uint16_t((stored_header_crc >> 8) | (stored_header_crc << 8)); - new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc; + const auto matched_crc = [&]() { + const uint16_t calculated_crc = parser.get_crc(); + uint16_t stored_crc = uint16_t(parser.get_next_short(serialiser)); + stored_crc = uint16_t((stored_crc >> 8) | (stored_crc << 8)); + return stored_crc == calculated_crc; + }; + + new_chunk->header_crc_matched = matched_crc(); if(!new_chunk->header_crc_matched) return nullptr; - parser.reset_crc(); - new_chunk->data.reserve(new_chunk->block_length); - for(int c = 0; c < new_chunk->block_length; c++) { - new_chunk->data.push_back(uint8_t(parser.get_next_byte(serialiser))); - } - + // Bit 6 of the block flag means 'empty block'; allow it to override declared block length. if(new_chunk->block_length && !(new_chunk->block_flag&0x40)) { - uint16_t calculated_data_crc = parser.get_crc(); - uint16_t stored_data_crc = uint16_t(parser.get_next_short(serialiser)); - stored_data_crc = uint16_t((stored_data_crc >> 8) | (stored_data_crc << 8)); - new_chunk->data_crc_matched = stored_data_crc == calculated_data_crc; + parser.reset_crc(); + new_chunk->data.reserve(new_chunk->block_length); + for(int c = 0; c < new_chunk->block_length; c++) { + new_chunk->data.push_back(uint8_t(parser.get_next_byte(serialiser))); + } + new_chunk->data_crc_matched = matched_crc(); } else { new_chunk->data_crc_matched = true; } @@ -85,42 +80,38 @@ static std::unique_ptr GetNextChunk( } static std::unique_ptr GetNextFile(std::deque &chunks) { - // find next chunk with a block number of 0 - while(chunks.size() && chunks.front().block_number) { + // Find next chunk with a block number of 0. + while(!chunks.empty() && chunks.front().block_number) { chunks.pop_front(); } + if(chunks.empty()) return nullptr; - if(!chunks.size()) return nullptr; - - // accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set + // Accumulate sequential blocks until end-of-file bit is set. auto file = std::make_unique(); - uint16_t block_number = 0; - - while(chunks.size()) { + while(!chunks.empty()) { if(chunks.front().block_number != block_number) return nullptr; - bool was_last = chunks.front().block_flag & 0x80; + const bool was_last = chunks.front().block_flag & 0x80; file->chunks.push_back(chunks.front()); chunks.pop_front(); - block_number++; + ++block_number; if(was_last) break; } - // accumulate total data, copy flags appropriately + // Grab metadata flags. file->name = file->chunks.front().name; file->load_address = file->chunks.front().load_address; file->execution_address = file->chunks.front().execution_address; - // I think the final chunk's flags are the ones that count; TODO: check. if(file->chunks.back().block_flag & 0x01) { - // File is locked, which in more generalised terms means it is - // for execution only. + // File is locked i.e. for execution only. file->flags |= File::Flags::ExecuteOnly; } - // copy all data into a single big block - for(File::Chunk chunk : file->chunks) { + // Copy data into a single big block. + file->data.reserve(file->chunks.size() * 256); + for(auto &chunk : file->chunks) { file->data.insert(file->data.end(), chunk.data.begin(), chunk.data.end()); } @@ -130,22 +121,21 @@ static std::unique_ptr GetNextFile(std::deque &chunks) { std::vector Analyser::Static::Acorn::GetFiles(Storage::Tape::TapeSerialiser &serialiser) { Storage::Tape::Acorn::Parser parser; - // populate chunk list + // Read all chunks. std::deque chunk_list; while(!serialiser.is_at_end()) { - std::unique_ptr chunk = GetNextChunk(serialiser, parser); + const std::unique_ptr chunk = GetNextChunk(serialiser, parser); if(chunk) { - chunk_list.push_back(*chunk); + chunk_list.push_back(std::move(*chunk)); } } - // decompose into file list + // Convert to files. std::vector file_list; - - while(chunk_list.size()) { - std::unique_ptr next_file = GetNextFile(chunk_list); + while(!chunk_list.empty()) { + const std::unique_ptr next_file = GetNextFile(chunk_list); if(next_file) { - file_list.push_back(*next_file); + file_list.push_back(std::move(*next_file)); } } diff --git a/InstructionSets/ARM/Executor.hpp b/InstructionSets/ARM/Executor.hpp index 6bc8b1d03..9371429d4 100644 --- a/InstructionSets/ARM/Executor.hpp +++ b/InstructionSets/ARM/Executor.hpp @@ -152,8 +152,9 @@ struct Executor { if constexpr (flags.set_condition_codes()) { // "For a subtraction, including the comparison instruction CMP, C is set to 0 if // the subtraction produced a borrow (that is, an unsigned underflow), and to 1 otherwise." - registers_.set_c(!Numeric::carried_out(lhs, rhs, conditions)); - registers_.set_v(Numeric::overflow(lhs, rhs, conditions)); + using NumOp = Numeric::Operation; + registers_.set_c(!Numeric::carried_out(lhs, rhs, conditions)); + registers_.set_v(Numeric::overflow(lhs, rhs, conditions)); } if constexpr (!is_comparison(flags.operation())) { @@ -185,8 +186,9 @@ struct Executor { } if constexpr (flags.set_condition_codes()) { - registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); - registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); + using NumOp = Numeric::Operation; + registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); + registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); } if constexpr (!is_comparison(flags.operation())) { diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index cf597ff54..2ec702b0e 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -28,10 +28,12 @@ namespace Primitive { /// Performs an add or subtract (as per @c is_add) between @c source and @c destination, /// updating @c status. @c is_extend indicates whether this is an extend operation (e.g. ADDX) /// or a plain one (e.g. ADD). -template +template static void add_sub(const IntT source, IntT &destination, Status &status) { static_assert(!std::numeric_limits::is_signed); + static_assert(operation == Numeric::Operation::Add || operation == Numeric::Operation::Subtract); + constexpr bool is_add = operation == Numeric::Operation::Add; IntT result = is_add ? destination + source : destination - source; @@ -59,7 +61,7 @@ static void add_sub(const IntT source, IntT &destination, Status &status) { status.zero_result = Status::FlagT(result); } status.set_negative(result); - status.overflow_flag = Numeric::overflow(destination, source, result); + status.overflow_flag = Numeric::overflow(destination, source, result); destination = result; } @@ -121,7 +123,7 @@ void compare(const IntT source, const IntT destination, Status &status) { const IntT result = destination - source; status.carry_flag = result > destination; status.set_neg_zero(result); - status.overflow_flag = Numeric::overflow(destination, source, result); + status.overflow_flag = Numeric::overflow(destination, source, result); } /// @returns the name of the bit to be used as a mask for BCLR, BCHG, BSET or BTST for @@ -279,7 +281,7 @@ template void negative(IntT &source, Status &sta } status.extend_flag = status.carry_flag = result; // i.e. any value other than 0 will result in carry. status.set_negative(result); - status.overflow_flag = Numeric::overflow(IntT(0), source, result); + status.overflow_flag = Numeric::overflow(IntT(0), source, result); source = result; } @@ -519,6 +521,7 @@ template < Status &status, FlowController &flow_controller ) { + using NumOp = Numeric::Operation; switch((operation != Operation::Undefined) ? operation : instruction.operation) { /* ABCD adds the lowest bytes from the source and destination using BCD arithmetic, @@ -551,20 +554,20 @@ template < // ADD and ADDA add two quantities, the latter sign extending and without setting any flags; // ADDQ and SUBQ act as ADD and SUB, but taking the second argument from the instruction code. - case Operation::ADDb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::SUBb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::ADDXb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::SUBXb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::ADDb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::SUBb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::ADDXb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::SUBXb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::ADDw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::SUBw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::ADDXw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::SUBXw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::ADDw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::SUBw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::ADDXw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::SUBXw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::ADDl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::SUBl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::ADDXl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::SUBXl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::ADDl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::SUBl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::ADDXl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::SUBXl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::ADDAw: dest.l += u_extend16(src.w); break; case Operation::ADDAl: dest.l += src.l; break; diff --git a/InstructionSets/x86/Implementation/Arithmetic.hpp b/InstructionSets/x86/Implementation/Arithmetic.hpp index 4c09dc5c4..ac7cafaa0 100644 --- a/InstructionSets/x86/Implementation/Arithmetic.hpp +++ b/InstructionSets/x86/Implementation/Arithmetic.hpp @@ -30,12 +30,13 @@ void add( */ const IntT result = destination + source + (with_carry ? context.flags.template carry_bit() : 0); + using NumOp = Numeric::Operation; context.flags.template set_from( - Numeric::carried_out() - 1>(destination, source, result)); + Numeric::carried_out() - 1>(destination, source, result)); context.flags.template set_from( Numeric::carried_in<4>(destination, source, result)); context.flags.template set_from( - Numeric::overflow(destination, source, result)); + Numeric::overflow(destination, source, result)); context.flags.template set_from(result); @@ -56,12 +57,13 @@ void sub( */ const IntT result = destination - source - (with_borrow ? context.flags.template carry_bit() : 0); + using NumOp = Numeric::Operation; context.flags.template set_from( - Numeric::carried_out() - 1>(destination, source, result)); + Numeric::carried_out() - 1>(destination, source, result)); context.flags.template set_from( Numeric::carried_in<4>(destination, source, result)); context.flags.template set_from( - Numeric::overflow(destination, source, result)); + Numeric::overflow(destination, source, result)); context.flags.template set_from(result); diff --git a/Numeric/CRC.hpp b/Numeric/CRC.hpp index 76fa186a9..80561b1c7 100644 --- a/Numeric/CRC.hpp +++ b/Numeric/CRC.hpp @@ -8,6 +8,9 @@ #pragma once +#include "Carry.hpp" + +#include #include #include @@ -26,30 +29,41 @@ constexpr uint8_t reverse_byte(uint8_t byte) { } /*! Provides a class capable of generating a CRC from source data. */ -template +template < + typename IntType, + IntType polynomial, + IntType reset_value, + IntType output_xor, + bool reflect_input, + bool reflect_output +> class Generator { public: /*! Instantiates a CRC16 that will compute the CRC16 specified by the supplied @c polynomial and @c reset_value. */ - constexpr Generator(IntType polynomial) noexcept: value_(reset_value) { - const IntType top_bit = IntType(~(IntType(~0) >> 1)); - for(int c = 0; c < 256; c++) { - IntType shift_value = IntType(c << multibyte_shift); - for(int b = 0; b < 8; b++) { - IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0; - shift_value = IntType(shift_value << 1) ^ exclusive_or; - } - xor_table[c] = shift_value; - } - } + constexpr Generator() noexcept: value_(reset_value) {} /// Resets the CRC to the reset value. void reset() { value_ = reset_value; } /// Updates the CRC to include @c byte. void add(uint8_t byte) { + static constexpr std::array xor_table = [] { + std::array table{}; + constexpr IntType top_bit = Numeric::top_bit(); + for(size_t c = 0; c < 256; c++) { + IntType shift_value = IntType(c << multibyte_shift); + for(int b = 0; b < 8; b++) { + const IntType exclusive_or = (shift_value & top_bit) ? polynomial : 0; + shift_value = IntType(shift_value << 1) ^ exclusive_or; + } + table[c] = shift_value; + } + return table; + } (); + if constexpr (reflect_input) byte = reverse_byte(byte); value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]); } @@ -100,7 +114,6 @@ public: private: static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8; - IntType xor_table[256]; IntType value_; }; @@ -108,15 +121,11 @@ private: Provides a generator of 16-bit CCITT CRCs, which amongst other uses are those used by the FM and MFM disk encodings. */ -struct CCITT: public Generator { - CCITT(): Generator(0x1021) {} -}; +using CCITT = Generator; /*! Provides a generator of "standard 32-bit" CRCs. */ -struct CRC32: public Generator { - CRC32(): Generator(0x04c11db7) {} -}; +using CRC32 = Generator; } diff --git a/Numeric/Carry.hpp b/Numeric/Carry.hpp index 0cae0061f..95614ec34 100644 --- a/Numeric/Carry.hpp +++ b/Numeric/Carry.hpp @@ -12,11 +12,17 @@ namespace Numeric { +enum class Operation { + Add, + Subtract, +}; + /// @returns @c true if from @c bit there was: /// • carry after calculating @c lhs + @c rhs if @c is_add is true; or /// • borrow after calculating @c lhs - @c rhs if @c is_add is false; /// producing @c result. -template bool carried_out(IntT lhs, IntT rhs, IntT result) { +template +constexpr bool carried_out(const IntT lhs, const IntT rhs, const IntT result) { // Additive: // // 0 and 0 => didn't. @@ -28,11 +34,10 @@ template bool carried_out(IntT lhs, IntT r // 1 and 0 => didn't // 1 and 1 or 0 and 0 => did if 1. // 0 and 1 => did. - if constexpr (!is_add) { - rhs = ~rhs; - } - const bool carry = IntT(1 << bit) & (lhs | rhs) & ((lhs & rhs) | ~result); - if constexpr (!is_add) { + static_assert(operation == Operation::Add || operation == Operation::Subtract); + const auto adj_rhs = (operation == Operation::Subtract) ? ~rhs : rhs; + const bool carry = IntT(1 << bit) & (lhs | adj_rhs) & ((lhs & adj_rhs) | ~result); + if constexpr (operation == Operation::Subtract) { return !carry; } else { return carry; @@ -43,21 +48,24 @@ template bool carried_out(IntT lhs, IntT r /// • @c lhs + @c rhs; or /// • @c lhs - @c rhs; /// producing @c result. -template bool carried_in(IntT lhs, IntT rhs, IntT result) { +template +constexpr bool carried_in(const IntT lhs, const IntT rhs, const IntT result) { // 0 and 0 or 1 and 1 => did if 1. // 0 and 1 or 1 and 0 => did if 0. return IntT(1 << bit) & (lhs ^ rhs ^ result); } /// @returns An int of type @c IntT with only the most-significant bit set. -template constexpr IntT top_bit() { +template +constexpr IntT top_bit() { static_assert(!std::numeric_limits::is_signed); constexpr IntT max = std::numeric_limits::max(); return max - (max >> 1); } /// @returns The number of bits in @c IntT. -template constexpr int bit_size() { +template +constexpr int bit_size() { return sizeof(IntT) * 8; } @@ -65,12 +73,13 @@ template constexpr int bit_size() { /// • @c lhs + @c rhs (if @c is_add is true); or /// • @c lhs - @c rhs (if @c is_add is false) /// and the result was @c result. All other bits will be clear. -template -IntT overflow(IntT lhs, IntT rhs, IntT result) { +template +constexpr IntT overflow(const IntT lhs, const IntT rhs, const IntT result) { const IntT output_changed = result ^ lhs; const IntT input_differed = lhs ^ rhs; - if constexpr (is_add) { + static_assert(operation == Operation::Add || operation == Operation::Subtract); + if constexpr (operation == Operation::Add) { return top_bit() & output_changed & ~input_differed; } else { return top_bit() & output_changed & input_differed; diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index cb2fc512b..79b7d953b 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -368,7 +368,7 @@ void Processor::run_for(const Cycles cycles) { // All flags are set based only on the decimal result. flags_.zero_result = result; - flags_.carry = Numeric::carried_out(a_, operand_, result); + flags_.carry = Numeric::carried_out(a_, operand_, result); flags_.negative_result = result; flags_.overflow = (( (result ^ a_) & (result ^ operand_) ) & 0x80) >> 1; @@ -418,7 +418,7 @@ void Processor::run_for(const Cycles cycles) { if(flags_.decimal && has_decimal_mode(personality)) { uint8_t result = a_ + operand_ + flags_.carry; flags_.zero_result = result; - flags_.carry = Numeric::carried_out(a_, operand_, result); + flags_.carry = Numeric::carried_out(a_, operand_, result); // General ADC logic: // diff --git a/Storage/Tape/Parsers/Acorn.cpp b/Storage/Tape/Parsers/Acorn.cpp index 11c3c8f62..8b2fb0f61 100644 --- a/Storage/Tape/Parsers/Acorn.cpp +++ b/Storage/Tape/Parsers/Acorn.cpp @@ -14,7 +14,7 @@ namespace { constexpr int PLLClockRate = 1920000; } -Parser::Parser(): crc_(0x1021) { +Parser::Parser() { shifter_.set_delegate(this); } diff --git a/Storage/Tape/Parsers/Acorn.hpp b/Storage/Tape/Parsers/Acorn.hpp index 810c552c8..0d731ca51 100644 --- a/Storage/Tape/Parsers/Acorn.hpp +++ b/Storage/Tape/Parsers/Acorn.hpp @@ -58,7 +58,7 @@ private: void process_pulse(const Storage::Tape::Pulse &) override; bool did_update_shifter(int new_value, int length); - CRC::Generator crc_; + CRC::Generator crc_; Shifter shifter_; };