// Each line of BASIC program has the form : // - 2 bytes | Line number : more significant byte, less significant byte // - 2 bytes | Lenght of text + ENTER : less significant byte, more significant byte // - Text // - ENTER = 00001101 (13) private static void ReadProgramLines(Stream binaryMemoryStream, int programLength, BasicProgram program) { int programBytesCounter = 0; while (programBytesCounter < programLength) { int lineNumber = ReadLineNumber(binaryMemoryStream, ref programBytesCounter); int lengthOfTextPlusEnter = ReadInteger(binaryMemoryStream, ref programBytesCounter); BasicLine line = new BasicLine(lineNumber, lengthOfTextPlusEnter); for (int i = 0; i < lengthOfTextPlusEnter; i++) { SpectrumChar spectrumChar = ReadSpectrumChar(binaryMemoryStream, ref programBytesCounter); // A numerical constant in the program is followed by its binary form, // using the character CHR$ 14 followed by five bytes for the number itself. if (spectrumChar.Code == 14) { SpectrumNumber spectrumNumber = ReadSpectrumNumber(binaryMemoryStream, ref programBytesCounter); i += 5; spectrumChar = spectrumChar.CloneForNumber(spectrumNumber); } line.SpectrumChars[i] = spectrumChar; } // Check : a basic line should end with Enter char if (line.SpectrumChars[line.SpectrumChars.Length - 1].Code != 13) { throw new Exception("Basic line does not end with enter char"); } else { program.Lines.Add(line); } } }
// The variables have different formats according to their different features. // The letters in the names should be imagined as starting off in lower case. private static void ReadProgramVariables(Stream binaryMemoryStream, int variablesLength, BasicProgram program) { int variablesBytesCounter = 0; while(variablesBytesCounter < variablesLength) { byte varTypeAndFirstLetter = ReadByte(binaryMemoryStream, ref variablesBytesCounter); byte varType = (byte)(varTypeAndFirstLetter & 0xE0); byte firstLetterCode = (byte)((varTypeAndFirstLetter & 0x1F) + 0x60); string name = SpectrumCharSet.GetSpectrumChar(firstLetterCode).Text; switch (varType) { // Number with one letter name // - 0 1 1 _ _ _ _ _ : letter - 60H // - 5 bytes : value case 96: NumberVariable numVariable = new NumberVariable() { Name = name }; numVariable.Value = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); program.Variables.Add(numVariable); break; // Number whose name is longer than one letter // - 1 0 1 _ _ _ _ _ : letter - 60H // - 0 _ _ _ _ _ _ _ : 2nd character // ... // - 1 _ _ _ _ _ _ _ : last character // - 5 bytes : value case 160: byte charCode; while(((charCode = ReadByte(binaryMemoryStream, ref variablesBytesCounter)) & 0x80) == 0) { name += SpectrumCharSet.GetSpectrumChar(charCode).Text; } name += SpectrumCharSet.GetSpectrumChar((byte)(charCode-128)).Text; numVariable = new NumberVariable() { Name = name }; numVariable.Value = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); program.Variables.Add(numVariable); break; // Array of numbers // - 1 0 0 _ _ _ _ _ : letter - 60H // - 2 bytes : total lenght of elements & dimensions + 1 for no. of dimensions // - 1 byte : no. of dimensions // - 2 bytes : first dimension // ... // - 2 bytes : last dimension // - 5 bytes each : values // The order of the elements is: // first, the elements for which the first subscript is 1; // next, the elements for which the first subscript is 2; // next, the elements for which the first subscript is 3; // and so on for all possible values of the first subscript. // The elements with a given first subscript are ordered in the same way using the second subscript, and so on down to the last. // As an example, the elements of the 3*6 array b in Chapter 12 are stored in the order b(1,1) b(1,2) b(1,3) b(1,4) b(1,5) b(1,6) b(2,1) b(2,2) .... b(2,6) b(3,1) b(3,2) ... b(3,6). case 128: NumberArrayVariable numArrayVariable = new NumberArrayVariable() { Name = name }; ReadInteger(binaryMemoryStream, ref variablesBytesCounter); numArrayVariable.NumberOfDimensions = ReadByte(binaryMemoryStream, ref variablesBytesCounter); numArrayVariable.DimensionsSizes = new int[numArrayVariable.NumberOfDimensions]; int valuesCount = 1; for (int i = 0; i < numArrayVariable.NumberOfDimensions ; i++) { numArrayVariable.DimensionsSizes[i] = ReadInteger(binaryMemoryStream, ref variablesBytesCounter); valuesCount *= numArrayVariable.DimensionsSizes[i]; } for (int i = 0; i < valuesCount; i++) { SpectrumNumber value = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); numArrayVariable.Values.Add(value); } program.Variables.Add(numArrayVariable); break; // Control variable of a FOR-NEXT loop // - 1 1 1 _ _ _ _ _ : letter - 60H // - 5 bytes : value // - 5 bytes : limit // - 5 bytes : step // - 2 bytes : looping line, lsb | msb // - 1 byte : statement number whitin line case 224: ForLoopControlVariable forVariable = new ForLoopControlVariable() { Name = name }; forVariable.Value = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); forVariable.Limit = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); forVariable.Step = ReadSpectrumNumber(binaryMemoryStream, ref variablesBytesCounter); forVariable.LoopingLine = ReadInteger(binaryMemoryStream, ref variablesBytesCounter); forVariable.StatementNumberWithinLine = ReadByte(binaryMemoryStream, ref variablesBytesCounter); program.Variables.Add(forVariable); break; // String // - 0 1 0 _ _ _ _ _ : letter - 60H // - 2 bytes : number of characters // - text of string (may be empty) case 64: StringVariable strVariable = new StringVariable() { Name = name + "$" }; strVariable.CharsCount = ReadInteger(binaryMemoryStream, ref variablesBytesCounter); strVariable.Chars = new SpectrumChar[strVariable.CharsCount]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < strVariable.CharsCount; i++) { strVariable.Chars[i] = ReadSpectrumChar(binaryMemoryStream, ref variablesBytesCounter); sb.Append(strVariable.Chars[i].Text); } strVariable.Text = sb.ToString(); program.Variables.Add(strVariable); break; // Array of characters // - 1 1 0 _ _ _ _ _ : letter - 60H // - 2 bytes : total no. elements & dims + 1 for no. of dims // - 1 byte : no. of dimensions // - 2 bytes : first dimension // ... // - 2 bytes : last dimension // - 1 byte each : elements case 192: CharArrayVariable charArrayVariable = new CharArrayVariable() { Name = name + "$" }; ReadInteger(binaryMemoryStream, ref variablesBytesCounter); charArrayVariable.NumberOfDimensions = ReadByte(binaryMemoryStream, ref variablesBytesCounter); charArrayVariable.DimensionsSizes = new int[charArrayVariable.NumberOfDimensions]; valuesCount = 1; for (int i = 0; i < charArrayVariable.NumberOfDimensions; i++) { charArrayVariable.DimensionsSizes[i] = ReadInteger(binaryMemoryStream, ref variablesBytesCounter); valuesCount *= charArrayVariable.DimensionsSizes[i]; } for (int i = 0; i < valuesCount; i++) { SpectrumChar value = ReadSpectrumChar(binaryMemoryStream, ref variablesBytesCounter); charArrayVariable.Values.Add(value); } program.Variables.Add(charArrayVariable); break; default: throw new Exception("Uknown variable type"); } } }
/// <summary> /// Source : ZXBasicManual/zxmanchap24.html /// </summary> public static BasicProgram ReadMemoryFormat(string sourcePath, Stream binaryMemoryStream, int programLength, int variablesLength) { BasicProgram program = new BasicProgram(sourcePath); ReadProgramLines(binaryMemoryStream, programLength, program); ReadProgramVariables(binaryMemoryStream, variablesLength, program); return program; }