예제 #1
0
        public string[] GetLines(int startLine, int endLine)
        {
            if (_protected)
            {
                throw new ReplRuntimeException(ReplExceptionCode.IllegalFunctionCall);
            }

            // 65529 is max insertable line number for GW-BASIC 3.23.
            // however, 65530-65535 are executed if present in tokenised form.
            // in GW-BASIC, 65530 appears in LIST, 65531 and above are hidden

            // sort by positions, not line numbers!
            var linesByPostion = _lineNumberMap.Where(x => x.Key >= startLine && x.Key <= endLine)
                                 .Select(x => x.Value).OrderBy(x => x);

            var lines   = new List <string>();
            var current = Bytecode.Position;

            foreach (var position in linesByPostion)
            {
                Bytecode.Seek(position + 1, SeekOrigin.Begin);
                lines.Add(Tokeniser.DetokeniseLine(Bytecode).Text);
            }

            Bytecode.Seek(current, SeekOrigin.Begin);
            return(lines.ToArray());
        }
예제 #2
0
        /// <summary>
        /// Update line number dictionary after deleting lines.
        /// </summary>
        private void UpdateLineMap(CodePosition position, int length)
        {
            // subtract length of line we replaced
            length -= (int)(position.AfterPosition - position.StartPosition);
            var address = Position + 1 + position.AfterPosition;

            Bytecode.Seek(position.AfterPosition + length + 1, SeekOrigin.Begin); // pass \x00
            while (true)
            {
                var nextAddressStr = Bytecode.Read(2);
                if (nextAddressStr.Length < 2 || nextAddressStr == "\0\0")
                {
                    break;
                }

                var nextAddress = nextAddressStr.ToUnsignedInteger();
                Bytecode.Seek(-2, SeekOrigin.Current);
                Bytecode.Write((nextAddress + length).ToBasicUnsignedInteger());
                Bytecode.Seek(nextAddress - address - 2, SeekOrigin.Current);

                address = nextAddress;
            }

            // update line number dict
            foreach (var line in position.Deleteable)
            {
                _lineNumberMap.Remove(line);
            }

            foreach (var line in position.Beyond)
            {
                _lineNumberMap[line] += length;
            }
        }
예제 #3
0
        /// <summary>
        /// Store the given tokenized line buffer.
        /// </summary>
        private void StoreLine(Stream stream)
        {
            if (_protected)
            {
                throw new ReplRuntimeException(ReplExceptionCode.IllegalFunctionCall);
            }

            stream.Seek(1, SeekOrigin.Begin);
            var scanLine = stream.ReadLineNumber();

            // check if stream is an empty line after the line number
            var nextNonWhitespace = stream.SkipWhitespace();
            var empty             = nextNonWhitespace == -1 || nextNonWhitespace == '\0';
            var codePosition      = FindCodePosition(scanLine, scanLine);

            if (empty && codePosition.Deleteable.Length == 0)
            {
                throw new ReplRuntimeException(ReplExceptionCode.UndefinedLineNumber);
            }

            // read the remainder of the program into a buffer to be pasted back after the write
            Bytecode.Seek(codePosition.AfterPosition, SeekOrigin.Begin);
            var rest = Bytecode.ReadToEnd();

            Bytecode.Seek(codePosition.StartPosition, SeekOrigin.Begin);
            var length = 0;

            // write the line buffer to the program buffer
            if (!empty)
            {
                // set offsets
                length = stream.ReadToEnd().Length;
                stream.Seek(0, SeekOrigin.Begin); // pass \x00\xC0\xDE

                Bytecode.WriteByte(0);
                Bytecode.Write(((int)(Position + 1 + codePosition.StartPosition + length)).ToBasicUnsignedInteger());
                Bytecode.WriteByte((byte)stream.ReadByte());
            }

            // write back the remainder of the program
            Truncate(rest);

            // update all next offsets by shifting them by the length of the added line
            UpdateLineMap(codePosition, length);

            if (!empty)
            {
                _lineNumberMap[scanLine] = codePosition.StartPosition;
            }

            _lastStored = scanLine;
        }
