Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/BizHawk.Emulation.Cores/Computers/Commodore64/Media/Tape.cs
2 views
using System;
using System.Text;
using BizHawk.Common;

namespace BizHawk.Emulation.Cores.Computers.Commodore64.Media
{
	public class Tape
	{
        [SaveState.DoNotSave] private readonly byte[] _tapeData;
		[SaveState.DoNotSave] private readonly byte _version;
	    [SaveState.DoNotSave] private readonly int _start;
        [SaveState.DoNotSave] private readonly int _end;

        [SaveState.SaveWithName("Position")]
	    private int _pos;
        [SaveState.SaveWithName("Cycle")]
        private int _cycle;
        [SaveState.SaveWithName("Data")]
        private bool _data;

		public Tape(byte version, byte[] tapeData, int start, int end)
		{
			_version = version;
			_tapeData = tapeData;
			_start = start;
			_end = end;
			Rewind();
		}

	    public void ExecuteCycle()
	    {
            if (_cycle == 0)
            {
                if (_pos >= _end)
                {
                    _data = true;
                    return;
                }

                _cycle = _tapeData[_pos++] * 8;
                if (_cycle == 0)
                {
                    if (_version == 0)
                    {
                        _cycle = 256 * 8; // unspecified overflow condition
                    }
                    else
                    {
                        _cycle = (int)(BitConverter.ToUInt32(_tapeData, _pos - 1) >> 8);
                        _pos += 3;
                        if (_cycle == 0)
                        {
                            throw new Exception("Bad tape data");
                        }
                    }
                }

                _cycle++;
            }

            // Send a single negative pulse at the end of a cycle
            _data = --_cycle != 0;
        }

        // Rewinds the tape back to start
        public void Rewind()
		{
			_pos = _start;
			_cycle = 0;
		}

		// Reads from tape, this will tell the caller if the flag pin should be raised
		public bool Read()
		{
		    return _data;
		}

		// Try to construct a tape file from file data. Returns null if not a tape file, throws exceptions for bad tape files.
		// (Note that some error conditions aren't caught right here.)
		public static Tape Load(byte[] tapeFile)
		{
			Tape result = null;

			if (Encoding.ASCII.GetString(tapeFile, 0, 12) == "C64-TAPE-RAW")
			{
				var version = tapeFile[12];
				if (version > 1) throw new Exception("This tape has an unsupported version");
				var size = BitConverter.ToUInt32(tapeFile, 16);
				if (size + 20 != tapeFile.Length)
				{
					throw new Exception("Tape file header specifies a length that doesn't match the file size");
				}
				result = new Tape(version, tapeFile, 20, tapeFile.Length);
			}
			return result;
		}

		public void SyncState(Serializer ser)
		{
			SaveState.SyncObject(ser, this);
		}
	}
}