예제 #1
0
        // parses options, returns array of file names
        static string[] ParseArguments(string[] args)
        {
            Action <sbyte> doExtraCycles = delegate(sbyte cycles)
            {
                HighLow16 new0 = Pulse.Zero(cycles);
                HighLow16 new1 = Pulse.One(cycles);
                _blockHeader.PilotPulse = Pulse.Pilot;
                _blockHeader.SyncPulse0 = _blockHeader.ZeroPulse = new0;
                _blockHeader.SyncPulse1 = _blockHeader.OnePulse = new1;
            };
            string pilot         = null;
            bool   noFixedLength = false;

            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].FirstOrDefault() != '-')
                {
                    // we've come to the end of the options, all the rest
                    // are files

                    if (noFixedLength && (_dynamicTable != null))
                    {
                        _dynamicTable.DisableFixedLength();
                    }

                    if (!string.IsNullOrWhiteSpace(pilot))
                    {
                        checked
                        {
                            HighLow16 pilotPulses, pilotClicks = 0;
                            pilot = pilot.ToLower();
                            switch (pilot)
                            {
                            case "resume":
                                pilotPulses = TinyPilotPulseCount;
                                break;

                            case "short":
                                pilotPulses = 600;
                                break;

                            case "standard":
                                pilot = "1000"; // one second
                                goto default;   // fall through

                            default:
                                if (pilot.StartsWith("click"))
                                {
                                    if (_blockHeader.PilotPulse == Pulse.Rom.Pilot)
                                    {
                                        Console.Error.WriteLine
                                            ("clicking pilots may not be used with ROM timings");
                                        Die();
                                    }

                                    // see if a click count was appended, default to 8 if not
                                    var clickStr = pilot.Substring(5).Trim();
                                    if (clickStr.Length > 0)
                                    {
                                        pilotClicks = ParseInteger <ushort>(clickStr);
                                    }
                                    if (pilotClicks < 1)
                                    {
                                        pilotClicks = 8;
                                    }
                                    pilotPulses = 300;
                                    break;
                                }

                                int ms      = ParseInteger <int>(pilot);
                                int tstates = ms * TStatesPerMillisecond;
                                pilotPulses =
                                    (ushort)(tstates / _blockHeader.PilotPulse);
                                if ((pilotPulses % 2) == 1)
                                {
                                    pilotPulses += 1;
                                }
                                break;
                            }
                            _blockHeader.PilotPulseCount = pilotPulses;
                            _blockHeader.PilotClickCount = pilotClicks;
                        }
                    }

                    return(args.Skip(i).ToArray());
                }

                switch (args[i].TrimStart('-'))
                {
                case "noheader":
                    _noHeader = true;
                    break;

                case "nofixedlength":
                    noFixedLength = true;
                    break;

                case "reverse":
                    _reverse = true;
                    break;

                case "pilot":
                    // next arg is pilot length in milliseconds, or a string, or
                    // click count
                    pilot = args[++i];
                    // defer calculation until end of option parsing in case
                    // ROM timings are requested and we're going to change
                    // the pilot pulse length
                    break;

                case "pause":
                    // next arg is pause length in milliseconds
                    checked
                    {
                        HighLow16 ms = ParseInteger <ushort>(args[++i]);
                        _blockHeader.Pause = ms;
                    }
                    break;

                case "extracycles":
                    sbyte cycles = ParseInteger <sbyte>(args[++i]);
                    doExtraCycles(cycles);
                    break;

                case "instascreen":
                    WriteData = WriteData_Instascreen;
                    break;

                case "fancyscreen":
                    // next arg is a fancy screen load type; one of the fields
                    // in the FancyScreen class
                    var loadTypeName = args[++i];
                    _dynamicTable = (from field in typeof(FancyScreen).GetFields
                                         (BindingFlags.Static | BindingFlags.NonPublic)
                                     where field.FieldType == typeof(DynamicTable)
                                     where field.Name.Equals(loadTypeName,
                                                             StringComparison.OrdinalIgnoreCase)
                                     select(DynamicTable) field.GetValue(null))
                                    .FirstOrDefault();
                    if (_dynamicTable == null)
                    {
                        Console.Error.WriteLine
                            ("unknown fancy screen type \"{0}\"", loadTypeName);
                        goto die;
                    }
                    WriteData = WriteData_FancyScreen;
                    break;

                case "progressive":
                    // next arg is number of progressive chunks
                    checked
                    {
                        _progressive = ParseInteger <ushort>(args[++i]);
                    }
                    WriteData = WriteData_Progressive;
                    break;

                case "countdown":
                    // next arg is number of progressive chunks, optionally
                    // followed by the number at which to start the countdown
                    var split = args[++i].Split(':');
                    if (split.Length < 1 || split.Length > 2)
                    {
                        goto badCountdown;
                    }
                    switch (split.Length)
                    {
                    case 1:
                        // start countdown at the first specified block
                        split = new[] { split[0], split[0] };
                        break;

                    case 2:
                        break;

                    default:
                        goto badCountdown;
                    }
                    checked
                    {
                        int countdownBlocks = ParseInteger <int>(split[0]);
                        int countdownStart  = ParseInteger <int>(split[1]);

                        if (countdownBlocks < 1 || countdownBlocks > 99)
                        {
                            goto badCountdown;
                        }
                        if (countdownStart < countdownBlocks || countdownStart > 99)
                        {
                            goto badCountdown;
                        }
                        _progressive    = (byte)countdownBlocks;
                        _countdownStart = (byte)countdownStart;
                    }
                    WriteData = WriteData_Progressive;
                    break;
badCountdown:
                    Console.Error.WriteLine("bad countdown argument");
                    goto die;

                case "bleep":
                    _bleep = true;
                    break;

                case "output":
                    // next arg is name of file to which to write
                    _outputFileName = args[++i];
                    if (File.Exists(_outputFileName))
                    {
                        _noHeader = true;
                    }
                    break;

                case "speed":
                    // next arg is a named timing
                    var    speedName = args[++i];
                    Timing speed;
                    if (!Enum.TryParse(speedName, true, out speed))
                    {
                        Console.Error.WriteLine("unknown speed \"{0}\"", speedName);
                        goto die;
                    }
                    if (speed != Timing.Rom)
                    {
                        doExtraCycles((sbyte)speed);
                    }
                    else
                    {
                        // convert block to ROM timings
                        _blockHeader.PilotPulse = Pulse.Rom.Pilot;
                        _blockHeader.SyncPulse0 = Pulse.Rom.Sync0;
                        _blockHeader.SyncPulse1 = Pulse.Rom.Sync1;
                        _blockHeader.ZeroPulse  = Pulse.Rom.Zero;
                        _blockHeader.OnePulse   = Pulse.Rom.One;
                    }
                    // recompute pilot pulse count
                    if (pilot == null)
                    {
                        pilot = "standard";
                    }
                    break;

                default:
                    // unknown option
                    Console.Error.WriteLine("Unknown option \"{0}\"", args[i]);
                    goto die;
                }
            }

            // still here?  Bad option or no files...
