コード例 #1
0
 /// <summary>
 /// Resets the tape device
 /// </summary>
 public void Reset()
 {
     TapeProvider?.Reset();
     _tapePlayer  = null;
     _currentMode = TapeOperationMode.Passive;
     _savePhase   = SavePhase.None;
     _micBitState = true;
 }
コード例 #2
0
 /// <summary>
 /// Puts the device in save mode. From now on, every MIC pulse is recorded
 /// </summary>
 private void EnterSaveMode()
 {
     _currentMode            = TapeOperationMode.Save;
     _savePhase              = SavePhase.None;
     _micBitState            = true;
     _lastMicBitActivityTact = _cpu.Tacts;
     _pilotPulseCount        = 0;
     _prevDataPulse          = MicPulseType.None;
     _dataBlockCount         = 0;
     SaveToTapeProvider?.CreateTapeFile();
 }
コード例 #3
0
        /// <summary>
        /// Processes the the change of the MIC bit
        /// </summary>
        /// <param name="micBit">MIC bit to process</param>
        public void ProcessMicBit(bool micBit)
        {
            if (_currentMode != TapeOperationMode.Save ||
                _micBitState == micBit)
            {
                return;
            }

            var length = _cpu.Tacts - _lastMicBitActivityTact;

            // --- Classify the pulse by its width
            var pulse = MicPulseType.None;

            if (length >= TapeDataBlockPlayer.BIT_0_PL - SAVE_PULSE_TOLERANCE &&
                length <= TapeDataBlockPlayer.BIT_0_PL + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.Bit0;
            }
            else if (length >= TapeDataBlockPlayer.BIT_1_PL - SAVE_PULSE_TOLERANCE &&
                     length <= TapeDataBlockPlayer.BIT_1_PL + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.Bit1;
            }
            if (length >= TapeDataBlockPlayer.PILOT_PL - SAVE_PULSE_TOLERANCE &&
                length <= TapeDataBlockPlayer.PILOT_PL + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.Pilot;
            }
            else if (length >= TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE &&
                     length <= TapeDataBlockPlayer.SYNC_1_PL + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.Sync1;
            }
            else if (length >= TapeDataBlockPlayer.SYNC_2_PL - SAVE_PULSE_TOLERANCE &&
                     length <= TapeDataBlockPlayer.SYNC_2_PL + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.Sync2;
            }
            else if (length >= TapeDataBlockPlayer.TERM_SYNC - SAVE_PULSE_TOLERANCE &&
                     length <= TapeDataBlockPlayer.TERM_SYNC + SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.TermSync;
            }
            else if (length < TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.TooShort;
            }
            else if (length > TapeDataBlockPlayer.PILOT_PL + 2 * SAVE_PULSE_TOLERANCE)
            {
                pulse = MicPulseType.TooLong;
            }

            _micBitState            = micBit;
            _lastMicBitActivityTact = _cpu.Tacts;

            // --- Lets process the pulse according to the current SAVE phase and pulse width
            var nextPhase = SavePhase.Error;

            switch (_savePhase)
            {
            case SavePhase.None:
                if (pulse == MicPulseType.TooShort || pulse == MicPulseType.TooLong)
                {
                    nextPhase = SavePhase.None;
                }
                else if (pulse == MicPulseType.Pilot)
                {
                    _pilotPulseCount = 1;
                    nextPhase        = SavePhase.Pilot;
                }
                break;

            case SavePhase.Pilot:
                if (pulse == MicPulseType.Pilot)
                {
                    _pilotPulseCount++;
                    nextPhase = SavePhase.Pilot;
                }
                else if (pulse == MicPulseType.Sync1 && _pilotPulseCount >= MIN_PILOT_PULSE_COUNT)
                {
                    nextPhase = SavePhase.Sync1;
                }
                break;

            case SavePhase.Sync1:
                if (pulse == MicPulseType.Sync2)
                {
                    nextPhase = SavePhase.Sync2;
                }
                break;

            case SavePhase.Sync2:
                if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1)
                {
                    // --- Next pulse starts data, prepare for receiving it
                    _prevDataPulse = pulse;
                    nextPhase      = SavePhase.Data;
                    _bitOffset     = 0;
                    _dataByte      = 0;
                    _dataLength    = 0;
                    _dataBuffer    = new byte[DATA_BUFFER_LENGTH];
                }
                break;

            case SavePhase.Data:
                if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1)
                {
                    if (_prevDataPulse == MicPulseType.None)
                    {
                        // --- We are waiting for the second half of the bit pulse
                        _prevDataPulse = pulse;
                        nextPhase      = SavePhase.Data;
                    }
                    else if (_prevDataPulse == pulse)
                    {
                        // --- We received a full valid bit pulse
                        nextPhase      = SavePhase.Data;
                        _prevDataPulse = MicPulseType.None;

                        // --- Add this bit to the received data
                        _bitOffset++;
                        _dataByte = (byte)(_dataByte * 2 + (pulse == MicPulseType.Bit0 ? 0 : 1));
                        if (_bitOffset == 8)
                        {
                            // --- We received a full byte
                            _dataBuffer[_dataLength++] = _dataByte;
                            _dataByte  = 0;
                            _bitOffset = 0;
                        }
                    }
                }
                else if (pulse == MicPulseType.TermSync)
                {
                    // --- We received the terminating pulse, the datablock has been completed
                    nextPhase = SavePhase.None;
                    _dataBlockCount++;

                    // --- Create and save the data block
                    var dataBlock = new TzxStandardSpeedDataBlock
                    {
                        Data       = _dataBuffer,
                        DataLength = (ushort)_dataLength
                    };

                    // --- If this is the first data block, extract the name from the header
                    if (_dataBlockCount == 1 && _dataLength == 0x13)
                    {
                        // --- It's a header!
                        var sb = new StringBuilder(16);
                        for (var i = 2; i <= 11; i++)
                        {
                            sb.Append((char)_dataBuffer[i]);
                        }
                        var name = sb.ToString().TrimEnd();
                        TapeProvider?.SetName(name);
                    }
                    TapeProvider?.SaveTapeBlock(dataBlock);
                }
                break;
            }
            _savePhase = nextPhase;
        }