예제 #4
0
        /// <summary>
        /// Deletes the stored program within specified range
        /// </summary>
        /// <param name="startLine">Starting line</param>
        /// <param name="lastLine">Ending line</param>
        public void Delete(int startLine, int lastLine)
        {
            var codePosition = FindCodePosition(startLine, lastLine);

            if (codePosition.Deleteable.Length == 0) // no lines selected
            {
                throw new ReplRuntimeException(ReplExceptionCode.IllegalFunctionCall);
            }

            Bytecode.Seek(codePosition.AfterPosition, SeekOrigin.Begin);
            var rest = Bytecode.ReadToEnd();

            Bytecode.Seek(codePosition.StartPosition, SeekOrigin.Begin);
            Truncate(rest);

            UpdateLineMap(codePosition, 0);
        }
예제 #5
0
        public int this[long offset]
        {
            get
            {
                offset -= Position;
                var bytes = Bytecode.ToArray();
                if (offset >= bytes.Length)
                {
                    return(-1);
                }

                return(bytes[offset]);
            }
            set
            {
                if (!AllowCodePoke)
                {
                    Trace.TraceWarning("Ignored POKE into program code");
                    return;
                }

                offset -= Position;
                var position = Bytecode.Position;

                // move pointer to end
                Bytecode.Seek(0, SeekOrigin.End);
                if (offset > Bytecode.Position)
                {
                    Bytecode.Write(new string('\0', (int)(offset - Bytecode.Position)));
                }
                else
                {
                    Bytecode.Seek(offset, SeekOrigin.Begin);
                }

                Bytecode.WriteByte((byte)value);
                // restore program pointer
                Bytecode.Seek(position, SeekOrigin.Begin);
                RebuildLineNumbers();
            }
        }
예제 #6
0
        /// <summary>
        /// Preparse to build line number dictionary.
        /// </summary>
        private void RebuildLineNumbers()
        {
            _lineNumberMap.Clear();
            var offsets = new List <long>();

            Bytecode.Seek(0, SeekOrigin.Begin);
            var scanPosition = 0L;

            while (true)
            {
                Bytecode.ReadByte(); // pass \x00
                var scanline = Bytecode.ReadLineNumber();
                if (scanline == -1)
                {
                    // if parse_line_number returns -1, it leaves the stream pointer here: 00 _00_ 00 1A
                    break;
                }

                _lineNumberMap[scanline] = scanPosition;
                Bytecode.SkipUntilLineEnd();
                scanPosition = Bytecode.Position;
                offsets.Add(scanPosition);
            }

            _lineNumberMap[BasicLastLineNumber] = scanPosition;
            // rebuild offsets
            Bytecode.Seek(0, SeekOrigin.Begin);
            var lastPosition = 0L;

            foreach (var postion in offsets)
            {
                Bytecode.ReadByte();
                Bytecode.Write(((int)(Position + 1 + postion)).ToBasicUnsignedInteger());
                Bytecode.Read((int)(postion - lastPosition - 3));
                lastPosition = postion;
            }

            // ensure program is properly sealed - last offset must be 00 00. keep, but ignore, anything after.
            Bytecode.Write("\0\0\0");
        }
예제 #7
0
        /// <summary>
        /// Save the program to stream stream in defined mode.
        /// </summary>
        /// <param name="stream">Stream to which to save</param>
        /// <param name="mode">Mode in which to save program</param>
        public void SaveTo(Stream stream, ProgramMode mode)
        {
            if (mode != ProgramMode.Protected && _protected)
            {
                throw new ReplRuntimeException(ReplExceptionCode.IllegalFunctionCall);
            }

            var current = Bytecode.Position;

            // skip first \x00 in bytecode
            Bytecode.Seek(1, SeekOrigin.Begin);
            switch (mode)
            {
            case ProgramMode.Binary:
                Bytecode.CopyTo(stream);
                break;

            case ProgramMode.Protected:
                ProtectedProgramEncoder.Encode(Bytecode).CopyTo(stream);
                break;

            case ProgramMode.Ascii:
                while (true)
                {
                    var output = Tokeniser.DetokeniseLine(Bytecode);
                    if (output.LineNumber == -1 || output.LineNumber > MaxLineNumber)
                    {
                        break;
                    }

                    stream.Write(output.Text);
                }
                break;
            }

            Bytecode.Seek(current, SeekOrigin.Begin);
        }