die:
            Die();
            return(null);
        }
예제 #2
0
        /// <summary>
        /// Writes data blocks for a fancy screen load.
        /// </summary>
        /// <param name='output'>
        /// The stream to which to write the blocks.
        /// </param>
        /// <param name='table'>
        /// The dynamic table defining the blocks to load.
        /// </param>
        /// <param name='screenData'>
        /// 6912 bytes of screen data.
        /// </param>
        /// <param name='blockHeaderTemplate'>
        /// Template values to use when writing block headers.
        /// </param>
        internal static void WriteData(Stream output,
                                       DynamicTable table,
                                       IList <byte> screenData,
                                       TurboBlockHeader blockHeaderTemplate)
        {
            if (screenData.Count != 6912)
            {
                Console.Error.WriteLine("not a 6912 byte screen");
                Environment.Exit(1);
            }

            var fixedLength = table.IsFixedLength;
            var tableBytes  = ((IEnumerable <byte>)table).ToArray();

            // first, write a block containg the length of the table
            var tableLengthBlockData =
                new HighLow16((ushort)tableBytes.Length).ToArray();
            var block = new Block
                            (blockHeaderTemplate.DeepCopy(), tableLengthBlockData);

            // all the blocks comprising the screen must be glued
            block.BlockHeader.Pause = 0;
            block.WriteBlock(output);

            // next, write the table itself
            block = new Block(blockHeaderTemplate.DeepCopy(), tableBytes);
            block.BlockHeader.Pause           = 0;
            block.BlockHeader.PilotPulseCount =
                FortySevenLoaderTzx.TinyPilotPulseCount; // for glued block
            block.WriteBlock(output);

            // now we write the blocks as specified by the table
            bool backwards = false;

            foreach (DynamicTable.Entry entry in table)
            {
                int offset = entry.Address - 16384;
                if (entry.ChangeDirection)
                {
                    backwards = !backwards;
                }
                // for backwards loading, the address is the last byte
                if (backwards)
                {
                    offset -= (entry.Length - 1);
                }
                var blockData = screenData.Skip(offset).Take(entry.Length);
                if (backwards)
                {
                    blockData = blockData.Reverse();
                }
                block = new Block(blockHeaderTemplate.DeepCopy(), blockData);
                block.BlockHeader.PilotPulseCount =
                    FortySevenLoaderTzx.TinyPilotPulseCount;
                // leave the pause length alone for the very last block
                if (!ReferenceEquals(entry, table.Last <DynamicTable.Entry>()))
                {
                    block.BlockHeader.Pause = 0;
                }
                block.WriteBlock(output);
            }

            Console.Error.WriteLine
                ("Dynamic table length: {0} bytes", tableBytes.Length);
            if (fixedLength)
            {
                Console.Error.WriteLine("Define LOADER_DYNAMIC_FIXED_LENGTH EQU " +
                                        (ushort)table[0].Length);
                return;
            }
            if (!table.Any <DynamicTable.Entry>(entry => entry.Length > 127))
            {
                // compact table -- need to define this
                Console.Error.WriteLine("Define LOADER_DYNAMIC_ONE_BYTE_LENGTHS");
            }
            if (!table.Any <DynamicTable.Entry>(entry => entry.ChangeDirection))
            {
                // defining this saves us a few bytes
                Console.Error.WriteLine("Define LOADER_DYNAMIC_FORWARDS_ONLY");
            }
            else
            {
                // remind the user to define this because I forget otherwise
                Console.Error.WriteLine("Define LOADER_CHANGE_DIRECTION");
            }
        }