コード例 #4
0
        public void Save(Stream stream)
        {
            _encoder.Clear();

            _phase = SavePhase.Initial;
            foreach (var section in _sections)
            {
                foreach (var vertex in section._items)
                {
                    vertex._offset    = GetCurrentOffset();
                    vertex._iteration = _iteration;
                    vertex.Save(this);

#if NATIVEFORMAT_COMPRESSION
                    // Ensure that the compressor state is fully flushed
                    Debug.Assert(_TentativelyWritten.Count == 0);
                    Debug.Assert(_compressionDepth == 0);
#endif
                }
            }

            // Aggresive phase that only allows offsets to shrink.
            _phase = SavePhase.Shrinking;
            for (; ;)
            {
                _iteration++;
                _encoder.Clear();

                _offsetAdjustment = 0;

                foreach (var section in _sections)
                {
                    foreach (var vertex in section._items)
                    {
                        int currentOffset = GetCurrentOffset();

                        // Only allow the offsets to shrink.
                        _offsetAdjustment = Math.Min(_offsetAdjustment, currentOffset - vertex._offset);

                        vertex._offset += _offsetAdjustment;

                        if (vertex._offset < currentOffset)
                        {
                            // It is possible for the encoding of relative offsets to grow during some iterations.
                            // Ignore this growth because of it should disappear during next iteration.
                            RollbackTo(vertex._offset);
                        }
                        Debug.Assert(vertex._offset == GetCurrentOffset());

                        vertex._iteration = _iteration;

                        vertex.Save(this);

#if NATIVEFORMAT_COMPRESSION
                        // Ensure that the compressor state is fully flushed
                        Debug.Assert(_tentativelyWritten.Count == 0);
                        Debug.Assert(_compressionDepth == 0);
#endif
                    }
                }

                // We are not able to shrink anymore. We cannot just return here. It is possible that we have rolledback
                // above because of we shrinked too much.
                if (_offsetAdjustment == 0)
                {
                    break;
                }

                // Limit number of shrinking interations. This limit is meant to be hit in corner cases only.
                if (_iteration > 10)
                {
                    break;
                }
            }

            // Conservative phase that only allows the offsets to grow. It is guaranteed to converge.
            _phase = SavePhase.Growing;
            for (; ;)
            {
                _iteration++;
                _encoder.Clear();

                _offsetAdjustment = 0;
                _paddingSize      = 0;

                foreach (var section in _sections)
                {
                    foreach (var vertex in section._items)
                    {
                        int currentOffset = GetCurrentOffset();

                        // Only allow the offsets to grow.
                        _offsetAdjustment = Math.Max(_offsetAdjustment, currentOffset - vertex._offset);

                        vertex._offset += _offsetAdjustment;

                        if (vertex._offset > currentOffset)
                        {
                            // Padding
                            int padding = vertex._offset - currentOffset;
                            _paddingSize += padding;
                            WritePad(padding);
                        }
                        Debug.Assert(vertex._offset == GetCurrentOffset());

                        vertex._iteration = _iteration;

                        vertex.Save(this);

#if NATIVEFORMAT_COMPRESSION
                        // Ensure that the compressor state is fully flushed
                        Debug.Assert(_tentativelyWritten.Count == 0);
                        Debug.Assert(_compressionDepth == 0);
#endif
                    }
                }

                if (_offsetAdjustment == 0)
                {
                    _encoder.Save(stream);
                    return;
                }
            }
        }