Пример #1
0
 public VariableRef([NotNull] SoftGlobal soft)
 {
     Soft = soft;
     Hard = null;
 }
Пример #2
0
        /// <summary>
        /// Analyzes the set of defined global variables and, if there are too many to fit in Z-machine variables ("hard globals"),
        /// does the necessary planning and allocation to move some of them into a table ("soft globals").
        /// </summary>
        /// <remarks>
        /// <para>This method sets <see cref="ZilGlobal.StorageType"/> for all globals,
        /// whether or not it ends up moving any of them.</para>
        /// <para>If it does decide to move some globals, it allocates the <see cref="SoftGlobalsTable"/> (<c>T?GLOBAL-VARS-TABLE</c>),
        /// assigns offsets within the table, and fills the table with the initial values.</para>
        /// </remarks>
        /// <param name="reservedGlobals">The number of hard globals that are reserved for other purposes (i.e. parser tables).
        ///     Subtracting <paramref name="reservedGlobals"/> from 240 gives the number of hard globals that are actually available
        ///     for storing <see cref="ZEnvironment.Globals"/>.</param>
        /// <param name="globalInitializers"></param>
        /// <exception cref="CompilerError"></exception>
        void DoFunnyGlobals(int reservedGlobals, Queue <System.Action> globalInitializers)
        {
            // if all the globals fit into Z-machine globals, no need for a table
            int remaining = 240 - reservedGlobals;

            if (Context.ZEnvironment.Globals.Count <= remaining)
            {
                foreach (var g in Context.ZEnvironment.Globals)
                {
                    g.StorageType = GlobalStorageType.Hard;
                }

                return;
            }

            // reserve one slot for GLOBAL-VARS-TABLE
            remaining--;

            // in V3, the status line variables need to be Z-machine globals
            if (Context.ZEnvironment.ZVersion < 4)
            {
                foreach (var g in Context.ZEnvironment.Globals)
                {
                    switch (g.Name.StdAtom)
                    {
                    case StdAtom.HERE:
                    case StdAtom.SCORE:
                    case StdAtom.MOVES:
                        g.StorageType = GlobalStorageType.Hard;
                        break;
                    }
                }
            }

            // variables used as operands need to be Z-machine globals too
            var globalsByName = Context.ZEnvironment.Globals.ToDictionary(g => g.Name);

            foreach (var r in Context.ZEnvironment.Routines)
            {
                r.WalkRoutineForms(f =>
                {
                    var args = f.Rest;
                    if (args != null && !args.IsEmpty)
                    {
                        // skip the first argument to operations that operate on a variable
                        if (f.First is ZilAtom firstAtom)
                        {
                            switch (firstAtom.StdAtom)
                            {
                            case StdAtom.SET:
                            case StdAtom.SETG:
                            case StdAtom.VALUE:
                            case StdAtom.GVAL:
                            case StdAtom.LVAL:
                            case StdAtom.INC:
                            case StdAtom.DEC:
                            case StdAtom.IGRTR_P:
                            case StdAtom.DLESS_P:
                                args = args.Rest;
                                break;
                            }
                        }

                        while (args != null && !args.IsEmpty)
                        {
                            if (args.First is ZilAtom atom && globalsByName.TryGetValue(atom, out var g))
                            {
                                g.StorageType = GlobalStorageType.Hard;
                            }

                            args = args.Rest;
                        }
                    }
                });
            }

            // determine which others to keep in Z-machine globals
            var lookup = Context.ZEnvironment.Globals.ToLookup(g => g.StorageType);

            var hardGlobals = new List <ZilGlobal>(remaining);

            if (lookup.Contains(GlobalStorageType.Hard))
            {
                hardGlobals.AddRange(lookup[GlobalStorageType.Hard]);

                if (hardGlobals.Count > remaining)
                {
                    throw new CompilerError(
                              CompilerMessages.Too_Many_0_1_Defined_Only_2_Allowed,
                              "hard globals",
                              hardGlobals.Count,
                              remaining);
                }
            }

            var softGlobals = new Queue <ZilGlobal>(Context.ZEnvironment.Globals.Count - hardGlobals.Count);

            if (lookup.Contains(GlobalStorageType.Any))
            {
                foreach (var g in lookup[GlobalStorageType.Any])
                {
                    softGlobals.Enqueue(g);
                }
            }

            if (lookup.Contains(GlobalStorageType.Soft))
            {
                foreach (var g in lookup[GlobalStorageType.Soft])
                {
                    softGlobals.Enqueue(g);
                }
            }

            while (hardGlobals.Count < remaining && softGlobals.Count > 0)
            {
                hardGlobals.Add(softGlobals.Dequeue());
            }

            // assign final StorageTypes
            foreach (var g in hardGlobals)
            {
                g.StorageType = GlobalStorageType.Hard;
            }

            foreach (var g in softGlobals)
            {
                g.StorageType = GlobalStorageType.Soft;
            }

            // create SoftGlobals entries, fill table, and assign offsets
            int byteOffset = 0;
            var table      = Game.DefineTable("T?GLOBAL-VARS-TABLE", false);

            var tableGlobal = Game.DefineGlobal("GLOBAL-VARS-TABLE");

            tableGlobal.DefaultValue = table;
            Globals.Add(Context.GetStdAtom(StdAtom.GLOBAL_VARS_TABLE), tableGlobal);
            SoftGlobalsTable = tableGlobal;

            foreach (var g in softGlobals)
            {
                if (!g.IsWord)
                {
                    var entry = new SoftGlobal
                    {
                        IsWord = false,
                        Offset = byteOffset
                    };
                    SoftGlobals.Add(g.Name, entry);

                    var gSave = g;
                    globalInitializers.Enqueue(() => table.AddByte(GetGlobalDefaultValue(gSave) ?? Game.Zero));

                    byteOffset++;
                }
            }

            if (byteOffset % 2 != 0)
            {
                byteOffset++;
                globalInitializers.Enqueue(() => table.AddByte(Game.Zero));
            }

            foreach (var g in softGlobals)
            {
                if (g.IsWord)
                {
                    var entry = new SoftGlobal
                    {
                        IsWord = true,
                        Offset = byteOffset / 2
                    };
                    SoftGlobals.Add(g.Name, entry);

                    var gSave = g;
                    globalInitializers.Enqueue(() => table.AddShort(GetGlobalDefaultValue(gSave) ?? Game.Zero));

                    byteOffset += 2;
                }
            }
        }
Пример #3
0
 public VariableRef([NotNull] IVariable hard)
 {
     Hard = hard;
     Soft = null;
 }