Example #1
0
            public IEnumerable <Tuple <CodeSequence, SpriteGeneratorState> > Successors(SpriteGeneratorState state)
            {
                if (state.LongA || !state.S.IsScreenOffset)
                {
                    yield break;
                }

                var openList = state.RemainingBytes();

                // Get the first byte with a non-zero mask that is within 255 bytes of the current location
                var nextByte = openList.Where(sb => (sb.Offset - state.S.Value) <= 255).Cast <SpriteByte?>().FirstOrDefault();

                if (nextByte != null)
                {
                    yield return(state.Apply(new MOVE_STACK(nextByte.Value.Offset - state.S.Value)));
                }

                // Check for the next run of solid bytes and move to the end
                var run = StateHelpers.FirstSolidRun(openList);

                if (run != null)
                {
                    yield return(state.Apply(new MOVE_STACK(run.Last.Offset - state.S.Value)));
                }

                yield break;
            }
Example #2
0
        public IEnumerable <Tuple <CodeSequence, SpriteGeneratorState> > Successors(SpriteGeneratorState state)
        {
            // Get the list of remaining bytes by removing the closed list from the global sprite dataset
            var open = state.RemainingBytes();

            // If the open list is empty, there can be only one reason -- we're in 8-bit
            // mode.
            if (!open.Any())
            {
                yield return(state.Apply(new LONG_M()));

                yield break;
            }

            // Get the first byte -- if we are expanding a state we can safely assume there
            // is still data to expand.
            var firstByte = open.First();

            // Identify the first run of solid bytes
            var firstSolid = StateHelpers.FirstSolidRun(open);

            // In an initial state, the stack has not been set, but the accumulator contains a screen
            // offset address.  There are three possible options
            //
            // 1. Set the stack to the accumulator value.  This is optimal when there are very few
            //    data bytes to set and the overhead of moving the stack negates any benefit from
            //    using stack push instructions
            //
            // 2. Set the stack to the first offset of the open set. This can be optimal if there are a few
            //    bytes and moving the stack forward a bit can allow the code to reach them without needing
            //    a second stack adjustment.
            //
            // 3. Set the stack to the first, right-most offset that ends a sequence of solid bytes
            if (!state.S.IsScreenOffset && state.A.IsScreenOffset && state.LongA)
            {
                // If the first byte is within 255 bytes of the accumulator, propose setting
                // the stack to the accumulator value, which is the fastest
                var delta = firstByte.Offset - state.A.Value;
                if (delta >= 0 && delta < 256)
                {
                    yield return(state.Apply(new MOVE_STACK(0)));
                }

                // If the first byte offset is not equal to the accumulator value, propose that
                if (delta > 0)
                {
                    yield return(state.Apply(new MOVE_STACK(delta)));
                }

                // Find the first edge of a solid run....TODO
                if (firstSolid != null && firstSolid.Count >= 2)
                {
                    yield return(state.Apply(new MOVE_STACK(firstSolid.Last.Offset - state.A.Value)));
                }

                yield break;
            }

            // If the first byte is 256 bytes or more ahead of the current stack location,
            // then we need to advance
            var firstByteDistance = firstByte.Offset - state.S.Value;

            if (state.S.IsScreenOffset && firstByteDistance >= 256 && state.LongA)
            {
                // If the accumulator is not set to the stack
                if (!state.A.IsScreenOffset)
                {
                    yield return(state.Apply(new TSC()));
                }

                // Go to the next byte, or the first solid edge
                else
                {
                    yield return(state.Apply(new MOVE_STACK(firstByteDistance)));
                }

                yield break;
            }

            var bytes = open.ToDictionary(x => x.Offset, x => x);

            // Get the current byte and current word that exist at the current stack location
            var topByte = state.TryGetStackByte(bytes);
            var topWord = state.TryGetStackWord(bytes);

            // If the top of the stack is a solid work, we can always emit a PEA regardless of 8/16-bit mode
            if (topWord.HasValue && topWord.Value.Mask == 0x000)
            {
                yield return(state.Apply(new PEA(topWord.Value.Data)));
            }

            // First set of operations for when the accumulator is in 8-bit mode.  We are basically limited
            // to either
            //
            // 1. Switching to 16-bit mode
            // 3. Using a PHA to push an 8-bit solid or masked value on the stack
            // 4. Using a STA 0,s to store an 8-bit solid or masked value on the stack

            if (!state.LongA)
            {
                // The byte to be stored at the top of the stack has some special methods available
                if (topByte.HasValue)
                {
                    var datum = topByte.Value;

                    // Solid byte
                    if (datum.Mask == 0x00)
                    {
                        if (state.A.IsLiteral && ((state.A.Value & 0xFF) == datum.Data))
                        {
                            yield return(state.Apply(new PHA_8()));
                        }
                        else
                        {
                            yield return(state.Apply(new LOAD_8_BIT_IMMEDIATE_AND_PUSH(datum.Data)));

                            yield return(state.Apply(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, 0)));
                        }
                    }

                    // Masked byte
                    else
                    {
                        yield return(state.Apply(new STACK_REL_8_BIT_READ_MODIFY_PUSH(datum.Data, datum.Mask)));
                    }
                }

                // Otherwise, just store the next byte closest to the stack
                if (state.S.IsScreenOffset)
                {
                    var addr = state.S.Value;

                    // We can LDA #$XX / STA X,s for any values within 256 bytes of the current address
                    foreach (var datum in open.Where(WithinRangeOf(addr, 256)))
                    {
                        var offset = (byte)(datum.Offset - addr);

                        // Easy case when mask is empty
                        if (datum.Mask == 0x00)
                        {
                            if (datum.Data == (state.A.Value & 0xFF))
                            {
                                yield return(state.Apply(new STACK_REL_8_BIT_STORE(offset)));
                            }
                            else
                            {
                                yield return(state.Apply(new STACK_REL_8_BIT_IMMEDIATE_STORE(datum.Data, offset)));
                            }
                        }

                        // Otherwise there is really only one choice LDA / AND / ORA / STA sequence
                        else
                        {
                            yield return(state.Apply(new STACK_REL_8_BIT_READ_MODIFY_WRITE(datum.Data, datum.Mask, offset)));
                        }
                    }
                }

                if (state.AllowModeChange)
                {
                    yield return(state.Apply(new LONG_M()));
                }
            }

            // Now consider what can be done when the accumulator is 16-bit.  All of the stack
            // manipulation happens here, too.
            if (state.LongA)
            {
                // Handle the special case of a value sitting right on top of the stack
                if (topWord.HasValue)
                {
                    var datum = topWord.Value;

                    // First, the simple case -- the data has no mask
                    if (datum.Mask == 0x000)
                    {
                        if (state.A.IsLiteral && state.A.Value == datum.Data)
                        {
                            yield return(state.Apply(new PHA_16()));
                        }
                        else
                        {
                            // Only consider this if the value appear in the sprite more than once
                            if (SpriteGeneratorState.DATASET_SOLID_WORDS[topWord.Value.Data] > 1)
                            {
                                yield return(state.Apply(new LOAD_16_BIT_IMMEDIATE_AND_PUSH(topWord.Value.Data)));
                            }
                        }
                    }
                }

                // If the stack is set, find the next word to store (just one to reduce branching factor)
                if (state.S.IsScreenOffset)
                {
                    var addr = state.S.Value;

                    var local = open.Where(WithinRangeOf(addr, 256)).ToList();
                    var words = local
                                .Where(x => SpriteGeneratorState.DATASET_BY_OFFSET.ContainsKey(x.Offset + 1))
                                .Select(x => new { Low = x, High = SpriteGeneratorState.DATASET_BY_OFFSET[x.Offset + 1] })
                                .ToList();

                    foreach (var word in words)
                    {
                        var offset = (byte)(word.Low.Offset - addr);
                        var data   = (ushort)(word.Low.Data + (word.High.Data << 8));
                        var mask   = (ushort)(word.Low.Mask + (word.High.Mask << 8));
                        // Easy case when mask is empty
                        if (mask == 0x0000)
                        {
                            if (data == state.A.Value)
                            {
                                yield return(state.Apply(new STACK_REL_16_BIT_STORE(offset)));
                            }
                            else
                            {
                                yield return(state.Apply(new STACK_REL_16_BIT_IMMEDIATE_STORE(data, offset)));
                            }
                        }

                        // Otherwise there is really only one choice LDA / AND / ORA / STA sequence
                        else
                        {
                            yield return(state.Apply(new STACK_REL_16_BIT_READ_MODIFY_WRITE(data, mask, offset)));
                        }
                    }
                }

                if (state.AllowModeChange)
                {
                    yield return(state.Apply(new SHORT_M()));
                }
            }
        }